feat: 新增静态路由、动态路由生成器与按钮权限指令
parent
fb22f8d99b
commit
cd663a1951
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<a-config-provider :locale="zhCN">
|
||||
<div style="padding: 24px">凡荣e链前端工程初始化成功</div>
|
||||
<router-view />
|
||||
</a-config-provider>
|
||||
</template>
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<div class="blank-layout">
|
||||
<router-view />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.blank-layout {
|
||||
min-height: 100vh;
|
||||
background: #f0f2f5;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -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
|
||||
})
|
||||
}
|
||||
|
|
@ -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
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<a-result status="403" title="403" sub-title="抱歉,您没有权限访问该页面。" />
|
||||
</template>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<a-result status="404" title="404" sub-title="抱歉,您访问的页面不存在或尚未开放。" />
|
||||
</template>
|
||||
Loading…
Reference in New Issue