2025-08-20 14:39:30 +08:00
|
|
|
<template>
|
2025-10-21 18:04:02 +08:00
|
|
|
<BasicDrawer
|
|
|
|
|
v-bind="$attrs"
|
|
|
|
|
@register="registerDrawer"
|
|
|
|
|
showFooter
|
|
|
|
|
destroyOnClose
|
|
|
|
|
:title="getTitle"
|
|
|
|
|
width="50%"
|
|
|
|
|
@ok="handleSubmit"
|
|
|
|
|
@visible-change="
|
|
|
|
|
() => {
|
|
|
|
|
activeKey = '1';
|
|
|
|
|
}
|
|
|
|
|
"
|
|
|
|
|
>
|
|
|
|
|
<a-tabs v-model:activeKey="activeKey">
|
|
|
|
|
<a-tab-pane key="1" :tab="t('菜单信息')">
|
|
|
|
|
<BasicForm @register="registerForm" />
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
<a-tab-pane key="2" :tab="t('按钮信息')">
|
|
|
|
|
<ButtonTable :menuId="rowId" :hasMetaFormId="hasMetaFormId" ref="buttonTable" v-if="activeKey" />
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
<a-tab-pane key="3" :tab="t('表格信息')">
|
|
|
|
|
<ColumnTable :menuId="rowId" ref="columnTable" v-if="activeKey" />
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
<a-tab-pane key="4" :tab="t('表单信息')">
|
|
|
|
|
<FormTable :menuId="rowId" ref="formTable" v-if="activeKey" />
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
</a-tabs>
|
|
|
|
|
</BasicDrawer>
|
2025-08-20 14:39:30 +08:00
|
|
|
</template>
|
|
|
|
|
<script lang="ts">
|
2025-10-21 18:04:02 +08:00
|
|
|
import { defineComponent, ref, computed } from 'vue';
|
|
|
|
|
import { BasicForm, FormSchema, useForm } from '/@/components/Form/index';
|
|
|
|
|
import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
|
|
|
|
|
import { BasicColumn } from '/@/components/Table';
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
import ButtonTable from './ButtonTable.vue';
|
|
|
|
|
import ColumnTable from './ColumnTable.vue';
|
|
|
|
|
import FormTable from './FormTable.vue';
|
|
|
|
|
import { getMenuTree, addMenu, updateMenu, getMenuButtonById, getMenuColumnById, getMenuFormById } from '/@/api/system/menu';
|
|
|
|
|
import { useMessage } from '/@/hooks/web/useMessage';
|
|
|
|
|
import { useI18n } from '/@/hooks/web/useI18n';
|
|
|
|
|
import { MenuButtonModel } from '/@/api/system/menuButton/model';
|
|
|
|
|
import { getSubSystemList } from '/@/api/system/subSystem';
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
import { usePermissionStore } from '/@/store/modules/permission';
|
|
|
|
|
const { t } = useI18n();
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
const isUpdate = ref<boolean>(false);
|
|
|
|
|
const componentType = ref<number>(0);
|
|
|
|
|
const hasMetaFormId = ref(false);
|
|
|
|
|
export const formSchema: FormSchema[] = [
|
|
|
|
|
{
|
|
|
|
|
field: 'title',
|
|
|
|
|
label: t('菜单名称'),
|
|
|
|
|
component: 'Input',
|
|
|
|
|
required: true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'name',
|
|
|
|
|
label: t('组件名称'),
|
|
|
|
|
component: 'Input',
|
|
|
|
|
required: true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'code',
|
|
|
|
|
label: t('编码'),
|
|
|
|
|
component: 'Input',
|
|
|
|
|
required: true
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'sortCode',
|
|
|
|
|
label: t('排序'),
|
|
|
|
|
component: 'InputNumber',
|
|
|
|
|
required: true
|
|
|
|
|
},
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
{
|
|
|
|
|
field: 'parentId',
|
|
|
|
|
label: t('上级菜单'),
|
|
|
|
|
component: 'MenuSelect',
|
|
|
|
|
colProps: { lg: 24, md: 24 }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'systemId',
|
|
|
|
|
label: t('所属系统'),
|
|
|
|
|
component: 'Select',
|
|
|
|
|
componentProps: {
|
|
|
|
|
fieldNames: {
|
|
|
|
|
label: 'name',
|
|
|
|
|
value: 'id'
|
|
|
|
|
},
|
|
|
|
|
getPopupContainer: () => document.body,
|
|
|
|
|
allowClear: false
|
|
|
|
|
},
|
|
|
|
|
colProps: { lg: 24, md: 24 },
|
|
|
|
|
dynamicDisabled: ({ values }) => {
|
|
|
|
|
return values.parentId ? true : false;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'path',
|
|
|
|
|
label: t('路由地址'),
|
|
|
|
|
component: 'Input',
|
|
|
|
|
required: true,
|
|
|
|
|
colProps: { lg: 24, md: 24 },
|
|
|
|
|
helpMessage: '如果是外链新开窗口,在此输入地址!',
|
|
|
|
|
dynamicDisabled: ({ values }) => {
|
|
|
|
|
return values.componentType === 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'outLink',
|
|
|
|
|
label: t('是否外链'),
|
|
|
|
|
component: 'RadioButtonGroup',
|
|
|
|
|
componentProps: {
|
|
|
|
|
options: [
|
|
|
|
|
{ label: t('是'), value: 1 },
|
|
|
|
|
{ label: t('否'), value: 0 }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'icon',
|
|
|
|
|
label: t('图标'),
|
|
|
|
|
component: 'IconPicker'
|
2025-08-20 14:39:30 +08:00
|
|
|
},
|
|
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
{
|
|
|
|
|
field: 'component',
|
|
|
|
|
label: t('组件路径'),
|
|
|
|
|
component: 'Input',
|
|
|
|
|
colProps: { lg: 24, md: 24 },
|
|
|
|
|
required: true,
|
|
|
|
|
ifShow: ({ values }) => {
|
|
|
|
|
return values.outLink === 0 && values.componentType === 0;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'iframeSrc',
|
|
|
|
|
label: t('外链地址'),
|
|
|
|
|
component: 'Input',
|
|
|
|
|
colProps: { lg: 24, md: 24 },
|
|
|
|
|
required: true,
|
|
|
|
|
helpMessage: '如果是外链内嵌,在此输入地址!',
|
|
|
|
|
ifShow: ({ values }) => {
|
|
|
|
|
return values.outLink == 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'remark',
|
|
|
|
|
label: t('备注'),
|
|
|
|
|
component: 'InputTextArea',
|
|
|
|
|
colProps: { lg: 24, md: 24 }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'menuType',
|
|
|
|
|
label: t('页面/菜单'),
|
|
|
|
|
component: 'RadioButtonGroup',
|
|
|
|
|
componentProps: {
|
|
|
|
|
options: [
|
|
|
|
|
{ label: t('页面'), value: 1 },
|
|
|
|
|
{ label: t('菜单'), value: 0 }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'enabledMark',
|
|
|
|
|
label: t('状态'),
|
|
|
|
|
component: 'RadioButtonGroup',
|
|
|
|
|
componentProps: {
|
|
|
|
|
options: [
|
|
|
|
|
{ label: t('启用'), value: 1 },
|
|
|
|
|
{ label: t('禁用'), value: 0 }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
},
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
{
|
|
|
|
|
field: 'display',
|
|
|
|
|
label: t('是否显示'),
|
|
|
|
|
component: 'RadioButtonGroup',
|
|
|
|
|
componentProps: {
|
|
|
|
|
options: [
|
|
|
|
|
{ label: t('是'), value: 1 },
|
|
|
|
|
{ label: t('否'), value: 0 }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'componentType', //隐藏组件
|
|
|
|
|
label: t('是否自定义'),
|
|
|
|
|
component: 'RadioButtonGroup',
|
|
|
|
|
componentProps: {
|
|
|
|
|
options: [
|
|
|
|
|
{ label: t('是'), value: 1 },
|
|
|
|
|
{ label: t('否'), value: 0 }
|
|
|
|
|
]
|
|
|
|
|
},
|
|
|
|
|
show: false
|
|
|
|
|
}
|
|
|
|
|
];
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
export const columns: BasicColumn[] = [
|
|
|
|
|
{
|
|
|
|
|
title: t('按钮名称'),
|
|
|
|
|
dataIndex: 'name',
|
|
|
|
|
width: 100,
|
|
|
|
|
align: 'left'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('编码'),
|
|
|
|
|
dataIndex: 'code',
|
|
|
|
|
width: 100
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('地址'),
|
|
|
|
|
dataIndex: 'url',
|
|
|
|
|
width: 200
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: t('请求方式'),
|
|
|
|
|
dataIndex: 'method',
|
|
|
|
|
width: 180,
|
|
|
|
|
format: (text: string | number) => {
|
|
|
|
|
if (text === 0) return 'GET';
|
|
|
|
|
else if (text === 1) return 'POST';
|
|
|
|
|
else if (text === 2) return 'PUT';
|
|
|
|
|
else return 'DELETE';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
];
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
export default defineComponent({
|
|
|
|
|
name: 'MenuDrawer',
|
|
|
|
|
components: {
|
|
|
|
|
BasicDrawer,
|
|
|
|
|
BasicForm,
|
|
|
|
|
FormTable,
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
ButtonTable,
|
|
|
|
|
ColumnTable
|
|
|
|
|
},
|
|
|
|
|
emits: ['success', 'register'],
|
|
|
|
|
setup(_, { emit }) {
|
|
|
|
|
const permissionStore = usePermissionStore();
|
|
|
|
|
const { notification } = useMessage();
|
|
|
|
|
const rowId = ref('');
|
|
|
|
|
const activeKey = ref('1');
|
|
|
|
|
const buttonTable = ref();
|
|
|
|
|
const columnTable = ref();
|
|
|
|
|
const formTable = ref();
|
|
|
|
|
const buttonDatas = ref<MenuButtonModel[]>([]);
|
|
|
|
|
const columnDatas = ref<MenuButtonModel[]>([]);
|
|
|
|
|
const formDatas = ref<MenuButtonModel[]>([]);
|
|
|
|
|
const systemId = ref('1');
|
|
|
|
|
const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
|
|
|
|
|
labelWidth: 100,
|
|
|
|
|
schemas: formSchema,
|
|
|
|
|
showActionButtonGroup: false,
|
|
|
|
|
baseColProps: { lg: 12, md: 24 }
|
|
|
|
|
});
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
|
|
|
|
|
resetFields();
|
|
|
|
|
setDrawerProps({ confirmLoading: false });
|
|
|
|
|
isUpdate.value = !!data?.isUpdate;
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
if (isUpdate.value) {
|
|
|
|
|
rowId.value = data.record.id;
|
|
|
|
|
if (data.record && data.record.meta && data.record.meta.formId) {
|
|
|
|
|
hasMetaFormId.value = true;
|
|
|
|
|
}
|
|
|
|
|
componentType.value = data.record.componentType;
|
|
|
|
|
if (data.record.outLink == 1) {
|
|
|
|
|
data.record.url = data.record.component;
|
|
|
|
|
}
|
|
|
|
|
if (data.record.parentId) {
|
|
|
|
|
data.record.systemId = null;
|
|
|
|
|
}
|
|
|
|
|
setFieldsValue({
|
|
|
|
|
...data.record
|
|
|
|
|
});
|
|
|
|
|
getMenuButtonById({ id: data.record.id }).then((res) => {
|
|
|
|
|
buttonDatas.value = res;
|
|
|
|
|
});
|
|
|
|
|
getMenuColumnById({ menuId: data.record.id }).then((res) => {
|
|
|
|
|
columnDatas.value = res;
|
|
|
|
|
});
|
|
|
|
|
getMenuFormById({ menuId: data.record.id }).then((res) => {
|
|
|
|
|
formDatas.value = res;
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
rowId.value = '';
|
|
|
|
|
componentType.value = 0;
|
|
|
|
|
setFieldsValue({
|
|
|
|
|
menuType: 1,
|
|
|
|
|
enabledMark: 1,
|
|
|
|
|
outLink: 0,
|
|
|
|
|
display: 1,
|
|
|
|
|
componentType: 0,
|
|
|
|
|
systemId: '1'
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
const treeData = await getMenuTree();
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
const system = await getSubSystemList();
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
updateSchema([
|
|
|
|
|
{
|
|
|
|
|
field: 'parentId',
|
|
|
|
|
componentProps: ({ formModel }) => {
|
|
|
|
|
return {
|
|
|
|
|
getPopupContainer: () => document.body,
|
|
|
|
|
onChange: (v) => {
|
|
|
|
|
findParentSystem(treeData, treeData, v);
|
|
|
|
|
formModel.systemId = systemId.value;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'systemId',
|
|
|
|
|
componentProps: { options: system }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'outLink',
|
|
|
|
|
dynamicDisabled: () => {
|
|
|
|
|
return isUpdate.value && componentType.value == 1;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
field: 'path',
|
|
|
|
|
dynamicDisabled: () => {
|
|
|
|
|
return isUpdate.value && componentType.value == 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]);
|
|
|
|
|
});
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
const getTitle = computed(() => (!isUpdate.value ? t('新增菜单') : t('编辑菜单')));
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
async function handleSubmit() {
|
|
|
|
|
try {
|
|
|
|
|
const values = await validate();
|
|
|
|
|
if (values.outLink == 1) {
|
|
|
|
|
values.component = values.url;
|
|
|
|
|
}
|
|
|
|
|
values.buttonList = buttonTable.value ? buttonTable.value.getDataSource() : buttonDatas.value;
|
|
|
|
|
values.columnList = columnTable.value ? columnTable.value.getDataSource() : columnDatas.value;
|
|
|
|
|
values.formList = formTable.value ? formTable.value.getDataSource() : formDatas.value;
|
|
|
|
|
setDrawerProps({ confirmLoading: true });
|
|
|
|
|
// TODO custom api
|
|
|
|
|
if (!isUpdate.value) {
|
|
|
|
|
//false 新增
|
|
|
|
|
await addMenu(values);
|
|
|
|
|
notification.success({
|
|
|
|
|
message: t('提示'),
|
|
|
|
|
description: t('新增菜单成功')
|
|
|
|
|
}); //提示消息
|
|
|
|
|
} else {
|
|
|
|
|
values.id = rowId.value;
|
|
|
|
|
await updateMenu(values);
|
|
|
|
|
notification.success({
|
|
|
|
|
message: t('提示'),
|
|
|
|
|
description: t('修改菜单成功')
|
|
|
|
|
}); //提示消息
|
|
|
|
|
}
|
|
|
|
|
await permissionStore.buildRoutesAction();
|
|
|
|
|
closeDrawer();
|
|
|
|
|
emit('success');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
setDrawerProps({ confirmLoading: false });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
function findParentSystem(treeData, tempTree, id) {
|
|
|
|
|
for (let i = 0; i < treeData.length; i++) {
|
|
|
|
|
let o = treeData[i];
|
2025-08-20 14:39:30 +08:00
|
|
|
|
2025-10-21 18:04:02 +08:00
|
|
|
if (o.id == id) {
|
|
|
|
|
systemId.value = o.systemId;
|
|
|
|
|
if (o.parentId) {
|
|
|
|
|
findParentSystem(tempTree, tempTree, o.parentId);
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} else if (o.children?.length > 0) {
|
|
|
|
|
findParentSystem(o.children, tempTree, id);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-08-20 14:39:30 +08:00
|
|
|
}
|
2025-10-21 18:04:02 +08:00
|
|
|
return {
|
|
|
|
|
registerDrawer,
|
|
|
|
|
registerForm,
|
|
|
|
|
getTitle,
|
|
|
|
|
hasMetaFormId,
|
|
|
|
|
handleSubmit,
|
|
|
|
|
buttonTable,
|
|
|
|
|
rowId,
|
|
|
|
|
columnTable,
|
|
|
|
|
formTable,
|
|
|
|
|
activeKey,
|
|
|
|
|
t
|
|
|
|
|
};
|
2025-08-20 14:39:30 +08:00
|
|
|
}
|
2025-10-21 18:04:02 +08:00
|
|
|
});
|
2025-08-20 14:39:30 +08:00
|
|
|
</script>
|