coder-common-thin-frontend/src/store/router/helper.ts
Leo 567e68234b feat(service): 完善服务层和状态管理
- 更新API服务配置(api/login.ts)
- 优化HTTP服务配置(http/alova.ts, config.ts, handle.ts)
- 完善认证状态管理(store/auth.ts)
- 优化路由状态管理(store/router/)

加强服务层架构和状态管理机制
2025-07-06 00:59:18 +08:00

222 lines
7.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import type { MenuOption } from 'naive-ui'
import type { RouteRecordRaw } from 'vue-router'
import { h } from 'vue'
import { usePermission } from '@/hooks'
import Layout from '@/layouts/index.vue'
import { arrayToTree, renderIcon } from '@/utils'
import { clone, min, omit, pick } from 'radash'
import { RouterLink } from 'vue-router'
const metaFields: AppRoute.MetaKeys[]
= ['title', 'icon', 'requiresAuth', 'roles', 'keepAlive', 'hide', 'order', 'href', 'activeMenu', 'withoutTab', 'pinTab', 'menuType']
// 将后端菜单数据转换为前端路由数据
function transformBackendToRoute(backendRoute: AppRoute.BackendRoute): AppRoute.RowRoute {
return {
id: backendRoute.menuId,
pid: backendRoute.parentId === 0 ? null : backendRoute.parentId,
name: backendRoute.name,
path: backendRoute.path,
componentPath: backendRoute.component || null,
redirect: backendRoute.redirect || undefined,
title: backendRoute.menuName,
icon: backendRoute.icon,
requiresAuth: true, // 动态路由都需要认证
hide: backendRoute.isHide === '0', // 0-隐藏 1-显示
keepAlive: backendRoute.isKeepAlive === '0', // 0-是 1-否
pinTab: backendRoute.isAffix === '0', // 0-是 1-否
activeMenu: backendRoute.activeMenu || undefined,
menuType: backendRoute.menuType as AppRoute.MenuType,
href: backendRoute.isLink === '0' ? backendRoute.path : undefined, // 如果是外链
}
}
function standardizedRoutes(route: AppRoute.RowRoute[]) {
return clone(route).map((i) => {
const route = omit(i, metaFields)
Reflect.set(route, 'meta', pick(i, metaFields))
return route
}) as AppRoute.Route[]
}
// 处理路由数据的主函数 - 支持动态和静态路由以及混合模式
export function createRoutes(routeData: (AppRoute.BackendRoute | AppRoute.RowRoute)[]) {
const { hasPermission } = usePermission()
// 处理混合数据:分别处理后端数据和前端数据
const backendRoutes = routeData.filter(item => 'menuId' in item) as AppRoute.BackendRoute[]
const frontendRoutes = routeData.filter(item => !('menuId' in item)) as AppRoute.RowRoute[]
// 转换后端路由数据
const transformedBackendRoutes = backendRoutes.map(transformBackendToRoute)
// 合并所有路由
const routes = [...frontendRoutes, ...transformedBackendRoutes]
// Structure the meta field
let resultRouter = standardizedRoutes(routes)
// Route permission filtering
resultRouter = resultRouter.filter(i => hasPermission(i.meta.roles))
// 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) {
// 对于动态路由,只有菜单类型才需要组件;对于静态路由,都需要组件
const needComponent = item.meta.menuType === '2' || !item.meta.menuType
if (needComponent) {
// 处理组件路径,确保正确的路径格式
let componentPath = item.componentPath
// 确保路径以 / 开头
if (!componentPath.startsWith('/')) {
componentPath = `/${componentPath}`
}
// 确保路径以 .vue 结尾
if (!componentPath.endsWith('.vue')) {
componentPath = `${componentPath}.vue`
}
const fullPath = `/src/views${componentPath}`
item.component = modules[fullPath]
// 如果组件未找到,输出调试信息并提供默认组件
if (!item.component) {
console.warn(`组件未找到: ${fullPath}`)
console.warn('可用组件路径:', Object.keys(modules).slice(0, 10)) // 只显示前10个避免日志过长
// 为找不到组件的页面提供一个默认的空页面组件
item.component = () => h('div', { class: 'p-4' }, [
h('div', { class: 'text-center text-gray-500' }, [
h('h3', '页面开发中'),
h('p', `组件路径: ${fullPath}`),
h('p', '请联系开发人员创建对应的页面组件'),
]),
])
}
}
else if (item.meta.menuType === '1') {
// 目录类型不需要组件但需要确保有children
item.component = undefined
}
}
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 || '/dashboard/monitor',
component: Layout,
meta: {
title: '',
icon: 'icon-park-outline:home',
},
children: [],
}
// Set the correct redirect path for the route
setRedirect(resultRouter)
// Insert the processed route into the root route
appRootRoute.children = resultRouter as unknown as RouteRecordRaw[]
return appRootRoute
}
// Generate an array of route names that need to be kept alive
export function generateCacheRoutes(routes: AppRoute.RowRoute[]) {
return routes
.filter(i => i.keepAlive)
.map(i => i.name)
}
function 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
if (target)
route.redirect = target.path
}
setRedirect(route.children)
}
})
}
/* 生成侧边菜单的数据 */
export function createMenus(routeData: (AppRoute.BackendRoute | AppRoute.RowRoute)[]) {
// 处理混合数据:分别处理后端数据和前端数据
const backendRoutes = routeData.filter(item => 'menuId' in item) as AppRoute.BackendRoute[]
const frontendRoutes = routeData.filter(item => !('menuId' in item)) as AppRoute.RowRoute[]
// 转换后端路由数据
const transformedBackendRoutes = backendRoutes.map(transformBackendToRoute)
// 合并所有路由
const userRoutes = [...frontendRoutes, ...transformedBackendRoutes]
const resultMenus = standardizedRoutes(userRoutes)
// filter menus that do not need to be displayed
const visibleMenus = resultMenus.filter(route => !route.meta.hide && route.meta.menuType !== '3') // 过滤按钮类型
// generate side menu
return arrayToTree(transformAuthRoutesToMenus(visibleMenus))
}
// render the returned routing table as a sidebar
function transformAuthRoutesToMenus(userRoutes: AppRoute.Route[]) {
const { hasPermission } = usePermission()
return userRoutes
// Filter out side menus without permission
.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 === '2')
? () =>
h(
RouterLink,
{
to: {
path: item.path,
},
},
{ default: () => item.meta.title },
)
: () => item.meta.title,
key: item.path,
icon: item.meta.icon ? renderIcon(item.meta.icon) : undefined,
}
return target
})
}