refactor(管理端): 优化菜单路由与图片选择遍历,去除嵌套循环与冗余深克隆

This commit is contained in:
From-wh 2026-10-10 11:44:13 +08:00 committed by 吴昊天
parent 288913c70c
commit 161050c4aa
7 changed files with 72 additions and 91 deletions

View File

@ -636,31 +636,23 @@ function showSelectData() {
function getRowKey(row) {
return row.att_id;
}
//对象数组去重;
//对象数组去重(按 att_id):Set 单趟完成,替代 reduce 内 find+concat 的双重 O(n²)
function unique(arr) {
let result = arr.reduce((acc, curr) => {
const x = acc.find((item) => item.att_id === curr.att_id);
if (!x) {
return acc.concat([curr]);
} else {
return acc;
const seen = new Set();
const result = [];
for (const item of arr) {
if (!seen.has(item.att_id)) {
seen.add(item.att_id);
result.push(item);
}
}, []);
}
return result;
}
// 选中某一行
function handleSelectRow(selection) {
let arr = unique(selection);
const uniqueArr = [];
const idsArr = [];
for (let i = 0; i < arr.length; i++) {
const item = arr[i];
if (!idsArr.includes(item.att_id)) {
uniqueArr.push(item);
idsArr.push(item.att_id);
}
}
ids.value = idsArr;
// unique 已保证 att_id 唯一,无需再做一遍 includes 去重
const uniqueArr = unique(selection);
ids.value = uniqueArr.map((item) => item.att_id);
multipleSelection.value = uniqueArr;
}
function pageChange(index) {
@ -727,13 +719,14 @@ function changImage(item, index, row) {
checkPicList.value.map((item, i) => {
ids.value.push(item.att_id);
});
// 已选列表先建 att_id → 序号 映射,再给整页图片编号;
// 原实现对每个选中图片都全量扫描 checkPicList,点选一张是 整页×已选 的嵌套遍历
const numMap = new Map(checkPicList.value.map((el, i) => [el.att_id, i + 1]));
pictrueList.value.map((el, i) => {
if (el.isSelect) {
checkPicList.value.filter((el2, j) => {
if (el.att_id == el2.att_id) {
el.num = j + 1;
}
});
if (numMap.has(el.att_id)) {
el.num = numMap.get(el.att_id);
}
} else {
el.num = 0;
}

View File

@ -35,7 +35,6 @@
import { ref, computed, onMounted } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { Local } from '@/utils/storage.js';
import { R } from '@/libs/util';
import { useMenusStore } from '@/store/module/menus';
import { useRoutesListStore } from '@/store/module/routesList';
import { useThemeConfigStore } from '@/store/module/themeConfig';
@ -77,16 +76,16 @@ const breadCrumbList = computed(() => {
});
const crumbPast = computed(() => {
let menuList = menusStore.menusName;
let allMenuList = R(menuList, []);
let selectMenu = [];
if (allMenuList.length > 0) {
allMenuList.forEach((a) => {
if (route.path === a.path) {
selectMenu.push(a);
}
});
}
// 只收集 path 命中的菜单节点引用:原先整树扁平化 + 逐属性深克隆后过滤,
// 每次面包屑重算都白付整棵树的克隆成本,而模板只读 path/icon/title/redirect
const selectMenu = [];
const walk = (menus) => {
for (const menu of menus) {
if (route.path === menu.path) selectMenu.push(menu);
if (menu.children && menu.children.length) walk(menu.children);
}
};
walk(menusStore.menusName);
return selectMenu;
});

View File

@ -13,17 +13,13 @@
* 因为用户权限 access 为数组,includes 方法无法直接得出结论
* */
function includeArray(list1, list2) {
let status = false;
if (list1 === true) {
return true;
} else {
if (typeof list2 !== 'object') {
return false;
}
list2.forEach((item) => {
if (list1.includes(item)) status = true;
});
return status;
}
if (typeof list2 !== 'object') {
return false;
}
// some 命中即短路;路由守卫每次导航都会调用
return list2.some((item) => list1.includes(item));
}
export { includeArray };

View File

@ -13,12 +13,11 @@ function getHeaderName(to, menuList) {
const allMenus = [];
menuList.forEach((menu) => {
const headerName = menu.path || '';
const menus = transferMenu(menu, headerName);
allMenus.push({
path: menu.path,
header: headerName,
});
menus.forEach((item) => allMenus.push(item));
transferMenu(menu, headerName, allMenus);
});
const currentMenu = allMenus.find((item) => {
let path = to.meta && to.meta.activeMenu ? to.meta.activeMenu : to.path;
@ -43,19 +42,19 @@ function getPath(to, path) {
return path + (params.length ? '/' + params.join('/') : '') + (query.length ? '?' + query.join('&') : '');
}
function transferMenu(menu, headerName) {
function transferMenu(menu, headerName, out) {
if (menu.children && menu.children.length) {
return menu.children.reduce((all, item) => {
all.push({
for (const item of menu.children) {
out.push({
path: item.path,
header: headerName,
});
const foundChildren = transferMenu(item, headerName);
return all.concat(foundChildren);
}, []);
transferMenu(item, headerName, out);
}
} else {
return [menu];
out.push(menu);
}
return out;
}
export { getHeaderName };
@ -166,8 +165,7 @@ function getAllSiderMenu(menuList) {
menuList.forEach((menu) => {
if (menu.children && menu.children.length) {
const menus = getMenuChildren(menu);
menus.forEach((item) => allMenus.push(item));
getMenuChildren(menu, allMenus);
} else {
allMenus.push(menu);
}
@ -176,15 +174,13 @@ function getAllSiderMenu(menuList) {
return allMenus;
}
function getMenuChildren(menu) {
function getMenuChildren(menu, out) {
if (menu.children && menu.children.length) {
return menu.children.reduce((all, item) => {
const foundChildren = getMenuChildren(item);
return all.concat(foundChildren);
}, []);
menu.children.forEach((item) => getMenuChildren(item, out));
} else {
return [menu];
out.push(menu);
}
return out;
}
export { getAllSiderMenu };
@ -243,15 +239,18 @@ export const findFirstNonNullChildrenKeys = (obj, lastArr) => {
return ids;
};
// 多级嵌套数组处理成一维数组
// 多级嵌套数组处理成一维数组(先序遍历,单趟收集,避免逐个 slice+concat 的 O(n²) 拷贝)
export const formatFlatteningRoutes = (arr) => {
if (arr.length <= 0) return false;
for (let i = 0; i < arr.length; i++) {
if (arr[i].children) {
arr = arr.slice(0, i + 1).concat(arr[i].children, arr.slice(i + 1));
const flat = [];
const walk = (list) => {
for (const item of list) {
flat.push(item);
if (item.children) walk(item.children);
}
}
return arr;
};
walk(arr);
return flat;
};
/**
@ -259,17 +258,13 @@ export const formatFlatteningRoutes = (arr) => {
* 因为用户权限 access 为数组,includes 方法无法直接得出结论
* */
function includeArray(list1, list2) {
let status = false;
if (list1 === true) {
return true;
} else {
if (typeof list2 !== 'object') {
return false;
}
list2.forEach((item) => {
if (list1.includes(item)) status = true;
});
return status;
}
if (typeof list2 !== 'object') {
return false;
}
// some 命中即短路;权限过滤对每个菜单节点都会调用
return list2.some((item) => list1.includes(item));
}
export { includeArray };

View File

@ -432,12 +432,10 @@ export const setTitle = (routeItem, vm) => {
export const R = (menuList, newOpenMenus) => {
menuList.forEach((item) => {
let newMenu = {};
for (let i in item) {
if (i !== 'children') newMenu[i] = cloneDeep(item[i]);
}
const { children, ...rest } = item;
const newMenu = cloneDeep(rest);
newOpenMenus.push(newMenu);
item.children && R(item.children, newOpenMenus);
children && R(children, newOpenMenus);
});
return newOpenMenus;
};

View File

@ -70,18 +70,19 @@ function findMenuTitleByPath(path, menuList) {
*/
function findBestMenuPath(currentPath, menuList) {
if (!menuList || !menuList.length) return null;
const flatPaths = [];
// Set 查找 O(1):该函数在每次路由导航都会执行,菜单树全量收集后反复 includes 会放大整体开销
const flatPaths = new Set();
(function collect(list) {
for (const item of list) {
if (item.path) flatPaths.push(item.path);
if (item.path) flatPaths.add(item.path);
if (item.children && item.children.length) collect(item.children);
}
})(menuList);
if (flatPaths.includes(currentPath)) return currentPath;
if (flatPaths.has(currentPath)) return currentPath;
const parts = currentPath.split('/').filter(Boolean);
for (let i = parts.length - 1; i > 0; i--) {
const parentPath = '/' + parts.slice(0, i).join('/');
if (flatPaths.includes(parentPath)) return parentPath;
if (flatPaths.has(parentPath)) return parentPath;
}
return null;
}

View File

@ -22,14 +22,13 @@ function filterMenu(menuList, access, lastList) {
let menuAccess = menu.auth;
if (!menuAccess || includeArray(menuAccess, access)) {
let newMenu = {};
for (let i in menu) {
if (i !== 'children') newMenu[i] = cloneDeep(menu[i]);
}
if (menu.children && menu.children.length) newMenu.children = [];
// 整节点剔除 children 后一次 cloneDeep,替代逐属性深克隆
const { children, ...rest } = menu;
const newMenu = cloneDeep(rest);
if (children && children.length) newMenu.children = [];
lastList.push(newMenu);
menu.children && filterMenu(menu.children, access, newMenu.children);
children && filterMenu(children, access, newMenu.children);
}
});
return lastList;