| <template> | 
|   <LoadingLayout :loading="state.loading"> | 
|     <AppContainer> | 
|       <ProTableQueryFilterBar @on-reset="handleReset"> | 
|         <template #query> | 
|           <QueryFilterItem> | 
|             <FieldRadio | 
|               v-model="state.clientType" | 
|               :value-enum="EnumClientTypeText" | 
|               buttonStyle | 
|               @change="getAllModule()" | 
|             /> | 
|           </QueryFilterItem> | 
|           <QueryFilterItem> | 
|             <FieldRadio | 
|               v-model="state.userType" | 
|               :value-enum="EnumUserTypeText" | 
|               buttonStyle | 
|               @change="getAllModule()" | 
|             /> | 
|           </QueryFilterItem> | 
|           <div class="query-filter-list-item"> | 
|             <SearchInput | 
|               v-model="state.searchValue" | 
|               placeholder="菜单名称" | 
|               @on-click-search="handleSearch" | 
|               @keyup.enter="handleSearch()" | 
|             > | 
|             </SearchInput> | 
|           </div> | 
|         </template> | 
|         <template #btn> | 
|           <el-button @click="handleAddOrEditRootModule()" icon="Plus" type="primary" | 
|             >新增</el-button | 
|           > | 
|           <el-button @click="toggleExpand(true)" icon="FolderOpened" type="primary" | 
|             >全部展开</el-button | 
|           > | 
|           <el-button @click="toggleExpand(false)" icon="Folder" type="primary">全部折叠</el-button> | 
|         </template> | 
|       </ProTableQueryFilterBar> | 
|       <ProTableV2 | 
|         :tableData="moduleTreeStore.data" | 
|         :table-props="{ | 
|           rowKey: 'id', | 
|         }" | 
|         :tableRef="(r) => (tableRef = r)" | 
|         :show-seq-column="false" | 
|         :columns="ModuleColumns" | 
|         :operationBtns="ModuleOperationBtns" | 
|         :operationColumnWidth="300" | 
|       > | 
|         <template #columns="{ row, column }"> | 
|           <template v-if="column.property === 'isCache'"> | 
|             <el-switch | 
|               v-show="!ModuleUtils.isRootModule(row)" | 
|               v-model="row.isCache" | 
|               inline-prompt | 
|               @change="(v: boolean) => handleChangeStatus(v, row, 'isCache')" | 
|             ></el-switch> | 
|           </template> | 
|           <template v-else-if="column.property === 'isMenu'"> | 
|             <el-switch | 
|               :modelValue="row.type === EnumMenuType.Menu" | 
|               :onUpdate:modelValue="(v: boolean) =>{ | 
|                  row.type=v?EnumMenuType.Menu:EnumMenuType.Page | 
|               }" | 
|               inline-prompt | 
|               @change="(v: boolean) => handleChangeStatus(v, row, 'isMenu')" | 
|             ></el-switch> | 
|           </template> | 
|           <template v-else-if="column.property === 'enabledMark'"> | 
|             <el-switch | 
|               :modelValue="!row.isDisabled" | 
|               :onUpdate:modelValue="(v: boolean) => row.isDisabled =!v" | 
|               @change="(v: boolean) => handleChangeStatus(v, row, 'enabledMark')" | 
|             ></el-switch> | 
|           </template> | 
|         </template> | 
|       </ProTableV2> | 
|       <AddOrEditModuleDialog | 
|         v-model="editDialogFormVisible" | 
|         v-model:form="editForm" | 
|         :moduleTreeStore="moduleTreeStore" | 
|         :currentAddingOrEditParentModule="currentAddingOrEditParentModule" | 
|         @on-confirm="handleAddOrEditModule" | 
|         @on-cancel="editDialogFormVisible = false" | 
|       /> | 
|       <SubModuleEditDrawer | 
|         v-model="drawerVisible" | 
|         :title="SubModuleTitle[drawerState.type]" | 
|         v-model:data="drawerState.tableData" | 
|         :type="drawerState.type" | 
|         @on-add-sub-module="handleAddSubModule" | 
|         @on-batch-add-column="handleBatchAddColumn" | 
|         @on-save-sub-module="handleSaveSubModule" | 
|         @on-delete-sub-module="handleDeleteSubModule" | 
|         @on-add-fast-btn="handleAddFastBtn" | 
|         @on-batch-save-column="handelBatchSaveColumn" | 
|       /> | 
|     </AppContainer> | 
|   </LoadingLayout> | 
| </template> | 
|   | 
| <script setup lang="ts"> | 
| import { | 
|   ProTableQueryFilterBar, | 
|   ProTableV2, | 
|   AppContainer, | 
|   LoadingLayout, | 
|   OperationBtnType, | 
|   SearchInput, | 
|   QueryFilterItem, | 
|   FieldRadio, | 
| } from '@bole-core/components'; | 
| import { ModuleUtils, TreeModuleDtoGroupDto, Message, TreeStore, flattenTree } from '@/utils'; | 
| import { TableInstance } from 'element-plus'; | 
| import { | 
|   SubModuleType, | 
|   SubModuleTitle, | 
|   SubModuleKey, | 
|   FastBtn, | 
|   ModuleColumns, | 
|   EnumMenuType, | 
|   EnumClientTypeText, | 
|   EnumUserTypeText, | 
| } from '@/constants'; | 
| import SubModuleEditDrawer from './components/SubModuleEditDrawer.vue'; | 
| import AddOrEditModuleDialog from './components/AddOrEditModuleDialog.vue'; | 
| import { useDebounceFn } from '@vueuse/core'; | 
| import _ from 'lodash'; | 
| import { useReset } from '@bole-core/core'; | 
| import * as menuServices from '@/services/api/menu'; | 
| import { DrawerTableDataItem } from './types'; | 
|   | 
| defineOptions({ | 
|   name: 'ModuleManage', | 
| }); | 
|   | 
| const ModuleOperationBtns: OperationBtnType[] = [ | 
|   { | 
|     data: { | 
|       name: '编辑', | 
|     }, | 
|     emits: { | 
|       onClick: (row) => { | 
|         ModuleUtils.isRootModule(row) ? handleAddOrEditRootModule(row) : handleEditChildModule(row); | 
|       }, | 
|     }, | 
|   }, | 
|   { | 
|     data: { | 
|       name: '新增子菜单', | 
|     }, | 
|     emits: { | 
|       onClick: (row) => handleAddChildModule(row), | 
|     }, | 
|   }, | 
|   { | 
|     data: { | 
|       name: '页面按钮', | 
|     }, | 
|     emits: { | 
|       onClick: (row) => openDrawer(row, SubModuleType.PageButton), | 
|     }, | 
|     extraProps: { | 
|       hide: (row) => ModuleUtils.isRootModule(row), | 
|     }, | 
|   }, | 
|   { | 
|     data: { | 
|       name: '数据按钮', | 
|     }, | 
|     emits: { | 
|       onClick: (row) => openDrawer(row, SubModuleType.DataButton), | 
|     }, | 
|     extraProps: { | 
|       hide: (row) => ModuleUtils.isRootModule(row), | 
|     }, | 
|   }, | 
|   { | 
|     data: { | 
|       name: '数据列', | 
|     }, | 
|     emits: { | 
|       onClick: (row) => openDrawer(row, SubModuleType.Column), | 
|     }, | 
|     extraProps: { | 
|       hide: (row) => ModuleUtils.isRootModule(row), | 
|     }, | 
|   }, | 
|   { | 
|     data: { | 
|       name: '删除', | 
|     }, | 
|     emits: { | 
|       onClick: (row) => handleDelete(row), | 
|     }, | 
|     props: { type: 'danger' }, | 
|   }, | 
| ]; | 
| const BaseState = { | 
|   loading: true, | 
|   searchValue: '', | 
|   group: 'default', | 
|   userType: EnumUserType.Operation, | 
|   clientType: EnumClientType.PcWeb, | 
| }; | 
| const state = reactive({ ...BaseState }); | 
| const moduleTreeStore = ref<TreeStore<TreeModuleDtoGroupDto>>(); | 
| // 源数据 | 
| const originModuleTree = ref<TreeModuleDtoGroupDto[]>([]); | 
| const tableRef = ref<TableInstance>(); | 
| // form | 
| const editDialogFormVisible = ref(false); | 
| const BaseFormData = { | 
|   enCode: '', | 
|   name: '', | 
|   icon: '', | 
|   parentId: '', | 
|   sortCode: 0, | 
|   path: '', | 
|   isCache: true, | 
|   isMenu: true, | 
|   enabledMark: true, | 
|   showCacheSelect: false, | 
|   showIconSelect: false, | 
|   showSubMenuIconSelect: false, | 
|   showParentSelect: false, | 
|   title: '', | 
| }; | 
| const editForm = reactive({ ...BaseFormData }); | 
| const currentDialogModule = ref<API.GetMenuQueryResult>({}); | 
| //drawer | 
| const drawerVisible = ref(false); | 
| const currentDrawerModule = ref<API.GetMenuQueryResult>({}); | 
|   | 
| const drawerState = reactive<{ | 
|   type: SubModuleType; | 
|   tableData: DrawerTableDataItem[]; | 
| }>({ | 
|   type: SubModuleType.PageButton, | 
|   tableData: [], | 
| }); | 
| onMounted(async () => { | 
|   await getAllModule(); | 
|   state.loading = false; | 
| }); | 
| async function getAllModule() { | 
|   try { | 
|     let res = await menuServices.getMenus( | 
|       { | 
|         userType: state.userType, | 
|         clientType: state.clientType, | 
|       }, | 
|       { | 
|         showLoading: false, | 
|       } | 
|     ); | 
|     const treeStore = ModuleUtils.convertToModuleGroup(flattenTree(res)); | 
|     moduleTreeStore.value = treeStore; | 
|     originModuleTree.value = [...treeStore.data]; | 
|   } catch (error) {} | 
| } | 
| type StatusEventType = { | 
|   isCache: boolean; | 
|   isMenu: boolean; | 
|   enabledMark: boolean; | 
| }; | 
| async function handleChangeStatus<T extends keyof StatusEventType>( | 
|   value: boolean, | 
|   data: TreeModuleDtoGroupDto, | 
|   statusType: T | 
| ) { | 
|   try { | 
|     let params: API.SetMenuSwitchCommand = { | 
|       ids: [data.id], | 
|       type: data.type, | 
|       isDisabled: data.isDisabled, | 
|       isCache: data.isCache, | 
|     }; | 
|     if (statusType === 'isMenu') { | 
|       params.type = value ? EnumMenuType.Menu : EnumMenuType.Page; | 
|     } else if (statusType === 'isCache') { | 
|       params.isCache = value; | 
|     } else { | 
|       params.isDisabled = !value; | 
|     } | 
|     await menuServices.setMenuSwitch(params); | 
|   } catch (error) {} | 
| } | 
|   | 
| async function getMenu(id: string) { | 
|   return await menuServices.getMenu({ id: id }); | 
| } | 
|   | 
| async function openDialog(options: { | 
|   module?: TreeModuleDtoGroupDto; | 
|   showCacheSelect: boolean; | 
|   showIconSelect: boolean; | 
|   showSubMenuIconSelect: boolean; | 
|   showParentSelect: boolean; | 
|   parentId: string; | 
|   baseSortCode?: number; | 
| }) { | 
|   try { | 
|     const { | 
|       module, | 
|       showCacheSelect, | 
|       showIconSelect, | 
|       showSubMenuIconSelect, | 
|       showParentSelect, | 
|       parentId, | 
|       baseSortCode, | 
|     } = options; | 
|     if (module) { | 
|       currentDialogModule.value = await getMenu(module.id); | 
|       useSetReactive(editForm, { | 
|         title: `${module.name}编辑`, | 
|         enCode: module.code, | 
|         name: module.name, | 
|         icon: module.icon, | 
|         sortCode: module.sort, | 
|         path: module.url, | 
|         isMenu: module.type == EnumMenuType.Menu, | 
|         enabledMark: !module.isDisabled, | 
|         parentId: parentId, | 
|         showCacheSelect, | 
|         showIconSelect, | 
|         showSubMenuIconSelect, | 
|         showParentSelect, | 
|         isCache: module.isCache, | 
|       }); | 
|     } else { | 
|       currentDialogModule.value = {}; | 
|       useSetReactive(editForm, BaseFormData, { | 
|         title: '新增功能模块', | 
|         parentId: parentId, | 
|         showCacheSelect, | 
|         showIconSelect, | 
|         showSubMenuIconSelect, | 
|         showParentSelect, | 
|         sortCode: baseSortCode, | 
|       }); | 
|     } | 
|     editDialogFormVisible.value = true; | 
|   } catch (error) {} | 
| } | 
| function handleAddOrEditRootModule(module?: TreeModuleDtoGroupDto) { | 
|   const showCacheSelect = false; | 
|   const showIconSelect = true; | 
|   const showSubMenuIconSelect = false; | 
|   const showParentSelect = false; | 
|   const parentId = ''; | 
|   openDialog({ | 
|     module, | 
|     showCacheSelect, | 
|     showIconSelect, | 
|     showSubMenuIconSelect, | 
|     showParentSelect, | 
|     parentId, | 
|     baseSortCode: ModuleUtils.getSortCode(moduleTreeStore.value, module) + 1, | 
|   }); | 
| } | 
| //当前正在新增子菜单的module或正在编辑的子菜单的父级菜单 | 
| const currentAddingOrEditParentModule = ref<TreeModuleDtoGroupDto>({}); | 
| function handleAddChildModule(parentModule: TreeModuleDtoGroupDto) { | 
|   handleAddOrEditChildModule(parentModule); | 
| } | 
| function handleEditChildModule(module: TreeModuleDtoGroupDto) { | 
|   const parentModule = ModuleUtils.getParentModule(moduleTreeStore.value, module); | 
|   handleAddOrEditChildModule(parentModule, module); | 
| } | 
| function handleAddOrEditChildModule( | 
|   parentModule: TreeModuleDtoGroupDto, | 
|   module?: TreeModuleDtoGroupDto | 
| ) { | 
|   const showCacheSelect = true; | 
|   const showIconSelect = false; | 
|   const showParentSelect = true; | 
|   const parentId = parentModule.id; | 
|   currentAddingOrEditParentModule.value = parentModule; | 
|   //如果是编辑 SortCode为上一个加1 | 
|   const baseSortCode = ModuleUtils.getLastChildSortCode(moduleTreeStore.value, parentModule) + 1; | 
|   openDialog({ | 
|     module: module, | 
|     showCacheSelect, | 
|     showIconSelect, | 
|     showSubMenuIconSelect: true, | 
|     showParentSelect, | 
|     parentId, | 
|     baseSortCode, | 
|   }); | 
| } | 
| async function handleAddOrEditModule() { | 
|   try { | 
|     let params: API.SaveMenuCommand = { | 
|       userType: state.userType, | 
|       clientType: state.clientType, | 
|       code: editForm.enCode, | 
|       name: editForm.name, | 
|       type: editForm.isMenu ? EnumMenuType.Menu : EnumMenuType.Page, | 
|       visitLevel: EnumMenuVisitLevel.NeedPower, | 
|       icon: editForm.icon, | 
|       url: editForm.path, | 
|       isDisabled: !editForm.enabledMark, | 
|       sort: editForm.sortCode, | 
|     }; | 
|     if (editForm.showCacheSelect) { | 
|       params.isCache = editForm.isCache; | 
|     } | 
|     if (editForm.parentId) { | 
|       params.parentId = editForm.parentId; | 
|     } | 
|     if (currentDialogModule.value.id) { | 
|       params.id = currentDialogModule.value.id; | 
|       params.groups = currentDialogModule.value.groups; | 
|     } | 
|     const res = await saveMenu(params); | 
|     if (res) { | 
|       Message.successMessage('保存成功'); | 
|       getAllModule(); | 
|       editDialogFormVisible.value = false; | 
|     } | 
|   } catch (e) {} | 
| } | 
|   | 
| async function saveMenu(params: API.SaveMenuCommand) { | 
|   try { | 
|     const res = await menuServices.saveMenu(params); | 
|     return res; | 
|   } catch (error) {} | 
| } | 
|   | 
| async function handleDelete(module: TreeModuleDtoGroupDto) { | 
|   try { | 
|     await Message.deleteMessage(); | 
|     const res = await menuServices.deleteMenu({ ids: [module.id] }); | 
|     if (res) { | 
|       Message.successMessage('删除成功'); | 
|       getAllModule(); | 
|     } | 
|   } catch (error) {} | 
| } | 
| async function openDrawer(module: TreeModuleDtoGroupDto, type: SubModuleType) { | 
|   try { | 
|     drawerState.type = type; | 
|     getBaseModuleGetAllSubModule(module, type); | 
|   } catch (error) {} | 
| } | 
| async function getBaseModuleGetAllSubModule(module: TreeModuleDtoGroupDto, type: SubModuleType) { | 
|   try { | 
|     const menu = await getMenu(module.id); | 
|     currentDrawerModule.value = menu; | 
|     const currentGroup = menu.groups.find((g) => g.group === state.group) ?? {}; | 
|     if (type === SubModuleType.Column) { | 
|       drawerState.tableData = (currentGroup.fields ?? []).map((f) => ({ | 
|         enCode: f.code, | 
|         name: f.name, | 
|         sortCode: f.sort, | 
|         width: f.width, | 
|         isEdit: false, | 
|         isShow: false, | 
|         id: f.id, | 
|       })); | 
|     } else { | 
|       drawerState.tableData = ( | 
|         currentGroup.buttonLocations?.find((b) => b.location === SubModuleKey[type])?.buttons ?? [] | 
|       ).map((b) => ({ | 
|         enCode: b.code, | 
|         name: b.name, | 
|         sortCode: b.sort, | 
|         width: b.width, | 
|         isEdit: false, | 
|         isShow: false, | 
|         id: b.id, | 
|       })); | 
|     } | 
|     drawerVisible.value = true; | 
|   } catch (error) {} | 
| } | 
| function handleAddSubModule() { | 
|   let sortCode = getDefaultSortCode(); | 
|   let baseData = { | 
|     enCode: '', | 
|     name: '', | 
|     sortCode: sortCode, | 
|     width: 0 as any as string, | 
|     isEdit: true, | 
|     // apiName: '', | 
|     // realColumn: '', | 
|     isShow: true, | 
|   }; | 
|   drawerState.tableData?.push(baseData); | 
| } | 
| function getDefaultSortCode() { | 
|   return drawerState.tableData.length > 0 | 
|     ? drawerState.tableData[drawerState.tableData.length - 1].sortCode + 1 | 
|     : 0; | 
| } | 
| function handleBatchAddColumn(keys: string[]) { | 
|   let sortCode = getDefaultSortCode(); | 
|   let baseDataList = keys.map((key, index) => ({ | 
|     enCode: key, | 
|     name: '', | 
|     sortCode: sortCode + index, | 
|     width: 0 as any as string, | 
|     isEdit: true, | 
|   })); | 
|   drawerState.tableData?.push(...baseDataList); | 
| } | 
| function handleAddFastBtn(fastBtn: FastBtn) { | 
|   let subModule: DrawerTableDataItem = { | 
|     enCode: fastBtn.enCode, | 
|     name: fastBtn.name, | 
|     sortCode: getDefaultSortCode(), | 
|   }; | 
|   if (drawerState.type === SubModuleType.Column) { | 
|     //@ts-ignore | 
|     subModule.width = fastBtn.width; | 
|   } | 
|   handleSaveSubModule(subModule); | 
| } | 
| async function handleSaveSubModule(subModule: DrawerTableDataItem) { | 
|   try { | 
|     let params: API.SaveMenuFieldCommand = { | 
|       parentId: currentDrawerModule.value.id, | 
|       code: subModule.enCode, | 
|       name: subModule.name, | 
|       group: state.group, | 
|       // location: 'string', | 
|       width: subModule.width, | 
|       sort: subModule.sortCode, | 
|     }; | 
|     if (subModule.id) { | 
|       params.id = subModule.id; | 
|     } | 
|     if (drawerState.type === SubModuleType.Column) { | 
|     } else { | 
|       params.location = SubModuleKey[drawerState.type]; | 
|     } | 
|     let res = null; | 
|     if (drawerState.type === SubModuleType.Column) { | 
|       res = await menuServices.saveMenuField(params); | 
|     } else { | 
|       res = await menuServices.saveMenuButton(params); | 
|     } | 
|     if (res) { | 
|       getBaseModuleGetAllSubModule(currentDrawerModule.value, drawerState.type); | 
|       Message.successMessage('保存成功'); | 
|     } | 
|   } catch (error) {} | 
| } | 
| async function handelBatchSaveColumn() { | 
|   try { | 
|     if (drawerState.type === SubModuleType.Column) { | 
|       let columnModuleList = drawerState.tableData; //.filter((d) => d.isEdit); | 
|       if (columnModuleList.length > 0) { | 
|         const groups = currentDrawerModule.value.groups.map((group) => { | 
|           if (group.group === state.group) { | 
|             group.fields = columnModuleList.map((c) => ({ | 
|               code: c.enCode, | 
|               name: c.name, | 
|               width: c.width, | 
|               sort: c.sortCode, | 
|             })); | 
|           } | 
|           return group; | 
|         }); | 
|         let params: API.SaveMenuCommand = { | 
|           ...currentDrawerModule.value, | 
|           groups: groups, | 
|         }; | 
|         const res = await saveMenu(params); | 
|         if (res) { | 
|           getBaseModuleGetAllSubModule(currentDrawerModule.value, drawerState.type); | 
|           Message.successMessage('保存成功'); | 
|         } | 
|       } | 
|     } | 
|   } catch (error) {} | 
| } | 
|   | 
| async function handleDeleteSubModule(subModule: DrawerTableDataItem) { | 
|   try { | 
|     await Message.deleteMessage(); | 
|     let res = await menuServices.deleteMenu({ | 
|       ids: [subModule.id], | 
|     }); | 
|     if (res) { | 
|       Message.successMessage('删除成功'); | 
|       getBaseModuleGetAllSubModule(currentDrawerModule.value, drawerState.type); | 
|     } | 
|   } catch (error) {} | 
| } | 
| function toggleExpand(expanded: boolean) { | 
|   if (!tableRef.value) return; | 
|   moduleTreeStore.value.data.forEach((module) => { | 
|     tableRef.value?.toggleRowExpansion(module, expanded); | 
|   }); | 
| } | 
| const handleSearch = useDebounceFn(() => { | 
|   if (state.searchValue) { | 
|     // @ts-ignore | 
|     moduleTreeStore.value.data = ModuleUtils.filterModuleTree( | 
|       state.searchValue, | 
|       originModuleTree.value | 
|     ); | 
|     nextTick(() => { | 
|       toggleExpand(true); | 
|     }); | 
|   } else { | 
|     moduleTreeStore.value.data = originModuleTree.value; | 
|   } | 
| }); | 
| const handleReset = useReset(state, BaseState, getAllModule); | 
| </script> |