Files
t-admin/components/uni-data-menu/util.js
T

117 lines
3.5 KiB
JavaScript

function buildMenu(menu, menuList, menuIds, visited) {
visited = visited || new Set()
let nextLayer = []
for (let i = menu.length - 1; i > -1; i--) {
const currentMenu = menu[i]
if (visited.has(currentMenu.menu_id)) continue
visited.add(currentMenu.menu_id)
const subMenu = menuList.filter(item => {
if (item.parent_id === currentMenu.menu_id) {
if (visited.has(item.menu_id)) return false
menuIds.push(item.menu_id)
return true
}
})
nextLayer = nextLayer.concat(subMenu)
currentMenu.children = subMenu
}
if (nextLayer.length) {
buildMenu(nextLayer, menuList, menuIds, visited)
}
}
function getParentIds(menuItem, menuList) {
const parentArr = []
const visited = new Set()
let currentItem = menuItem
while (currentItem && currentItem.parent_id) {
if (visited.has(currentItem.parent_id)) {
// B7-1: 数据成环(A→B、B→A)时立即返回已收集链,不再死循环
parentArr.push(currentItem.parent_id)
break
}
visited.add(currentItem.parent_id)
parentArr.push(currentItem.parent_id)
currentItem = menuList.find(item => item.menu_id === currentItem.parent_id)
}
return parentArr
}
// B7-1: 判断把 menuId 的 parent 改为 newParentId 是否会成环
function wouldCreateCycle(menuList, menuId, newParentId) {
if (!newParentId) return false
if (newParentId === menuId) return true
const visited = new Set()
let current = newParentId
while (current) {
if (current === menuId) return true
if (visited.has(current)) return true
visited.add(current)
const item = menuList.find(m => m.menu_id === current)
current = item && item.parent_id
}
return false
}
function buildMenus(menuList, trim = true) {
// B7-1: sort 比较器不再每次重算 getParentIds —— 记忆化一次算完(链式 O(n))
const byId = new Map()
for (const item of menuList) byId.set(item.menu_id, item)
const parentMap = new Map()
function ancestors(id) {
if (parentMap.has(id)) return parentMap.get(id)
const chain = []
const visited = new Set()
let cur = byId.get(id)
while (cur && cur.parent_id) {
if (visited.has(cur.parent_id)) { chain.push(cur.parent_id); break }
visited.add(cur.parent_id)
chain.push(cur.parent_id)
if (parentMap.has(cur.parent_id)) { chain.push(...parentMap.get(cur.parent_id)); break }
cur = byId.get(cur.parent_id)
}
parentMap.set(id, chain)
return chain
}
for (const item of menuList) ancestors(item.menu_id)
// 保证父子级顺序
menuList = menuList.sort(function(a, b) {
const parentIdsA = parentMap.get(a.menu_id) || []
const parentIdsB = parentMap.get(b.menu_id) || []
if (parentIdsA.includes(b.menu_id)) {
return 1
}
return parentIdsA.length - parentIdsB.length || a.sort - b.sort
})
// 删除无subMenu且非子节点的菜单项
if (trim) {
for (let i = menuList.length - 1; i > -1; i--) {
const currentMenu = menuList[i]
const subMenu = menuList.filter(subMenuItem => subMenuItem.parent_id === currentMenu.menu_id)
if (!currentMenu.isLeafNode && !subMenu.length) {
menuList.splice(i, 1)
}
}
}
const menuIds = []
const menu = menuList.filter(item => {
if (!item.parent_id) {
menuIds.push(item.menu_id)
return true
}
})
buildMenu(menu, menuList, menuIds, new Set())
// 包含所有无效菜单
if (!trim && menuIds.length !== menuList.length) {
menu.push(...menuList.filter(item => !menuIds.includes(item.menu_id)))
}
return menu
}
export {
buildMenu,
buildMenus,
getParentIds,
wouldCreateCycle
}