nova-admin/src/store/route.ts
2024-04-22 11:37:30 +08:00

208 lines
6.3 KiB
TypeScript

import type { MenuOption } from 'naive-ui'
import { RouterLink } from 'vue-router'
import { h } from 'vue'
import { clone, construct, min } from 'radash'
import type { RouteRecordRaw } from 'vue-router'
import { $t, arrayToTree, local, renderIcon } from '@/utils'
import { router } from '@/router'
import { fetchUserRoutes } from '@/service'
import { staticRoutes } from '@/router/routes.static'
import { usePermission } from '@/hooks'
import Layout from '@/layouts/index.vue'
import { useAuthStore } from '@/store/auth'
interface RoutesStatus {
isInitAuthRoute: boolean
menus: AppRoute.Route[]
rowRoutes: AppRoute.RowRoute[]
activeMenu: string | null
cacheRoutes: string[]
}
export const useRouteStore = defineStore('route-store', {
state: (): RoutesStatus => {
return {
isInitAuthRoute: false,
menus: [],
rowRoutes: [],
activeMenu: null,
cacheRoutes: [],
}
},
actions: {
resetRouteStore() {
this.resetRoutes()
this.$reset()
},
resetRoutes() {
router.removeRoute('appRoot')
},
// set the currently highlighted menu key
setActiveMenu(key: string) {
this.activeMenu = key
},
/* 生成侧边菜单的数据 */
createMenus(userRoutes: AppRoute.RowRoute[]) {
const resultMenus = clone(userRoutes).map(i => construct(i)) as AppRoute.Route[]
// filter menus that do not need to be displayed
const visibleMenus = resultMenus.filter(route => !route.meta.hide)
// generate side menu
this.menus = arrayToTree(this.transformAuthRoutesToMenus(visibleMenus))
},
// render the returned routing table as a sidebar
transformAuthRoutesToMenus(userRoutes: AppRoute.Route[]): MenuOption[] {
const { hasPermission } = usePermission()
// Filter out side menus without permission
return userRoutes.filter(i => hasPermission(i.meta.roles))
// Sort the menu according to the order size
.sort((a, b) => {
if (a.meta && a.meta.order && b.meta && b.meta.order)
return a.meta.order - b.meta.order
else if (a.meta && a.meta.order)
return -1
else if (b.meta && b.meta.order)
return 1
else return 0
})
// Convert to side menu data structure
.map((item) => {
const target: MenuOption = {
id: item.id,
pid: item.pid,
label:
(!item.meta.menuType || item.meta.menuType === 'page')
? () =>
h(
RouterLink,
{
to: {
path: item.path,
},
},
{ default: () => $t(`route.${String(item.name)}`, item.meta.title) },
)
: () => $t(`route.${String(item.name)}`, item.meta.title),
key: item.path,
icon: item.meta.icon ? renderIcon(item.meta.icon) : undefined,
}
return target
})
},
createRoutes(routes: AppRoute.RowRoute[]) {
const { hasPermission } = usePermission()
// Structure the meta field
let resultRouter = clone(routes).map(i => construct(i)) as AppRoute.Route[]
// Route permission filtering
resultRouter = resultRouter.filter(i => hasPermission(i.meta.roles))
// Generate an array of route names that need to be kept alive
this.cacheRoutes = resultRouter.filter((i) => {
return i.meta.keepAlive
})
.map(i => i.name)
// Generate routes, no need to import files for those with redirect
const modules = import.meta.glob('@/views/**/*.vue')
resultRouter = resultRouter.map((item: AppRoute.Route) => {
if (item.componentPath && !item.redirect)
item.component = modules[`/src/views${item.componentPath}`]
return item
})
// Generate route tree
resultRouter = arrayToTree(resultRouter) as AppRoute.Route[]
const appRootRoute: RouteRecordRaw = {
path: '/appRoot',
name: 'appRoot',
redirect: import.meta.env.VITE_HOME_PATH,
component: Layout,
meta: {
title: '',
icon: 'icon-park-outline:home',
},
children: [],
}
// Set the correct redirect path for the route
this.setRedirect(resultRouter)
// Insert the processed route into the root route
appRootRoute.children = resultRouter as unknown as RouteRecordRaw[]
router.addRoute(appRootRoute)
},
setRedirect(routes: AppRoute.Route[]) {
routes.forEach((route) => {
if (route.children) {
if (!route.redirect) {
// Filter out a collection of child elements that are not hidden
const visibleChilds = route.children.filter(child => !child.meta.hide)
// Redirect page to the path of the first child element by default
let target = visibleChilds[0]
// Filter out pages with the order attribute
const orderChilds = visibleChilds.filter(child => child.meta.order)
if (orderChilds.length > 0)
target = min(orderChilds, i => i.meta.order!) as AppRoute.Route
route.redirect = target.path
}
this.setRedirect(route.children)
}
})
},
async initRouteInfo() {
if (import.meta.env.VITE_AUTH_ROUTE_MODE === 'dynamic') {
const userInfo = local.get('userInfo')
if (!userInfo || !userInfo.id) {
const authStore = useAuthStore()
authStore.resetAuthStore()
return
}
// Get user's route
const { data } = await fetchUserRoutes({
id: userInfo.id,
})
if (!data)
return
return data
}
else {
this.rowRoutes = staticRoutes
return staticRoutes
}
},
async initAuthRoute() {
this.isInitAuthRoute = false
// Initialize route information
const rowRoutes = await this.initRouteInfo()
if (!rowRoutes) {
window.$message.error($t(`app.getRouteError`))
return
}
this.rowRoutes = rowRoutes
// Generate actual route and insert
this.createRoutes(rowRoutes)
// Generate side menu
this.createMenus(rowRoutes)
this.isInitAuthRoute = true
},
},
})