优化修复网站导航的bug

This commit is contained in:
2024-08-28 23:55:01 +08:00
parent 31ec8e057a
commit 2655874948
34 changed files with 1764 additions and 849 deletions

View File

@@ -141,3 +141,13 @@ export async function checkExistence(
}
return Promise.reject(new Error(res.data.message));
}
export async function getCount(params: ArticleParam) {
const res = await request.get(MODULES_API_URL + '/cms/article/data', {
params
});
if (res.data.code === 0) {
return res.data.data;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -66,7 +66,7 @@ export interface ArticleParam extends PageParam {
articleId?: number;
categoryId?: number;
navigationId?: number;
status?: string;
status?: number;
sortNumber?: string;
createTime?: string;
username?: string;

View File

@@ -4,13 +4,20 @@
export interface Navigation {
navigationId?: number;
parentId?: number;
parentName?: string;
parentPath?: string;
title?: string;
code?: string;
path?: string;
icon?: string;
component?: string;
model?: string;
modelName?: string;
type?: number;
sortNumber?: number;
hide?: number;
permission?: number;
password?: string;
home?: number;
position?: number;
top?: number;
@@ -31,6 +38,8 @@ export interface Navigation {
pageName?: string;
createTime?: string;
isMpWeixin?: boolean;
label?: string;
value?: number;
}
/**

View File

@@ -9,6 +9,8 @@ export interface WebsiteField {
value?: string;
comments?: string;
userId?: number;
defaultValue?: string;
modifyRange?: string;
type?: number;
status?: any;
sortNumber?: any;

View File

@@ -4,15 +4,15 @@ import type {
Config,
WebsiteField,
WebsiteFieldParam
} from "@/api/cms/website/field/model";
import { MODULES_API_URL } from '@/config/setting';
} from '@/api/cms/website/field/model';
import { MODULES_API_URL, SERVER_API_URL } from "@/config/setting";
/**
* 分页查询项目参数
*/
export async function pageWebsiteField(params: WebsiteFieldParam) {
const res = await request.get<ApiResult<PageResult<WebsiteField>>>(
MODULES_API_URL + '/cms/website-field/page',
SERVER_API_URL + '/system/website-field/page',
{
params
}
@@ -28,7 +28,7 @@ export async function pageWebsiteField(params: WebsiteFieldParam) {
*/
export async function listWebsiteField(params?: WebsiteFieldParam) {
const res = await request.get<ApiResult<WebsiteField[]>>(
'https://modules.gxwebsoft.com/api/cms/website-field',
SERVER_API_URL + '/system/website-field',
{
params
}
@@ -44,7 +44,7 @@ export async function listWebsiteField(params?: WebsiteFieldParam) {
*/
export async function getWebsiteField(id: number) {
const res = await request.get<ApiResult<WebsiteField>>(
MODULES_API_URL + '/cms/website-field/' + id
SERVER_API_URL + '/system/website-field/' + id
);
if (res.data.code === 0 && res.data.data) {
return res.data.data;
@@ -57,7 +57,7 @@ export async function getWebsiteField(id: number) {
*/
export async function addWebsiteField(data: WebsiteField) {
const res = await request.post<ApiResult<unknown>>(
MODULES_API_URL + '/cms/website-field',
SERVER_API_URL + '/system/website-field',
data
);
if (res.data.code === 0) {
@@ -71,7 +71,7 @@ export async function addWebsiteField(data: WebsiteField) {
*/
export async function updateWebsiteField(data: WebsiteField) {
const res = await request.put<ApiResult<unknown>>(
MODULES_API_URL + '/cms/website-field',
SERVER_API_URL + '/system/website-field',
data
);
if (res.data.code === 0) {
@@ -85,7 +85,7 @@ export async function updateWebsiteField(data: WebsiteField) {
*/
export async function removeWebsiteField(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/website-field/' + id
SERVER_API_URL + '/system/website-field/' + id
);
if (res.data.code === 0) {
return res.data.message;
@@ -98,7 +98,7 @@ export async function removeWebsiteField(id?: number) {
*/
export async function removeBatchWebsiteField(data: (number | undefined)[]) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/website-field/batch',
SERVER_API_URL + '/system/website-field/batch',
{
data
}
@@ -118,7 +118,7 @@ export async function checkExistence(
id?: number
) {
const res = await request.get<ApiResult<unknown>>(
MODULES_API_URL + '/cms/website-field/existence',
SERVER_API_URL + '/system/website-field/existence',
{
params: { field, value, id }
}
@@ -134,7 +134,7 @@ export async function checkExistence(
*/
export async function configWebsiteField(params?: WebsiteFieldParam) {
const res = await request.get<ApiResult<Config>>(
'https://modules.gxwebsoft.com/api/cms/website-field/config',
'https://modules.gxwebsoft.com/api/system/website-field/config',
{
params
}
@@ -144,3 +144,16 @@ export async function configWebsiteField(params?: WebsiteFieldParam) {
}
return Promise.reject(new Error(res.data.message));
}
/**
* 恢复项目参数
*/
export async function undeleteWebsiteField(id?: number) {
const res = await request.delete<ApiResult<unknown>>(
MODULES_API_URL + '/cms/website-field/undelete/' + id
);
if (res.data.code === 0) {
return res.data.message;
}
return Promise.reject(new Error(res.data.message));
}

View File

@@ -9,10 +9,31 @@ export interface WebsiteField {
value?: string;
comments?: string;
userId?: number;
websiteId?: number;
defaultValue?: string;
modifyRange?: string;
type?: number;
status?: any;
sortNumber?: any;
createTime?: string;
deleted?: number;
}
// 约定的网站参数名称
export interface WebsiteParam {
// 网站名称
site_logo?: string;
// 登录页面标题
login_name?: string;
// 登录页面的背景图片
login_bg_img?: string;
}
// 约定的小程序参数名称
export interface MpWeixinParam {
// 小程序LOGO
site_logo?: string;
// 我的页面顶部背景图片
mp_user_top?: string;
}
/**
@@ -21,5 +42,22 @@ export interface WebsiteField {
export interface WebsiteFieldParam extends PageParam {
id?: number;
userId?: number;
name?: string;
websiteId?: number;
}
export interface Config {
siteName?: string;
siteLogo?: string;
domain?: string;
icpNo?: string;
copyright?: string;
loginBgImg?: string;
address?: string;
tel?: string;
kefu2?: string;
kefu1?: string;
email?: string;
loginTitle?: string;
sysLogo?: string;
}

View File

@@ -0,0 +1,72 @@
<!-- 公共参数数据源 -->
<template>
<a-select
:allow-clear="allowClear"
show-search
optionFilterProp="label"
:options="data"
:value="value"
class="w-full"
:placeholder="placeholder"
@update:value="updateValue"
@change="change"
@blur="onBlur"
/>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { listDictionaryData } from '@/api/system/dictionary-data';
import type { DictionaryData } from '@/api/system/dictionary-data/model';
const emit = defineEmits<{
(e: 'update:value', value: string): void;
(e: 'index', index: number): void;
(e: 'blur'): void;
(e: 'done', item: DictionaryData): void;
}>();
const props = withDefaults(
defineProps<{
value?: string;
placeholder?: string;
allowClear?: boolean;
width?: number;
index?: number;
}>(),
{
placeholder: '请选择'
}
);
const data = ref<DictionaryData[]>();
/* 更新选中数据 */
const updateValue = (value: string) => {
emit('update:value', value);
emit('index', Number(props.index));
};
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
const change = (e: any, item: any) => {
emit('done', item);
};
const reload = () => {
data.value = [];
listDictionaryData({ dictCode: 'NavigationModel' }).then((list) => {
data.value = list.map((d) => {
return {
label: d.dictDataName,
value: d.dictDataCode
};
});
});
};
reload();
</script>

View File

@@ -0,0 +1,72 @@
<!-- 公共参数数据源 -->
<template>
<a-select
:allow-clear="allowClear"
show-search
optionFilterProp="label"
:options="data"
:value="value"
class="w-full"
:placeholder="placeholder"
@update:value="updateValue"
@change="change"
@blur="onBlur"
/>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { listWebsiteField } from '@/api/system/website/field';
import { WebsiteField } from '@/api/cms/website/field/model';
const emit = defineEmits<{
(e: 'update:value', value: string): void;
(e: 'index', index: number): void;
(e: 'blur'): void;
(e: 'done', item: WebsiteField): void;
}>();
const props = withDefaults(
defineProps<{
value?: string;
placeholder?: string;
allowClear?: boolean;
width?: number;
index?: number;
}>(),
{
placeholder: '请选择'
}
);
const data = ref<WebsiteField[]>();
/* 更新选中数据 */
const updateValue = (value: string) => {
emit('update:value', value);
emit('index', Number(props.index));
};
/* 失去焦点 */
const onBlur = () => {
emit('blur');
};
const change = (e, item) => {
emit('done', item);
};
const reload = () => {
data.value = [];
listWebsiteField({}).then((list) => {
data.value = list.map((d) => {
return {
label: d.name,
value: d.value
};
});
});
};
reload();
</script>

View File

@@ -48,9 +48,8 @@
} from 'ele-admin-pro/es/ele-pro-table/types';
import { EleProTable } from 'ele-admin-pro';
import useSearch from '@/utils/use-search';
import { pageDictData } from '@/api/system/dict-data';
import { DictData, DictDataParam } from '@/api/system/dict-data/model';
import { pageDictionaryData } from "@/api/system/dictionary-data";
import { pageDictionaryData } from '@/api/system/dictionary-data';
const props = defineProps<{
// 弹窗是否打开

View File

@@ -47,9 +47,23 @@
</a-space>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'title'">
<a-button
size="small"
type="link"
@click="onRadio"
:disabled="record.type == model"
class="cursor-pointer"
>{{ record.title }}</a-button
>
</template>
<template v-if="column.key === 'action'">
<template v-if="pageId == record.id">
<a-radio v-model:checked="checked" @click="onRadio(record)" />
<a-radio
v-model:checked="checked"
disabled
@click="onRadio(record)"
/>
</template>
<template v-else>
<lebal>
@@ -65,14 +79,16 @@
<script lang="ts" setup>
import { ref } from 'vue';
import {
ColumnItem,
DatasourceFunction
} from 'ele-admin-pro/es/ele-pro-table/types';
import { listNavigation, pageNavigation } from '@/api/cms/navigation';
import { EleProTable } from 'ele-admin-pro';
import { toTreeData } from 'ele-admin-pro/es';
import { toTreeData, eachTreeData } from 'ele-admin-pro/es';
import type {
DatasourceFunction,
ColumnItem,
EleProTableDone
} from 'ele-admin-pro/es/ele-pro-table/types';
import type { Navigation, NavigationParam } from '@/api/cms/navigation/model';
import type { Menu } from '@/api/system/menu/model';
const props = defineProps<{
// 弹窗是否打开
@@ -81,6 +97,8 @@
title?: string;
// 修改回显的数据
data?: Navigation | null;
// 当前模型
model?: string;
}>();
const emit = defineEmits<{
@@ -100,6 +118,8 @@
const expandedRowKeys = ref<number[]>([]);
const searchText = ref('');
const position = ref(1);
// 菜单数据
const menuData = ref<Navigation[]>([]);
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -137,9 +157,13 @@
return listNavigation({ ...where });
};
/* 表格渲染完成回调 */
const onDone: EleProTableDone<Navigation> = ({ data }) => {
menuData.value = data;
};
/* 数据转为树形结构 */
const parseData = (data: Navigation[]) => {
console.log(data);
return toTreeData({
data: data.map((d) => {
return { ...d, key: d.navigationId, value: d.navigationId };
@@ -149,6 +173,22 @@
});
};
/* 展开全部 */
const expandAll = () => {
let keys: number[] = [];
eachTreeData(menuData.value, (d) => {
if (d.children && d.children.length && d.navigationId) {
keys.push(d.navigationId);
}
});
expandedRowKeys.value = keys;
};
/* 折叠全部 */
const foldAll = () => {
expandedRowKeys.value = [];
};
/* 点击展开图标时触发 */
const onExpand = (expanded: boolean, record: Navigation) => {
if (expanded) {

View File

@@ -16,6 +16,7 @@
v-model:visible="showEdit"
:data="current"
:title="placeholder"
:model="model"
@done="onChange"
/>
</div>
@@ -31,6 +32,7 @@
defineProps<{
value?: any;
placeholder?: string;
model?: string;
}>(),
{
placeholder: '请选择'

View File

@@ -0,0 +1,170 @@
<template>
<ele-modal
width="70%"
:visible="visible"
:maskClosable="false"
:title="title"
:footer="null"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
>
<ele-pro-table
ref="tableRef"
row-key="id"
:datasource="datasource"
:columns="columns"
:customRow="customRow"
:pagination="false"
>
<template #toolbar>
<a-input-search
allow-clear
v-model:value="searchText"
placeholder="请输入搜索关键词"
style="width: 200px"
@search="reload"
@pressEnter="reload"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div class="ele-text-heading" @click="onRadio(record)">
{{ record.name }}
</div>
<div class="ele-text-placeholder">{{ record.comments }}</div>
</template>
<template v-if="column.key === 'value'">
<a-image v-if="record.type === 1" :src="record.value" :width="120" />
<div v-else>{{ record.value }}</div>
</template>
<template v-if="column.key === 'comments'">
<a-popover>
<template #content>
{{ record.comments }}
</template>
<ExclamationCircleOutlined />
</a-popover>
</template>
<template v-if="column.key === 'action'">
<a @click="onRadio(record)">选择</a>
</template>
</template>
</ele-pro-table>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import {
ColumnItem,
DatasourceFunction
} from 'ele-admin-pro/es/ele-pro-table/types';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { EleProTable } from 'ele-admin-pro';
import { Company, CompanyParam } from '@/api/oa/company/model';
import { pageWebsiteField } from '@/api/system/website/field';
import { WebsiteField } from '@/api/system/website/field/model';
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
// 标题
title?: string;
// 修改回显的数据
data?: WebsiteField | null;
}>();
const emit = defineEmits<{
(e: 'done', data: Company): void;
(e: 'update:visible', visible: boolean): void;
}>();
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
// 搜索内容
const searchText = ref(null);
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格配置
const columns = ref<ColumnItem[]>([
{
title: '参数名',
dataIndex: 'name',
width: 180,
key: 'name'
},
{
title: '描述',
dataIndex: 'comments',
key: 'comments',
width: 120,
align: 'center'
},
{
title: '默认值',
width: 120,
align: 'center',
dataIndex: 'defaultValue'
},
{
title: '可设置范围',
width: 120,
align: 'center',
dataIndex: 'modifyRange'
},
{
title: '操作',
key: 'action',
width: 180,
align: 'center',
hideInSetting: true
}
]);
// 表格数据源
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
where = {};
// 搜索条件
if (searchText.value) {
where.keywords = searchText.value;
}
return pageWebsiteField({
...where,
...orders,
page,
limit
});
};
/* 搜索 */
const reload = (where?: CompanyParam) => {
tableRef?.value?.reload({ page: 1, where });
};
const onRadio = (record: Company) => {
updateVisible(false);
emit('done', record);
};
/* 自定义行属性 */
const customRow = (record: Company) => {
return {
// 行点击事件
// onClick: () => {
// updateVisible(false);
// emit('done', record);
// },
// 行双击事件
onDblclick: () => {
updateVisible(false);
emit('done', record);
}
};
};
</script>
<style lang="less"></style>

View File

@@ -0,0 +1,61 @@
<template>
<div>
<a-input-group compact>
<a-input
disabled
style="width: calc(100% - 32px)"
v-model:value="value"
:placeholder="placeholder"
/>
<a-button @click="openEdit">
<template #icon><BulbOutlined class="ele-text-warning" /></template>
</a-button>
</a-input-group>
<!-- 选择弹窗 -->
<SelectData
v-model:visible="showEdit"
:data="current"
:title="placeholder"
:customer-type="customerType"
@done="onChange"
/>
</div>
</template>
<script lang="ts" setup>
import { BulbOutlined } from '@ant-design/icons-vue';
import { ref } from 'vue';
import SelectData from './components/select-data.vue';
import { Company } from '@/api/system/company/model';
withDefaults(
defineProps<{
value?: any;
customerType?: string;
placeholder?: string;
}>(),
{
placeholder: '请选择数据'
}
);
const emit = defineEmits<{
(e: 'done', Customer): void;
(e: 'clear'): void;
}>();
// 是否显示编辑弹窗
const showEdit = ref(false);
// 当前编辑数据
const current = ref<Company | null>(null);
/* 打开编辑弹窗 */
const openEdit = (row?: Company) => {
current.value = row ?? null;
showEdit.value = true;
};
const onChange = (row) => {
emit('done', row);
};
</script>

View File

@@ -30,16 +30,16 @@ export const routes = [
// component: () => import('@/views/passport/token-login/index.vue'),
// meta: { title: '快捷登录' }
// },
{
path: '/cms/category/:id',
component: () => import('@/views/cms/category/preview/index.vue'),
meta: { title: '文章列表' }
},
{
path: '/cms/article/:id',
component: () => import('@/views/cms/article/preview/index.vue'),
meta: { title: '文章详情' }
},
// {
// path: '/cms/category/:id',
// component: () => import('@/views/cms/category/preview/index.vue'),
// meta: { title: '文章列表' }
// },
// {
// path: '/cms/article/:id',
// component: () => import('@/views/cms/article/preview/index.vue'),
// meta: { title: '文章详情' }
// },
// 404
{
path: '/:path(.*)*',

View File

@@ -21,13 +21,17 @@
>
<a-tabs type="card" v-model:active-key="active" @change="onChange">
<a-tab-pane tab="基本信息" key="base">
<a-form-item label="选择栏目" name="categoryId">
<SelectNavigsation
:data="data"
placeholder="请选择栏目"
<a-form-item label="文章标题" name="categoryId">
<a-tree-select
allow-clear
:tree-data="navigationList"
tree-default-expand-all
style="width: 558px"
v-model:value="form.categoryName"
@done="chooseCategory"
placeholder="请选择栏目"
:value="form.categoryId || undefined"
:dropdown-style="{ maxHeight: '360px', overflow: 'auto' }"
@update:value="(value?: number) => (form.categoryId = value)"
@change="onCategoryId"
/>
</a-form-item>
<a-form-item label="文章标题" name="title">
@@ -177,6 +181,8 @@
data?: Article | null;
// ID
merchantId?: number;
//
navigationList?: Navigation[];
}>();
const emit = defineEmits<{
@@ -340,14 +346,13 @@
form.image = data.path;
};
const chooseCategory = (data: Navigation) => {
form.categoryName = data.title
form.categoryId = data.navigationId
}
//
const onCategoryId = (id: number) => {
form.categoryId = id;
};
const onChange = (text: string) => {
const onChange = () => {
//
};
const onDeleteItem = (index: number) => {

View File

@@ -9,27 +9,25 @@
</a-button>
<a-radio-group v-model:value="type" @change="handleSearch">
<a-radio-button value="已发布"
>已发布({{ goodsCount?.totalNum }})</a-radio-button
>已发布({{ articleCount?.totalNum }})</a-radio-button
>
<a-radio-button value="待审核"
>待审核({{ goodsCount?.totalNum2 }})</a-radio-button
>待审核({{ articleCount?.totalNum2 }})</a-radio-button
>
<a-radio-button value="已驳回"
>已驳回({{ goodsCount?.totalNum3 }})</a-radio-button
>已驳回({{ articleCount?.totalNum3 }})</a-radio-button
>
</a-radio-group>
<!-- <SelectMerchant-->
<!-- :placeholder="`选择商户`"-->
<!-- class="input-item"-->
<!-- v-if="!merchantId"-->
<!-- v-model:value="where.merchantName"-->
<!-- @done="chooseMerchantId"-->
<!-- />-->
<SelectNavigsation
class="input-item"
:placeholder="`选择栏目`"
v-model:value="where.categoryName"
@done="chooseArticleCategory"
<a-tree-select
allow-clear
:tree-data="navigationList"
tree-default-expand-all
style="width: 230px"
placeholder="请选择栏目"
:value="where.categoryId || undefined"
:dropdown-style="{ maxHeight: '360px', overflow: 'auto' }"
@update:value="(value?: number) => (where.categoryId = value)"
@change="onCategoryId"
/>
<a-input-search
allow-clear
@@ -45,10 +43,9 @@
<script lang="ts" setup>
import { PlusOutlined } from '@ant-design/icons-vue';
import { ref, watch } from 'vue';
import { getCount } from '@/api/shop/goods';
import { getCount } from '@/api/cms/article';
import type { ArticleCount, ArticleParam } from '@/api/cms/article/model';
import useSearch from '@/utils/use-search';
import { Merchant } from '@/api/shop/merchant/model';
import { Navigation } from '@/api/cms/navigation/model';
const props = withDefaults(
@@ -56,13 +53,15 @@
//
selection?: [];
merchantId?: number;
navigationList?: Navigation[];
model?: string;
}>(),
{}
);
const type = ref<string>();
//
const goodsCount = ref<ArticleCount>();
const articleCount = ref<ArticleCount>();
//
const { where, resetFields } = useSearch<ArticleParam>({
@@ -70,6 +69,7 @@
navigationId: undefined,
categoryId: undefined,
merchantId: undefined,
status: undefined,
keywords: ''
});
@@ -89,10 +89,10 @@
const text = e.target.value;
resetFields();
if (text === '已发布') {
where.status = 1;
where.status = 0;
}
if (text === '待审核') {
where.status = 0;
where.status = 1;
}
if (text === '已驳回') {
where.status = 2;
@@ -102,22 +102,15 @@
const reload = () => {
getCount(where).then((data: any) => {
goodsCount.value = data;
articleCount.value = data;
});
emit('search', where);
};
/* 搜索 */
const chooseMerchantId = (item: Merchant) => {
where.merchantName = item.merchantName;
where.merchantId = item.merchantId;
reload();
};
const chooseArticleCategory = (category: Navigation) => {
where.categoryName = category.title;
where.navigationId = category.navigationId;
reload();
//
const onCategoryId = (id: number) => {
where.categoryId = id;
emit('search', where);
};
/* 重置 */
@@ -127,10 +120,6 @@
reload();
};
// watch(
// () => props.selection,
// () => {}
// );
watch(
() => props.merchantId,
(id) => {

View File

@@ -1,220 +1,292 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-split-layout
width="266px"
allow-collapse
:right-style="{ overflow: 'hidden' }"
:style="{ minHeight: 'calc(100vh - 152px)' }"
<ele-pro-table
ref="tableRef"
row-key="articleId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:scroll="{ x: 1200 }"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<div>
<ele-toolbar theme="default">
<a-space :size="10">
<a-button
type="primary"
:size="`small`"
class="ele-btn-icon"
@click="openEdit()"
>
<template #icon>
<plus-outlined />
<template #toolbar>
<search
@search="reload"
:selection="selection"
:navigationList="navigationList"
:merchantId="merchantId"
:model="model"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
</a-button>
<a-button
type="primary"
:size="`small`"
:disabled="!current"
class="ele-btn-icon"
@click="openEdit(current)"
>
<template #icon>
<edit-outlined />
</template>
</a-button>
<a-button
danger
type="primary"
:size="`small`"
:disabled="!current"
class="ele-btn-icon"
@click="remove"
>
<template #icon>
<delete-outlined />
</template>
</a-button>
</a-space>
</ele-toolbar>
<div class="ele-border-split sys-category-list">
<a-tree
:tree-data="data"
v-model:expanded-keys="expandedRowKeys"
v-model:selected-keys="selectedRowKeys"
@select="onTreeSelect"
>
<template #title="{ type, key: treeKey, title }">
<a-dropdown :trigger="['contextmenu']">
<span>{{ title }}</span>
<template #overlay v-if="type == 0">
<a-menu
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'title'">
<a
@click="
({ key: menuKey }) =>
onContextMenuClick(treeKey, menuKey)
openSpmUrl(
`/article/detail/${record.articleId}.html`,
record,
record.articleId
)
"
>{{ record.title }}</a
>
<a-menu-item key="1">修改</a-menu-item>
<a-menu-item key="2">删除</a-menu-item>
</a-menu>
</template>
</a-dropdown>
<template v-if="column.key === 'type'">
<a-tag v-if="record.type === 0">虚拟文章</a-tag>
<a-tag v-if="record.type === 1">实物文章</a-tag>
</template>
</a-tree>
</div>
</div>
<template #content>
<list
v-if="current"
:category-list="data"
:data="current"
:category-id="current.categoryId"
/>
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="80" />
</template>
</ele-split-layout>
<template v-if="column.key === 'salePrice'">
{{ formatNumber(record.salePrice) }}
</template>
<template v-if="column.key === 'status'">
<a-tag
:color="record.status == 0 ? 'green' : 'red'"
class="cursor-pointer"
@click="onUpdate(record)"
>{{ record.statusText }}</a-tag
>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<category-edit
<ArticleEdit
v-model:visible="showEdit"
:data="editData"
:category-list="data"
:category-id="current?.categoryId"
@done="query"
:navigationList="navigationList"
:data="current"
@done="reload"
/>
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { createVNode, ref, watch } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import ArticleEdit from './components/articleEdit.vue';
import {
PlusOutlined,
EditOutlined,
DeleteOutlined,
ExclamationCircleOutlined
} from '@ant-design/icons-vue';
import { toTreeData, eachTreeData } from 'ele-admin-pro';
import List from './list.vue';
import CategoryEdit from './components/category-edit.vue';
import {
listArticleCategory,
removeArticleCategory
} from '@/api/cms/category';
import { ArticleCategory } from '@/api/cms/category/model';
pageArticle,
removeArticle,
removeBatchArticle
} from '@/api/cms/article';
import type { Article, ArticleParam } from '@/api/cms/article/model';
import { formatNumber } from 'ele-admin-pro/es';
import router from '@/router';
import { toTreeData } from 'ele-admin-pro';
import { openSpmUrl } from '@/utils/common';
import { getSiteDomain } from '@/utils/domain';
import { listNavigation } from '@/api/cms/navigation';
import { Navigation } from '@/api/cms/navigation/model';
// 加载状态
const loading = ref(true);
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 树形数据
const data = ref<ArticleCategory[]>([]);
// 树展开的key
const expandedRowKeys = ref<number[]>([]);
// 树选中的key
const selectedRowKeys = ref<number[]>([]);
// 选中数据
const current = ref<ArticleCategory | any>(null);
// 是否显示表单弹窗
// 表格选中数据
const selection = ref<Article[]>([]);
// 当前编辑数据
const current = ref<Article | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 店铺ID
const merchantId = ref<number>();
// 栏目ID
const categoryId = ref<number>();
// 当前模型
const model = ref<number>();
// 网站域名
const domain = getSiteDomain();
// 随机数
const token = ref<any>('');
// 栏目数据
const navigationList = ref<Navigation[]>();
// 编辑回显数据
const editData = ref<ArticleCategory | null>(null);
// 表格数据
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
if (categoryId.value) {
where.categoryId = categoryId.value;
}
return pageArticle({
...where,
...orders,
page,
limit
});
};
/* 查询 */
const query = () => {
loading.value = true;
listArticleCategory()
.then((list) => {
loading.value = false;
const eks: number[] = [];
list.forEach((d) => {
d.key = d.categoryId;
d.value = d.categoryId;
if (typeof d.categoryId === 'number') {
eks.push(d.categoryId);
}
});
expandedRowKeys.value = eks;
data.value = toTreeData({
data: list.map((d) => {
d.disabled = d.type != 0;
return d;
}),
idField: 'categoryId',
parentIdField: 'parentId'
});
if (list.length) {
if (typeof list[0].categoryId === 'number') {
selectedRowKeys.value = [list[0].categoryId];
}
current.value = list[0];
} else {
selectedRowKeys.value = [];
current.value = null;
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'articleId',
key: 'articleId',
align: 'center',
width: 90
},
{
title: '文章标题',
dataIndex: 'title',
key: 'title'
},
{
title: '封面图',
dataIndex: 'image',
key: 'image',
width: 120,
align: 'center'
},
{
title: '所属栏目',
dataIndex: 'categoryId',
key: 'categoryId',
align: 'center',
hideInTable: true
},
{
title: '实际阅读量',
dataIndex: 'actualViews',
key: 'actualViews',
sorter: true,
width: 120,
align: 'center'
},
{
title: '虚拟阅读量',
dataIndex: 'virtualViews',
key: 'virtualViews',
width: 120,
align: 'center'
},
{
title: '推荐',
dataIndex: 'recommend',
key: 'recommend',
sorter: true,
align: 'center',
hideInTable: true
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
sorter: true,
width: 120,
align: 'center'
},
{
title: '排序号',
dataIndex: 'sortNumber',
key: 'sortNumber',
sorter: true,
align: 'center',
hideInTable: true
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: ArticleParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: Article) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
const onUpdate = (row?: Article) => {
// const isShow = row?.isShow == 0 ? 1 : 0;
// updateArticle({ ...row, isShow }).then((msg) => {
// message.success(msg);
// reload();
// });
};
/* 删除单个 */
const remove = (row: Article) => {
const hide = message.loading('请求中..', 0);
removeArticle(row.articleId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
loading.value = false;
hide();
message.error(e.message);
});
};
/* 选择数据 */
const onTreeSelect = () => {
current.value = {};
eachTreeData(data.value, (d) => {
if (
typeof d.categoryId === 'number' &&
selectedRowKeys.value.includes(d.categoryId)
) {
current.value = d;
return false;
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
});
};
const onContextMenuClick = (treeKey: number, menuKey: number) => {
// 修改
if (menuKey == 1) {
openEdit(current.value);
}
// 删除
if (menuKey == 2) {
remove();
}
};
/* 打开编辑弹窗 */
const openEdit = (item?: ArticleCategory | null) => {
editData.value = item ?? null;
showEdit.value = true;
};
/* 删除 */
const remove = () => {
Modal.confirm({
title: '提示',
content: '确定要删除选中的分类吗?',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeArticleCategory(current.value?.categoryId)
removeBatchArticle(selection.value.map((d) => d.articleId))
.then((msg) => {
hide();
message.success(msg);
query();
reload();
})
.catch((e) => {
hide();
@@ -224,21 +296,55 @@
});
};
query();
/* 自定义行属性 */
const customRow = (record: Article) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
// 加载栏目数据
if (!navigationList.value) {
listNavigation({}).then((res) => {
navigationList.value = toTreeData({
data: res?.map((d) => {
d.value = d.navigationId;
d.label = d.title;
if (d.type != 2) {
d.disabled = true;
}
return d;
}),
idField: 'navigationId',
parentIdField: 'parentId'
});
});
}
watch(
() => router.currentRoute.value.query,
(query) => {
if (query) {
categoryId.value = Number(query.categoryId);
model.value = Number(query.type);
reload();
}
},
{ immediate: true }
);
</script>
<script lang="ts">
export default {
name: 'ArticleIndex'
name: 'ArticleV2'
};
</script>
<style lang="less" scoped>
.sys-category-list {
padding: 12px 6px;
height: calc(100vh - 242px);
border-width: 1px;
border-style: solid;
overflow: auto;
}
</style>
<style lang="less" scoped></style>

View File

@@ -0,0 +1,244 @@
<template>
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-split-layout
width="266px"
allow-collapse
:right-style="{ overflow: 'hidden' }"
:style="{ minHeight: 'calc(100vh - 152px)' }"
>
<div>
<ele-toolbar theme="default">
<a-space :size="10">
<a-button
type="primary"
:size="`small`"
class="ele-btn-icon"
@click="openEdit()"
>
<template #icon>
<plus-outlined />
</template>
</a-button>
<a-button
type="primary"
:size="`small`"
:disabled="!current"
class="ele-btn-icon"
@click="openEdit(current)"
>
<template #icon>
<edit-outlined />
</template>
</a-button>
<a-button
danger
type="primary"
:size="`small`"
:disabled="!current"
class="ele-btn-icon"
@click="remove"
>
<template #icon>
<delete-outlined />
</template>
</a-button>
</a-space>
</ele-toolbar>
<div class="ele-border-split sys-category-list">
<a-tree
:tree-data="data"
v-model:expanded-keys="expandedRowKeys"
v-model:selected-keys="selectedRowKeys"
@select="onTreeSelect"
>
<template #title="{ type, key: treeKey, title }">
<a-dropdown :trigger="['contextmenu']">
<span>{{ title }}</span>
<template #overlay v-if="type == 0">
<a-menu
@click="
({ key: menuKey }) =>
onContextMenuClick(treeKey, menuKey)
"
>
<a-menu-item key="1">修改</a-menu-item>
<a-menu-item key="2">删除</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</template>
</a-tree>
</div>
</div>
<template #content>
<list
v-if="current"
:category-list="data"
:data="current"
:category-id="current.categoryId"
/>
</template>
</ele-split-layout>
</a-card>
<!-- 编辑弹窗 -->
<category-edit
v-model:visible="showEdit"
:data="editData"
:category-list="data"
:category-id="current?.categoryId"
@done="query"
/>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref } from 'vue';
import { message, Modal } from 'ant-design-vue';
import {
PlusOutlined,
EditOutlined,
DeleteOutlined,
ExclamationCircleOutlined
} from '@ant-design/icons-vue';
import { toTreeData, eachTreeData } from 'ele-admin-pro';
import List from './list.vue';
import CategoryEdit from './components/category-edit.vue';
import {
listArticleCategory,
removeArticleCategory
} from '@/api/cms/category';
import { ArticleCategory } from '@/api/cms/category/model';
// 加载状态
const loading = ref(true);
// 树形数据
const data = ref<ArticleCategory[]>([]);
// 树展开的key
const expandedRowKeys = ref<number[]>([]);
// 树选中的key
const selectedRowKeys = ref<number[]>([]);
// 选中数据
const current = ref<ArticleCategory | any>(null);
// 是否显示表单弹窗
const showEdit = ref(false);
// 编辑回显数据
const editData = ref<ArticleCategory | null>(null);
/* 查询 */
const query = () => {
loading.value = true;
listArticleCategory()
.then((list) => {
loading.value = false;
const eks: number[] = [];
list.forEach((d) => {
d.key = d.categoryId;
d.value = d.categoryId;
if (typeof d.categoryId === 'number') {
eks.push(d.categoryId);
}
});
expandedRowKeys.value = eks;
data.value = toTreeData({
data: list.map((d) => {
d.disabled = d.type != 0;
return d;
}),
idField: 'categoryId',
parentIdField: 'parentId'
});
if (list.length) {
if (typeof list[0].categoryId === 'number') {
selectedRowKeys.value = [list[0].categoryId];
}
current.value = list[0];
} else {
selectedRowKeys.value = [];
current.value = null;
}
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
};
/* 选择数据 */
const onTreeSelect = () => {
current.value = {};
eachTreeData(data.value, (d) => {
if (
typeof d.categoryId === 'number' &&
selectedRowKeys.value.includes(d.categoryId)
) {
current.value = d;
return false;
}
});
};
const onContextMenuClick = (treeKey: number, menuKey: number) => {
// 修改
if (menuKey == 1) {
openEdit(current.value);
}
// 删除
if (menuKey == 2) {
remove();
}
};
/* 打开编辑弹窗 */
const openEdit = (item?: ArticleCategory | null) => {
editData.value = item ?? null;
showEdit.value = true;
};
/* 删除 */
const remove = () => {
Modal.confirm({
title: '提示',
content: '确定要删除选中的分类吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeArticleCategory(current.value?.categoryId)
.then((msg) => {
hide();
message.success(msg);
query();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
query();
</script>
<script lang="ts">
export default {
name: 'ArticleIndex'
};
</script>
<style lang="less" scoped>
.sys-category-list {
padding: 12px 6px;
height: calc(100vh - 242px);
border-width: 1px;
border-style: solid;
overflow: auto;
}
</style>

View File

@@ -34,7 +34,7 @@
* 加载数据
*/
const reload = (id: number) => {
console.log(id);
console.log(id,'0000');
//
getArticle(id).then((data) => {
assignFields(data);

View File

@@ -1,324 +0,0 @@
<template>
<div class="page">
<div class="ele-body">
<a-card :bordered="false" :body-style="{ padding: '16px' }">
<ele-pro-table
ref="tableRef"
row-key="articleId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:scroll="{ x: 1200 }"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<search
@search="reload"
:selection="selection"
:merchantId="merchantId"
@add="openEdit"
@remove="removeBatch"
@batchMove="openMove"
/>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'title'">
<a
@click="
openSpmUrl(
`/article/detail/${record.articleId}.html`,
record,
record.articleId
)
"
>{{ record.title }}</a
>
</template>
<template v-if="column.key === 'type'">
<a-tag v-if="record.type === 0">虚拟文章</a-tag>
<a-tag v-if="record.type === 1">实物文章</a-tag>
</template>
<template v-if="column.key === 'image'">
<a-image :src="record.image" :width="80" />
</template>
<template v-if="column.key === 'salePrice'">
{{ formatNumber(record.salePrice) }}
</template>
<template v-if="column.key === 'status'">
<a-tag
:color="record.status == 0 ? 'green' : 'red'"
class="cursor-pointer"
@click="onUpdate(record)"
>{{ record.statusText }}</a-tag
>
</template>
<template v-if="column.key === 'action'">
<a-space>
<a @click="openEdit(record)">修改</a>
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</a-space>
</template>
</template>
</ele-pro-table>
</a-card>
<!-- 编辑弹窗 -->
<ArticleEdit v-model:visible="showEdit" :data="current" @done="reload" />
</div>
</div>
</template>
<script lang="ts" setup>
import { createVNode, ref, watch } from 'vue';
import { message, Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import type { EleProTable } from 'ele-admin-pro';
import { uuid } from 'ele-admin-pro';
import type {
DatasourceFunction,
ColumnItem
} from 'ele-admin-pro/es/ele-pro-table/types';
import Search from './components/search.vue';
import ArticleEdit from './components/articleEdit.vue';
import {
pageArticle,
removeArticle,
removeBatchArticle
} from '@/api/cms/article';
import type { Article, ArticleParam } from '@/api/cms/article/model';
import { formatNumber } from 'ele-admin-pro/es';
import router from '@/router';
import { openSpmUrl, openUrl, openUrlSpm } from '@/utils/common';
import { getSiteDomain } from '@/utils/domain';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
// 表格选中数据
const selection = ref<Article[]>([]);
// 当前编辑数据
const current = ref<Article | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 是否显示批量移动弹窗
const showMove = ref(false);
// 店铺ID
const merchantId = ref<number>();
// 网站域名
const domain = getSiteDomain();
// 随机数
const token = ref<any>('');
// 表格数据源
const datasource: DatasourceFunction = ({
page,
limit,
where,
orders,
filters
}) => {
if (filters) {
where.status = filters.status;
}
if (merchantId.value) {
where.merchantId = merchantId.value;
}
return pageArticle({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<ColumnItem[]>([
{
title: 'ID',
dataIndex: 'articleId',
key: 'articleId',
align: 'center',
width: 90
},
{
title: '文章标题',
dataIndex: 'title',
key: 'title'
},
{
title: '封面图',
dataIndex: 'image',
key: 'image',
width: 120,
align: 'center'
},
{
title: '所属栏目',
dataIndex: 'categoryId',
key: 'categoryId',
align: 'center',
hideInTable: true
},
{
title: '实际阅读量',
dataIndex: 'actualViews',
key: 'actualViews',
sorter: true,
width: 120,
align: 'center'
},
{
title: '虚拟阅读量',
dataIndex: 'virtualViews',
key: 'virtualViews',
width: 120,
align: 'center'
},
{
title: '推荐',
dataIndex: 'recommend',
key: 'recommend',
sorter: true,
align: 'center',
hideInTable: true
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
sorter: true,
width: 120,
align: 'center'
},
{
title: '排序号',
dataIndex: 'sortNumber',
key: 'sortNumber',
sorter: true,
align: 'center',
hideInTable: true
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
align: 'center',
width: 180,
sorter: true
},
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
align: 'center',
hideInSetting: true
}
]);
/* 搜索 */
const reload = (where?: ArticleParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 打开编辑弹窗 */
const openEdit = (row?: Article) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 打开批量移动弹窗 */
const openMove = () => {
showMove.value = true;
};
const onUpdate = (row?: Article) => {
// const isShow = row?.isShow == 0 ? 1 : 0;
// updateArticle({ ...row, isShow }).then((msg) => {
// message.success(msg);
// reload();
// });
};
/* 删除单个 */
const remove = (row: Article) => {
const hide = message.loading('请求中..', 0);
removeArticle(row.articleId)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 批量删除 */
const removeBatch = () => {
if (!selection.value.length) {
message.error('请至少选择一条数据');
return;
}
Modal.confirm({
title: '提示',
content: '确定要删除选中的记录吗?',
icon: createVNode(ExclamationCircleOutlined),
maskClosable: true,
onOk: () => {
const hide = message.loading('请求中..', 0);
removeBatchArticle(selection.value.map((d) => d.articleId))
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
}
});
};
/* 自定义行属性 */
const customRow = (record: Article) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
watch(
() => router.currentRoute.value.params.id,
(id) => {
merchantId.value = Number(id);
token.value = uuid();
reload();
},
{ immediate: true }
);
</script>
<script lang="ts">
export default {
name: 'ArticleV2'
};
</script>
<style lang="less" scoped></style>

View File

@@ -4,7 +4,7 @@
:width="500"
:visible="visible"
:maskClosable="false"
:title="isUpdate ? '编辑字段' : '添加字段'"
:title="isUpdate ? '编辑参数' : '添加参数'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
@@ -13,23 +13,34 @@
ref="formRef"
:model="form"
:rules="rules"
:label-col="{ md: { span: 4 }, sm: { span: 4 }, xs: { span: 24 } }"
:label-col="{ md: { span: 5 }, sm: { span: 4 }, xs: { span: 24 } }"
:wrapper-col="{ md: { span: 21 }, sm: { span: 22 }, xs: { span: 24 } }"
>
<a-form-item label="" name="name">
<a-input
allow-clear
placeholder="site_name"
<a-form-item label="参数名" name="name">
<SelectWebsiteField
:placeholder="`添加参数`"
class="input-item"
v-model:value="form.name"
@done="chooseData"
/>
</a-form-item>
<a-form-item label="值" name="value">
<a-form-item label="配置值" name="value">
<a-input allow-clear placeholder="淘宝网" v-model:value="form.value" />
</a-form-item>
<a-form-item label="图片">
<SelectFile
:placeholder="`请选择图片`"
:limit="1"
:data="images"
@done="chooseImage"
@del="onDeleteItem"
/>
</a-form-item>
<a-form-item label="描述" name="comments">
<a-input
allow-clear
placeholder="网站名称"
<a-textarea
:rows="4"
:maxlength="200"
placeholder="描述"
v-model:value="form.comments"
/>
</a-form-item>
@@ -41,15 +52,6 @@
v-model:value="form.sortNumber"
/>
</a-form-item>
<a-form-item label="(选填)">
<SelectFile
:placeholder="`请选择图片`"
:limit="1"
:data="images"
@done="chooseImage"
@del="onDeleteItem"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
@@ -92,6 +94,8 @@
type: 0,
name: undefined,
value: undefined,
modifyRange: undefined,
defaultValue: undefined,
comments: '',
status: 0,
sortNumber: 100
@@ -142,6 +146,11 @@
form.type = 0;
};
const chooseData = (data: WebsiteField) => {
assignFields(data);
form.value = data.defaultValue;
};
/* 保存编辑 */
const save = () => {
if (!formRef.value) {

View File

@@ -9,6 +9,7 @@
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:need-page="false"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
@@ -17,8 +18,12 @@
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div class="ele-text-heading">{{ record.name }}</div>
<span class="ele-text-placeholder">{{ record.comments }}</span>
<div
class="ele-text-heading"
@click="copyText(`config.${record.name}`)"
>{{ record.name }}</div
>
<!-- <span class="ele-text-placeholder">{{ record.comments }}</span>-->
</template>
<template v-if="column.key === 'value'">
<a-image
@@ -26,7 +31,15 @@
:src="record.value"
:width="120"
/>
<span v-else>{{ record.value }}</span>
<div v-else>{{ record.value }}</div>
</template>
<template v-if="column.key === 'comments'">
<a-popover>
<template #content>
{{ record.comments }}
</template>
<ExclamationCircleOutlined />
</a-popover>
</template>
<template v-if="column.key === 'action'">
<a @click="copyText('{{ config.' + record.name + ' }}')">调用</a>
@@ -66,6 +79,7 @@
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import type { EleProTable } from 'ele-admin-pro';
import type { DatasourceFunction } from 'ele-admin-pro/es/ele-pro-table/types';
@@ -77,6 +91,7 @@
WebsiteFieldParam
} from '@/api/cms/website/field/model';
import {
listWebsiteField,
pageWebsiteField,
removeWebsiteField,
undeleteWebsiteField,
@@ -100,7 +115,7 @@
// 表格数据源
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
// 搜索条件
return pageWebsiteField({
return listWebsiteField({
...where,
...orders,
page,
@@ -111,14 +126,32 @@
// 表格列配置
const columns = ref<any[]>([
{
title: '',
title: '参数名',
dataIndex: 'name',
width: 180,
key: 'name'
},
{
title: '',
title: '描述',
dataIndex: 'comments',
key: 'comments',
width: 120,
align: 'center'
},
{
title: '配置值',
dataIndex: 'value',
key: 'value'
key: 'value',
width: 400,
ellipsis: true
},
{
title: '默认值',
dataIndex: 'defaultValue'
},
{
title: '可设置范围',
dataIndex: 'modifyRange'
},
{
title: '排序',

View File

@@ -1,7 +1,7 @@
<!-- 编辑弹窗 -->
<template>
<ele-modal
:width="740"
:width="800"
:visible="visible"
:confirm-loading="loading"
:title="isUpdate ? '修改导航' : '新建导航'"
@@ -41,123 +41,70 @@
@pressEnter="save"
/>
</a-form-item>
<a-form-item label="选择页面" v-if="form.type == 1">
<SelectDesign
:placeholder="`请选择页面`"
v-model:value="form.pageName"
@done="choosePageId"
/>
</a-form-item>
<a-form-item label="选择列表" v-if="form.type == 2">
<SelectArticleCategory
:placeholder="`请选择列表`"
v-model:value="form.pageName"
@done="chooseArticleCategoryId"
/>
</a-form-item>
<a-form-item label="选择文章" v-if="form.type == 3">
<SelectArticle
:placeholder="`请选择文章`"
v-model:value="form.pageName"
@done="chooseArticle"
/>
</a-form-item>
<a-form-item label="选择表单" v-if="form.type == 4">
<SelectForm
:placeholder="`请选择表单`"
v-model:value="form.pageName"
@done="chooseForm"
/>
</a-form-item>
<a-form-item label="选择文档" v-if="form.type == 5">
<SelectDocsBook
:placeholder="`请选择知识文档`"
v-model:value="form.title"
@done="chooseDocsBook"
/>
</a-form-item>
<a-form-item label="选择分类" v-if="form.type == 6">
<SelectGoodsCategory
:placeholder="`请选择商品分类`"
v-model:value="form.title"
@done="chooseGoodsCategory"
/>
</a-form-item>
<a-form-item label="选择商品详情" v-if="form.type == 7">
<SelectDocsBook
:placeholder="`请选择商品详情`"
v-model:value="form.title"
@done="chooseGoods"
/>
</a-form-item>
<a-form-item
:label="form.type == 9 ? '链接地址' : '路由地址'"
name="path"
>
<a-input
allow-clear
:placeholder="form.type == 9 ? '请输入链接地址' : '/about.html'"
:placeholder="
form.type == 9 ? '请输入链接地址' : '/iphone-15-pro'
"
v-model:value="form.path"
@pressEnter="save"
/>
</a-form-item>
<a-form-item label="组件路径" name="component" v-if="form.type == 0">
<a-form-item label="组件路径" name="component">
<a-input
allow-clear
placeholder="/custom/index"
placeholder="/pages/[custom].vue"
v-model:value="form.component"
@pressEnter="save"
/>
</a-form-item>
<a-form-item label="描述" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="请输入描述"
v-model:value="form.comments"
/>
</a-form-item>
</a-col>
<a-col
v-bind="styleResponsive ? { md: 12, sm: 24, xs: 24 } : { span: 12 }"
>
<!-- <a-form-item label="模型" name="type">-->
<!-- <a-select-->
<!-- ref="select"-->
<!-- v-model:value="form.type"-->
<!-- style="width: 253px"-->
<!-- @change="onType"-->
<!-- >-->
<!-- <a-select-option :value="0">通用模型</a-select-option>-->
<!-- <a-select-option :value="1">单页内容</a-select-option>-->
<!-- <a-select-option :value="2">新闻分类</a-select-option>-->
<!-- <a-select-option :value="3">新闻详情</a-select-option>-->
<!-- <a-select-option :value="4">表单设计</a-select-option>-->
<!-- <a-select-option :value="5">知识文档</a-select-option>-->
<!-- <a-select-option :value="6">商品分类</a-select-option>-->
<!-- <a-select-option :value="7">商品详情</a-select-option>-->
<!-- <a-select-option :value="9">外部链接</a-select-option>-->
<!-- </a-select>-->
<!-- </a-form-item>-->
<!-- <a-form-item label="位置" name="position">-->
<!-- <a-radio-group v-model:value="form.position">-->
<!-- <a-radio :value="0">全部</a-radio>-->
<!-- <a-radio :value="1">顶部</a-radio>-->
<!-- <a-radio :value="2">底部</a-radio>-->
<!-- </a-radio-group>-->
<!-- </a-form-item>-->
<a-form-item label="模型" name="model">
<ChooseDictionary
dict-code="NavigationModel"
:placeholder="`选择模型`"
v-model:value="form.model"
@done="chooseModel"
/>
</a-form-item>
<a-form-item label="位置" name="top">
<a-radio-group v-model:value="form.position" @change="onPosition">
<a-radio-button :value="0">不限</a-radio-button>
<a-radio-button :value="1"></a-radio-button>
<a-radio-button :value="2">底部</a-radio-button>
<a-radio :value="1">顶部</a-radio>
<a-radio :value="2"></a-radio>
<a-radio :value="0">不限</a-radio>
</a-radio-group>
<!-- <a-space>-->
<!-- <a-switch-->
<!-- checked-children="显示"-->
<!-- un-checked-children="隐藏"-->
<!-- :checked="form.top === 0"-->
<!-- @update:checked="updateTopValue"-->
<!-- />-->
<!-- <a-switch-->
<!-- checked-children="显示"-->
<!-- un-checked-children="隐藏"-->
<!-- :checked="form.bottom === 0"-->
<!-- @update:checked="updateBottomValue"-->
<!-- />-->
<!-- </a-space>-->
</a-form-item>
<a-form-item label="权限" name="">
<a-radio-group v-model:value="form.permission">
<a-radio :value="0">所有人</a-radio>
<a-radio :value="1">登录可见</a-radio>
<a-radio :value="2">密码可见</a-radio>
</a-radio-group>
</a-form-item>
<a-form-item label="允许评论" name="">
<a-space>
<a-switch
checked-children=""
un-checked-children=""
:checked="form.hide === 0"
@update:checked="updateHideValue"
/>
</a-space>
</a-form-item>
<a-form-item label="状态" name="hide">
<a-space>
@@ -191,26 +138,6 @@
</a-form-item>
</a-col>
</a-row>
<!-- <div style="margin-bottom: 22px">-->
<!-- <a-divider />-->
<!-- </div>-->
<!-- <a-form-item-->
<!-- label="备注"-->
<!-- name="comments"-->
<!-- :label-col="-->
<!-- styleResponsive ? { md: 3, sm: 4, xs: 24 } : { flex: '90px' }-->
<!-- "-->
<!-- :wrapper-col="-->
<!-- styleResponsive ? { md: 21, sm: 20, xs: 24 } : { flex: '1' }-->
<!-- "-->
<!-- >-->
<!-- <a-textarea-->
<!-- :rows="4"-->
<!-- :maxlength="200"-->
<!-- placeholder="请输入备注信息"-->
<!-- v-model:value="form.comments"-->
<!-- />-->
<!-- </a-form-item>-->
</a-form>
</ele-modal>
</template>
@@ -224,16 +151,10 @@
import useFormData from '@/utils/use-form-data';
import { Navigation } from '@/api/cms/navigation/model';
import { addNavigation, updateNavigation } from '@/api/cms/navigation';
import { Design } from '@/api/cms/design/model';
import { removeSiteInfoCache } from '@/api/cms/website';
import { ArticleCategory } from '@/api/cms/category/model';
import { Article } from '@/api/cms/article/model';
import { Form } from '@/api/cms/form/model';
import { DocsBook } from '@/api/cms/docs-book/model';
import { Goods } from '@/api/shop/goods/model';
import { GoodsCategory } from '@/api/shop/goodsCategory/model';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FileRecord } from '@/api/system/file/model';
import { uuid } from 'ele-admin-pro';
// 是否开启响应式布局
const themeStore = useThemeStore();
@@ -269,15 +190,22 @@
// 表单数据
const { form, resetFields, assignFields } = useFormData<Navigation>({
navigationId: undefined,
model: '[custom]',
code: undefined,
modelName: '',
type: 0,
title: '',
parentId: 0,
path: '',
component: '',
parentName: undefined,
parentPath: undefined,
path: undefined,
component: undefined,
sortNumber: 100,
hide: 0,
permission: 0,
password: uuid(),
position: 1,
top: 1,
top: 0,
bottom: 1,
status: 0,
pageId: 0,
@@ -297,6 +225,14 @@
trigger: 'blur'
}
],
model: [
{
required: true,
message: '请选择模型',
type: 'string',
trigger: 'blur'
}
],
// component: [
// {
// required: true,
@@ -367,77 +303,10 @@
]
});
const onType = (index: number) => {
if (index == 0) {
form.path = '/';
form.component = '';
}
if (index == 1) {
form.path = '';
form.component = '';
}
if (index == 2) {
form.path = '';
form.component = '';
}
if (index == 9) {
form.path = undefined;
form.component = '';
}
};
const choosePageId = (data: Design) => {
form.pageId = data.pageId;
form.pageName = data.name;
form.title = data.name;
form.path = data.path;
form.component = data.component;
};
const chooseArticleCategoryId = (data: ArticleCategory) => {
form.articleCategoryId = data.categoryId;
form.pageName = data.title;
form.title = data.title;
form.path = '/article/' + data.categoryId;
form.component = '/article/index';
};
const chooseArticle = (data: Article) => {
form.articleId = data.articleId;
form.title = data.title;
form.pageName = data.title;
form.path = '/a/' + data.articleId;
form.component = '/article/detail';
};
const chooseForm = (data: Form) => {
form.formId = data.formId;
form.title = data.name;
form.pageName = data.name;
form.path = '/form/' + data.formId;
form.component = '/form/detail';
};
const chooseDocsBook = (data: DocsBook) => {
form.bookCode = data.code;
form.title = data.name;
form.pageName = data.name;
form.path = '/docs/' + data.code;
form.component = '/docs/index';
};
const chooseGoodsCategory = (data: GoodsCategory) => {
form.goodsCategoryId = data.categoryId;
form.title = data.title;
form.pageName = data.title;
form.path = '/goods/search?categoryId=' + data.categoryId;
form.component = '/goods/search';
};
const chooseGoods = (data: Goods) => {
form.goodsId = data.goodsId;
form.path = '/goods/detail/' + data.goodsId;
form.component = '/goods/search';
const chooseModel = (item: Navigation) => {
form.model = `${item.value}`;
form.modelName = `${item.label}`;
form.component = `/pages/${item.value}`;
};
const chooseFile = (data: FileRecord) => {
@@ -480,10 +349,6 @@
const navigationForm = {
...form
};
// if (form.path != '' && form.path?.charAt(0) != '/') {
// message.error('路由必须以"/"开头');
// return false;
// }
const saveOrUpdate = isUpdate.value ? updateNavigation : addNavigation;
saveOrUpdate(navigationForm)
.then((msg) => {
@@ -509,14 +374,6 @@
form.hide = value ? 0 : 1;
};
const updateTopValue = (value: boolean) => {
form.top = value ? 0 : 1;
};
const updateBottomValue = (value: boolean) => {
form.bottom = value ? 0 : 1;
};
watch(
() => props.visible,
(visible) => {
@@ -525,8 +382,7 @@
if (props.data) {
assignFields({
...props.data,
parentId:
props.data.parentId === 0 ? undefined : props.data.parentId,
parentId: props.data.parentId ? props.data.parentId : 0,
tempPath: props.data.path
});
if (props.data.type == 2) {
@@ -534,7 +390,6 @@
}
isUpdate.value = true;
} else {
form.parentId = props.parentId;
isUpdate.value = false;
}
} else {

View File

@@ -60,19 +60,22 @@
</template>
</template>
<template v-if="column.key === 'type'">
<a-tag v-if="isExternalLink(record.path)" color="red">外链</a-tag>
<a-tag v-if="isExternalLink(record.path)" color="purple"
>外部链接</a-tag
>
<a-tag v-else-if="index === 0" color="orange">首页</a-tag>
<a-tag v-else-if="isExternalLink(record.component)" color="orange">
内链
</a-tag>
<span v-else-if="isDirectory(record)"></span>
<a-tag v-else-if="record.type === 0">通用</a-tag>
<a-tag v-else-if="record.type === 0">通用模型</a-tag>
<a-tag v-else-if="record.type === 1" color="purple">页面</a-tag>
<a-tag v-else-if="record.type === 2">列表</a-tag>
<a-tag v-else-if="record.type === 2" color="blue">文章列表</a-tag>
<a-tag v-else-if="record.type === 3">文章</a-tag>
<a-tag v-else-if="record.type === 4" color="cyan">表单</a-tag>
<a-tag v-else-if="record.type === 5" color="green">文档</a-tag>
<a-tag v-else-if="record.type === 9" color="orange">链接</a-tag>
<a-tag v-else-if="record.type === 6" color="pink">图片列表</a-tag>
<a-tag v-else-if="record.type === 9" color="purple">外部链接</a-tag>
</template>
<template v-else-if="column.key === 'title'">
<a-avatar
@@ -82,10 +85,9 @@
style="margin-right: 10px"
v-if="record.image"
/>
<a v-if="!isDirectory(record)" @click="openSpmUrl(record.path)">{{
<a @click="openSpmUrl(record.path, record, record.navigationId)">{{
record.title
}}</a>
<a v-else>{{ record.title }}</a>
</template>
<template v-if="column.key === 'showIndex'">
<a-tag v-if="record.showIndex === 1" color="green">显示</a-tag>
@@ -112,7 +114,9 @@
<a-space>
<a class="text-fuchsia-300" @click="openLayout(record)">布局</a>
<a-divider type="vertical" />
<a class="text-gray-400" @click="openDesign(record)">设置</a>
<a-tooltip :title="`配置SEO及页面元素`">
<a class="text-gray-400" @click="openDesign(record)">内容</a>
</a-tooltip>
<a-divider type="vertical" />
<a @click="openEdit(null, record.navigationId)">添加</a>
<a-divider type="vertical" />
@@ -183,10 +187,11 @@
updateNavigation
} from '@/api/cms/navigation';
import type { Navigation, NavigationParam } from '@/api/cms/navigation/model';
import { openPreview, openSpmUrl, openUrl } from '@/utils/common';
import { openSpmUrl } from '@/utils/common';
import { getSiteInfo } from '@/api/layout';
import { Design } from '@/api/cms/design/model';
import { listDesign } from '@/api/cms/design';
import router from '@/router';
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
@@ -294,7 +299,7 @@
// 表格数据源
const datasource: DatasourceFunction = ({ where }) => {
where = {};
where.title = searchText.value;
where.keywords = searchText.value;
// where.position = position.value;
where.top = 0;
where.bottom = undefined;
@@ -350,6 +355,7 @@
};
const openDesign = (row?: Navigation) => {
// TODO 通用模型
listDesign({
navigationId: row?.navigationId,
limit: 1
@@ -361,6 +367,15 @@
.finally(() => {
showDesignEdit.value = true;
});
// TODO 文章列表
if (row?.type === 2) {
router.push({
path: '/cms/article',
query: { categoryId: row.navigationId, type: row.type }
});
}
console.log(row);
// /cms/article
};
const openLayout = (row?: Navigation) => {

View File

@@ -0,0 +1,230 @@
<!-- 用户编辑弹窗 -->
<template>
<ele-modal
:width="500"
:visible="visible"
:maskClosable="false"
:title="isUpdate ? '编辑参数' : '添加参数'"
:body-style="{ paddingBottom: '28px' }"
@update:visible="updateVisible"
@ok="save"
>
<a-form
ref="formRef"
:model="form"
:rules="rules"
:label-col="{ md: { span: 5 }, sm: { span: 4 }, xs: { span: 24 } }"
:wrapper-col="{ md: { span: 21 }, sm: { span: 22 }, xs: { span: 24 } }"
>
<a-form-item label="参数名" name="name">
<!-- <ChooseWebsiteField-->
<!-- v-model:value="form.name"-->
<!-- :placeholder="`选择参数`"-->
<!-- @done="chooseData"-->
<!-- />-->
<SelectWebsiteField
:placeholder="`添加参数`"
class="input-item"
v-model:value="form.name"
@done="chooseData"
/>
</a-form-item>
<a-form-item label="配置值" name="value">
<a-input allow-clear placeholder="淘宝网" v-model:value="form.value" />
</a-form-item>
<a-form-item label="图片格式">
<SelectFile
:placeholder="`请选择图片`"
:limit="1"
:data="images"
@done="chooseImage"
@del="onDeleteItem"
/>
</a-form-item>
<a-form-item label="可配置范围" name="modifyRange">
<a-input
allow-clear
placeholder="[0-1]"
v-model:value="form.modifyRange"
/>
</a-form-item>
<a-form-item label="默认值" name="defaultValue">
<a-input
allow-clear
placeholder="0"
v-model:value="form.defaultValue"
/>
</a-form-item>
<a-form-item label="描述" name="comments">
<a-textarea
:rows="4"
:maxlength="200"
placeholder="描述"
v-model:value="form.comments"
/>
</a-form-item>
<a-form-item label="排序" name="sortNumber">
<a-input-number
:min="0"
:max="99999"
placeholder="请输入排序号"
v-model:value="form.sortNumber"
/>
</a-form-item>
</a-form>
</ele-modal>
</template>
<script lang="ts" setup>
import { ref, reactive, watch } from 'vue';
import { FormInstance } from 'ant-design-vue/es/form';
import { WebsiteField } from '@/api/system/website/field/model';
import useFormData from '@/utils/use-form-data';
import { addWebsiteField, updateWebsiteField } from '@/api/system/website/field';
import { message } from 'ant-design-vue/es';
import { removeSiteInfoCache } from '@/api/system/website';
import { ItemType } from 'ele-admin-pro/es/ele-image-upload/types';
import { FileRecord } from '@/api/system/file/model';
import { uuid } from 'ele-admin-pro';
// 是否是修改
const isUpdate = ref(false);
const props = defineProps<{
// 弹窗是否打开
visible: boolean;
websiteId: number | null | undefined;
// 修改回显的数据
data?: WebsiteField | null;
}>();
const emit = defineEmits<{
(e: 'done'): void;
(e: 'update:visible', visible: boolean): void;
}>();
// 提交状态
const loading = ref(false);
const images = ref<ItemType[]>([]);
const formRef = ref<FormInstance | null>(null);
const { form, resetFields, assignFields } = useFormData<WebsiteField>({
id: undefined,
type: 0,
name: undefined,
value: undefined,
modifyRange: undefined,
defaultValue: undefined,
comments: '',
status: 0,
sortNumber: 100
});
// 表单验证规则
const rules = reactive({
name: [
{
required: true,
type: 'string',
message: '请输入参数名称(英语)'
}
],
// comments: [
// {
// required: true,
// type: 'string',
// message: '请输入描述'
// }
// ],
value: [
{
required: true,
type: 'string',
message: '请填写参数值'
}
]
});
/* 更新visible */
const updateVisible = (value: boolean) => {
emit('update:visible', value);
};
const chooseImage = (data: FileRecord) => {
images.value.push({
uid: data.id,
url: data.path,
status: 'done'
});
form.value = data.downloadUrl;
form.type = 1;
};
const onDeleteItem = (index: number) => {
images.value.splice(index, 1);
form.type = 0;
};
const chooseData = (data: WebsiteField) => {
assignFields(data);
};
/* 保存编辑 */
const save = () => {
if (!formRef.value) {
return;
}
formRef.value
.validate()
.then(() => {
loading.value = true;
const data = {
...form,
// name: form.name?.toUpperCase(),
websiteId: props.websiteId
};
const saveOrUpdate = isUpdate.value
? updateWebsiteField
: addWebsiteField;
saveOrUpdate(data)
.then((msg) => {
loading.value = false;
message.success(msg);
updateVisible(false);
// 清除缓存
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId'));
emit('done');
})
.catch((e) => {
loading.value = false;
message.error(e.message);
});
})
.catch(() => {});
};
watch(
() => props.visible,
(visible) => {
if (visible) {
images.value = [];
if (props.data) {
assignFields(props.data);
form.comments = props.data.comments;
if (form.type == 1) {
images.value.push({
uid: uuid(),
url: props.data.value,
status: 'done'
});
}
isUpdate.value = true;
} else {
isUpdate.value = false;
}
} else {
resetFields();
}
}
);
</script>

View File

@@ -0,0 +1,13 @@
<template>
<a-button @click="add">添加参数</a-button>
</template>
<script lang="ts" setup>
const emit = defineEmits<{
(e: 'add'): void;
}>();
const add = () => {
emit('add');
};
</script>

View File

@@ -0,0 +1,252 @@
<template>
<a-page-header :title="getPageTitle()" @back="() => $router.go(-1)">
<a-card :bordered="false">
<div class="website-field">
<!-- 表格 -->
<ele-pro-table
ref="tableRef"
row-key="websiteId"
:columns="columns"
:datasource="datasource"
:customRow="customRow"
:need-page="false"
tool-class="ele-toolbar-form"
class="sys-org-table"
>
<template #toolbar>
<WebsiteFieldSearch @add="openEdit" />
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<div
class="ele-text-heading"
@click="copyText(`config.${record.name}`)"
>{{ record.name }}</div
>
</template>
<template v-if="column.key === 'value'">
<a-image
v-if="record.type === 1"
:src="record.value"
:width="120"
/>
<div v-else>{{ record.value }}</div>
</template>
<template v-if="column.key === 'comments'">
<a-popover>
<template #content>
{{ record.comments }}
</template>
<ExclamationCircleOutlined />
</a-popover>
</template>
<template v-if="column.key === 'action'">
<a @click="copyText('{{ config.' + record.name + ' }}')">调用</a>
<a-divider type="vertical" />
<a @click="openEdit(record)">编辑</a>
<template v-if="record.deleted == 0">
<a-divider type="vertical" />
<a-popconfirm
title="确定要删除此记录吗?"
@confirm="remove(record)"
>
<a class="ele-text-danger">删除</a>
</a-popconfirm>
</template>
<template v-if="record.deleted == 1">
<a-divider type="vertical" />
<a-popconfirm
title="确定要放回原处吗?"
@confirm="recovery(record)"
>
<a class="ele-text-danger">恢复</a>
</a-popconfirm>
</template>
</template>
</template>
</ele-pro-table>
<!-- 编辑弹窗 -->
<WebsiteFieldEdit
v-model:visible="showEdit"
:data="current"
@done="reload"
/>
</div>
</a-card>
</a-page-header>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import type { EleProTable } from 'ele-admin-pro';
import type { DatasourceFunction } from 'ele-admin-pro/es/ele-pro-table/types';
import WebsiteFieldSearch from './components/website-field-search.vue';
import { Website } from '@/api/system/website/model';
import WebsiteFieldEdit from './components/website-field-edit.vue';
import {
WebsiteField,
WebsiteFieldParam
} from '@/api/system/website/field/model';
import {
listWebsiteField,
removeWebsiteField,
undeleteWebsiteField,
updateWebsiteField
} from '@/api/system/website/field';
import { copyText, getPageTitle } from '@/utils/common';
const props = defineProps<{
websiteId: any;
data: Website;
}>();
// 表格实例
const tableRef = ref<InstanceType<typeof EleProTable> | null>(null);
const selection = ref<any[]>();
// 当前编辑数据
const current = ref<WebsiteField | null>(null);
// 是否显示编辑弹窗
const showEdit = ref(false);
// 表格数据源
const datasource: DatasourceFunction = ({ page, limit, where, orders }) => {
// 搜索条件
return listWebsiteField({
...where,
...orders,
page,
limit
});
};
// 表格列配置
const columns = ref<any[]>([
{
title: '参数名',
dataIndex: 'name',
width: 180,
key: 'name'
},
{
title: '描述',
dataIndex: 'comments',
key: 'comments',
width: 120,
align: 'center'
},
{
title: '配置值',
dataIndex: 'value',
key: 'value',
width: 300,
ellipsis: true
},
{
title: '默认值',
width: 180,
dataIndex: 'defaultValue'
},
{
title: '可设置范围',
width: 180,
dataIndex: 'modifyRange'
},
{
title: '排序',
dataIndex: 'sortNumber',
width: 120,
align: 'center'
},
{
title: '操作',
key: 'action',
width: 180,
align: 'center',
hideInSetting: true
}
]);
const moveUp = (row?: WebsiteField) => {
updateWebsiteField({
id: row?.id,
sortNumber: Number(row?.sortNumber) + 1
}).then((msg) => {
message.success(msg);
reload();
});
};
/* 打开编辑弹窗 */
const openEdit = (row?: WebsiteField) => {
current.value = row ?? null;
showEdit.value = true;
};
/* 搜索 */
const reload = (where?: WebsiteFieldParam) => {
selection.value = [];
tableRef?.value?.reload({ where: where });
};
/* 删除单个 */
const remove = (row: WebsiteField) => {
const hide = message.loading('请求中..', 0);
removeWebsiteField(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
// 从回收站放回原处
const recovery = (row: WebsiteField) => {
const hide = message.loading('请求中..', 0);
undeleteWebsiteField(row.id)
.then((msg) => {
hide();
message.success(msg);
reload();
})
.catch((e) => {
hide();
message.error(e.message);
});
};
/* 自定义行属性 */
const customRow = (record: WebsiteField) => {
return {
// 行点击事件
onClick: () => {
// console.log(record);
},
// 行双击事件
onDblclick: () => {
openEdit(record);
}
};
};
watch(
() => props.websiteId,
(websiteId) => {
if (websiteId) {
reload();
}
},
{ immediate: true }
);
</script>
<script lang="ts">
export default {
name: 'WebsiteFieldIndex'
};
</script>