Files
shop-pc/app/utils/permission.ts
赵忠林 91e9a8c20f feat(pages): 添加文章和商品详情页及API代理配置
- 添加了.dockerignore、.env.example和.gitignore配置文件
- 实现了文件服务器、模块API和服务器API的代理功能
- 创建了动态路由页面用于展示文章列表和详情
- 实现了商品详情页面包括图片展示和价格信息
- 添加了静态页面展示功能支持富文本内容渲染
- 配置了SEO元数据和面包屑导航组件
2026-02-12 13:52:55 +08:00

248 lines
6.4 KiB
TypeScript
Raw 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)
}
})
}
}