feat(modules/permission.ts): 动态路由完成

This commit is contained in:
有来技术
2021-11-30 23:56:53 +08:00
parent 1d21c6b098
commit 2e787ec921
6 changed files with 108 additions and 35 deletions

View File

@@ -29,7 +29,10 @@ export default defineComponent({
setup(props) { setup(props) {
const router = useRouter() const router = useRouter()
const push = () => { const push = () => {
router.push(props.to) console.log(props.to)
router.push(props.to).catch((err) => {
console.log(err)
})
} }
return { return {
push, push,

View File

@@ -1,41 +1,49 @@
<template> <template>
<div> <div v-if="!item.meta || !item.meta.hidden" >
<div v-if="!item.meta ||!item.meta.hidden">
<!-- 非嵌套路由 -->
<template <template
v-if="!item.children||item.children.length==0"> v-if="!alwaysShowRootMenu && theOnlyOneChild && !theOnlyOneChild.children"
<app-link v-if="item.meta" :to="resolvePath(item.path)"> >
<el-menu-item :index="resolvePath(item.path)" :class="{'submenu-title-noDropdown':!isNest}"> <AppLink
<svg-icon v-if="item.meta && item.meta.icon" :icon-class="item.meta.icon"></svg-icon> v-if="theOnlyOneChild.meta"
<span v-if="item.meta && item.meta.title">{{ item.meta.title }}</span> :to="resolvePath(theOnlyOneChild.path)"
>
<el-menu-item
:index="resolvePath(theOnlyOneChild.path)"
:class="{'submenu-title-noDropdown': isFirstLevel}"
>
<svg-icon v-if="theOnlyOneChild.meta&&theOnlyOneChild.meta.icon" :icon-class="theOnlyOneChild.meta.icon"></svg-icon>
<span v-if="theOnlyOneChild.meta && theOnlyOneChild.meta.title">{{ theOnlyOneChild.meta.title }}</span>
</el-menu-item> </el-menu-item>
</app-link> </AppLink>
</template> </template>
<el-sub-menu
<!-- 嵌套路由 --> v-else
<el-sub-menu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> :index="resolvePath(item.path)"
>
<!-- popper-append-to-body -->
<template #title> <template #title>
<svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon> <svg-icon v-if="item.meta&&item.meta.icon" :icon-class="item.meta.icon"></svg-icon>
<span v-if="item.meta && item.meta.title">{{ item.meta.title }}</span> <span v-if="item.meta && item.meta.title">{{ item.meta.title }}</span>
</template> </template>
<template v-if="item.children">
<sidebar-item <sidebar-item
v-for="child in item.children" v-for="child in item.children"
:key="child.path" :key="child.path"
:is-nest="true" :item="child"
:item="child" :is-collapse="isCollapse"
:base-path="resolvePath(child.path)" :is-first-level="false"
class="nest-menu" :base-path="resolvePath(child.path)"
/> class="nest-menu"
/>
</template>
</el-sub-menu> </el-sub-menu>
</div> </div>
</div> </template>
</template>
<script lang="ts"> <script lang="ts">
import path from 'path-browserify' import path from 'path-browserify'
import {defineComponent, PropType} from "vue"; import {defineComponent, PropType, reactive,computed } from "vue";
import {RouteRecordRaw} from 'vue-router' import {RouteRecordRaw} from 'vue-router'
import {isExternal} from '@utils/validate' import {isExternal} from '@utils/validate'
import AppLink from './Link.vue' import AppLink from './Link.vue'
@@ -43,23 +51,67 @@ import SvgIcon from '@/components/SvgIcon/index.vue';
export default defineComponent({ export default defineComponent({
name: 'SidebarItem',
components: {SvgIcon, AppLink},
props: { props: {
item: { item: {
type: Object as PropType<RouteRecordRaw>, type: Object as PropType<RouteRecordRaw>,
required: true required: true
}, },
isNest: { isCollapse: {
type: Boolean, type: Boolean,
default: false required: false
},
isFirstLevel: {
type: Boolean,
required: true
}, },
basePath: { basePath: {
type: String, type: String,
default: '' required: true
} }
}, },
components: {
AppLink,
SvgIcon
},
setup(props) { setup(props) {
const alwaysShowRootMenu = computed(() => {
if (props.item.meta && props.item.meta.alwaysShow) {
return true
} else {
return false
}
})
const showingChildNumber = computed(() => {
if (props.item.children) {
const showingChildren = props.item.children.filter((item) => {
if (item.meta && item.meta.hidden) {
return false
} else {
return true
}
})
return showingChildren.length
}
return 0
})
const theOnlyOneChild = computed(() => {
if (showingChildNumber.value > 1) {
return null
}
if (props.item.children) {
for (const child of props.item.children) {
if (!child.meta || !child.meta.hidden) {
return child
}
}
}
// If there is no children, return itself with path removed,
// because this.basePath already conatins item's path information
return { ...props.item, path: '' }
})
const resolvePath = (routePath: string) => { const resolvePath = (routePath: string) => {
if (isExternal(routePath)) { if (isExternal(routePath)) {
return routePath return routePath
@@ -67,11 +119,13 @@ export default defineComponent({
if (isExternal(props.basePath)) { if (isExternal(props.basePath)) {
return props.basePath return props.basePath
} }
console.log(props.basePath,routePath)
return path.resolve(props.basePath, routePath) return path.resolve(props.basePath, routePath)
} }
return { return {
alwaysShowRootMenu,
showingChildNumber,
theOnlyOneChild,
resolvePath resolvePath
} }
} }

View File

@@ -21,6 +21,7 @@
:item="route" :item="route"
:key="route.path" :key="route.path"
:base-path="route.path" :base-path="route.path"
:is-collapse="isCollapse"
/> />
</el-menu> </el-menu>
</el-scrollbar> </el-scrollbar>

View File

@@ -34,7 +34,6 @@ export const constantRoutes: Array<RouteRecordRaw> = [
path: '/', path: '/',
component: Layout, component: Layout,
redirect: '/dashboard', redirect: '/dashboard',
meta:{hidden:true},
children: [ children: [
{ {
path: 'dashboard', path: 'dashboard',

View File

@@ -2,8 +2,10 @@ import {Module} from "vuex";
import {PermissionState, RootStateTypes} from "@store/interface"; import {PermissionState, RootStateTypes} from "@store/interface";
import {RouteRecordRaw} from 'vue-router' import {RouteRecordRaw} from 'vue-router'
import {constantRoutes} from '@/router' import {constantRoutes} from '@/router'
import {getRouteList} from "@api/system/menu"; import {getRouteList} from "@/api/system/menu";
const modules = import.meta.glob("../../views/**/**.vue");
import Layout from '@/layout/index.vue'
const hasPermission = (roles: string[], route: RouteRecordRaw) => { const hasPermission = (roles: string[], route: RouteRecordRaw) => {
// 超级管理员放行 // 超级管理员放行
@@ -26,10 +28,21 @@ export const filterAsyncRoutes = (routes: RouteRecordRaw[], roles: string[]) =>
routes.forEach(route => { routes.forEach(route => {
const tmp = {...route} const tmp = {...route}
if (hasPermission(roles, tmp)) { if (hasPermission(roles, tmp)) {
if (tmp.component == 'Layout') {
tmp.component = Layout
} else {
const component = modules[`../../views/${tmp.component}.vue`] as any;
if (component) {
tmp.component = modules[`../../views/${tmp.component}.vue`];
} else {
tmp.component = modules[`../../views/error-page/404.vue`];
}
}
res.push(tmp)
if (tmp.children) { if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles) tmp.children = filterAsyncRoutes(tmp.children, roles)
} }
res.push(tmp)
} }
}) })
return res return res

View File

@@ -1,5 +1,8 @@
<template> <template>
<div>
123
</div>
</template> </template>
<script> <script>