feat(frontend): 完善前端权限系统

- 扩展 auth/roles.ts 添加13个新角色和40+权限定义
- 创建 services/permission.ts 权限API服务
- 创建 composables/usePermission.ts 权限组合函数
- 创建 router/permissionGuard.ts 路由权限守卫
- 更新路由配置使用新角色系统
- 更新 App.vue, LoginView, UsersView, PermissionsView 等使用新角色
- 更新 DemoDataService 使用新角色
- 前端编译验证通过
This commit is contained in:
Your Name
2026-03-05 07:36:38 +08:00
parent 62b1eef3af
commit 64bae7c13b
16 changed files with 882 additions and 119 deletions

View File

@@ -17,7 +17,10 @@
"Edit", "Edit",
"Read", "Read",
"Glob", "Glob",
"Grep" "Grep",
"Bash(cd /home/long/project/蚊子/frontend/admin && npm run build 2>&1 | head -30)",
"Bash(npm run build 2>&1 | head -40)",
"Bash(npm run build 2>&1 | head -50)"
], ],
"deny": [] "deny": []
}, },

View File

@@ -6,9 +6,9 @@
- **Max Iterations**: 100 - **Max Iterations**: 100
## Current State ## Current State
- **Iteration**: 5 - **Iteration**: 6
- **Status**: In Progress - **Status**: In Progress
- **Current Phase**: Phase 2 - 权限核心模块后端完成 - **Current Phase**: Phase 2 - 前端权限组件开发
## Progress - Phase 2 ## Progress - Phase 2
- [x] Phase 1: 数据库表创建10张表 - [x] Phase 1: 数据库表创建10张表
@@ -19,17 +19,24 @@
- [x] 权限判断服务 (PermissionCheckService) - 已完善 - [x] 权限判断服务 (PermissionCheckService) - 已完善
- [x] 用户角色关联 (SysUserRole + UserRoleRepository) - [x] 用户角色关联 (SysUserRole + UserRoleRepository)
- [x] 角色权限关联 (SysRolePermission + RolePermissionRepository) - [x] 角色权限关联 (SysRolePermission + RolePermissionRepository)
- [ ] Phase 2: 前端页面和组件 - [x] Phase 2: 前端权限组件
- [x] 扩展 auth/roles.ts - 添加13个新角色和40+权限
- [x] 创建 services/permission.ts - 权限API服务
- [x] 创建 composables/usePermission.ts - 权限组合函数
- [x] 创建 router/permissionGuard.ts - 路由权限守卫
- [x] 更新路由配置 - 使用新角色系统
- [x] 更新 App.vue, LoginView, UsersView 等使用新角色
- [ ] Phase 3: 审批流引擎 - [ ] Phase 3: 审批流引擎
## Completion Criteria ## Completion Criteria
- [x] Phase 1: 数据库表创建 - 100% - [x] Phase 1: 数据库表创建 - 100%
- [x] Phase 2: 后端核心模块 - 100% - [x] Phase 2: 后端核心模块 - 100%
- [ ] Phase 2: 前端页面 - 0% - [x] Phase 2: 前端权限组件 - 90%
- [ ] Phase 3: 审批流引擎 - 0% - [ ] Phase 3: 审批流引擎 - 0%
- [ ] Phase 4: 业务模块开发 - 0% - [ ] Phase 4: 业务模块开发 - 0%
## Recent Changes (Iteration 5) ## Recent Changes (Iteration 6)
- 创建 UserRoleRepository 实现用户角色关联查询 - 扩展前端角色权限类型定义
- 创建 RolePermissionRepository 实现角色权限关联查询 - 创建权限服务和路由守卫
- 完善 PermissionCheckService 实现核心权限验证逻辑 - 更新前端视图使用新角色系统
- 前端编译成功

View File

@@ -27,7 +27,7 @@
活动 活动
</RouterLink> </RouterLink>
<RouterLink <RouterLink
v-if="auth.hasPermission('manage:users')" v-if="auth.hasPermission('user:view')"
to="/users" to="/users"
class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition" class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition"
:class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/users') }" :class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/users') }"
@@ -35,7 +35,7 @@
用户 用户
</RouterLink> </RouterLink>
<RouterLink <RouterLink
v-if="auth.hasPermission('manage:rewards')" v-if="auth.hasPermission('reward:view')"
to="/rewards" to="/rewards"
class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition" class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition"
:class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/rewards') }" :class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/rewards') }"
@@ -43,7 +43,7 @@
奖励 奖励
</RouterLink> </RouterLink>
<RouterLink <RouterLink
v-if="auth.hasPermission('manage:risk')" v-if="auth.hasPermission('risk:view')"
to="/risk" to="/risk"
class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition" class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition"
:class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/risk') }" :class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/risk') }"
@@ -51,7 +51,7 @@
风控 风控
</RouterLink> </RouterLink>
<RouterLink <RouterLink
v-if="auth.hasPermission('view:audit')" v-if="auth.hasPermission('audit:view')"
to="/audit" to="/audit"
class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition" class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition"
:class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/audit') }" :class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/audit') }"
@@ -59,7 +59,7 @@
审计 审计
</RouterLink> </RouterLink>
<RouterLink <RouterLink
v-if="auth.hasPermission('manage:users')" v-if="auth.hasPermission('approval:view')"
to="/approvals" to="/approvals"
class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition" class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition"
:class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/approvals') }" :class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/approvals') }"
@@ -67,7 +67,7 @@
审批 审批
</RouterLink> </RouterLink>
<RouterLink <RouterLink
v-if="auth.hasPermission('manage:users')" v-if="auth.hasPermission('permission:view')"
to="/permissions" to="/permissions"
class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition" class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition"
:class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/permissions') }" :class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/permissions') }"
@@ -75,7 +75,7 @@
权限 权限
</RouterLink> </RouterLink>
<RouterLink <RouterLink
v-if="auth.hasPermission('view:notifications')" v-if="auth.hasPermission('dashboard:view')"
to="/notifications" to="/notifications"
class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition" class="rounded-full px-3 py-1.5 text-mosquito-ink/70 transition"
:class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/notifications') }" :class="{ 'bg-mosquito-accent/10 text-mosquito-ink': route.path.startsWith('/notifications') }"
@@ -89,8 +89,18 @@
<label v-if="auth.mode === 'demo'" class="flex items-center gap-2 text-xs text-mosquito-ink/70"> <label v-if="auth.mode === 'demo'" class="flex items-center gap-2 text-xs text-mosquito-ink/70">
角色 角色
<select class="mos-input !py-1 !px-2 !text-xs" v-model="selectedRole" @change="onRoleChange"> <select class="mos-input !py-1 !px-2 !text-xs" v-model="selectedRole" @change="onRoleChange">
<option value="admin">管理员</option> <option value="super_admin">超级管理员</option>
<option value="operator">运营</option> <option value="system_admin">系统管理员</option>
<option value="operation_manager">运营经理</option>
<option value="operation_member">运营成员</option>
<option value="marketing_manager">市场经理</option>
<option value="marketing_member">市场成员</option>
<option value="finance_manager">财务经理</option>
<option value="finance_member">财务成员</option>
<option value="risk_manager">风控经理</option>
<option value="risk_member">风控成员</option>
<option value="customer_service">客服</option>
<option value="auditor">审计员</option>
<option value="viewer">只读</option> <option value="viewer">只读</option>
</select> </select>
</label> </label>
@@ -140,15 +150,15 @@ import { useAuthStore } from './stores/auth'
import { downloadCsv } from './utils/export' import { downloadCsv } from './utils/export'
import ExportFieldPanel, { type ExportField } from './components/ExportFieldPanel.vue' import ExportFieldPanel, { type ExportField } from './components/ExportFieldPanel.vue'
import { useExportFields } from './composables/useExportFields' import { useExportFields } from './composables/useExportFields'
import { RoleLabels, type AdminRole } from './auth/roles'
const route = useRoute() const route = useRoute()
const auth = useAuthStore() const auth = useAuthStore()
const selectedRole = ref(auth.role) const selectedRole = ref<AdminRole>(auth.role)
const roleLabel = computed(() => { const roleLabel = computed(() => {
if (auth.role === 'admin') return '管理员' const role = auth.role as AdminRole
if (auth.role === 'operator') return '运营' return RoleLabels[role] || '未知角色'
return '只读'
}) })
const onRoleChange = () => { const onRoleChange = () => {
@@ -158,7 +168,7 @@ const onRoleChange = () => {
watch( watch(
() => auth.role, () => auth.role,
(value) => { (value) => {
selectedRole.value = value selectedRole.value = value as AdminRole
} }
) )

View File

@@ -1,28 +1,45 @@
import type { AdminRole, Permission } from '../roles' import type { AdminRole, Permission } from '../roles'
import { RolePermissions } from '../roles' import { RolePermissions, RoleLabels } from '../roles'
import type { AuthAdapter, AuthUser, LoginResult } from '../types' import type { AuthAdapter, AuthUser, LoginResult } from '../types'
// 角色名称映射
const roleNameMap: Record<AdminRole, string> = {
'super_admin': '演示超级管理员',
'system_admin': '演示系统管理员',
'operation_manager': '演示运营经理',
'operation_member': '演示运营成员',
'marketing_manager': '演示市场经理',
'marketing_member': '演示市场成员',
'finance_manager': '演示财务经理',
'finance_member': '演示财务成员',
'risk_manager': '演示风控经理',
'risk_member': '演示风控成员',
'customer_service': '演示客服',
'auditor': '演示审计员',
'viewer': '演示访客'
}
const demoUser = (role: AdminRole): AuthUser => ({ const demoUser = (role: AdminRole): AuthUser => ({
id: `demo-${role}`, id: `demo-${role}`,
name: role === 'admin' ? '演示管理员' : role === 'operator' ? '演示运营' : '演示访客', name: roleNameMap[role] || '演示用户',
email: 'demo@mosquito.local', email: 'demo@mosquito.local',
role role
}) })
export class DemoAuthAdapter implements AuthAdapter { export class DemoAuthAdapter implements AuthAdapter {
private currentUser: AuthUser | null = demoUser('admin') private currentUser: AuthUser = demoUser('super_admin')
async loginWithPassword(_username: string, _password: string): Promise<LoginResult> { async loginWithPassword(_username: string, _password: string): Promise<LoginResult> {
return { user: demoUser('admin') } return { user: demoUser('super_admin') }
} }
async loginDemo(role: AdminRole = 'admin'): Promise<LoginResult> { async loginDemo(role: AdminRole = 'super_admin'): Promise<LoginResult> {
this.currentUser = demoUser(role) this.currentUser = demoUser(role)
return { user: this.currentUser } return { user: this.currentUser }
} }
async logout(): Promise<void> { async logout(): Promise<void> {
this.currentUser = null this.currentUser = null as any
} }
async switchRole(role: AdminRole): Promise<AuthUser> { async switchRole(role: AdminRole): Promise<AuthUser> {
@@ -35,6 +52,6 @@ export class DemoAuthAdapter implements AuthAdapter {
} }
hasPermission(role: AdminRole, permission: Permission): boolean { hasPermission(role: AdminRole, permission: Permission): boolean {
return RolePermissions[role].includes(permission) return RolePermissions[role]?.includes(permission) ?? false
} }
} }

View File

@@ -1,44 +1,265 @@
export type AdminRole = 'admin' | 'operator' | 'viewer' /**
* 角色权限类型定义
* 对应后端数据库中的角色和权限
*/
// 角色类型 - 对应 sys_role 表
export type AdminRole =
| 'super_admin' // 超级管理员
| 'system_admin' // 系统管理员
| 'operation_manager' // 运营经理
| 'operation_member' // 运营成员
| 'marketing_manager' // 市场经理
| 'marketing_member' // 市场成员
| 'finance_manager' // 财务经理
| 'finance_member' // 财务成员
| 'risk_manager' // 风控经理
| 'risk_member' // 风控成员
| 'customer_service' // 客服
| 'auditor' // 审计员
| 'viewer' // 只读
// 权限代码 - 对应 sys_permission 表
export type Permission = export type Permission =
| 'view:dashboard' // 仪表盘
| 'view:activities' | 'dashboard:view'
| 'view:leaderboard' | 'dashboard:export'
| 'view:alerts'
| 'view:notifications'
| 'manage:users'
| 'manage:rewards'
| 'manage:risk'
| 'manage:config'
| 'view:audit'
// 用户管理
| 'user:view'
| 'user:create'
| 'user:update'
| 'user:delete'
| 'user:freeze'
| 'user:unfreeze'
| 'user:certify'
| 'user:export'
// 活动管理
| 'activity:view'
| 'activity:create'
| 'activity:update'
| 'activity:delete'
| 'activity:publish'
| 'activity:pause'
| 'activity:end'
| 'activity:export'
// 奖励管理
| 'reward:view'
| 'reward:approve'
| 'reward:发放'
| 'reward:reject'
| 'reward:export'
// 风险管理
| 'risk:view'
| 'risk:rule'
| 'risk:audit'
| 'risk:blacklist'
| 'risk:export'
// 审批中心
| 'approval:view'
| 'approval:handle'
| 'approval:delegate'
// 审计日志
| 'audit:view'
| 'audit:export'
// 系统配置
| 'system:view'
| 'system:config'
| 'system:cache'
// 权限管理
| 'permission:view'
| 'permission:manage'
| 'role:view'
| 'role:manage'
| 'dept:view'
| 'dept:manage'
// 数据权限范围
export type DataScope = 'ALL' | 'DEPARTMENT' | 'OWN'
// 角色信息
export interface RoleInfo {
roleCode: string
roleName: string
roleLevel: number
dataScope: DataScope
description?: string
status: number
}
// 权限信息
export interface PermissionInfo {
permissionCode: string
permissionName: string
moduleCode: string
resourceCode: string
operationCode: string
dataScope: DataScope
description?: string
}
// 角色权限映射
export const RolePermissions: Record<AdminRole, Permission[]> = { export const RolePermissions: Record<AdminRole, Permission[]> = {
admin: [ super_admin: [
'view:dashboard', 'dashboard:view', 'dashboard:export',
'view:activities', 'user:view', 'user:create', 'user:update', 'user:delete', 'user:freeze', 'user:unfreeze', 'user:certify', 'user:export',
'view:leaderboard', 'activity:view', 'activity:create', 'activity:update', 'activity:delete', 'activity:publish', 'activity:pause', 'activity:end', 'activity:export',
'view:alerts', 'reward:view', 'reward:approve', 'reward:发放', 'reward:reject', 'reward:export',
'view:notifications', 'risk:view', 'risk:rule', 'risk:audit', 'risk:blacklist', 'risk:export',
'manage:users', 'approval:view', 'approval:handle', 'approval:delegate',
'manage:rewards', 'audit:view', 'audit:export',
'manage:risk', 'system:view', 'system:config', 'system:cache',
'manage:config', 'permission:view', 'permission:manage', 'role:view', 'role:manage', 'dept:view', 'dept:manage'
'view:audit'
], ],
operator: [ system_admin: [
'view:dashboard', 'dashboard:view', 'dashboard:export',
'view:activities', 'user:view', 'user:create', 'user:update', 'user:delete', 'user:freeze', 'user:unfreeze', 'user:export',
'view:leaderboard', 'activity:view', 'activity:create', 'activity:update', 'activity:delete', 'activity:export',
'view:alerts', 'approval:view', 'approval:handle',
'view:notifications', 'audit:view', 'audit:export',
'manage:rewards', 'system:view', 'system:config', 'system:cache',
'manage:risk' 'permission:view', 'role:view', 'dept:view', 'dept:manage'
],
operation_manager: [
'dashboard:view', 'dashboard:export',
'user:view', 'user:export',
'activity:view', 'activity:create', 'activity:update', 'activity:publish', 'activity:pause', 'activity:end', 'activity:export',
'reward:view', 'reward:approve', 'reward:export',
'approval:view', 'approval:handle'
],
operation_member: [
'dashboard:view',
'activity:view', 'activity:create', 'activity:update',
'reward:view'
],
marketing_manager: [
'dashboard:view', 'dashboard:export',
'user:view', 'user:export',
'activity:view', 'activity:create', 'activity:update', 'activity:publish', 'activity:export',
'reward:view', 'reward:approve', 'reward:export',
'approval:view', 'approval:handle'
],
marketing_member: [
'dashboard:view',
'activity:view', 'activity:create', 'activity:update'
],
finance_manager: [
'dashboard:view', 'dashboard:export',
'reward:view', 'reward:approve', 'reward:发放', 'reward:export',
'approval:view', 'approval:handle',
'audit:view', 'audit:export'
],
finance_member: [
'dashboard:view',
'reward:view', 'reward:approve'
],
risk_manager: [
'dashboard:view', 'dashboard:export',
'risk:view', 'risk:rule', 'risk:audit', 'risk:blacklist', 'risk:export',
'user:view', 'user:freeze', 'user:export',
'approval:view', 'approval:handle',
'audit:view', 'audit:export'
],
risk_member: [
'dashboard:view',
'risk:view', 'risk:audit', 'risk:blacklist'
],
customer_service: [
'dashboard:view',
'user:view', 'user:update', 'user:certify',
'activity:view'
],
auditor: [
'dashboard:view', 'dashboard:export',
'user:view', 'user:export',
'activity:view', 'activity:export',
'reward:view', 'reward:export',
'risk:view', 'risk:export',
'audit:view', 'audit:export',
'system:view'
], ],
viewer: [ viewer: [
'view:dashboard', 'dashboard:view',
'view:activities', 'user:view',
'view:leaderboard', 'activity:view',
'view:alerts', 'reward:view',
'view:notifications' 'risk:view'
] ]
} }
// 兼容旧版角色
export const LegacyRoleMapping: Record<string, AdminRole> = {
'admin': 'super_admin',
'operator': 'operation_manager',
'viewer': 'viewer'
}
// 角色显示名称
export const RoleLabels: Record<AdminRole, string> = {
super_admin: '超级管理员',
system_admin: '系统管理员',
operation_manager: '运营经理',
operation_member: '运营成员',
marketing_manager: '市场经理',
marketing_member: '市场成员',
finance_manager: '财务经理',
finance_member: '财务成员',
risk_manager: '风控经理',
risk_member: '风控成员',
customer_service: '客服',
auditor: '审计员',
viewer: '只读'
}
// 权限显示名称
export const PermissionLabels: Record<Permission, string> = {
'dashboard:view': '查看仪表盘',
'dashboard:export': '导出仪表盘',
'user:view': '查看用户',
'user:create': '创建用户',
'user:update': '更新用户',
'user:delete': '删除用户',
'user:freeze': '冻结用户',
'user:unfreeze': '解冻用户',
'user:certify': '实名认证',
'user:export': '导出用户',
'activity:view': '查看活动',
'activity:create': '创建活动',
'activity:update': '更新活动',
'activity:delete': '删除活动',
'activity:publish': '发布活动',
'activity:pause': '暂停活动',
'activity:end': '结束活动',
'activity:export': '导出活动',
'reward:view': '查看奖励',
'reward:approve': '审批奖励',
'reward:发放': '发放奖励',
'reward:reject': '拒绝奖励',
'reward:export': '导出奖励',
'risk:view': '查看风控',
'risk:rule': '管理风控规则',
'risk:audit': '审核风控',
'risk:blacklist': '管理黑名单',
'risk:export': '导出风控',
'approval:view': '查看审批',
'approval:handle': '处理审批',
'approval:delegate': '委托审批',
'audit:view': '查看审计',
'audit:export': '导出审计',
'system:view': '查看系统',
'system:config': '系统配置',
'system:cache': '缓存管理',
'permission:view': '查看权限',
'permission:manage': '权限管理',
'role:view': '查看角色',
'role:manage': '角色管理',
'dept:view': '查看部门',
'dept:manage': '部门管理'
}

View File

@@ -0,0 +1,199 @@
/**
* 权限组合式函数
* 用于在组件中方便地使用权限功能
*/
import { ref, computed, onMounted } from 'vue'
import { RolePermissions, type Permission, type AdminRole, type DataScope } from '../auth/roles'
import { permissionService, type UserPermissions } from '../services/permission'
// 当前用户权限
const currentPermissions = ref<Permission[]>([])
const currentRoles = ref<AdminRole[]>([])
const currentDataScope = ref<DataScope>('OWN')
const isLoading = ref(false)
const isInitialized = ref(false)
/**
* 初始化权限信息
*/
export function usePermission() {
const initialized = computed(() => isInitialized.value)
/**
* 从后端加载权限信息
*/
async function loadPermissions() {
if (isLoading.value) return
isLoading.value = true
try {
const perms = await permissionService.getUserPermissions()
currentPermissions.value = perms.permissions as Permission[]
currentRoles.value = perms.roles as AdminRole[]
currentDataScope.value = perms.dataScope
isInitialized.value = true
} catch (error) {
console.error('加载权限失败:', error)
// 使用本地角色权限作为后备
loadLocalPermissions()
} finally {
isLoading.value = false
}
}
/**
* 使用本地角色权限(后备方案)
*/
function loadLocalPermissions() {
// 从 localStorage 获取用户角色
const storedRole = localStorage.getItem('userRole') as AdminRole
if (storedRole && RolePermissions[storedRole]) {
currentRoles.value = [storedRole]
currentPermissions.value = RolePermissions[storedRole]
// 模拟数据权限
if (storedRole === 'super_admin' || storedRole === 'system_admin' || storedRole === 'auditor') {
currentDataScope.value = 'ALL'
} else if (storedRole.includes('manager')) {
currentDataScope.value = 'DEPARTMENT'
} else {
currentDataScope.value = 'OWN'
}
}
isInitialized.value = true
}
/**
* 检查是否拥有指定权限
*/
function hasPermission(permission: Permission): boolean {
return currentPermissions.value.includes(permission)
}
/**
* 检查是否拥有指定角色
*/
function hasRole(role: AdminRole): boolean {
return currentRoles.value.includes(role)
}
/**
* 检查是否拥有任意一个权限
*/
function hasAnyPermission(permissions: Permission[]): boolean {
return permissions.some(p => hasPermission(p))
}
/**
* 检查是否拥有所有权限
*/
function hasAllPermissions(permissions: Permission[]): boolean {
return permissions.every(p => hasPermission(p))
}
/**
* 获取当前数据权限范围
*/
function getDataScope(): DataScope {
return currentDataScope.value
}
/**
* 检查是否拥有所有数据权限
*/
function hasAllDataScope(): boolean {
return currentDataScope.value === 'ALL'
}
/**
* 检查是否拥有部门数据权限
*/
function hasDepartmentDataScope(): boolean {
return currentDataScope.value === 'DEPARTMENT' || currentDataScope.value === 'ALL'
}
/**
* 获取当前用户角色列表
*/
function getRoles(): AdminRole[] {
return currentRoles.value
}
/**
* 获取当前用户权限列表
*/
function getPermissions(): Permission[] {
return currentPermissions.value
}
/**
* 设置用户角色(用于本地开发/测试)
*/
function setUserRole(role: AdminRole) {
localStorage.setItem('userRole', role)
currentRoles.value = [role]
currentPermissions.value = RolePermissions[role] || []
if (role === 'super_admin' || role === 'system_admin' || role === 'auditor') {
currentDataScope.value = 'ALL'
} else if (role.includes('manager')) {
currentDataScope.value = 'DEPARTMENT'
} else {
currentDataScope.value = 'OWN'
}
isInitialized.value = true
}
/**
* 清除权限信息
*/
function clearPermissions() {
currentPermissions.value = []
currentRoles.value = []
currentDataScope.value = 'OWN'
isInitialized.value = false
localStorage.removeItem('userRole')
}
// 初始化时自动加载
onMounted(() => {
if (!isInitialized.value) {
loadPermissions()
}
})
return {
// 状态
initialized,
isLoading,
currentPermissions,
currentRoles,
currentDataScope,
// 方法
loadPermissions,
hasPermission,
hasRole,
hasAnyPermission,
hasAllPermissions,
getDataScope,
hasAllDataScope,
hasDepartmentDataScope,
getRoles,
getPermissions,
setUserRole,
clearPermissions
}
}
/**
* 权限指令 composable
* 用于模板中快速检查权限
*/
export function usePermissionCheck() {
const { hasPermission, hasRole, hasAnyPermission } = usePermission()
return {
can: hasPermission,
is: hasRole,
canAny: hasAnyPermission
}
}

View File

@@ -16,6 +16,26 @@ import ActivityConfigWizardView from '../views/ActivityConfigWizardView.vue'
import ApprovalCenterView from '../views/ApprovalCenterView.vue' import ApprovalCenterView from '../views/ApprovalCenterView.vue'
import UserDetailView from '../views/UserDetailView.vue' import UserDetailView from '../views/UserDetailView.vue'
import PermissionsView from '../views/PermissionsView.vue' import PermissionsView from '../views/PermissionsView.vue'
import type { AdminRole } from '../auth/roles'
// 路由权限配置 - 使用新的角色系统
const routeRoles: Record<string, AdminRole[]> = {
'dashboard': ['super_admin', 'system_admin', 'operation_manager', 'operation_member', 'marketing_manager', 'marketing_member', 'finance_manager', 'finance_member', 'risk_manager', 'risk_member', 'customer_service', 'auditor', 'viewer'],
'activities': ['super_admin', 'system_admin', 'operation_manager', 'operation_member', 'marketing_manager', 'marketing_member', 'customer_service', 'auditor', 'viewer'],
'activity-create': ['super_admin', 'system_admin', 'operation_manager', 'operation_member', 'marketing_manager', 'marketing_member'],
'activity-detail': ['super_admin', 'system_admin', 'operation_manager', 'operation_member', 'marketing_manager', 'marketing_member', 'customer_service', 'auditor', 'viewer'],
'activity-config': ['super_admin', 'system_admin', 'operation_manager', 'marketing_manager'],
'users': ['super_admin', 'system_admin'],
'user-detail': ['super_admin', 'system_admin'],
'user-invite': ['super_admin', 'system_admin'],
'rewards': ['super_admin', 'system_admin', 'operation_manager', 'operation_member', 'marketing_manager', 'marketing_member', 'finance_manager', 'finance_member', 'auditor', 'viewer'],
'risk': ['super_admin', 'system_admin', 'risk_manager', 'risk_member', 'auditor'],
'audit': ['super_admin', 'system_admin', 'finance_manager', 'auditor'],
'approvals': ['super_admin', 'system_admin', 'operation_manager', 'marketing_manager', 'finance_manager', 'risk_manager'],
'permissions': ['super_admin', 'system_admin'],
'notifications': ['super_admin', 'system_admin', 'operation_manager', 'operation_member', 'marketing_manager', 'marketing_member', 'finance_manager', 'finance_member', 'risk_manager', 'risk_member', 'customer_service', 'auditor', 'viewer'],
'system': ['super_admin', 'system_admin', 'auditor']
}
const router = createRouter({ const router = createRouter({
history: createWebHistory(), history: createWebHistory(),
@@ -29,91 +49,91 @@ const router = createRouter({
path: '/', path: '/',
name: 'dashboard', name: 'dashboard',
component: DashboardView, component: DashboardView,
meta: { roles: ['admin', 'operator', 'viewer'] } meta: { roles: routeRoles.dashboard }
}, },
{ {
path: '/activities', path: '/activities',
name: 'activities', name: 'activities',
component: ActivityListView, component: ActivityListView,
meta: { roles: ['admin', 'operator', 'viewer'] } meta: { roles: routeRoles.activities }
}, },
{ {
path: '/activities/new', path: '/activities/new',
name: 'activity-create', name: 'activity-create',
component: ActivityCreateView, component: ActivityCreateView,
meta: { roles: ['admin', 'operator'] } meta: { roles: routeRoles['activity-create'] }
}, },
{ {
path: '/activities/:id', path: '/activities/:id',
name: 'activity-detail', name: 'activity-detail',
component: ActivityDetailView, component: ActivityDetailView,
meta: { roles: ['admin', 'operator', 'viewer'] } meta: { roles: routeRoles['activity-detail'] }
}, },
{ {
path: '/activities/config', path: '/activities/config',
name: 'activity-config', name: 'activity-config',
component: ActivityConfigWizardView, component: ActivityConfigWizardView,
meta: { roles: ['admin', 'operator'] } meta: { roles: routeRoles['activity-config'] }
}, },
{ {
path: '/activities/:id', path: '/activities/:id',
name: 'activity-detail', name: 'activity-detail',
component: ActivityDetailView, component: ActivityDetailView,
meta: { roles: ['admin', 'operator', 'viewer'] } meta: { roles: routeRoles['activity-detail'] }
}, },
{ {
path: '/users', path: '/users',
name: 'users', name: 'users',
component: UsersView, component: UsersView,
meta: { roles: ['admin'] } meta: { roles: routeRoles.users }
}, },
{ {
path: '/users/:id', path: '/users/:id',
name: 'user-detail', name: 'user-detail',
component: UserDetailView, component: UserDetailView,
meta: { roles: ['admin'] } meta: { roles: routeRoles['user-detail'] }
}, },
{ {
path: '/users/invite', path: '/users/invite',
name: 'user-invite', name: 'user-invite',
component: InviteUserView, component: InviteUserView,
meta: { roles: ['admin'] } meta: { roles: routeRoles['user-invite'] }
}, },
{ {
path: '/rewards', path: '/rewards',
name: 'rewards', name: 'rewards',
component: RewardsView, component: RewardsView,
meta: { roles: ['admin', 'operator'] } meta: { roles: routeRoles.rewards }
}, },
{ {
path: '/risk', path: '/risk',
name: 'risk', name: 'risk',
component: RiskView, component: RiskView,
meta: { roles: ['admin', 'operator'] } meta: { roles: routeRoles.risk }
}, },
{ {
path: '/audit', path: '/audit',
name: 'audit', name: 'audit',
component: AuditLogView, component: AuditLogView,
meta: { roles: ['admin'] } meta: { roles: routeRoles.audit }
}, },
{ {
path: '/approvals', path: '/approvals',
name: 'approvals', name: 'approvals',
component: ApprovalCenterView, component: ApprovalCenterView,
meta: { roles: ['admin'] } meta: { roles: routeRoles.approvals }
}, },
{ {
path: '/permissions', path: '/permissions',
name: 'permissions', name: 'permissions',
component: PermissionsView, component: PermissionsView,
meta: { roles: ['admin'] } meta: { roles: routeRoles.permissions }
}, },
{ {
path: '/notifications', path: '/notifications',
name: 'notifications', name: 'notifications',
component: NotificationsView, component: NotificationsView,
meta: { roles: ['admin', 'operator', 'viewer'] } meta: { roles: routeRoles.notifications }
}, },
{ {
path: '/403', path: '/403',
@@ -126,10 +146,10 @@ const router = createRouter({
router.beforeEach(async (to) => { router.beforeEach(async (to) => {
const auth = useAuthStore() const auth = useAuthStore()
if (!auth.isAuthenticated && to.name !== 'login') { if (!auth.isAuthenticated && to.name !== 'login') {
await auth.loginDemo('admin') await auth.loginDemo('super_admin')
} }
const roles = (to.meta?.roles as string[] | undefined) ?? null const roles = (to.meta?.roles as AdminRole[] | undefined) ?? null
if (roles && !roles.includes(auth.role)) { if (roles && !roles.includes(auth.role as AdminRole)) {
return { name: 'forbidden' } return { name: 'forbidden' }
} }
return true return true

View File

@@ -0,0 +1,140 @@
/**
* 权限路由守卫
* 根据用户权限控制页面访问
*/
import type { Router } from 'vue-router'
import type { Permission } from '../auth/roles'
import { usePermission } from '../composables/usePermission'
export interface RoutePermission {
/** 路由名称 */
name: string
/** 所需权限 */
requiredPermissions?: Permission[]
/** 所需角色 */
requiredRoles?: string[]
/** 是否需要登录 */
requiresAuth?: boolean
}
/**
* 默认路由权限配置
*/
export const routePermissions: RoutePermission[] = [
// 仪表盘
{ name: 'Dashboard', requiredPermissions: ['dashboard:view'] },
// 用户管理
{ name: 'Users', requiredPermissions: ['user:view'] },
{ name: 'UserDetail', requiredPermissions: ['user:view'] },
// 活动管理
{ name: 'Activities', requiredPermissions: ['activity:view'] },
{ name: 'ActivityDetail', requiredPermissions: ['activity:view'] },
{ name: 'ActivityCreate', requiredPermissions: ['activity:create'] },
{ name: 'ActivityConfigWizard', requiredPermissions: ['activity:create'] },
// 奖励管理
{ name: 'Rewards', requiredPermissions: ['reward:view'] },
// 风险管理
{ name: 'Risk', requiredPermissions: ['risk:view'] },
// 审批中心
{ name: 'Approvals', requiredPermissions: ['approval:view'] },
// 审计日志
{ name: 'AuditLogs', requiredPermissions: ['audit:view'] },
// 系统配置
{ name: 'System', requiredPermissions: ['system:view'] },
// 权限管理
{ name: 'Permissions', requiredPermissions: ['permission:view'] },
// 邀请用户
{ name: 'InviteUser', requiredPermissions: ['user:create'] },
// 通知
{ name: 'Notifications', requiredPermissions: ['dashboard:view'] }
]
/**
* 创建权限路由守卫
*/
export function createPermissionGuard(router: Router) {
const { hasPermission, hasRole, initialized } = usePermission()
router.beforeEach(async (to, from, next) => {
// 等待权限初始化
if (!initialized.value) {
await new Promise(resolve => {
const checkInit = setInterval(() => {
if (initialized.value) {
clearInterval(checkInit)
resolve(true)
}
}, 100)
// 超时5秒后继续
setTimeout(() => {
clearInterval(checkInit)
resolve(true)
}, 5000)
})
}
// 检查路由权限
const routePermission = routePermissions.find(rp => rp.name === to.name)
if (routePermission) {
// 检查所需权限
if (routePermission.requiredPermissions?.length) {
const hasRequired = routePermission.requiredPermissions.some(permission =>
hasPermission(permission as Permission)
)
if (!hasRequired) {
// 没有权限跳转到403页面
return next({ name: 'Forbidden' })
}
}
// 检查所需角色
if (routePermission.requiredRoles?.length) {
const hasRequiredRole = routePermission.requiredRoles.some(role =>
hasRole(role as any)
)
if (!hasRequiredRole) {
return next({ name: 'Forbidden' })
}
}
}
next()
})
}
/**
* 检查路由是否有权限访问
*/
export function canAccessRoute(routeName: string): boolean {
const { hasPermission } = usePermission()
const routePermission = routePermissions.find(rp => rp.name === routeName)
if (!routePermission) {
return true // 没有配置权限的路由默认允许访问
}
if (routePermission.requiredPermissions?.length) {
return routePermission.requiredPermissions.some(permission =>
hasPermission(permission as Permission)
)
}
if (routePermission.requiredRoles?.length) {
const { hasRole } = usePermission()
return routePermission.requiredRoles.some(role => hasRole(role as any))
}
return true
}

View File

@@ -177,8 +177,8 @@ const demoAlerts: DemoAlert[] = [
] ]
const demoUsers: DemoUser[] = [ const demoUsers: DemoUser[] = [
{ id: 'u-1001', name: '王晨', email: 'wangchen@demo.com', role: 'operator', status: '正常', managerName: '演示管理员' }, { id: 'u-1001', name: '王晨', email: 'wangchen@demo.com', role: 'operation_manager', status: '正常', managerName: '演示管理员' },
{ id: 'u-1002', name: '李雪', email: 'lixue@demo.com', role: 'operator', status: '正常', managerName: '演示管理员' }, { id: 'u-1002', name: '李雪', email: 'lixue@demo.com', role: 'operation_member', status: '正常', managerName: '演示管理员' },
{ id: 'u-1003', name: '周宁', email: 'zhouning@demo.com', role: 'viewer', status: '冻结', managerName: '王晨' } { id: 'u-1003', name: '周宁', email: 'zhouning@demo.com', role: 'viewer', status: '冻结', managerName: '王晨' }
] ]
@@ -236,7 +236,7 @@ export const demoDataService = {
{ {
id: 'invite-1', id: 'invite-1',
email: 'newuser@demo.com', email: 'newuser@demo.com',
role: 'operator', role: 'operation_manager',
status: '待接受', status: '待接受',
invitedAt: isoDays(-1) invitedAt: isoDays(-1)
}, },
@@ -251,7 +251,7 @@ export const demoDataService = {
{ {
id: 'invite-3', id: 'invite-3',
email: 'accepted@demo.com', email: 'accepted@demo.com',
role: 'admin', role: 'super_admin',
status: '已接受', status: '已接受',
invitedAt: isoDays(-6), invitedAt: isoDays(-6),
acceptedAt: isoDays(-4) acceptedAt: isoDays(-4)
@@ -263,8 +263,8 @@ export const demoDataService = {
{ {
id: 'role-1', id: 'role-1',
userId: 'u-1002', userId: 'u-1002',
currentRole: 'operator', currentRole: 'operation_member',
targetRole: 'admin', targetRole: 'operation_manager',
reason: '需要管理活动权限', reason: '需要管理活动权限',
status: '待审批', status: '待审批',
requestedAt: isoDays(-2) requestedAt: isoDays(-2)

View File

@@ -0,0 +1,138 @@
/**
* 权限服务 - 与后端权限API交互
*/
import type { AdminRole, Permission, DataScope, RoleInfo, PermissionInfo } from '../auth/roles'
export interface UserPermissions {
userId: number
roles: string[]
permissions: string[]
dataScope: DataScope
}
export interface ApiResponse<T> {
code: number
data: T
message?: string
}
/**
* 权限服务类
*/
class PermissionService {
private baseUrl = '/api'
/**
* 获取当前用户权限信息
*/
async getUserPermissions(): Promise<UserPermissions> {
const response = await fetch(`${this.baseUrl}/permissions/current`, {
credentials: 'include'
})
const result = await response.json() as ApiResponse<UserPermissions>
if (result.code !== 200) {
throw new Error(result.message || '获取权限失败')
}
return result.data
}
/**
* 检查用户是否拥有指定权限
*/
async hasPermission(permissionCode: Permission): Promise<boolean> {
const response = await fetch(`${this.baseUrl}/permissions/check?permissionCode=${permissionCode}`, {
credentials: 'include'
})
const result = await response.json() as ApiResponse<boolean>
return result.code === 200 && result.data
}
/**
* 检查用户是否拥有指定角色
*/
async hasRole(roleCode: AdminRole): Promise<boolean> {
const response = await fetch(`${this.baseUrl}/permissions/role?roleCode=${roleCode}`, {
credentials: 'include'
})
const result = await response.json() as ApiResponse<boolean>
return result.code === 200 && result.data
}
/**
* 获取用户数据权限范围
*/
async getDataScope(): Promise<DataScope> {
const response = await fetch(`${this.baseUrl}/permissions/datascope`, {
credentials: 'include'
})
const result = await response.json() as ApiResponse<DataScope>
if (result.code !== 200) {
return 'OWN' // 默认个人权限
}
return result.data
}
/**
* 获取所有角色列表
*/
async getRoles(): Promise<RoleInfo[]> {
const response = await fetch(`${this.baseUrl}/roles`, {
credentials: 'include'
})
const result = await response.json() as ApiResponse<RoleInfo[]>
if (result.code !== 200) {
throw new Error(result.message || '获取角色列表失败')
}
return result.data
}
/**
* 获取所有权限列表
*/
async getPermissions(): Promise<PermissionInfo[]> {
const response = await fetch(`${this.baseUrl}/permissions`, {
credentials: 'include'
})
const result = await response.json() as ApiResponse<PermissionInfo[]>
if (result.code !== 200) {
throw new Error(result.message || '获取权限列表失败')
}
return result.data
}
/**
* 分配角色给用户
*/
async assignRole(userId: number, roleIds: number[]): Promise<void> {
const response = await fetch(`${this.baseUrl}/users/${userId}/roles`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ roleIds })
})
const result = await response.json() as ApiResponse<void>
if (result.code !== 200) {
throw new Error(result.message || '分配角色失败')
}
}
/**
* 分配权限给角色
*/
async assignPermissions(roleId: number, permissionIds: number[]): Promise<void> {
const response = await fetch(`${this.baseUrl}/roles/${roleId}/permissions`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'include',
body: JSON.stringify({ permissionIds })
})
const result = await response.json() as ApiResponse<void>
if (result.code !== 200) {
throw new Error(result.message || '分配权限失败')
}
}
}
export const permissionService = new PermissionService()
export default permissionService

View File

@@ -6,7 +6,7 @@ describe('useUserStore invites', () => {
it('expires invite', () => { it('expires invite', () => {
setActivePinia(createPinia()) setActivePinia(createPinia())
const store = useUserStore() const store = useUserStore()
store.init([], [{ id: 'invite-1', email: 'a@demo.com', role: 'operator', status: '待接受', invitedAt: '2026-02-01T00:00:00Z' }], []) store.init([], [{ id: 'invite-1', email: 'a@demo.com', role: 'operation_manager', status: '待接受', invitedAt: '2026-02-01T00:00:00Z' }], [])
store.expireInvite('invite-1') store.expireInvite('invite-1')

View File

@@ -10,9 +10,9 @@ export const useAuthStore = defineStore('auth', {
state: (): AuthState => ({ state: (): AuthState => ({
user: { user: {
id: 'demo-admin', id: 'demo-admin',
name: '演示管理员', name: '演示超级管理员',
email: 'demo@mosquito.local', email: 'demo@mosquito.local',
role: 'admin' role: 'super_admin'
}, },
mode: (import.meta.env.VITE_MOSQUITO_AUTH_MODE as AuthState['mode']) || 'demo' mode: (import.meta.env.VITE_MOSQUITO_AUTH_MODE as AuthState['mode']) || 'demo'
}), }),
@@ -25,7 +25,7 @@ export const useAuthStore = defineStore('auth', {
} }
}, },
actions: { actions: {
async loginDemo(role: AdminRole = 'admin') { async loginDemo(role: AdminRole = 'super_admin') {
const result = await demoAdapter.loginDemo(role) const result = await demoAdapter.loginDemo(role)
this.user = result.user this.user = result.user
this.mode = 'demo' this.mode = 'demo'

View File

@@ -13,9 +13,19 @@
<div> <div>
<label class="text-xs font-semibold text-mosquito-ink/70">角色</label> <label class="text-xs font-semibold text-mosquito-ink/70">角色</label>
<select class="mos-input mt-2 w-full" v-model="form.role"> <select class="mos-input mt-2 w-full" v-model="form.role">
<option value="管理员">管理员</option> <option value="super_admin">超级管理员</option>
<option value="运营">运营</option> <option value="system_admin">系统管理员</option>
<option value="只读">只读</option> <option value="operation_manager">运营经理</option>
<option value="operation_member">运营成员</option>
<option value="marketing_manager">市场经理</option>
<option value="marketing_member">市场成员</option>
<option value="finance_manager">财务经理</option>
<option value="finance_member">财务成员</option>
<option value="risk_manager">风控经理</option>
<option value="risk_member">风控成员</option>
<option value="customer_service">客服</option>
<option value="auditor">审计员</option>
<option value="viewer">只读</option>
</select> </select>
</div> </div>
<button class="mos-btn mos-btn-accent w-full" @click="sendInvite">发送邀请演示</button> <button class="mos-btn mos-btn-accent w-full" @click="sendInvite">发送邀请演示</button>
@@ -75,6 +85,7 @@ import { useAuditStore } from '../stores/audit'
import { useUserStore } from '../stores/users' import { useUserStore } from '../stores/users'
import { useDataService } from '../services' import { useDataService } from '../services'
import ListSection from '../components/ListSection.vue' import ListSection from '../components/ListSection.vue'
import { RoleLabels, type AdminRole } from '../auth/roles'
const auditStore = useAuditStore() const auditStore = useAuditStore()
const userStore = useUserStore() const userStore = useUserStore()
@@ -85,7 +96,7 @@ const page = ref(0)
const pageSize = 6 const pageSize = 6
const form = ref({ const form = ref({
email: '', email: '',
role: '运营' role: 'operation_manager'
}) })
onMounted(async () => { onMounted(async () => {
@@ -94,18 +105,16 @@ onMounted(async () => {
}) })
const roleLabel = (role: string) => { const roleLabel = (role: string) => {
if (role === 'admin') return '管理员' return RoleLabels[role as AdminRole] || role
if (role === 'operator') return '运营'
return '只读'
} }
const formatDate = (value: string) => new Date(value).toLocaleString('zh-CN') const formatDate = (value: string) => new Date(value).toLocaleString('zh-CN')
const sendInvite = () => { const sendInvite = () => {
userStore.addInvite(form.value.email || '未填写邮箱', form.value.role === '管理员' ? 'admin' : form.value.role === '运营' ? 'operator' : 'viewer') userStore.addInvite(form.value.email || '未填写邮箱', form.value.role as AdminRole)
auditStore.addLog('发送用户邀请', form.value.email || '未填写邮箱') auditStore.addLog('发送用户邀请', form.value.email || '未填写邮箱')
form.value.email = '' form.value.email = ''
form.value.role = '运营' form.value.role = 'operation_manager'
} }
const resendInvite = (id: string) => { const resendInvite = (id: string) => {

View File

@@ -36,7 +36,7 @@ const auth = useAuthStore()
const router = useRouter() const router = useRouter()
const loginDemo = async () => { const loginDemo = async () => {
await auth.loginDemo('admin') await auth.loginDemo('super_admin')
await router.push('/') await router.push('/')
} }
</script> </script>

View File

@@ -46,8 +46,9 @@ import { computed } from 'vue'
import { RolePermissions, type AdminRole, type Permission } from '../auth/roles' import { RolePermissions, type AdminRole, type Permission } from '../auth/roles'
const roles: { key: AdminRole; label: string }[] = [ const roles: { key: AdminRole; label: string }[] = [
{ key: 'admin', label: '管理员' }, { key: 'super_admin', label: '超级管理员' },
{ key: 'operator', label: '运营' }, { key: 'system_admin', label: '系统管理员' },
{ key: 'operation_manager', label: '运营经理' },
{ key: 'viewer', label: '只读' } { key: 'viewer', label: '只读' }
] ]
@@ -55,21 +56,20 @@ const permissionSections: { group: string; items: { key: Permission; label: stri
{ {
group: '可视化与运营查看', group: '可视化与运营查看',
items: [ items: [
{ key: 'view:dashboard', label: '看板查看', description: '访问运营概览与关键指标' }, { key: 'dashboard:view', label: '看板查看', description: '访问运营概览与关键指标' },
{ key: 'view:activities', label: '活动查看', description: '查看活动列表与详情信息' }, { key: 'activity:view', label: '活动查看', description: '查看活动列表与详情信息' },
{ key: 'view:leaderboard', label: '排行榜查看', description: '查看活动排行榜与排名' }, { key: 'dashboard:export', label: '导出数据', description: '导出看板数据' },
{ key: 'view:alerts', label: '告警查看', description: '查看风控与系统告警信息' }, { key: 'risk:view', label: '告警查看', description: '查看风控与系统告警信息' }
{ key: 'view:notifications', label: '通知查看', description: '查看审批与系统通知' }
] ]
}, },
{ {
group: '运营与风控管理', group: '运营与风控管理',
items: [ items: [
{ key: 'manage:users', label: '用户管理', description: '管理运营成员、审批与角色' }, { key: 'user:view', label: '用户管理', description: '管理运营成员、审批与角色' },
{ key: 'manage:rewards', label: '奖励管理', description: '配置与执行奖励发放' }, { key: 'reward:view', label: '奖励管理', description: '配置与执行奖励发放' },
{ key: 'manage:risk', label: '风控管理', description: '维护风控规则与黑名单' }, { key: 'risk:rule', label: '风控管理', description: '维护风控规则与黑名单' },
{ key: 'manage:config', label: '配置管理', description: '管理系统配置与策略' }, { key: 'system:config', label: '配置管理', description: '管理系统配置与策略' },
{ key: 'view:audit', label: '审计查看', description: '查看关键操作审计日志' } { key: 'audit:view', label: '审计查看', description: '查看关键操作审计日志' }
] ]
} }
] ]

View File

@@ -136,6 +136,7 @@ import { useUserStore } from '../stores/users'
import { useActivityStore } from '../stores/activities' import { useActivityStore } from '../stores/activities'
import { useAuthStore } from '../stores/auth' import { useAuthStore } from '../stores/auth'
import ListSection from '../components/ListSection.vue' import ListSection from '../components/ListSection.vue'
import { RoleLabels, type AdminRole } from '../auth/roles'
type UserItem = { type UserItem = {
id: string id: string
@@ -190,14 +191,12 @@ const toggleUser = (user: UserItem) => {
} }
const requestRole = (user: UserItem) => { const requestRole = (user: UserItem) => {
userStore.requestRoleChange(user.id, 'admin', '需要更高权限') userStore.requestRoleChange(user.id, 'super_admin' as AdminRole, '需要更高权限')
auditStore.addLog('提交角色变更申请', user.name) auditStore.addLog('提交角色变更申请', user.name)
} }
const roleLabel = (role: string) => { const roleLabel = (role: string) => {
if (role === 'admin') return '管理员' return RoleLabels[role as AdminRole] || role
if (role === 'operator') return '运营'
return '只读'
} }
const resolveUserId = () => { const resolveUserId = () => {