feat: 新增静态路由、动态路由生成器与按钮权限指令

main
halo 2026-07-08 15:51:43 +08:00
parent fb22f8d99b
commit cd663a1951
8 changed files with 125 additions and 1 deletions

View File

@ -1,6 +1,6 @@
<template>
<a-config-provider :locale="zhCN">
<div style="padding: 24px">凡荣e链前端工程初始化成功</div>
<router-view />
</a-config-provider>
</template>

View File

@ -0,0 +1,10 @@
import { useAuthStore } from '@/stores/auth'
export const permission = {
mounted(el, binding) {
const authStore = useAuthStore()
if (!authStore.hasButton(binding.value)) {
el.parentNode && el.parentNode.removeChild(el)
}
}
}

View File

@ -0,0 +1,12 @@
<template>
<div class="blank-layout">
<router-view />
</div>
</template>
<style scoped>
.blank-layout {
min-height: 100vh;
background: #f0f2f5;
}
</style>

View File

@ -3,8 +3,12 @@ import { createPinia } from 'pinia'
import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/reset.css'
import App from './App.vue'
import router from './router'
import { permission } from './directives/permission'
const app = createApp(App)
app.use(createPinia())
app.use(router)
app.use(Antd)
app.directive('permission', permission)
app.mount('#app')

53
src/router/dynamic.js Normal file
View File

@ -0,0 +1,53 @@
import MainLayout from '@/layouts/MainLayout.vue'
const componentModules = import.meta.glob('@/views/**/*.vue')
function resolveComponent(componentPath) {
const key = `/src/views/${componentPath}.vue`
const loader = componentModules[key]
if (!loader) {
console.error(`[dynamic route] 找不到组件: ${key}`)
return () => import('@/views/error/NotFound.vue')
}
return loader
}
export function collectFirstPath(nodes) {
for (const node of nodes) {
if (node.path && node.component) return node.path
if (node.children?.length) {
const p = collectFirstPath(node.children)
if (p) return p
}
}
return null
}
export function buildRouteRecords(menuTree) {
const children = []
function walk(nodes) {
nodes.forEach((node) => {
if (node.component) {
children.push({
path: node.path.replace(/^\//, ''),
name: node.id,
component: resolveComponent(node.component),
meta: { title: node.name }
})
}
if (node.children?.length) walk(node.children)
})
}
walk(menuTree)
return children
}
export function installDynamicRoutes(router, menuTree) {
const children = buildRouteRecords(menuTree)
router.addRoute({
path: '/',
component: MainLayout,
redirect: collectFirstPath(menuTree) || '/403',
children
})
}

39
src/router/index.js Normal file
View File

@ -0,0 +1,39 @@
import { createRouter, createWebHistory } from 'vue-router'
import BlankLayout from '@/layouts/BlankLayout.vue'
import { useAuthStore } from '@/stores/auth'
import { installDynamicRoutes } from './dynamic'
const staticRoutes = [
{
path: '/login',
component: BlankLayout,
children: [{ path: '', component: () => import('@/views/login/Login.vue') }]
},
{ path: '/403', component: () => import('@/views/error/Forbidden.vue') },
{ path: '/:pathMatch(.*)*', component: () => import('@/views/error/NotFound.vue') }
]
const router = createRouter({
history: createWebHistory(),
routes: staticRoutes
})
router.beforeEach(async (to) => {
const authStore = useAuthStore()
if (to.path === '/login') return true
if (!authStore.accessToken) return { path: '/login', query: { redirect: to.fullPath } }
if (!authStore.routesReady) {
try {
await authStore.fetchMenu()
installDynamicRoutes(router, authStore.menuTree)
authStore.markRoutesReady()
return { path: to.fullPath, replace: true }
} catch (e) {
authStore.clear()
return { path: '/login', query: { redirect: to.fullPath } }
}
}
return true
})
export default router

View File

@ -0,0 +1,3 @@
<template>
<a-result status="403" title="403" sub-title=",访" />
</template>

View File

@ -0,0 +1,3 @@
<template>
<a-result status="404" title="404" sub-title=",访" />
</template>