<template> <div class="menu-manage-content"> <a-page-header class="ns-page-header" title="栏目管理"> <template #extra> <!-- todo 隐藏取消--> <a-button type="primary" @click="submit">提交</a-button> <a-button style="margin-left: 10px" @click="navigateBack">取消</a-button> </template> </a-page-header> <ns-tree v-model:expandedKeys="expandedKeys" :tree-data="treeData" :fieldNames="fieldNames" :autoExpandParent="true" :draggable="true" @drop="drop"> <template #title="{ code: treeKey, label }"> <a-dropdown :trigger="['contextmenu']"> <span>{{ label }}</span> <template #overlay> <a-menu @click="({ key: menuKey }) => onContextMenuClick(treeKey, menuKey, label)"> <a-menu-item key="edit">编辑</a-menu-item> </a-menu> </template> </a-dropdown> </template> </ns-tree> </div> <a-drawer title="栏目编辑" :width="500" :visible="visible" :body-style="{ paddingBottom: '80px' }" :footer-style="{ textAlign: 'right' }" @close="onClose"> <ns-form :schemas="formSchema" :model="formData" /> <a-button style="margin-right: 8px" @click="onClose">取消</a-button> <a-button type="primary" @click="onEdit">提交</a-button> </a-drawer> </template> <script lang="ts"> import { computed, defineComponent, reactive, ref, watch } from 'vue'; import { cloneDeep } from 'lodash-es'; import { dropMenu, RouteMenu, editMenuLabel } from '/nerv-base/router/helper/menu-helper'; import { message } from 'ant-design-vue'; import { useRouteStore } from '/nerv-base/store/modules/route'; import { storeToRefs } from 'pinia'; import { useNavigate } from '/nerv-lib/use/use-navigate'; import { appConfigStore } from '/nerv-base/store/modules/app-config'; export default defineComponent({ name: 'NsViewMenuManage', components: {}, setup() { const { routeModule } = storeToRefs(useRouteStore()); const routeStore = useRouteStore(); const visible = ref(false); const onClose = () => { visible.value = false; }; const { navigateBack } = useNavigate(); const formData = reactive({ key: '', className: '', }); const formSchema = reactive([ { field: 'className', label: '栏目名称', component: 'NsInput', componentProps: { placeholder: '请输入栏目名称', rules: [ { required: true, message: '请输入栏目名称', trigger: 'blur', }, ], }, }, ]); /** * 去除菜单里的最终子节点 * @param data */ function getTreeData(data: RouteMenu[]) { const list = cloneDeep(data); const _list: any = []; list.forEach((module) => { module.menus && (module.menus = checkMenu(module.menus)); if (module.code !== 'root') _list.push(module); }); return _list; } function checkMenu(menus) { menus.forEach((children) => { if (children.route?.meta?.hideChildren) { children.menus = []; } else { if (children.menus) children.menus = checkMenu(children.menus); } }); return menus; } const treeData = computed(() => { return getTreeData(routeModule.value); }); const expandedKeys = ref<string[]>([]); const fieldNames = { children: 'menus', title: 'label', key: 'code' }; function drop(info) { console.log('event', info); const data = dropMenu(routeModule.value, info); if (data.success === false) { message.error(data.msg); } if (data.success === true) { // 移动成功 } } const onContextMenuClick = (treeKey: string, menuKey: string | number, label: string) => { switch (menuKey) { case 'edit': visible.value = true; formData.key = treeKey; formData.className = label; break; default: message.error('无此操作'); } console.log(`treeKey: ${treeKey}, menuKey: ${menuKey}`, label); }; const onEdit = () => { visible.value = false; editMenuLabel(routeModule.value, formData.key, formData.className); // console.log('formData.className', formData.key, formData.className); }; //保存 function submit() { //todo 提交给后端 routeModule.value 成功后同步菜单 const appConfig = appConfigStore(); const routeModuleObject: Recordable = {}; function loop(routeModule: ModuleMenu[]) { for (let i = 0, j = routeModule.length; i < j; i++) { routeModuleObject[routeModule[i].code] = routeModule[i]; routeModule[i].menus && loop(routeModule[i].menus); } } loop(routeStore.routeModule); routeStore.routeModuleObject = routeModuleObject; function loopRoute(routeList) { for (let i = 0, j = routeList.length; i < j; i++) { const route = routeList[i]; !route.meta && (route.meta = {}); if (routeModuleObject[route.name] === undefined) { console.error(`route ${route.name} is not in menu`); return; } if (!route.meta.index || route.meta.index !== routeModuleObject[route.name].sort) { route.meta.index = routeModuleObject[route.name].sort; } if (!route.meta.title || route.meta.title !== routeModuleObject[route.name].label) { route.meta.title = routeModuleObject[route.name].label; } route.children && loopRoute(route.children); } } loopRoute(routeStore.route); routeStore.route.sort((a, b) => { return a.meta?.index - b.meta?.index; }); const initPcResource = { application: {}, menus: [] }; routeStore.routeModule.sort((a, b) => { return a.route?.meta?.index - b.route?.meta?.index; }); const info = JSON.parse(JSON.stringify(routeStore.routeModule)); initRouteMouleList(info); function initRouteMouleList(info) { info.forEach((item) => { if (item.menus) { initRouteMouleList(item.menus); } delete item.route; }); } initPcResource.application = appConfig.resourceInfo?.application as object; // initPcResource.menus = appConfig.resourceInfo?.dealReosurceList // ? appConfig.resourceInfo?.dealReosurceList(info) // : info; initPcResource.menus = info; console.log('-----------', info); allResoutceList.value = []; info.forEach((item) => { initResourceList(item); }); // testTags.testEdit(2); // routeStore.syncRoute(); } const allResoutceList = ref([]); const initResourceList = (info: object) => { if (allResoutceList.value.findIndex((x) => x === info.code) === -1) { allResoutceList.value.push(info.code); if (info.menus && info.menus.length) { info.menus.forEach((item: object) => { initResourceList(item); }); } } else { console.error(`${info.code}已存在,${info.label}`); return; } }; return { navigateBack, expandedKeys, fieldNames, treeData, drop, onContextMenuClick, visible, onClose, onEdit, formSchema, formData, submit, }; }, }); </script> <style lang="less" scoped> .menu-manage-content { padding: 0 16px 16px 16px; } :deep(.ant-tree .ant-tree-treenode) { padding: 2px 0 4px 0; } </style>