Files
pc-10584/app/utils/permission.ts
赵忠林 775841eed3 feat(core): 初始化项目基础架构和CMS功能模块
- 添加Docker相关配置文件(.dockerignore, .env.example, .gitignore)
- 实现服务端API代理功能,支持文件、模块和服务器API转发
- 创建文章详情页、栏目文章列表页和单页内容展示页面
- 集成Ant Design Vue组件库并实现SSR样式提取功能
- 定义API响应数据结构类型和应用布局组件
- 开发开发者应用中心和文章管理页面
- 实现CMS导航菜单获取和多租户切换功能
2026-01-27 00:14:08 +08:00

248 lines
6.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 按钮级权限控制
*
* 参考 mp-vue/src/utils/permission.ts
* 当前项目未引入 Pinia 的 user store因此改为从 Nuxt state / localStorage 读取。
*/
import type { App } from 'vue'
import { useState } from '#imports'
import type { User } from '@/api/system/user/model'
import type { Role } from '@/api/system/role/model'
import type { Menu } from '@/api/system/menu/model'
type AuthzState = {
roles: string[]
authorities: string[]
}
const AUTHZ_STORAGE_KEY = 'Authz'
function uniqNonEmpty(values: Array<string | undefined | null>) {
const seen = new Set<string>()
for (const v of values) {
const s = typeof v === 'string' ? v.trim() : ''
if (!s || s === 'null' || s === 'undefined') continue
seen.add(s)
}
return Array.from(seen)
}
function safeJsonParse(value: string): unknown {
try {
return JSON.parse(value)
} catch {
return undefined
}
}
function normalizeStringArray(value: unknown): string[] {
if (Array.isArray(value)) {
return uniqNonEmpty(value.filter((v): v is string => typeof v === 'string'))
}
if (typeof value === 'string') {
return uniqNonEmpty(
value
.split(',')
.map((v) => v.trim())
.filter(Boolean)
)
}
return []
}
function readAuthzFromStorage(): AuthzState {
if (!import.meta.client) return { roles: [], authorities: [] }
try {
const raw = localStorage.getItem(AUTHZ_STORAGE_KEY)
if (!raw) return { roles: [], authorities: [] }
const parsed = safeJsonParse(raw)
if (!parsed || typeof parsed !== 'object') return { roles: [], authorities: [] }
const obj = parsed as Record<string, unknown>
return {
roles: normalizeStringArray(obj.roles),
authorities: normalizeStringArray(obj.authorities)
}
} catch {
return { roles: [], authorities: [] }
}
}
function writeAuthzToStorage(next: AuthzState) {
if (!import.meta.client) return
try {
localStorage.setItem(AUTHZ_STORAGE_KEY, JSON.stringify(next))
} catch {
// ignore
}
}
function getAuthzStateRef() {
try {
return useState<AuthzState>('authz', () => readAuthzFromStorage())
} catch {
return null
}
}
function getAuthzSnapshot(): AuthzState {
const state = getAuthzStateRef()
return state?.value ?? readAuthzFromStorage()
}
export function setAuthz(next: Partial<AuthzState>) {
const current = getAuthzSnapshot()
const merged: AuthzState = {
roles: normalizeStringArray(next.roles ?? current.roles),
authorities: normalizeStringArray(next.authorities ?? current.authorities)
}
const state = getAuthzStateRef()
if (state) state.value = merged
writeAuthzToStorage(merged)
}
export function clearAuthz() {
const state = getAuthzStateRef()
if (state) state.value = { roles: [], authorities: [] }
if (!import.meta.client) return
try {
localStorage.removeItem(AUTHZ_STORAGE_KEY)
} catch {
// ignore
}
}
function getRoleCodesFromUser(user?: User | null) {
const roleCodes: string[] = []
const fromUserRoleCode = normalizeStringArray(user?.roleCode)
roleCodes.push(...fromUserRoleCode)
const roles: Role[] | undefined = user?.roles
if (Array.isArray(roles)) {
for (const role of roles) {
if (!role) continue
roleCodes.push(role.roleCode)
}
}
return uniqNonEmpty(roleCodes)
}
function getAuthoritiesFromUser(user?: User | null) {
const authorities: string[] = []
const list: Menu[] | undefined = user?.authorities
if (Array.isArray(list)) {
for (const item of list) {
if (!item) continue
authorities.push(item.authority)
}
}
return uniqNonEmpty(authorities)
}
export function setAuthzFromUser(user?: User | null) {
setAuthz({
roles: getRoleCodesFromUser(user),
authorities: getAuthoritiesFromUser(user)
})
}
/* 判断数组是否有某些值(全包含) */
function normalizeNeedles(value: string | string[]) {
if (Array.isArray(value)) return uniqNonEmpty(value)
const s = typeof value === 'string' ? value.trim() : ''
return s ? [s] : []
}
function normalizeHaystack(array: (string | undefined)[]) {
return uniqNonEmpty(array)
}
function arrayHas(array: (string | undefined)[], value: string | string[]): boolean {
if (!value) return true
if (!array) return false
const needles = normalizeNeedles(value)
if (needles.length === 0) return true
const haystack = new Set(normalizeHaystack(array))
for (let i = 0; i < needles.length; i++) {
if (!haystack.has(needles[i])) return false
}
return true
}
/* 判断数组是否有任意值(任一包含) */
function arrayHasAny(array: (string | undefined)[], value: string | string[]): boolean {
if (!value) return true
if (!array) return false
const needles = normalizeNeedles(value)
if (needles.length === 0) return true
const haystack = new Set(normalizeHaystack(array))
for (let i = 0; i < needles.length; i++) {
if (haystack.has(needles[i])) return true
}
return false
}
/**
* 是否有某些角色
* @param value 角色字符或字符数组
*/
export function hasRole(value: string | string[]): boolean {
const { roles } = getAuthzSnapshot()
return arrayHas(roles, value)
}
/**
* 是否有任意角色
* @param value 角色字符或字符数组
*/
export function hasAnyRole(value: string | string[]): boolean {
const { roles } = getAuthzSnapshot()
return arrayHasAny(roles, value)
}
/**
* 是否有某些权限
* @param value 权限字符或字符数组
*/
export function hasPermission(value: string | string[]): boolean {
const { authorities } = getAuthzSnapshot()
return arrayHas(authorities, value)
}
/**
* 是否有任意权限
* @param value 权限字符或字符数组
*/
export function hasAnyPermission(value: string | string[]): boolean {
const { authorities } = getAuthzSnapshot()
return arrayHasAny(authorities, value)
}
export default {
install(app: App) {
// 添加自定义指令
app.directive('role', {
mounted: (el, binding) => {
if (!hasRole(binding.value)) el.parentNode?.removeChild(el)
}
})
app.directive('any-role', {
mounted: (el, binding) => {
if (!hasAnyRole(binding.value)) el.parentNode?.removeChild(el)
}
})
app.directive('permission', {
mounted: (el, binding) => {
if (!hasPermission(binding.value)) el.parentNode?.removeChild(el)
}
})
app.directive('any-permission', {
mounted: (el, binding) => {
if (!hasAnyPermission(binding.value)) el.parentNode?.removeChild(el)
}
})
}
}