feat: 新增静态路由、动态路由生成器与按钮权限指令
parent
fb22f8d99b
commit
cd663a1951
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<a-config-provider :locale="zhCN">
|
<a-config-provider :locale="zhCN">
|
||||||
<div style="padding: 24px">凡荣e链前端工程初始化成功</div>
|
<router-view />
|
||||||
</a-config-provider>
|
</a-config-provider>
|
||||||
</template>
|
</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 Antd from 'ant-design-vue'
|
||||||
import 'ant-design-vue/dist/reset.css'
|
import 'ant-design-vue/dist/reset.css'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
|
import { permission } from './directives/permission'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
app.use(createPinia())
|
app.use(createPinia())
|
||||||
|
app.use(router)
|
||||||
app.use(Antd)
|
app.use(Antd)
|
||||||
|
app.directive('permission', permission)
|
||||||
app.mount('#app')
|
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