update: 菜单管理

This commit is contained in:
easy 2023-02-01 18:10:53 +08:00
parent 5c0c82e0d9
commit d4bf79f802
7 changed files with 215 additions and 81 deletions

View File

@ -14,4 +14,6 @@ export const getMenuInfo_api = (id:string) => server.get(`/menu/${id}`);
// 编辑菜单信息 // 编辑菜单信息
export const saveMenuInfo_api = (data: object) => server.patch(`/menu`, data); export const saveMenuInfo_api = (data: object) => server.patch(`/menu`, data);
// 新增菜单信息 // 新增菜单信息
export const addMenuInfo_api = (data: object) => server.post(`/menu`, data); export const addMenuInfo_api = (data: object) => server.post(`/menu`, data);
// 删除菜单信息
export const delMenuInfo_api = (id: string) => server.remove(`/menu/${id}`);

View File

@ -181,11 +181,18 @@
</a-form-item> </a-form-item>
</a-form-item> </a-form-item>
<a-form-item label="权限"> <a-form-item label="权限">
<PermissChoose :first-width="3" max-height="350px" v-model:value="form.data.permissions" /> <PermissChoose
:first-width="3"
max-height="350px"
v-model:value="form.data.permissions"
/>
</a-form-item> </a-form-item>
</a-form> </a-form>
<a-button type="primary" @click="clickSave" v-loading="saveLoading" <a-button
type="primary"
@click="form.clickSave"
v-loading="form.saveLoading"
>保存</a-button >保存</a-button
> >
</a-card> </a-card>
@ -215,14 +222,17 @@ import {
addMenuInfo_api, addMenuInfo_api,
} from '@/api/system/menu'; } from '@/api/system/menu';
//
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const routeParams = { const routeParams = {
id: route.params.id === ':id' ? undefined : (route.params.id as string), id: route.params.id === ':id' ? undefined : (route.params.id as string),
...route.query, ...route.query,
url: route.query.basePath, url: route.query.basePath,
parentId: route.query.pid,
}; };
//
const basicFormRef = ref<FormInstance>(); const basicFormRef = ref<FormInstance>();
const permissFormRef = ref<FormInstance>(); const permissFormRef = ref<FormInstance>();
const form = reactive({ const form = reactive({
@ -238,15 +248,18 @@ const form = reactive({
indirectMenus: [], indirectMenus: [],
...routeParams, ...routeParams,
} as formType, } as formType,
treeData: [], // treeData: [], //
assetsType: [] as assetType[], // assetsType: [] as assetType[], //
saveLoading: false,
init: () => { init: () => {
// //
routeParams.id && routeParams.id &&
getMenuInfo_api(routeParams.id).then((resp) => { getMenuInfo_api(routeParams.id).then((resp: any) => {
form.data = resp.result as formType form.data = {
...(resp.result as formType),
accessSupport: resp.result.accessSupport.value,
};
}); });
// //
getMenuTree_api({ paging: false }).then((resp: any) => { getMenuTree_api({ paging: false }).then((resp: any) => {
@ -260,9 +273,52 @@ const form = reactive({
})); }));
}); });
}, },
clickSave: () => {
if (!basicFormRef || !permissFormRef) return;
Promise.all([
basicFormRef.value?.validate(),
permissFormRef.value?.validate(),
])
.then(() => {
const api = routeParams.id ? saveMenuInfo_api : addMenuInfo_api;
form.saveLoading = true;
const accessSupportValue = form.data.accessSupport;
const params = {
...form.data,
accessSupport: {
value: accessSupportValue,
label:
accessSupportValue === 'unsupported'
? '不支持'
: accessSupportValue === 'support'
? '支持'
: '间接控制',
},
};
api(params)
.then((resp: any) => {
if (resp.status === 200) {
message.success('操作成功!');
//
if (!routeParams.id) {
router.push(
`/system/Menu/detail/${resp.result.id}`,
);
routeParams.id = resp.result.id;
form.init();
}
} else {
message.error('操作失败!');
}
})
.finally(() => (form.saveLoading = false));
})
.catch((err) => {});
},
}); });
form.init(); form.init();
//
const ChooseIconRef = ref<any>(null); const ChooseIconRef = ref<any>(null);
const dialog = { const dialog = {
openDialog: () => { openDialog: () => {
@ -272,36 +328,6 @@ const dialog = {
form.data.icon = typeStr || form.data.icon; form.data.icon = typeStr || form.data.icon;
}, },
}; };
const saveLoading = ref<boolean>(false);
const clickSave = () => {
if (!basicFormRef || !permissFormRef) return;
Promise.all([
basicFormRef.value?.validate(),
permissFormRef.value?.validate(),
])
.then(() => {
const api = routeParams.id ? saveMenuInfo_api : addMenuInfo_api;
saveLoading.value = true;
api(form.data)
.then((resp: any) => {
if (resp.status === 200) {
message.success('操作成功!');
//
if (!routeParams.id) {
router.push(
`/system/Menu/detail/${resp.result.id}`,
);
routeParams.id = resp.result.id;
form.init();
}
} else {
message.error('操作失败!');
}
})
.finally(() => (saveLoading.value = false));
})
.catch((err) => {});
};
type formType = { type formType = {
name: string; name: string;
@ -314,6 +340,7 @@ type formType = {
accessSupport: string; accessSupport: string;
assetType: string | undefined; assetType: string | undefined;
indirectMenus: any[]; indirectMenus: any[];
parentId?: string;
}; };
type assetType = { type assetType = {

View File

@ -5,6 +5,7 @@
:columns="table.columns" :columns="table.columns"
model="TABLE" model="TABLE"
:dataSource="table.tableData" :dataSource="table.tableData"
noPagination
> >
<template #headerTitle> <template #headerTitle>
<a-button <a-button
@ -33,7 +34,7 @@
type="link" type="link"
@click="() => dialog.openDialog('查看', slotProps)" @click="() => dialog.openDialog('查看', slotProps)"
> >
<edit-outlined /> <search-outlined />
</a-button> </a-button>
</a-tooltip> </a-tooltip>
@ -41,7 +42,7 @@
title="确认删除" title="确认删除"
ok-text="确定" ok-text="确定"
cancel-text="取消" cancel-text="取消"
:disabled="slotProps.status" @confirm="table.clickDel(slotProps)"
> >
<a-tooltip> <a-tooltip>
<template #title>删除</template> <template #title>删除</template>
@ -55,7 +56,11 @@
</JTable> </JTable>
<div class="dialog"> <div class="dialog">
<ButtonAddDialog ref="dialogRef" @confirm="dialog.confirm" /> <ButtonAddDialog
ref="dialogRef"
@confirm="dialog.confirm"
:menu-info="menuInfo"
/>
</div> </div>
</div> </div>
</template> </template>
@ -63,12 +68,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { import {
EditOutlined, EditOutlined,
SearchOutlined,
DeleteOutlined, DeleteOutlined,
PlusOutlined, PlusOutlined,
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import ButtonAddDialog from '../components/ButtonAddDialog.vue'; import ButtonAddDialog from '../components/ButtonAddDialog.vue';
import { getMenuInfo_api } from '@/api/system/menu'; import { getMenuInfo_api, saveMenuInfo_api } from '@/api/system/menu';
import { message } from 'ant-design-vue';
// //
const route = useRoute(); const route = useRoute();
@ -82,10 +89,14 @@ const dialogRef = ref<any>(null);
const dialog = { const dialog = {
// //
openDialog: (mode: string, row?: object) => { openDialog: (mode: string, row?: object) => {
dialogRef.value && dialogRef.value.openDialog(mode, row); dialogRef.value && dialogRef.value.openDialog(mode, { ...row });
},
confirm: () => {
table.getList();
}, },
confirm: () => {},
}; };
// -
const menuInfo = ref<any>({});
// //
const table = reactive({ const table = reactive({
columns: [ columns: [
@ -118,9 +129,23 @@ const table = reactive({
getList: () => { getList: () => {
routeParams.id && routeParams.id &&
getMenuInfo_api(routeParams.id).then((resp: any) => { getMenuInfo_api(routeParams.id).then((resp: any) => {
menuInfo.value = resp.result;
table.tableData = resp.result.buttons as tableDataItem[]; table.tableData = resp.result.buttons as tableDataItem[];
}); });
}, },
clickDel: (row: tableDataItem) => {
const buttons = menuInfo.value.buttons.filter(
(item: tableDataItem) => item.id !== row.id,
);
const params = {
...menuInfo.value,
buttons,
};
saveMenuInfo_api(params).then(() => {
message.success('操作成功');
table.getList();
});
},
}); });
table.getList(); table.getList();
type tableDataItem = { type tableDataItem = {

View File

@ -0,0 +1,16 @@
<template>
<div class="setting-container">
<a-card>
<h5>
<info-circle-outlined />
<span>基于系统源代码中的菜单数据配置系统菜单</span>
</h5>
</a-card>
</div>
</template>
<script setup lang="ts">
import { InfoCircleOutlined } from '@ant-design/icons-vue';
</script>
<style scoped></style>

View File

@ -4,8 +4,12 @@
:title="form.mode" :title="form.mode"
width="660px" width="660px"
@ok="dialog.handleOk" @ok="dialog.handleOk"
:maskClosable="false"
cancelText="取消"
okText="确定"
:confirmLoading="dialog.loading"
> >
<a-form :model="form.data" class="basic-form"> <a-form :model="form.data" class="basic-form" ref="formRef">
<a-form-item <a-form-item
label="编码" label="编码"
name="id" name="id"
@ -34,14 +38,29 @@
</a-form-item> </a-form-item>
<a-form-item <a-form-item
label="权限" label="权限"
:rules="[{ required: true, message: '请选择权限', validator: form.checkPermission, }]" name="permissions"
:rules="[
{
required: true,
message: '请选择权限',
validator: form.checkPermission,
},
]"
> >
<!-- <a-form-item-rest>
<PermissChoose
:first-width="8"
max-height="350px"
v-model:value="form.data.permissions"
:disabled="form.mode === '查看'"
/>
</a-form-item-rest> -->
<PermissChoose <PermissChoose
:first-width="8" :first-width="8"
max-height="350px" max-height="350px"
v-model:value="form.data.permissions" v-model:value="form.data.permissions"
:disabled="form.mode === '查看'" :disabled="form.mode === '查看'"
/> />
</a-form-item> </a-form-item>
<a-form-item label="说明" name="describe"> <a-form-item label="说明" name="describe">
<a-textarea <a-textarea
@ -56,20 +75,56 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { FormInstance, message } from 'ant-design-vue';
import { Rule } from 'ant-design-vue/es/form'; import { Rule } from 'ant-design-vue/es/form';
import PermissChoose from '../components/PermissChoose.vue'; import PermissChoose from '../components/PermissChoose.vue';
import { saveMenuInfo_api } from '@/api/system/menu';
const props = defineProps<{
menuInfo: {
buttons: formType[];
id: string;
};
}>();
const emits = defineEmits(['confirm']); const emits = defineEmits(['confirm']);
const dialog = reactive({ const dialog = reactive({
visible: false, visible: false,
loading: false,
handleOk: () => { handleOk: () => {
dialog.changeVisible(); props.menuInfo.id && formRef.value &&
formRef.value
.validate()
.then(() => {
const buttons = toRaw(props.menuInfo.buttons);
const button = buttons.find(
(item) => item.id === form.data.id,
);
if (button) {
Object.entries(form.data).forEach(([key, value]) => {
button[key] = value;
});
} else buttons.push({ ...form.data });
const params = {
...props.menuInfo,
buttons,
};
dialog.loading = true;
saveMenuInfo_api(params)
.then((resp) => {
dialog.changeVisible();
message.success('操作成功')
emits('confirm');
})
.finally(() => (dialog.loading = false));
})
.catch(() => {});
}, },
changeVisible: (mode?: string, formValue?: formType) => { changeVisible: (mode?: string, formValue?: formType) => {
dialog.visible = !dialog.visible; dialog.visible = !dialog.visible;
form.data = formValue || { ...initForm }; form.data = { ...initForm, ...formValue };
form.mode = mode || ''; form.mode = mode || '';
console.log(1111111111, form.data);
formRef.value && formRef.value.clearValidate();
}, },
}); });
const initForm = { const initForm = {
@ -78,13 +133,14 @@ const initForm = {
permissions: [], permissions: [],
describe: '', describe: '',
} as formType; } as formType;
const formRef = ref<FormInstance>();
const form = reactive({ const form = reactive({
data: { ...initForm }, data: { ...initForm },
mode: '', mode: '',
checkPermission:async (_rule: Rule, value: string[])=>{ checkPermission: async (_rule: Rule, value: string[]) => {
if(!value || value.length < 1) return Promise.reject('请选择权限') if (!value || value.length < 1) return Promise.reject('请选择权限');
return Promise.resolve() return Promise.resolve();
} },
}); });
// //

View File

@ -46,12 +46,13 @@
<script setup lang="ts"> <script setup lang="ts">
import { exportPermission_api } from '@/api/system/permission'; import { exportPermission_api } from '@/api/system/permission';
import { Form } from 'ant-design-vue';
Form.useInjectFormItemContext()
const props = defineProps<{ const props = defineProps<{
value: any[]; value: any[];
firstWidth: number; firstWidth: number;
maxHeight: string; maxHeight: string;
disabled: boolean; disabled?: boolean;
}>(); }>();
const emits = defineEmits(['update:value']); const emits = defineEmits(['update:value']);
const searchValue = ref<string>(''); const searchValue = ref<string>('');
@ -131,18 +132,14 @@ const permission = reactive({
emits('update:value', newProp); emits('update:value', newProp);
}, },
makeList: (checkedValue: any[], sourceList: any[]): permissionType[] => { makeList: (checkedValue: any[], sourceList: any[]): permissionType[] => {
console.log(checkedValue);
const result = sourceList.map((item) => { const result = sourceList.map((item) => {
const checked = checkedValue.find( const checked = checkedValue?.find(
(checkedItem) => checkedItem.permission === item.id, (checkedItem) => checkedItem.permission === item.id,
); );
const options = item.actions.map((actionItem: any) => ({ const options = item.actions.map((actionItem: any) => ({
label: actionItem.name, label: actionItem.name,
value: actionItem.action, value: actionItem.action,
})); }));
console.log(item, checked);
return { return {
id: item.id, id: item.id,
name: item.name, name: item.name,
@ -150,7 +147,7 @@ const permission = reactive({
checkAll: checkAll:
(checked && (checked &&
item.actions && item.actions &&
checked?.actions.length === item.actions.length) || checked.actions.length === item.actions.length) ||
false, false,
indeterminate: indeterminate:
(checked && (checked &&
@ -183,6 +180,9 @@ type paramsType = {
<style lang="less" scoped> <style lang="less" scoped>
.permission-choose-container { .permission-choose-container {
.ant-input-affix-wrapper {
border-color: #d9d9d9 !important;
}
.permission-table { .permission-table {
margin-top: 12px; margin-top: 12px;
font-size: 14px; font-size: 14px;

View File

@ -8,7 +8,6 @@
:request="table.getList" :request="table.getList"
model="TABLE" model="TABLE"
:params="query.params" :params="query.params"
> >
<template #headerTitle> <template #headerTitle>
<a-button <a-button
@ -17,7 +16,7 @@
style="margin-right: 10px" style="margin-right: 10px"
><plus-outlined />新增</a-button ><plus-outlined />新增</a-button
> >
<a-button>菜单实例</a-button> <a-button @click="router.push('/system/Menu/Setting')">菜单实例</a-button>
</template> </template>
<template #createTime="slotProps"> <template #createTime="slotProps">
{{ moment(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss') }} {{ moment(slotProps.createTime).format('YYYY-MM-DD HH:mm:ss') }}
@ -31,7 +30,7 @@
type="link" type="link"
@click="table.toDetails(slotProps)" @click="table.toDetails(slotProps)"
> >
<search-outlined /> <search-outlined />
</a-button> </a-button>
</a-tooltip> </a-tooltip>
<a-tooltip> <a-tooltip>
@ -39,9 +38,9 @@
<a-button <a-button
style="padding: 0" style="padding: 0"
type="link" type="link"
@click="table.toDetails(slotProps)" @click="table.addChildren(slotProps)"
> >
<plus-circle-outlined /> <plus-circle-outlined />
</a-button> </a-button>
</a-tooltip> </a-tooltip>
@ -50,7 +49,6 @@
ok-text="确定" ok-text="确定"
cancel-text="取消" cancel-text="取消"
@confirm="table.clickDel(slotProps)" @confirm="table.clickDel(slotProps)"
:disabled="slotProps.status"
> >
<a-tooltip> <a-tooltip>
<template #title>删除</template> <template #title>删除</template>
@ -66,13 +64,14 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { getMenuTree_api } from '@/api/system/menu'; import { getMenuTree_api, delMenuInfo_api } from '@/api/system/menu';
import { import {
SearchOutlined, SearchOutlined,
DeleteOutlined, DeleteOutlined,
PlusOutlined, PlusOutlined,
PlusCircleOutlined PlusCircleOutlined,
} from '@ant-design/icons-vue'; } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import moment from 'moment'; import moment from 'moment';
const router = useRouter(); const router = useRouter();
@ -217,7 +216,7 @@ const table = reactive({
const resp: any = await getMenuTree_api(params); const resp: any = await getMenuTree_api(params);
const lastItem = resp.result[resp.result.length - 1]; const lastItem = resp.result[resp.result.length - 1];
table.total = lastItem ? lastItem.sortIndex + 1 : 1; table.total = lastItem ? lastItem.sortIndex + 1 : 1;
return { return {
code: resp.message, code: resp.message,
result: { result: {
@ -229,22 +228,32 @@ const table = reactive({
status: resp.status, status: resp.status,
}; };
}, },
addChildren: (row: any) => {
console.log(row);
router.push(
`/system/Menu/detail/:id?pid=${row.id}&basePath=${
row.url || ''
}&sortIndex=${row.children.length + 1}`,
);
},
// //
toDetails: (row: any) => { toDetails: (row: any) => {
router.push( router.push(
`/system/Menu/detail/${row.id || ':id'}?pid=${ `/system/Menu/detail/${row.id || ':id'}?pid=${
row.pid || '' row.pid || ''
}&basePath=${row.basePath || ''}&sortIndex=${table.total}`, }&basePath=${row.url|| ''}&sortIndex=${table.total}`,
); );
}, },
// //
clickDel: (row: any) => { clickDel: (row: any) => {
// delPermission_api(row.id).then((resp: any) => { console.log(row.id);
// if (resp.status === 200) {
// tableRef.value?.reload(); delMenuInfo_api(row.id).then((resp: any) => {
// message.success('!'); if (resp.status === 200) {
// } tableRef.value?.reload();
// }); message.success('操作成功!');
}
});
}, },
// //
refresh: () => { refresh: () => {
@ -257,5 +266,4 @@ const table = reactive({
.menu-container { .menu-container {
padding: 24px; padding: 24px;
} }
</style> </style>