|
27 | 27 | <el-col :span="1.5"> |
28 | 28 | <el-button v-hasPermi="['system:menu:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增 </el-button> |
29 | 29 | </el-col> |
30 | | - <el-col :span="1.5"> |
31 | | - <el-button type="info" plain icon="Sort" @click="handleToggleExpandAll">展开/折叠</el-button> |
32 | | - </el-col> |
33 | 30 | <el-col :span="1.5"> |
34 | 31 | <el-button type="danger" plain icon="Delete" @click="handleCascadeDelete" :loading="deleteLoading">级联删除</el-button> |
35 | 32 | </el-col> |
|
44 | 41 | row-key="menuId" |
45 | 42 | border |
46 | 43 | :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" |
47 | | - :default-expand-all="isExpandAll" |
| 44 | + :default-expand-all="false" |
| 45 | + lazy |
| 46 | + :load="getChildrenList" |
48 | 47 | > |
49 | 48 | <el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column> |
50 | 49 | <el-table-column prop="icon" label="图标" align="center" width="100"> |
@@ -299,10 +298,11 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance; |
299 | 298 | const { sys_show_hide, sys_normal_disable } = toRefs<any>(proxy?.useDict('sys_show_hide', 'sys_normal_disable')); |
300 | 299 |
|
301 | 300 | const menuList = ref<MenuVO[]>([]); |
| 301 | +const menuChildrenListMap = ref({}); |
| 302 | +const menuExpandMap = ref({}); |
302 | 303 | const loading = ref(true); |
303 | 304 | const showSearch = ref(true); |
304 | 305 | const menuOptions = ref<MenuOptionsType[]>([]); |
305 | | -const isExpandAll = ref(false); |
306 | 306 |
|
307 | 307 | const dialog = reactive<DialogOption>({ |
308 | 308 | visible: false, |
@@ -340,14 +340,62 @@ const data = reactive<PageData<MenuForm, MenuQuery>>({ |
340 | 340 | const menuTableRef = ref<ElTableInstance>(); |
341 | 341 |
|
342 | 342 | const { queryParams, form, rules } = toRefs<PageData<MenuForm, MenuQuery>>(data); |
| 343 | +
|
| 344 | +/** 获取子菜单列表 */ |
| 345 | +const getChildrenList = async (row: any, treeNode: unknown, resolve: (data: any[]) => void) => { |
| 346 | + menuExpandMap.value[row.menuId] = { row, treeNode, resolve }; |
| 347 | + const children = menuChildrenListMap.value[row.menuId] || []; |
| 348 | + // 菜单的子菜单清空后关闭展开 |
| 349 | + if (children.length == 0) { |
| 350 | + // fix: 处理当菜单只有一个子菜单并被删除,需要将父菜单的展开状态关闭 |
| 351 | + menuTableRef.value?.updateKeyChildren(row.menuId, children); |
| 352 | + } |
| 353 | + resolve(children); |
| 354 | +}; |
| 355 | +
|
| 356 | +/** 刷新展开的菜单数据 */ |
| 357 | +const refreshLoadTree = (parentId: string | number) => { |
| 358 | + if (menuExpandMap.value[parentId]) { |
| 359 | + const { row, treeNode, resolve } = menuExpandMap.value[parentId]; |
| 360 | + if (row) { |
| 361 | + getChildrenList(row, treeNode, resolve); |
| 362 | + if (row.parentId) { |
| 363 | + const grandpaMenu = menuExpandMap.value[row.parentId]; |
| 364 | + getChildrenList(grandpaMenu.row, grandpaMenu.treeNode, grandpaMenu.resolve); |
| 365 | + } |
| 366 | + } |
| 367 | + } |
| 368 | +}; |
| 369 | +
|
| 370 | +/** 重新加载所有已展开的菜单的数据 */ |
| 371 | +const refreshAllExpandMenuData = () => { |
| 372 | + for (const menuId in menuExpandMap.value) { |
| 373 | + refreshLoadTree(menuId); |
| 374 | + } |
| 375 | +}; |
| 376 | +
|
343 | 377 | /** 查询菜单列表 */ |
344 | 378 | const getList = async () => { |
345 | 379 | loading.value = true; |
346 | 380 | const res = await listMenu(queryParams.value); |
347 | | - const data = proxy?.handleTree<MenuVO>(res.data, 'menuId'); |
348 | | - if (data) { |
349 | | - menuList.value = data; |
| 381 | +
|
| 382 | + const tempMap = {}; |
| 383 | + // 存储 父菜单:子菜单列表 |
| 384 | + for (const menu of res.data) { |
| 385 | + const parentId = menu.parentId; |
| 386 | + if (!tempMap[parentId]) { |
| 387 | + tempMap[parentId] = []; |
| 388 | + } |
| 389 | + tempMap[parentId].push(menu); |
350 | 390 | } |
| 391 | + // 设置有没有子菜单 |
| 392 | + for (const menu of res.data) { |
| 393 | + menu['hasChildren'] = tempMap[menu.menuId]?.length > 0; |
| 394 | + } |
| 395 | + menuChildrenListMap.value = tempMap; |
| 396 | + menuList.value = tempMap[0] || []; |
| 397 | + // 根据新数据重新加载子菜单数据 |
| 398 | + refreshAllExpandMenuData(); |
351 | 399 | loading.value = false; |
352 | 400 | }; |
353 | 401 | /** 查询菜单下拉树结构 */ |
@@ -386,18 +434,6 @@ const handleAdd = (row?: MenuVO) => { |
386 | 434 | dialog.visible = true; |
387 | 435 | dialog.title = '添加菜单'; |
388 | 436 | }; |
389 | | -/** 展开/折叠操作 */ |
390 | | -const handleToggleExpandAll = () => { |
391 | | - isExpandAll.value = !isExpandAll.value; |
392 | | - toggleExpandAll(menuList.value, isExpandAll.value); |
393 | | -}; |
394 | | -/** 展开/折叠所有 */ |
395 | | -const toggleExpandAll = (data: MenuVO[], status: boolean) => { |
396 | | - data.forEach((item: MenuVO) => { |
397 | | - menuTableRef.value?.toggleRowExpansion(item, status); |
398 | | - if (item.children && item.children.length > 0) toggleExpandAll(item.children, status); |
399 | | - }); |
400 | | -}; |
401 | 437 | /** 修改按钮操作 */ |
402 | 438 | const handleUpdate = async (row: MenuVO) => { |
403 | 439 | reset(); |
|
0 commit comments