新版官网模板

This commit is contained in:
2026-04-29 01:33:33 +08:00
commit 0d82386f8f
341 changed files with 64526 additions and 0 deletions

View File

@@ -0,0 +1,310 @@
/**
* 应用级权限管理 composable
*
* 管理当前用户在各应用中的角色权限,提供:
* 1. 开发者中心访问检查
* 2. 可访问应用列表 + 角色映射
* 3. 按角色判断某应用的具体权限
*/
import type { AppRole, AppPermissionInfo } from '@/api/app/appUser/model'
// ============ 角色层级 ============
/** 角色层级:数字越大权限越高 */
export const ROLE_HIERARCHY: Record<AppRole, number> = {
viewer: 1,
developer: 2,
admin: 3,
owner: 4,
}
/** 角色中文名 */
export const ROLE_LABEL: Record<AppRole, string> = {
owner: '所有者',
admin: '管理员',
developer: '开发者',
viewer: '只读',
}
/** 角色颜色(用于 RoleTag */
export const ROLE_COLOR: Record<AppRole, string> = {
owner: 'gold',
admin: 'blue',
developer: 'green',
viewer: 'default',
}
// ============ 权限定义 ============
export interface AppPermission {
appId: number
productName: string
productCode?: string
icon?: string
role: AppRole
isOwner: boolean
canManageMembers: boolean // owner | admin
canEditApp: boolean // owner | admin
canDeleteApp: boolean // owner only
canSubmitReview: boolean // owner | admin
canEditResource: boolean // owner | admin | developer
canViewSensitive: boolean // owner | admin | developer
canCreateApiKey: boolean // owner | admin | developer
canEditConfig: boolean // owner | admin
canTriggerBuild: boolean // owner | admin | developer
}
/** 根据 role 计算权限字段 */
function buildPermission(info: AppPermissionInfo): AppPermission {
const { role } = info
const isOwner = role === 'owner'
const isAtLeastAdmin = isOwner || role === 'admin'
const isAtLeastDeveloper = isAtLeastAdmin || role === 'developer'
return {
appId: info.appId,
productName: info.productName,
productCode: info.productCode,
icon: info.icon,
role,
isOwner,
canManageMembers: isAtLeastAdmin,
canEditApp: isAtLeastAdmin,
canDeleteApp: isOwner,
canSubmitReview: isAtLeastAdmin,
canEditResource: isAtLeastDeveloper,
canViewSensitive: isAtLeastDeveloper,
canCreateApiKey: isAtLeastDeveloper,
canEditConfig: isAtLeastAdmin,
canTriggerBuild: isAtLeastDeveloper,
}
}
// ============ 全局状态(模块级单例) ============
const appPermissionsMap = ref<Map<number, AppPermission>>(new Map())
const isPlatformDeveloper = ref(false)
const hasCheckedAccess = ref(false)
const loading = ref(false)
// 用户信息(供其他组件复用,避免重复请求)
const user = ref<{
userId?: number
username?: string
nickname?: string
phone?: string
mobile?: string
type?: number
} | null>(null)
/**
* 设置当前用户信息(布局中调用)
*/
function setCurrentUser(userInfo: typeof user.value) {
user.value = userInfo
}
/**
* 获取当前用户信息
*/
function getCurrentUser() {
return user.value
}
// ============ 核心方法 ============
/**
* 检查用户是否有开发者中心访问权限
* 并自动加载可访问的应用列表
*/
async function checkDeveloperAccess(): Promise<{
accessible: boolean
isPlatformDeveloper: boolean
hasJoinedApps: boolean
}> {
if (loading.value) return { accessible: false, isPlatformDeveloper: false, hasJoinedApps: false }
loading.value = true
try {
// 尝试获取可访问应用列表后端接口GET /api/app/app-user/check-access
const { checkAppAccess } = await import('@/api/app/appUser')
const res = await checkAppAccess()
if (res?.accessible) {
isPlatformDeveloper.value = res.isPlatformDeveloper ?? false
hasCheckedAccess.value = true
// 如果返回了可访问应用列表,直接缓存
if (res.apps?.length) {
const newMap = new Map<number, AppPermission>()
for (const info of res.apps) {
newMap.set(info.appId, buildPermission(info))
}
appPermissionsMap.value = newMap
}
return {
accessible: true,
isPlatformDeveloper: res.isPlatformDeveloper ?? false,
hasJoinedApps: (res.apps?.length ?? 0) > 0,
}
}
return { accessible: false, isPlatformDeveloper: false, hasJoinedApps: false }
}
catch {
// 接口失败时降级:如果 localStorage 有 UserId 且是 type=2仍允许访问
if (import.meta.client) {
const userType = localStorage.getItem('UserType')
if (userType === '2') {
isPlatformDeveloper.value = true
hasCheckedAccess.value = true
return { accessible: true, isPlatformDeveloper: true, hasJoinedApps: false }
}
}
return { accessible: false, isPlatformDeveloper: false, hasJoinedApps: false }
}
finally {
loading.value = false
}
}
/**
* 加载用户所有可访问应用的权限
* 后端接口GET /api/app/product/accessible
*/
async function loadAppPermissions(): Promise<Map<number, AppPermission>> {
try {
const { getMyAccessibleApps } = await import('@/api/app/appProduct')
const apps = await getMyAccessibleApps()
const newMap = new Map<number, AppPermission>()
for (const app of apps) {
const role = (app.myRole as AppRole) || 'viewer'
newMap.set(app.productId!, {
appId: app.productId!,
productName: app.productName || '',
productCode: app.productCode,
icon: app.icon,
role,
...(buildPermission({ appId: app.productId!, productName: app.productName || '', role } as AppPermissionInfo)),
})
}
appPermissionsMap.value = newMap
return newMap
}
catch {
console.warn('[useAppPermission] 加载应用权限失败')
return appPermissionsMap.value
}
}
/**
* 设置单个应用的权限缓存
*/
function setAppPermission(info: AppPermissionInfo) {
const permission = buildPermission(info)
appPermissionsMap.value.set(info.appId, permission)
}
/**
* 获取指定应用的权限
*/
function getAppPermission(appId: number | undefined | null): AppPermission | null {
if (!appId) return null
return appPermissionsMap.value.get(appId) ?? null
}
/**
* 检查指定应用是否拥有某项权限
*/
function hasPermission(
appId: number | undefined | null,
permission: keyof AppPermission,
): boolean {
const perm = getAppPermission(appId)
if (!perm) return false
return !!perm[permission]
}
/**
* 检查当前用户在指定应用中的角色是否 >= 某个级别
*/
function hasRole(appId: number | undefined | null, minRole: AppRole): boolean {
const perm = getAppPermission(appId)
if (!perm) return false
return ROLE_HIERARCHY[perm.role] >= ROLE_HIERARCHY[minRole]
}
/**
* 清空权限缓存(登出时调用)
*/
function clearPermissions() {
appPermissionsMap.value.clear()
isPlatformDeveloper.value = false
hasCheckedAccess.value = false
}
// ============ Computed ============
/** 可访问的应用 ID 列表 */
const accessibleAppIds = computed(() => [...appPermissionsMap.value.keys()])
/** 是否已检查过访问权限 */
const isChecked = computed(() => hasCheckedAccess.value)
/** 是否正在加载 */
const isLoading = computed(() => loading.value)
/** 权限不足时的提示文字 */
function getNoPermissionTip(role?: AppRole): string {
const roleName = role ? ROLE_LABEL[role] : '当前角色'
return `当前身份为「${roleName}」,无法执行此操作。如需更多权限,请联系应用管理员。`
}
// ============ 敏感信息遮罩 ============
/**
* 遮罩敏感字段值
* @param value 原始值
* @param canView 是否有查看权限
* @param maskLength 遮罩长度,默认 6 个 *
*/
function maskSensitiveValue(value: string | null | undefined, canView: boolean, maskLength = 6): string {
if (canView) return value || ''
if (!value) return ''
const mask = '*'.repeat(maskLength)
// 对于较短的值直接全部遮罩,较长的值显示前几个字符
if (value.length <= maskLength) return mask
return value.slice(0, 4) + mask
}
// ============ 组合式函数导出 ============
export function useAppPermission() {
return {
// 状态
user,
appPermissions: appPermissionsMap,
isPlatformDeveloper: readonly(isPlatformDeveloper),
isChecked: readonly(isChecked),
isLoading: readonly(isLoading),
// 方法
setCurrentUser,
getCurrentUser,
checkDeveloperAccess,
loadAppPermissions,
setAppPermission,
getAppPermission,
hasPermission,
hasRole,
clearPermissions,
getNoPermissionTip,
// 工具
accessibleAppIds,
maskSensitiveValue,
}
}