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:
@@ -17,7 +17,10 @@
|
||||
"Edit",
|
||||
"Read",
|
||||
"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": []
|
||||
},
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
- **Max Iterations**: 100
|
||||
|
||||
## Current State
|
||||
- **Iteration**: 5
|
||||
- **Iteration**: 6
|
||||
- **Status**: In Progress
|
||||
- **Current Phase**: Phase 2 - 权限核心模块后端完成
|
||||
- **Current Phase**: Phase 2 - 前端权限组件开发
|
||||
|
||||
## Progress - Phase 2
|
||||
- [x] Phase 1: 数据库表创建(10张表)✅
|
||||
@@ -19,17 +19,24 @@
|
||||
- [x] 权限判断服务 (PermissionCheckService) - 已完善
|
||||
- [x] 用户角色关联 (SysUserRole + UserRoleRepository)
|
||||
- [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: 审批流引擎
|
||||
|
||||
## Completion Criteria
|
||||
- [x] Phase 1: 数据库表创建 - 100%
|
||||
- [x] Phase 2: 后端核心模块 - 100%
|
||||
- [ ] Phase 2: 前端页面 - 0%
|
||||
- [x] Phase 2: 前端权限组件 - 90%
|
||||
- [ ] Phase 3: 审批流引擎 - 0%
|
||||
- [ ] Phase 4: 业务模块开发 - 0%
|
||||
|
||||
## Recent Changes (Iteration 5)
|
||||
- 创建 UserRoleRepository 实现用户角色关联查询
|
||||
- 创建 RolePermissionRepository 实现角色权限关联查询
|
||||
- 完善 PermissionCheckService 实现核心权限验证逻辑
|
||||
## Recent Changes (Iteration 6)
|
||||
- 扩展前端角色权限类型定义
|
||||
- 创建权限服务和路由守卫
|
||||
- 更新前端视图使用新角色系统
|
||||
- 前端编译成功
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
活动
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="auth.hasPermission('manage:users')"
|
||||
v-if="auth.hasPermission('user:view')"
|
||||
to="/users"
|
||||
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') }"
|
||||
@@ -35,7 +35,7 @@
|
||||
用户
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="auth.hasPermission('manage:rewards')"
|
||||
v-if="auth.hasPermission('reward:view')"
|
||||
to="/rewards"
|
||||
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') }"
|
||||
@@ -43,7 +43,7 @@
|
||||
奖励
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="auth.hasPermission('manage:risk')"
|
||||
v-if="auth.hasPermission('risk:view')"
|
||||
to="/risk"
|
||||
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') }"
|
||||
@@ -51,7 +51,7 @@
|
||||
风控
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="auth.hasPermission('view:audit')"
|
||||
v-if="auth.hasPermission('audit:view')"
|
||||
to="/audit"
|
||||
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') }"
|
||||
@@ -59,7 +59,7 @@
|
||||
审计
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="auth.hasPermission('manage:users')"
|
||||
v-if="auth.hasPermission('approval:view')"
|
||||
to="/approvals"
|
||||
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') }"
|
||||
@@ -67,7 +67,7 @@
|
||||
审批
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="auth.hasPermission('manage:users')"
|
||||
v-if="auth.hasPermission('permission:view')"
|
||||
to="/permissions"
|
||||
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') }"
|
||||
@@ -75,7 +75,7 @@
|
||||
权限
|
||||
</RouterLink>
|
||||
<RouterLink
|
||||
v-if="auth.hasPermission('view:notifications')"
|
||||
v-if="auth.hasPermission('dashboard:view')"
|
||||
to="/notifications"
|
||||
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') }"
|
||||
@@ -89,8 +89,18 @@
|
||||
<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">
|
||||
<option value="admin">管理员</option>
|
||||
<option value="operator">运营</option>
|
||||
<option value="super_admin">超级管理员</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>
|
||||
</select>
|
||||
</label>
|
||||
@@ -140,15 +150,15 @@ import { useAuthStore } from './stores/auth'
|
||||
import { downloadCsv } from './utils/export'
|
||||
import ExportFieldPanel, { type ExportField } from './components/ExportFieldPanel.vue'
|
||||
import { useExportFields } from './composables/useExportFields'
|
||||
import { RoleLabels, type AdminRole } from './auth/roles'
|
||||
|
||||
const route = useRoute()
|
||||
const auth = useAuthStore()
|
||||
const selectedRole = ref(auth.role)
|
||||
const selectedRole = ref<AdminRole>(auth.role)
|
||||
|
||||
const roleLabel = computed(() => {
|
||||
if (auth.role === 'admin') return '管理员'
|
||||
if (auth.role === 'operator') return '运营'
|
||||
return '只读'
|
||||
const role = auth.role as AdminRole
|
||||
return RoleLabels[role] || '未知角色'
|
||||
})
|
||||
|
||||
const onRoleChange = () => {
|
||||
@@ -158,7 +168,7 @@ const onRoleChange = () => {
|
||||
watch(
|
||||
() => auth.role,
|
||||
(value) => {
|
||||
selectedRole.value = value
|
||||
selectedRole.value = value as AdminRole
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -1,28 +1,45 @@
|
||||
import type { AdminRole, Permission } from '../roles'
|
||||
import { RolePermissions } from '../roles'
|
||||
import { RolePermissions, RoleLabels } from '../roles'
|
||||
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 => ({
|
||||
id: `demo-${role}`,
|
||||
name: role === 'admin' ? '演示管理员' : role === 'operator' ? '演示运营' : '演示访客',
|
||||
name: roleNameMap[role] || '演示用户',
|
||||
email: 'demo@mosquito.local',
|
||||
role
|
||||
})
|
||||
|
||||
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> {
|
||||
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)
|
||||
return { user: this.currentUser }
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
this.currentUser = null
|
||||
this.currentUser = null as any
|
||||
}
|
||||
|
||||
async switchRole(role: AdminRole): Promise<AuthUser> {
|
||||
@@ -35,6 +52,6 @@ export class DemoAuthAdapter implements AuthAdapter {
|
||||
}
|
||||
|
||||
hasPermission(role: AdminRole, permission: Permission): boolean {
|
||||
return RolePermissions[role].includes(permission)
|
||||
return RolePermissions[role]?.includes(permission) ?? false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
| 'view:dashboard'
|
||||
| 'view:activities'
|
||||
| 'view:leaderboard'
|
||||
| 'view:alerts'
|
||||
| 'view:notifications'
|
||||
| 'manage:users'
|
||||
| 'manage:rewards'
|
||||
| 'manage:risk'
|
||||
| 'manage:config'
|
||||
| 'view:audit'
|
||||
// 仪表盘
|
||||
| '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'
|
||||
|
||||
// 数据权限范围
|
||||
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[]> = {
|
||||
admin: [
|
||||
'view:dashboard',
|
||||
'view:activities',
|
||||
'view:leaderboard',
|
||||
'view:alerts',
|
||||
'view:notifications',
|
||||
'manage:users',
|
||||
'manage:rewards',
|
||||
'manage:risk',
|
||||
'manage:config',
|
||||
'view:audit'
|
||||
super_admin: [
|
||||
'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'
|
||||
],
|
||||
operator: [
|
||||
'view:dashboard',
|
||||
'view:activities',
|
||||
'view:leaderboard',
|
||||
'view:alerts',
|
||||
'view:notifications',
|
||||
'manage:rewards',
|
||||
'manage:risk'
|
||||
system_admin: [
|
||||
'dashboard:view', 'dashboard:export',
|
||||
'user:view', 'user:create', 'user:update', 'user:delete', 'user:freeze', 'user:unfreeze', 'user:export',
|
||||
'activity:view', 'activity:create', 'activity:update', 'activity:delete', 'activity:export',
|
||||
'approval:view', 'approval:handle',
|
||||
'audit:view', 'audit:export',
|
||||
'system:view', 'system:config', 'system:cache',
|
||||
'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: [
|
||||
'view:dashboard',
|
||||
'view:activities',
|
||||
'view:leaderboard',
|
||||
'view:alerts',
|
||||
'view:notifications'
|
||||
'dashboard:view',
|
||||
'user:view',
|
||||
'activity:view',
|
||||
'reward:view',
|
||||
'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': '部门管理'
|
||||
}
|
||||
|
||||
199
frontend/admin/src/composables/usePermission.ts
Normal file
199
frontend/admin/src/composables/usePermission.ts
Normal 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
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,26 @@ import ActivityConfigWizardView from '../views/ActivityConfigWizardView.vue'
|
||||
import ApprovalCenterView from '../views/ApprovalCenterView.vue'
|
||||
import UserDetailView from '../views/UserDetailView.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({
|
||||
history: createWebHistory(),
|
||||
@@ -29,91 +49,91 @@ const router = createRouter({
|
||||
path: '/',
|
||||
name: 'dashboard',
|
||||
component: DashboardView,
|
||||
meta: { roles: ['admin', 'operator', 'viewer'] }
|
||||
meta: { roles: routeRoles.dashboard }
|
||||
},
|
||||
{
|
||||
path: '/activities',
|
||||
name: 'activities',
|
||||
component: ActivityListView,
|
||||
meta: { roles: ['admin', 'operator', 'viewer'] }
|
||||
meta: { roles: routeRoles.activities }
|
||||
},
|
||||
{
|
||||
path: '/activities/new',
|
||||
name: 'activity-create',
|
||||
component: ActivityCreateView,
|
||||
meta: { roles: ['admin', 'operator'] }
|
||||
meta: { roles: routeRoles['activity-create'] }
|
||||
},
|
||||
{
|
||||
path: '/activities/:id',
|
||||
name: 'activity-detail',
|
||||
component: ActivityDetailView,
|
||||
meta: { roles: ['admin', 'operator', 'viewer'] }
|
||||
meta: { roles: routeRoles['activity-detail'] }
|
||||
},
|
||||
{
|
||||
path: '/activities/config',
|
||||
name: 'activity-config',
|
||||
component: ActivityConfigWizardView,
|
||||
meta: { roles: ['admin', 'operator'] }
|
||||
meta: { roles: routeRoles['activity-config'] }
|
||||
},
|
||||
{
|
||||
path: '/activities/:id',
|
||||
name: 'activity-detail',
|
||||
component: ActivityDetailView,
|
||||
meta: { roles: ['admin', 'operator', 'viewer'] }
|
||||
meta: { roles: routeRoles['activity-detail'] }
|
||||
},
|
||||
{
|
||||
path: '/users',
|
||||
name: 'users',
|
||||
component: UsersView,
|
||||
meta: { roles: ['admin'] }
|
||||
meta: { roles: routeRoles.users }
|
||||
},
|
||||
{
|
||||
path: '/users/:id',
|
||||
name: 'user-detail',
|
||||
component: UserDetailView,
|
||||
meta: { roles: ['admin'] }
|
||||
meta: { roles: routeRoles['user-detail'] }
|
||||
},
|
||||
{
|
||||
path: '/users/invite',
|
||||
name: 'user-invite',
|
||||
component: InviteUserView,
|
||||
meta: { roles: ['admin'] }
|
||||
meta: { roles: routeRoles['user-invite'] }
|
||||
},
|
||||
{
|
||||
path: '/rewards',
|
||||
name: 'rewards',
|
||||
component: RewardsView,
|
||||
meta: { roles: ['admin', 'operator'] }
|
||||
meta: { roles: routeRoles.rewards }
|
||||
},
|
||||
{
|
||||
path: '/risk',
|
||||
name: 'risk',
|
||||
component: RiskView,
|
||||
meta: { roles: ['admin', 'operator'] }
|
||||
meta: { roles: routeRoles.risk }
|
||||
},
|
||||
{
|
||||
path: '/audit',
|
||||
name: 'audit',
|
||||
component: AuditLogView,
|
||||
meta: { roles: ['admin'] }
|
||||
meta: { roles: routeRoles.audit }
|
||||
},
|
||||
{
|
||||
path: '/approvals',
|
||||
name: 'approvals',
|
||||
component: ApprovalCenterView,
|
||||
meta: { roles: ['admin'] }
|
||||
meta: { roles: routeRoles.approvals }
|
||||
},
|
||||
{
|
||||
path: '/permissions',
|
||||
name: 'permissions',
|
||||
component: PermissionsView,
|
||||
meta: { roles: ['admin'] }
|
||||
meta: { roles: routeRoles.permissions }
|
||||
},
|
||||
{
|
||||
path: '/notifications',
|
||||
name: 'notifications',
|
||||
component: NotificationsView,
|
||||
meta: { roles: ['admin', 'operator', 'viewer'] }
|
||||
meta: { roles: routeRoles.notifications }
|
||||
},
|
||||
{
|
||||
path: '/403',
|
||||
@@ -126,10 +146,10 @@ const router = createRouter({
|
||||
router.beforeEach(async (to) => {
|
||||
const auth = useAuthStore()
|
||||
if (!auth.isAuthenticated && to.name !== 'login') {
|
||||
await auth.loginDemo('admin')
|
||||
await auth.loginDemo('super_admin')
|
||||
}
|
||||
const roles = (to.meta?.roles as string[] | undefined) ?? null
|
||||
if (roles && !roles.includes(auth.role)) {
|
||||
const roles = (to.meta?.roles as AdminRole[] | undefined) ?? null
|
||||
if (roles && !roles.includes(auth.role as AdminRole)) {
|
||||
return { name: 'forbidden' }
|
||||
}
|
||||
return true
|
||||
|
||||
140
frontend/admin/src/router/permissionGuard.ts
Normal file
140
frontend/admin/src/router/permissionGuard.ts
Normal 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
|
||||
}
|
||||
@@ -177,8 +177,8 @@ const demoAlerts: DemoAlert[] = [
|
||||
]
|
||||
|
||||
const demoUsers: DemoUser[] = [
|
||||
{ id: 'u-1001', name: '王晨', email: 'wangchen@demo.com', role: 'operator', status: '正常', managerName: '演示管理员' },
|
||||
{ id: 'u-1002', name: '李雪', email: 'lixue@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: 'operation_member', status: '正常', managerName: '演示管理员' },
|
||||
{ id: 'u-1003', name: '周宁', email: 'zhouning@demo.com', role: 'viewer', status: '冻结', managerName: '王晨' }
|
||||
]
|
||||
|
||||
@@ -236,7 +236,7 @@ export const demoDataService = {
|
||||
{
|
||||
id: 'invite-1',
|
||||
email: 'newuser@demo.com',
|
||||
role: 'operator',
|
||||
role: 'operation_manager',
|
||||
status: '待接受',
|
||||
invitedAt: isoDays(-1)
|
||||
},
|
||||
@@ -251,7 +251,7 @@ export const demoDataService = {
|
||||
{
|
||||
id: 'invite-3',
|
||||
email: 'accepted@demo.com',
|
||||
role: 'admin',
|
||||
role: 'super_admin',
|
||||
status: '已接受',
|
||||
invitedAt: isoDays(-6),
|
||||
acceptedAt: isoDays(-4)
|
||||
@@ -263,8 +263,8 @@ export const demoDataService = {
|
||||
{
|
||||
id: 'role-1',
|
||||
userId: 'u-1002',
|
||||
currentRole: 'operator',
|
||||
targetRole: 'admin',
|
||||
currentRole: 'operation_member',
|
||||
targetRole: 'operation_manager',
|
||||
reason: '需要管理活动权限',
|
||||
status: '待审批',
|
||||
requestedAt: isoDays(-2)
|
||||
|
||||
138
frontend/admin/src/services/permission.ts
Normal file
138
frontend/admin/src/services/permission.ts
Normal 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
|
||||
@@ -6,7 +6,7 @@ describe('useUserStore invites', () => {
|
||||
it('expires invite', () => {
|
||||
setActivePinia(createPinia())
|
||||
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')
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ export const useAuthStore = defineStore('auth', {
|
||||
state: (): AuthState => ({
|
||||
user: {
|
||||
id: 'demo-admin',
|
||||
name: '演示管理员',
|
||||
name: '演示超级管理员',
|
||||
email: 'demo@mosquito.local',
|
||||
role: 'admin'
|
||||
role: 'super_admin'
|
||||
},
|
||||
mode: (import.meta.env.VITE_MOSQUITO_AUTH_MODE as AuthState['mode']) || 'demo'
|
||||
}),
|
||||
@@ -25,7 +25,7 @@ export const useAuthStore = defineStore('auth', {
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
async loginDemo(role: AdminRole = 'admin') {
|
||||
async loginDemo(role: AdminRole = 'super_admin') {
|
||||
const result = await demoAdapter.loginDemo(role)
|
||||
this.user = result.user
|
||||
this.mode = 'demo'
|
||||
|
||||
@@ -13,9 +13,19 @@
|
||||
<div>
|
||||
<label class="text-xs font-semibold text-mosquito-ink/70">角色</label>
|
||||
<select class="mos-input mt-2 w-full" v-model="form.role">
|
||||
<option value="管理员">管理员</option>
|
||||
<option value="运营">运营</option>
|
||||
<option value="只读">只读</option>
|
||||
<option value="super_admin">超级管理员</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>
|
||||
</select>
|
||||
</div>
|
||||
<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 { useDataService } from '../services'
|
||||
import ListSection from '../components/ListSection.vue'
|
||||
import { RoleLabels, type AdminRole } from '../auth/roles'
|
||||
|
||||
const auditStore = useAuditStore()
|
||||
const userStore = useUserStore()
|
||||
@@ -85,7 +96,7 @@ const page = ref(0)
|
||||
const pageSize = 6
|
||||
const form = ref({
|
||||
email: '',
|
||||
role: '运营'
|
||||
role: 'operation_manager'
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
@@ -94,18 +105,16 @@ onMounted(async () => {
|
||||
})
|
||||
|
||||
const roleLabel = (role: string) => {
|
||||
if (role === 'admin') return '管理员'
|
||||
if (role === 'operator') return '运营'
|
||||
return '只读'
|
||||
return RoleLabels[role as AdminRole] || role
|
||||
}
|
||||
|
||||
const formatDate = (value: string) => new Date(value).toLocaleString('zh-CN')
|
||||
|
||||
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 || '未填写邮箱')
|
||||
form.value.email = ''
|
||||
form.value.role = '运营'
|
||||
form.value.role = 'operation_manager'
|
||||
}
|
||||
|
||||
const resendInvite = (id: string) => {
|
||||
|
||||
@@ -36,7 +36,7 @@ const auth = useAuthStore()
|
||||
const router = useRouter()
|
||||
|
||||
const loginDemo = async () => {
|
||||
await auth.loginDemo('admin')
|
||||
await auth.loginDemo('super_admin')
|
||||
await router.push('/')
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -46,8 +46,9 @@ import { computed } from 'vue'
|
||||
import { RolePermissions, type AdminRole, type Permission } from '../auth/roles'
|
||||
|
||||
const roles: { key: AdminRole; label: string }[] = [
|
||||
{ key: 'admin', label: '管理员' },
|
||||
{ key: 'operator', label: '运营' },
|
||||
{ key: 'super_admin', label: '超级管理员' },
|
||||
{ key: 'system_admin', label: '系统管理员' },
|
||||
{ key: 'operation_manager', label: '运营经理' },
|
||||
{ key: 'viewer', label: '只读' }
|
||||
]
|
||||
|
||||
@@ -55,21 +56,20 @@ const permissionSections: { group: string; items: { key: Permission; label: stri
|
||||
{
|
||||
group: '可视化与运营查看',
|
||||
items: [
|
||||
{ key: 'view:dashboard', label: '看板查看', description: '访问运营概览与关键指标' },
|
||||
{ key: 'view:activities', label: '活动查看', description: '查看活动列表与详情信息' },
|
||||
{ key: 'view:leaderboard', label: '排行榜查看', description: '查看活动排行榜与排名' },
|
||||
{ key: 'view:alerts', label: '告警查看', description: '查看风控与系统告警信息' },
|
||||
{ key: 'view:notifications', label: '通知查看', description: '查看审批与系统通知' }
|
||||
{ key: 'dashboard:view', label: '看板查看', description: '访问运营概览与关键指标' },
|
||||
{ key: 'activity:view', label: '活动查看', description: '查看活动列表与详情信息' },
|
||||
{ key: 'dashboard:export', label: '导出数据', description: '导出看板数据' },
|
||||
{ key: 'risk:view', label: '告警查看', description: '查看风控与系统告警信息' }
|
||||
]
|
||||
},
|
||||
{
|
||||
group: '运营与风控管理',
|
||||
items: [
|
||||
{ key: 'manage:users', label: '用户管理', description: '管理运营成员、审批与角色' },
|
||||
{ key: 'manage:rewards', label: '奖励管理', description: '配置与执行奖励发放' },
|
||||
{ key: 'manage:risk', label: '风控管理', description: '维护风控规则与黑名单' },
|
||||
{ key: 'manage:config', label: '配置管理', description: '管理系统配置与策略' },
|
||||
{ key: 'view:audit', label: '审计查看', description: '查看关键操作审计日志' }
|
||||
{ key: 'user:view', label: '用户管理', description: '管理运营成员、审批与角色' },
|
||||
{ key: 'reward:view', label: '奖励管理', description: '配置与执行奖励发放' },
|
||||
{ key: 'risk:rule', label: '风控管理', description: '维护风控规则与黑名单' },
|
||||
{ key: 'system:config', label: '配置管理', description: '管理系统配置与策略' },
|
||||
{ key: 'audit:view', label: '审计查看', description: '查看关键操作审计日志' }
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -136,6 +136,7 @@ import { useUserStore } from '../stores/users'
|
||||
import { useActivityStore } from '../stores/activities'
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import ListSection from '../components/ListSection.vue'
|
||||
import { RoleLabels, type AdminRole } from '../auth/roles'
|
||||
|
||||
type UserItem = {
|
||||
id: string
|
||||
@@ -190,14 +191,12 @@ const toggleUser = (user: UserItem) => {
|
||||
}
|
||||
|
||||
const requestRole = (user: UserItem) => {
|
||||
userStore.requestRoleChange(user.id, 'admin', '需要更高权限')
|
||||
userStore.requestRoleChange(user.id, 'super_admin' as AdminRole, '需要更高权限')
|
||||
auditStore.addLog('提交角色变更申请', user.name)
|
||||
}
|
||||
|
||||
const roleLabel = (role: string) => {
|
||||
if (role === 'admin') return '管理员'
|
||||
if (role === 'operator') return '运营'
|
||||
return '只读'
|
||||
return RoleLabels[role as AdminRole] || role
|
||||
}
|
||||
|
||||
const resolveUserId = () => {
|
||||
|
||||
Reference in New Issue
Block a user