feat: 完善权限

This commit is contained in:
chansee97 2025-09-05 00:49:41 +08:00
parent cf41abc5a5
commit f9ef71f0c7
11 changed files with 154 additions and 51 deletions

View File

@ -1,21 +1,57 @@
import type { App, Directive } from 'vue' import type { App, Directive } from 'vue'
import { usePermission } from '@/hooks' import { useMenuPermission, usePermission, useRolePermission } from '@/hooks'
export function install(app: App) { export function install(app: App) {
const { hasPermission } = usePermission() const { hasPermission } = usePermission()
const { hasMenuPermission } = useMenuPermission()
const { hasRolePermission } = useRolePermission()
function updatapermission(el: HTMLElement, permission: string | string[]) { function updatePermission(el: HTMLElement, permission: string | string[]) {
if (!permission) if (!permission)
throw new Error('v-permissson Directive with no explicit role attached') throw new Error('v-permission Directive with no explicit permission attached')
if (!hasPermission(permission)) if (!hasPermission(permission))
el.parentElement?.removeChild(el) el.parentElement?.removeChild(el)
} }
function updateMenuPermission(el: HTMLElement, permission: string | string[]) {
if (!permission)
throw new Error('v-menu Directive with no explicit menu permission attached')
if (!hasMenuPermission(permission))
el.parentElement?.removeChild(el)
}
function updateRolePermission(el: HTMLElement, role: string | string[]) {
if (!role)
throw new Error('v-role Directive with no explicit role attached')
if (!hasRolePermission(role))
el.parentElement?.removeChild(el)
}
// 通用权限指令(兼容旧版本)
const permissionDirective: Directive<HTMLElement, string | string[]> = { const permissionDirective: Directive<HTMLElement, string | string[]> = {
mounted(el, binding) { mounted(el, binding) {
updatapermission(el, binding.value) updatePermission(el, binding.value)
}, },
} }
// 菜单权限指令
const menuPermissionDirective: Directive<HTMLElement, string | string[]> = {
mounted(el, binding) {
updateMenuPermission(el, binding.value)
},
}
// 角色权限指令
const rolePermissionDirective: Directive<HTMLElement, string | string[]> = {
mounted(el, binding) {
updateRolePermission(el, binding.value)
},
}
app.directive('permission', permissionDirective) app.directive('permission', permissionDirective)
app.directive('menu', menuPermissionDirective)
app.directive('role', rolePermissionDirective)
} }

View File

@ -1,6 +1,60 @@
import { useAuthStore } from '@/store' import { useAuthStore } from '@/store'
/** 权限判断 */ /** 菜单权限判断 */
export function useMenuPermission() {
const authStore = useAuthStore()
function hasMenuPermission(
permissions?: string | string[],
) {
if (!permissions)
return true
// 确保 permissions 是数组
const permissionArray = Array.isArray(permissions) ? permissions : [permissions]
// 全部权限
if (permissionArray.includes('admin'))
return true
const { permissions: userPermissions } = authStore
return permissionArray.some(i => userPermissions.includes(i))
}
return {
hasMenuPermission,
}
}
/** 角色权限判断 */
export function useRolePermission() {
const authStore = useAuthStore()
function hasRolePermission(
roles?: string | string[],
) {
if (!roles)
return true
// 确保 roles 是数组
const roleArray = Array.isArray(roles) ? roles : [roles]
// 全部权限
if (roleArray.includes('admin'))
return true
const { roles: userRoles } = authStore
return roleArray.some(i => userRoles.includes(i))
}
return {
hasRolePermission,
}
}
/** 合并权限判断 */
export function usePermission() { export function usePermission() {
const authStore = useAuthStore() const authStore = useAuthStore()
@ -10,16 +64,20 @@ export function usePermission() {
if (!permissions) if (!permissions)
return true return true
// 全部权限
if (permissions === '*:*:*')
return true
const { permissions: userPermissions } = authStore
// 确保 permissions 是数组 // 确保 permissions 是数组
const permissionArray = Array.isArray(permissions) ? permissions : [permissions] const permissionArray = Array.isArray(permissions) ? permissions : [permissions]
return permissionArray.some(i => userPermissions.includes(i)) // 全部权限
if (permissionArray.includes('admin'))
return true
const { permissions: userPermissions, roles: userRoles } = authStore
const hasPermission = permissionArray.some(i => userPermissions.includes(i))
if (hasPermission)
return true
return permissionArray.some(i => userRoles.includes(i))
} }
return { return {

View File

@ -6,12 +6,14 @@ import { useTabStore } from './tab'
interface AuthStatus { interface AuthStatus {
userInfo: Entity.User | Record<string, any> userInfo: Entity.User | Record<string, any>
roles: string[]
permissions: string[] permissions: string[]
} }
export const useAuthStore = defineStore('auth-store', { export const useAuthStore = defineStore('auth-store', {
state: (): AuthStatus => { state: (): AuthStatus => {
return { return {
userInfo: {}, userInfo: {},
roles: [],
permissions: [], permissions: [],
} }
}, },
@ -71,6 +73,8 @@ export const useAuthStore = defineStore('auth-store', {
async updataUserInfo() { async updataUserInfo() {
const { data } = await fetchUserInfo() const { data } = await fetchUserInfo()
this.userInfo = data this.userInfo = data
this.roles = data.roles.map((role: Entity.Role) => role.roleKey)
this.permissions = data.permissions
}, },
/* 处理登录返回的数据 */ /* 处理登录返回的数据 */
async handleLoginInfo(data: any) { async handleLoginInfo(data: any) {

View File

@ -33,5 +33,7 @@ namespace Entity {
roles: Entity.Role[] roles: Entity.Role[]
/** 所属部门 */ /** 所属部门 */
dept: Entity.Dept dept: Entity.Dept
/** 用户权限, 登陆时返回该字段 */
permissions: string[]
} }
} }

View File

@ -8,6 +8,9 @@ const settings = ref({
<template> <template>
<div class="space-y-4"> <div class="space-y-4">
<n-alert type="warning" style="margin-bottom: 16px;">
示例组件实际功能未实现
</n-alert>
<!-- 邮件通知 --> <!-- 邮件通知 -->
<n-card title="邮件通知" size="small"> <n-card title="邮件通知" size="small">
<n-list> <n-list>

View File

@ -8,12 +8,11 @@ const preferences = ref({
<template> <template>
<div class="space-y-4"> <div class="space-y-4">
<n-alert type="warning" style="margin-bottom: 16px;">
示例组件实际功能未实现
</n-alert>
<!-- 实验性功能 --> <!-- 实验性功能 -->
<n-card title="实验性功能" size="small"> <n-card title="实验性功能" size="small">
<n-alert type="warning" style="margin-bottom: 16px;">
以下功能仍在测试阶段可能会影响系统稳定性
</n-alert>
<n-list> <n-list>
<n-list-item> <n-list-item>
<n-thing> <n-thing>

View File

@ -86,7 +86,7 @@ onMounted(async () => {
</script> </script>
<template> <template>
<n-card> <n-card class="max-w-1024px m-auto">
<n-flex :wrap="false" style="height: 100%;"> <n-flex :wrap="false" style="height: 100%;">
<!-- 左侧区域 --> <!-- 左侧区域 -->
<div class="w-[220px] border-r border-[var(--n-border-color)] flex flex-col"> <div class="w-[220px] border-r border-[var(--n-border-color)] flex flex-col">

View File

@ -46,7 +46,7 @@
/> />
<pro-input <pro-input
title="权限标识" title="权限标识"
tooltip="页面访问权限标识" tooltip="后端装饰器一致,如@RequirePermissions('system:user:list')"
path="perms" path="perms"
placeholder="Eg: system:user:list" placeholder="Eg: system:user:list"
/> />

View File

@ -2,7 +2,7 @@
<pro-input <pro-input
required required
title="权限标识" title="权限标识"
tooltip="按钮权限唯一标识符" tooltip="后端装饰器一致,如@RequirePermissions('system:user:add')"
path="perms" path="perms"
placeholder="Eg: system:user:add" placeholder="Eg: system:user:add"
/> />

View File

@ -46,9 +46,6 @@ async function getAllRoutes(params?: MenuSearchQuery) {
parentProperty: 'parentId', parentProperty: 'parentId',
}) })
} }
catch {
window.$message.error('获取菜单列表失败')
}
finally { finally {
endLoading() endLoading()
} }

View File

@ -78,14 +78,16 @@ export function createRoleColumns(actions: RoleColumnActions): DataTableColumns<
width: 100, width: 100,
render: (row) => { render: (row) => {
return ( return (
<NSwitch row.roleKey !== 'admin' && (
value={row.status} <NSwitch
checked-value={0} value={row.status}
unchecked-value={1} checked-value={0}
onUpdateValue={(value: 0 | 1) => actions.onStatusChange(row.id, value)} unchecked-value={1}
> onUpdateValue={(value: 0 | 1) => actions.onStatusChange(row.id, value)}
{{ checked: () => '启用', unchecked: () => '禁用' }} >
</NSwitch> {{ checked: () => '启用', unchecked: () => '禁用' }}
</NSwitch>
)
) )
}, },
}, },
@ -102,27 +104,29 @@ export function createRoleColumns(actions: RoleColumnActions): DataTableColumns<
width: 200, width: 200,
render: (row) => { render: (row) => {
return ( return (
<NSpace justify="center"> row.roleKey !== 'admin' && (
<NButton <NSpace justify="center">
text <NButton
type="primary" text
onClick={() => actions.onEdit(row)} type="primary"
> onClick={() => actions.onEdit(row)}
>
</NButton>
<NPopconfirm </NButton>
onPositiveClick={() => actions.onDelete(row.id)} <NPopconfirm
> onPositiveClick={() => actions.onDelete(row.id)}
{{ >
default: () => '确认删除该角色?', {{
trigger: () => ( default: () => '确认删除该角色?',
<NButton text type="error"> trigger: () => (
<NButton text type="error">
</NButton>
), </NButton>
}} ),
</NPopconfirm> }}
</NSpace> </NPopconfirm>
</NSpace>
)
) )
}, },
}, },