sparkles: 引入菜单权限控制
This commit is contained in:
parent
566da2e791
commit
ee0525e62b
100
src/api/auth/api.d.ts
vendored
100
src/api/auth/api.d.ts
vendored
@ -1,71 +1,71 @@
|
||||
declare namespace auth {
|
||||
/**
|
||||
* AuthUser
|
||||
*/
|
||||
export interface AuthUser {
|
||||
/** anonymous */
|
||||
anonymous?: boolean;
|
||||
/**
|
||||
* AuthUser
|
||||
*/
|
||||
export interface AuthUser {
|
||||
/** anonymous */
|
||||
anonymous?: boolean
|
||||
|
||||
/** 用户头像 */
|
||||
avatar?: string;
|
||||
/** 用户头像 */
|
||||
avatar?: string
|
||||
|
||||
/** 邮箱 */
|
||||
email?: string;
|
||||
/** 邮箱 */
|
||||
email?: string
|
||||
|
||||
/** extInfo */
|
||||
extInfo?: auth.NutMap;
|
||||
/** extInfo */
|
||||
extInfo?: auth.NutMap
|
||||
|
||||
/** 姓名 */
|
||||
fullName: string;
|
||||
/** 姓名 */
|
||||
fullName: string
|
||||
|
||||
/** 电话 */
|
||||
mobile?: string;
|
||||
/** 电话 */
|
||||
mobile?: string
|
||||
|
||||
/** 密码 */
|
||||
password: string;
|
||||
/** 密码 */
|
||||
password: string
|
||||
|
||||
/** 权限列表 */
|
||||
permissions: Array<string>;
|
||||
/** 权限列表 */
|
||||
permissions: Array<string>
|
||||
|
||||
/** jwt refreshToken */
|
||||
refreshToken: string;
|
||||
/** jwt refreshToken */
|
||||
refreshToken: string
|
||||
|
||||
/** 角色列表 */
|
||||
roles: Array<string>;
|
||||
/** 角色列表 */
|
||||
roles: Array<string>
|
||||
|
||||
/** 性别 */
|
||||
sex?: 'MALE' | 'FEMALE';
|
||||
/** 性别 */
|
||||
sex?: 'MALE' | 'FEMALE'
|
||||
|
||||
/** sexInfo */
|
||||
sexInfo?: auth.Codebook;
|
||||
/** sexInfo */
|
||||
sexInfo?: auth.Codebook
|
||||
|
||||
/** jwt Token */
|
||||
token: string;
|
||||
/** jwt Token */
|
||||
token: string
|
||||
|
||||
/** 用户名 */
|
||||
userName: string;
|
||||
}
|
||||
/** 用户名 */
|
||||
userName: string
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录实体
|
||||
*/
|
||||
export interface LoginDTO {
|
||||
/** 验证码 */
|
||||
captcha?: string;
|
||||
/**
|
||||
* 登录实体
|
||||
*/
|
||||
export interface LoginDTO {
|
||||
/** 验证码 */
|
||||
captcha?: string
|
||||
|
||||
/** 手机号 */
|
||||
mobile?: string;
|
||||
/** 手机号 */
|
||||
mobile?: string
|
||||
|
||||
/** 密码 */
|
||||
password?: string;
|
||||
/** 密码 */
|
||||
password?: string
|
||||
|
||||
/** 登录类型 */
|
||||
type: 'ACCOUNT' | 'WECHAT';
|
||||
/** 登录类型 */
|
||||
type: 'ACCOUNT' | 'WECHAT'
|
||||
|
||||
/** 用户名 */
|
||||
userName?: string;
|
||||
/** 用户名 */
|
||||
userName?: string
|
||||
|
||||
/** uuid */
|
||||
uuid?: string;
|
||||
}
|
||||
/** uuid */
|
||||
uuid?: string
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { RouteRecordRaw } from 'vue-router'
|
||||
import XEUtils from 'xe-utils'
|
||||
import i18n from '@/locales'
|
||||
import { routes } from '@/router'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
const { t } = i18n.global
|
||||
export interface MenuNode {
|
||||
key: string
|
||||
@ -73,8 +74,8 @@ export function menuTree() {
|
||||
|
||||
export function hasPermission(key?: string) {
|
||||
console.log(key)
|
||||
// return useUserStore().hasPermission(key);
|
||||
return true
|
||||
return useUserStore().hasPermission(key) // 过滤菜单是否显示
|
||||
// return true
|
||||
}
|
||||
|
||||
export function permissions() {
|
||||
@ -91,6 +92,7 @@ export function fliteredMenus() {
|
||||
return !item.hidden
|
||||
})
|
||||
.filter((item) => {
|
||||
if (item.name === 'dashboard') return true // 首页显示
|
||||
return hasPermission(item.keyPath)
|
||||
})
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
{
|
||||
"index": "首页",
|
||||
"dashboard": "仪表盘",
|
||||
"acl": {
|
||||
"name": "系统管理",
|
||||
"users": "用户管理",
|
||||
|
@ -4,6 +4,12 @@ import AdminLayout from '@/layout/admin-layout.vue'
|
||||
import BlankLayout from '@/layout/blank-layout.vue'
|
||||
|
||||
export const routes: Array<RouteRecordRaw> = [
|
||||
{
|
||||
path: '/admin/dashboard',
|
||||
meta: { title: 'menus.dashboard', icon: 'icon-dashboard', flat: true },
|
||||
name: 'Dashboard',
|
||||
component: () => import('../views/admin/dashboard-page.vue'),
|
||||
},
|
||||
{
|
||||
path: '/admin/acl', // 系统管理
|
||||
name: 'ACL',
|
||||
@ -99,48 +105,93 @@ export const routes: Array<RouteRecordRaw> = [
|
||||
},
|
||||
]
|
||||
|
||||
const getNewRouter = () => {
|
||||
// TODO 用户store判断是否登录,登录就返回过滤的,没有登录就返回全量
|
||||
return createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Index',
|
||||
redirect: '/login',
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
redirect: '/login/user',
|
||||
component: UserLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
name: 'LoginPage',
|
||||
component: () => import('../views/login/user-login.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
name: 'Admin',
|
||||
redirect: '/admin/dashboard',
|
||||
component: AdminLayout,
|
||||
meta: { title: 'menus.index', icon: 'icon-home' },
|
||||
children: routes,
|
||||
},
|
||||
{
|
||||
path: '/:catchAll(.*)',
|
||||
redirect: '/message',
|
||||
},
|
||||
{
|
||||
path: '/message',
|
||||
name: 'Message',
|
||||
component: () => import('@/views/message/message-page.vue'),
|
||||
},
|
||||
],
|
||||
})
|
||||
export default createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes: [
|
||||
{
|
||||
path: '/',
|
||||
name: 'Index',
|
||||
redirect: '/login',
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
name: 'Login',
|
||||
redirect: '/login/user',
|
||||
component: UserLayout,
|
||||
children: [
|
||||
{
|
||||
path: 'user',
|
||||
name: 'LoginPage',
|
||||
component: () => import('../views/login/user-login.vue'),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/admin',
|
||||
name: 'Admin',
|
||||
redirect: '/admin/dashboard',
|
||||
component: AdminLayout,
|
||||
meta: { title: 'menus.index', icon: 'icon-home' },
|
||||
children: routes,
|
||||
},
|
||||
{
|
||||
path: '/:catchAll(.*)',
|
||||
redirect: '/message',
|
||||
},
|
||||
{
|
||||
path: '/message',
|
||||
name: 'Message',
|
||||
component: () => import('@/views/message/message-page.vue'),
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
export const getP = (permissions: Array<string>): RouteRecordRaw => {
|
||||
const newRoutes: RouteRecordRaw[] = filterRoutesByPermission(routes, permissions)
|
||||
return {
|
||||
path: '/admin',
|
||||
name: 'Admin',
|
||||
redirect: '/admin/dashboard',
|
||||
component: AdminLayout,
|
||||
meta: { title: 'menus.index', icon: 'icon-home' },
|
||||
children: newRoutes,
|
||||
}
|
||||
}
|
||||
|
||||
export default getNewRouter()
|
||||
// 过滤路由,最多两层
|
||||
const filterRoutesByPermission = (routes: RouteRecordRaw[], permissions: string[]): RouteRecordRaw[] => {
|
||||
return routes
|
||||
.map((route) => {
|
||||
// 确保 route.name 是字符串
|
||||
const parentName = typeof route.name === 'string' ? route.name : undefined
|
||||
if ('Dashboard' === parentName) return route // 首页不做权限过滤
|
||||
|
||||
const hasParentPermission = parentName && permissions.includes(parentName)
|
||||
|
||||
// 如果有子路由,过滤子路由
|
||||
if (Array.isArray(route.children) && route.children.length > 0) {
|
||||
route.children = route.children.filter((child) => {
|
||||
// 确保 child.name 是字符串
|
||||
const childName = typeof child.name === 'string' ? child.name : undefined
|
||||
|
||||
// 拼接父级和子级的权限字符串
|
||||
const fullName = parentName && childName ? `${parentName}.${childName}` : childName
|
||||
|
||||
// 确保 fullName 是字符串并且存在于 permissions 中
|
||||
return typeof fullName === 'string' && permissions.includes(fullName)
|
||||
})
|
||||
|
||||
// 如果子路由中有任何一个有权限,或者父级路由本身有权限,则保留该父级路由
|
||||
if (route.children.length > 0 || hasParentPermission) {
|
||||
return route
|
||||
}
|
||||
} else if (hasParentPermission) {
|
||||
// 如果没有子路由,直接检查父级路由的权限
|
||||
return route
|
||||
}
|
||||
|
||||
// 如果不符合任何条件,返回 undefined,表示该路由将被过滤掉
|
||||
return undefined
|
||||
})
|
||||
.filter((route): route is RouteRecordRaw => route !== undefined) // 过滤掉 undefined 的项并确保类型正确
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import router from '@/router'
|
||||
import router, { getP } from '@/router'
|
||||
|
||||
export const useUserStore = defineStore('user', {
|
||||
state: (): auth.AuthUser => ({
|
||||
@ -31,10 +31,13 @@ export const useUserStore = defineStore('user', {
|
||||
},
|
||||
userLogin(user: auth.AuthUser) {
|
||||
Object.assign(this, user)
|
||||
// 过了路由的地址
|
||||
// router.addRoute
|
||||
|
||||
router.push({ path: '/admin/acl/users' }) // 登录后跳转到用户列表页面
|
||||
// 根据权限动态添加地址
|
||||
if (!this.isAdmin) {
|
||||
const path = getP(this.permissions)
|
||||
router.addRoute(path)
|
||||
// router.replace(router.currentRoute.value.fullPath)
|
||||
}
|
||||
router.push({ path: '/admin/dashboard' })
|
||||
},
|
||||
logout() {
|
||||
this.mobile = ''
|
||||
@ -47,7 +50,11 @@ export const useUserStore = defineStore('user', {
|
||||
this.sex = 'FEMALE'
|
||||
this.token = ''
|
||||
this.refreshToken = ''
|
||||
router.push({ path: '/login/user' }) // 退出登录后跳转到登录页面,未实现,要清理缓存?
|
||||
router.push({ path: '/login/user' })
|
||||
},
|
||||
hasPermission(permission?: string) {
|
||||
if (this.isAdmin) return true
|
||||
return permission !== undefined && this.permissions.includes(permission)
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
|
4
src/views/admin/dashboard-page.vue
Normal file
4
src/views/admin/dashboard-page.vue
Normal file
@ -0,0 +1,4 @@
|
||||
<template>
|
||||
<p>这是首页,欢迎使用库管系统!!</p>
|
||||
</template>
|
||||
<script setup lang="ts"></script>
|
Loading…
x
Reference in New Issue
Block a user