|
|
|
|
import path from 'path'
|
|
|
|
|
import {useBackendRoute} from '@/config'
|
|
|
|
|
import {addDynamicRoutes} from '@/router'
|
|
|
|
|
import {getDynamicRoutes} from '@/router/define'
|
|
|
|
|
import {parseRoutes, metaExtend} from "@/router/util"
|
|
|
|
|
import {needAuth} from "@/util/auth"
|
|
|
|
|
import {createTree} from "@/util/tree"
|
|
|
|
|
import {getAll} from "@/api/system/resource"
|
|
|
|
|
import {isEmpty} from "@/util"
|
|
|
|
|
import {isExternal} from "@/util/validate"
|
|
|
|
|
|
|
|
|
|
const state = {
|
|
|
|
|
menus: [],
|
|
|
|
|
resourceMap: {},
|
|
|
|
|
resourceTree: [],
|
|
|
|
|
init: false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const mutations = {
|
|
|
|
|
menus(state, menus) {
|
|
|
|
|
sort(menus)
|
|
|
|
|
state.menus = menus
|
|
|
|
|
},
|
|
|
|
|
resource(state, {data, admin}) {
|
|
|
|
|
state.resourceMap = generateResourceMap(data)
|
|
|
|
|
|
|
|
|
|
//如果不是admin用户,那么过滤admin类型的权限
|
|
|
|
|
if (!admin) data = data.filter(i => !i.admin)
|
|
|
|
|
|
|
|
|
|
//解析meta,这里不处理meta.dynamicTitle
|
|
|
|
|
data.forEach(i => {
|
|
|
|
|
if (typeof i.meta === 'string' && !isEmpty(i.meta)) {
|
|
|
|
|
i.meta = JSON.parse(i.meta)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
//为path为'/'的根节点设置默认名称
|
|
|
|
|
const root = data.find(i => i.pid === 0 && i.path === '/')
|
|
|
|
|
if (root) {
|
|
|
|
|
root.meta = {title: '初始节点'}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
state.resourceTree = createTree(data)
|
|
|
|
|
},
|
|
|
|
|
init(state, sign) {
|
|
|
|
|
state.init = sign
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const actions = {
|
|
|
|
|
init({commit}, {resources, admin, addRoutes = false}) {
|
|
|
|
|
return getAll
|
|
|
|
|
.request()
|
|
|
|
|
.then(data => {
|
|
|
|
|
const routes = transformOriginRouteData(data)
|
|
|
|
|
metaExtend(routes)
|
|
|
|
|
addRoutes && addDynamicRoutes(routes)
|
|
|
|
|
const menus = getAuthorizedMenus({resources, admin}, routes)
|
|
|
|
|
commit('menus', menus)
|
|
|
|
|
commit('resource', {data: data || [], admin})
|
|
|
|
|
commit('init', true)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//将后台返回的数据转换为合法的route数组
|
|
|
|
|
function transformOriginRouteData(data) {
|
|
|
|
|
if (!Array.isArray(data)) return []
|
|
|
|
|
if (!useBackendRoute) return getDynamicRoutes()
|
|
|
|
|
data = JSON.parse(JSON.stringify(data))
|
|
|
|
|
data = data.filter(i => {
|
|
|
|
|
if (i.type === 3 || !i.enable) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
['name', 'component', 'meta'].forEach(key => {
|
|
|
|
|
if (key in i && isEmpty(i[key])) {
|
|
|
|
|
delete i[key]
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
if (typeof i.meta === 'string') {
|
|
|
|
|
i.meta = parseRoutes(i.meta)
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
})
|
|
|
|
|
return createTree(data)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//获取经过权限控制后的菜单
|
|
|
|
|
function getAuthorizedMenus({resources, admin}, menus) {
|
|
|
|
|
menus = JSON.parse(JSON.stringify(menus))
|
|
|
|
|
clean(menus)
|
|
|
|
|
addFullPath(menus)
|
|
|
|
|
if (admin === true) return menus
|
|
|
|
|
if (!resources) return []
|
|
|
|
|
filter(menus, i => !needAuth(i) || i.fullPath in resources)
|
|
|
|
|
return menus
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//菜单添加全路径
|
|
|
|
|
function addFullPath(routes, basePath = '/') {
|
|
|
|
|
routes.forEach(route => {
|
|
|
|
|
route.fullPath = isExternal(route.path) ? route.path : path.resolve(basePath, route.path)
|
|
|
|
|
route.children && addFullPath(route.children, route.fullPath)
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//若没有children且未通过,则删除,若有,当children长度为0时删除
|
|
|
|
|
function filter(arr, fun) {
|
|
|
|
|
for (let i = arr.length - 1; i >= 0; i--) {
|
|
|
|
|
const {children} = arr[i]
|
|
|
|
|
|
|
|
|
|
if (!children || children.length <= 0) {
|
|
|
|
|
!fun(arr[i]) && arr.splice(i, 1)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
filter(children, fun)
|
|
|
|
|
|
|
|
|
|
children.length <= 0 && arr.splice(i, 1)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//删除不显示的菜单(没有children且没有meta.title,左侧菜单需清除meta.hidden=true)
|
|
|
|
|
function clean(menus, cleanHidden = true) {
|
|
|
|
|
for (let i = menus.length - 1; i >= 0; i--) {
|
|
|
|
|
const {children = [], meta: {title, alwaysShow, hidden} = {}} = menus[i]
|
|
|
|
|
|
|
|
|
|
if (cleanHidden && hidden) {
|
|
|
|
|
menus.splice(i, 1)
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (children.length === 0) {
|
|
|
|
|
if (isEmpty(title)) {
|
|
|
|
|
menus.splice(i, 1)
|
|
|
|
|
}
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
clean(children, cleanHidden)
|
|
|
|
|
|
|
|
|
|
if (children.length === 0 && !alwaysShow) {
|
|
|
|
|
menus.splice(i, 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//移除类似首页那样的无标题父级节点
|
|
|
|
|
if (isEmpty(title)) {
|
|
|
|
|
menus.splice(i, 1, ...children)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//菜单排序
|
|
|
|
|
function sort(routes) {
|
|
|
|
|
const getSortValue = item => {
|
|
|
|
|
const sort = deepTap(item)
|
|
|
|
|
return isEmpty(sort) ? 10000 : sort
|
|
|
|
|
}
|
|
|
|
|
const deepTap = item => {
|
|
|
|
|
const {name, children = [], meta: {title, hidden, sort} = {}} = item
|
|
|
|
|
if (hidden) return null
|
|
|
|
|
if (!isEmpty(sort)) return sort
|
|
|
|
|
//如果是类似首页那样的路由层级
|
|
|
|
|
if (isEmpty(name, title) && children.length === 1) {
|
|
|
|
|
return deepTap(children[0])
|
|
|
|
|
}
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
routes.sort((pre, next) => {
|
|
|
|
|
pre = getSortValue(pre)
|
|
|
|
|
next = getSortValue(next)
|
|
|
|
|
if (pre < next) return -1
|
|
|
|
|
else if (pre === next) return 0
|
|
|
|
|
else return 1
|
|
|
|
|
})
|
|
|
|
|
routes.forEach(route => {
|
|
|
|
|
const {children} = route
|
|
|
|
|
if (children && children.length) {
|
|
|
|
|
sort(children)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//根据权限列表生成哈希表:<权限路径,权限id>
|
|
|
|
|
function generateResourceMap(resources) {
|
|
|
|
|
if (!Array.isArray(resources) || resources.length <= 0) {
|
|
|
|
|
return {}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
resources = JSON.parse(JSON.stringify(resources))
|
|
|
|
|
|
|
|
|
|
const result = {}
|
|
|
|
|
|
|
|
|
|
//临时表:<id,resource>
|
|
|
|
|
const map = resources.reduce((map, i) => {
|
|
|
|
|
//如果是接口类型的节点,直接放入结果表
|
|
|
|
|
if (i.type === 3) result[i.path] = i.id
|
|
|
|
|
else {
|
|
|
|
|
i.children = []
|
|
|
|
|
map[i.id] = i
|
|
|
|
|
}
|
|
|
|
|
return map
|
|
|
|
|
}, {})
|
|
|
|
|
|
|
|
|
|
//获取所有的菜单叶子节点
|
|
|
|
|
const menuLeaves = resources.filter(node => {
|
|
|
|
|
const parent = map[node.pid]
|
|
|
|
|
if (parent) {
|
|
|
|
|
parent.children.push(node)
|
|
|
|
|
node.parent = parent
|
|
|
|
|
}
|
|
|
|
|
return node.type === 2
|
|
|
|
|
})
|
|
|
|
|
//获取叶子菜单的全路径
|
|
|
|
|
const getMenuFullPath = menu => {
|
|
|
|
|
const parents = [menu.path]
|
|
|
|
|
let {parent} = menu
|
|
|
|
|
while (parent) {
|
|
|
|
|
parents.push(parent.path)
|
|
|
|
|
parent = parent.parent
|
|
|
|
|
}
|
|
|
|
|
return path.resolve(...parents.reverse())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//填充结果表
|
|
|
|
|
for (const menu of menuLeaves) {
|
|
|
|
|
result[getMenuFullPath(menu)] = menu.id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
namespaced: true,
|
|
|
|
|
state,
|
|
|
|
|
mutations,
|
|
|
|
|
actions
|
|
|
|
|
}
|