From 161050c4aa7ec211f8c179df342e3e0bc1408338 Mon Sep 17 00:00:00 2001 From: From-wh Date: Sat, 10 Oct 2026 11:44:13 +0800 Subject: [PATCH] =?UTF-8?q?refactor(=E7=AE=A1=E7=90=86=E7=AB=AF):=20?= =?UTF-8?q?=E4=BC=98=E5=8C=96=E8=8F=9C=E5=8D=95=E8=B7=AF=E7=94=B1=E4=B8=8E?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E9=80=89=E6=8B=A9=E9=81=8D=E5=8E=86=EF=BC=8C?= =?UTF-8?q?=E5=8E=BB=E9=99=A4=E5=B5=8C=E5=A5=97=E5=BE=AA=E7=8E=AF=E4=B8=8E?= =?UTF-8?q?=E5=86=97=E4=BD=99=E6=B7=B1=E5=85=8B=E9=9A=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/uploadPictures/index.vue | 41 ++++++------- .../layout/navBars/breadcrumb/breadcrumb.vue | 21 ++++--- frontend/admin/src/libs/auth.js | 14 ++--- frontend/admin/src/libs/system/index.js | 59 +++++++++---------- frontend/admin/src/libs/util.js | 8 +-- frontend/admin/src/router/index.js | 9 +-- frontend/admin/src/store/module/menu.js | 11 ++-- 7 files changed, 72 insertions(+), 91 deletions(-) diff --git a/frontend/admin/src/components/uploadPictures/index.vue b/frontend/admin/src/components/uploadPictures/index.vue index c4126fb1c..7d79ba76c 100644 --- a/frontend/admin/src/components/uploadPictures/index.vue +++ b/frontend/admin/src/components/uploadPictures/index.vue @@ -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; } diff --git a/frontend/admin/src/layout/navBars/breadcrumb/breadcrumb.vue b/frontend/admin/src/layout/navBars/breadcrumb/breadcrumb.vue index 17975d342..57d645168 100644 --- a/frontend/admin/src/layout/navBars/breadcrumb/breadcrumb.vue +++ b/frontend/admin/src/layout/navBars/breadcrumb/breadcrumb.vue @@ -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; }); diff --git a/frontend/admin/src/libs/auth.js b/frontend/admin/src/libs/auth.js index 1b34fccb9..c03da5526 100644 --- a/frontend/admin/src/libs/auth.js +++ b/frontend/admin/src/libs/auth.js @@ -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 }; diff --git a/frontend/admin/src/libs/system/index.js b/frontend/admin/src/libs/system/index.js index c0accbacb..849e0b28d 100644 --- a/frontend/admin/src/libs/system/index.js +++ b/frontend/admin/src/libs/system/index.js @@ -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 }; diff --git a/frontend/admin/src/libs/util.js b/frontend/admin/src/libs/util.js index 41c66da22..9699684af 100644 --- a/frontend/admin/src/libs/util.js +++ b/frontend/admin/src/libs/util.js @@ -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; }; diff --git a/frontend/admin/src/router/index.js b/frontend/admin/src/router/index.js index 7cadee93d..2e4cb11ba 100644 --- a/frontend/admin/src/router/index.js +++ b/frontend/admin/src/router/index.js @@ -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; } diff --git a/frontend/admin/src/store/module/menu.js b/frontend/admin/src/store/module/menu.js index 16de6be23..e1c5e1bbb 100644 --- a/frontend/admin/src/store/module/menu.js +++ b/frontend/admin/src/store/module/menu.js @@ -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;