| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267 |
- import type { Route } from '#/global'
- import type { RouteRecordRaw } from 'vue-router'
- import { systemRoutes } from '@/router/routes'
- import { resolveRoutePath } from '@/utils'
- import { cloneDeep } from 'lodash-es'
- import useSettingsStore from './settings'
- import api from '@/api'
- import { ElMessage } from 'element-plus'
- const useRouteStore = defineStore(
- // 唯一ID
- 'route',
- () => {
- const settingsStore = useSettingsStore()
- const isGenerate = ref(false)
- const routesRaw = ref<Route.recordMainRaw[]>([])
- const filesystemRoutesRaw = ref<RouteRecordRaw[]>([])
- const currentRemoveRoutes = ref<(() => void)[]>([])
- // 将多层嵌套路由处理成两层,保留顶层和最子层路由,中间层级将被拍平
- function flatAsyncRoutes<T extends RouteRecordRaw>(route: T): T {
- if (route.children) {
- route.children = flatAsyncRoutesRecursive(route.children, [{
- path: route.path,
- title: route.meta?.title,
- icon: route.meta?.icon,
- hide: !route.meta?.breadcrumb && route.meta?.breadcrumb === false,
- }], route.path)
- }
- return route
- }
- function flatAsyncRoutesRecursive(routes: RouteRecordRaw[], breadcrumb: Route.breadcrumb[] = [], baseUrl = ''): RouteRecordRaw[] {
- const res: RouteRecordRaw[] = []
- routes.forEach((route) => {
- if (route.children) {
- const childrenBaseUrl = resolveRoutePath(baseUrl, route.path)
- const tmpBreadcrumb = cloneDeep(breadcrumb)
- tmpBreadcrumb.push({
- path: childrenBaseUrl,
- title: route.meta?.title,
- icon: route.meta?.icon,
- hide: !route.meta?.breadcrumb && route.meta?.breadcrumb === false,
- })
- const tmpRoute = cloneDeep(route)
- tmpRoute.path = childrenBaseUrl
- if (!tmpRoute.meta) {
- tmpRoute.meta = {}
- }
- tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb
- delete tmpRoute.children
- res.push(tmpRoute)
- const childrenRoutes = flatAsyncRoutesRecursive(route.children, tmpBreadcrumb, childrenBaseUrl)
- childrenRoutes.forEach((item) => {
- // 如果 path 一样则覆盖,因为子路由的 path 可能设置为空,导致和父路由一样,直接注册会提示路由重复
- if (res.some(v => v.path === item.path)) {
- res.forEach((v, i) => {
- if (v.path === item.path) {
- res[i] = item
- }
- })
- }
- else {
- res.push(item)
- }
- })
- }
- else {
- const tmpRoute = cloneDeep(route)
- tmpRoute.path = resolveRoutePath(baseUrl, tmpRoute.path)
- // 处理面包屑导航
- const tmpBreadcrumb = cloneDeep(breadcrumb)
- tmpBreadcrumb.push({
- path: tmpRoute.path,
- title: tmpRoute.meta?.title,
- icon: tmpRoute.meta?.icon,
- hide: !tmpRoute.meta?.breadcrumb && tmpRoute.meta?.breadcrumb === false,
- })
- if (!tmpRoute.meta) {
- tmpRoute.meta = {}
- }
- tmpRoute.meta.breadcrumbNeste = tmpBreadcrumb
- res.push(tmpRoute)
- }
- })
- return res
- }
- // 扁平化路由(将三级及以上路由数据拍平成二级)
- const flatRoutes = computed(() => {
- const returnRoutes: RouteRecordRaw[] = []
- if (settingsStore.settings.app.routeBaseOn !== 'filesystem') {
- if (routesRaw.value) {
- routesRaw.value.forEach((item) => {
- const tmpRoutes = cloneDeep(item.children) as RouteRecordRaw[]
- tmpRoutes.map((v) => {
- if (!v.meta) {
- v.meta = {}
- }
- v.meta.auth = item.meta?.auth ?? v.meta?.auth
- return v
- })
- returnRoutes.push(...tmpRoutes)
- })
- returnRoutes.forEach(item => flatAsyncRoutes(item))
- }
- }
- else {
- returnRoutes.push(...cloneDeep(filesystemRoutesRaw.value) as RouteRecordRaw[])
- }
- return returnRoutes
- })
- const flatSystemRoutes = computed(() => {
- const routes = [...systemRoutes]
- routes.forEach(item => flatAsyncRoutes(item))
- return routes
- })
- // TODO 将设置 meta.sidebar 的属性转换成 meta.menu ,过渡处理,未来将被弃用
- let isUsedDeprecatedAttribute = false
- function converDeprecatedAttribute<T extends Route.recordMainRaw[]>(routes: T): T {
- routes.forEach((route) => {
- route.children = converDeprecatedAttributeRecursive(route.children)
- })
- if (isUsedDeprecatedAttribute) {
- // turbo-console-disable-next-line
- console.warn('[Fantastic-admin] 路由配置中的 "sidebar" 属性即将被弃用, 请尽快替换为 "menu" 属性')
- }
- return routes
- }
- function converDeprecatedAttributeRecursive(routes: RouteRecordRaw[]) {
- if (routes) {
- routes.forEach((route) => {
- if (typeof route.meta?.sidebar === 'boolean') {
- isUsedDeprecatedAttribute = true
- route.meta.menu = route.meta.sidebar
- delete route.meta.sidebar
- }
- if (route.children) {
- converDeprecatedAttributeRecursive(route.children)
- }
- })
- }
- return routes
- }
- // 生成路由(前端生成)
- function generateRoutesAtFront(asyncRoutes: Route.recordMainRaw[]) {
- // 设置 routes 数据
- routesRaw.value = converDeprecatedAttribute(cloneDeep(asyncRoutes) as any)
- isGenerate.value = true
- }
- // 格式化后端路由数据
- // function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/*.vue')): Route.recordMainRaw[] {
- // return routes.map((route: any) => {
- // switch (route.component) {
- // case 'Layout':
- // route.component = () => import('@/layouts/index.vue')
- // break
- // default:
- // if (route.component) {
- // route.component = views[`../../views/${route.component}`]
- // }
- // else {
- // delete route.component
- // }
- // }
- // if (route.children) {
- // route.children = formatBackRoutes(route.children, views)
- // }
- // return route
- // })
- // }
- // 格式化后端路由数据
- function formatBackRoutes(routes: any, views = import.meta.glob('../../views/**/*.vue')): Route.recordMainRaw[] {
- // 需要从外层移动到meta对象的字段列表
- const metaFields = ['title', 'icon', 'auth', 'menu', 'breadcrumb', 'activeMenu', 'cache', 'noCache', 'link']// 从外层移动到meta对象的字段列表
- return routes.filter((route: any) => route.type != 2).map((route: any) => {
- // 处理组件引用
- switch (route.component) {
- case 'Layout':
- route.component = () => import('@/layouts/index.vue')
- break
- default:
- if (route.component) {
- route.component = views[`../../views${route.component}.vue`]
- if (route.menu ==0) {
- route.menu = false;
- }
- }
- else {
- delete route.component
- }
- }
- // 处理元数据格式转换:将指定字段从外层移动到meta对象中
- const hasMetaFields = metaFields.some(field => route[field] !== undefined)
- if (hasMetaFields) {
- // 确保meta对象存在
- if (!route.meta) {
- route.meta = {}
- }
- // 批量移动字段
- metaFields.forEach(field => {
- if (route[field] !== undefined) {
- route.meta[field] = route[field]
- delete route[field]
- }
- })
- }
- // 递归处理子路由
- if (route.children) {
- route.children = formatBackRoutes(route.children, views)
- }
- return route
- })
- }
- // 生成路由(后端获取)
- async function generateRoutesAtBack() {
- await api.menuApi.getRoleMenuTree().then((res) => {
- if(res.data.length > 0) {
- // 设置 routes 数据
- routesRaw.value = converDeprecatedAttribute(formatBackRoutes(res.data) as any)
- isGenerate.value = true
- }else{
- // 路由获取失败
- ElMessage.error('路由获取失败')
- }
- }).catch(() => {})
- }
- // 生成路由(文件系统生成)
- function generateRoutesAtFilesystem(asyncRoutes: RouteRecordRaw[]) {
- // 设置 routes 数据
- filesystemRoutesRaw.value = cloneDeep(asyncRoutes) as any
- isGenerate.value = true
- }
- // 记录 accessRoutes 路由,用于登出时删除路由
- function setCurrentRemoveRoutes(routes: (() => void)[]) {
- currentRemoveRoutes.value = routes
- }
- // 清空动态路由
- function removeRoutes() {
- isGenerate.value = false
- routesRaw.value = []
- filesystemRoutesRaw.value = []
- currentRemoveRoutes.value.forEach((removeRoute) => {
- removeRoute()
- })
- currentRemoveRoutes.value = []
- }
- return {
- isGenerate,
- routesRaw,
- currentRemoveRoutes,
- flatRoutes,
- flatSystemRoutes,
- generateRoutesAtFront,
- generateRoutesAtBack,
- generateRoutesAtFilesystem,
- setCurrentRemoveRoutes,
- removeRoutes,
- }
- },
- )
- export default useRouteStore
|