Files
template-10586/app/utils/permission.ts
赵忠林 5e26fdc7fb feat(app): 初始化项目配置和页面结构
- 添加 .dockerignore 和 .env.example 配置文件
- 添加 .gitignore 忽略规则配置
- 创建服务端代理API路由(_file、_modules、_server)
- 集成 Ant Design Vue 组件库并配置SSR样式提取
- 定义API响应类型封装
- 创建基础布局组件(blank、console)
- 实现应用中心页面和组件(AppsCenter)
- 添加文章列表测试页面
- 配置控制台导航菜单结构
- 实现控制台头部组件
- 创建联系页面表单
2026-01-17 18:23:37 +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)
}
})
}
}