feat: 角色权限管理v3.0及错误处理优化
1. 角色权限管理升级: - 添加路由下展开式API权限管理功能 - 新增 getRoleRoutesWithPermissions 和 saveRoleApiPermissions API - 支持按路由展开/收起查看和勾选权限 - 过滤"所有权限"选项,只显示具体权限 2. 错误处理优化: - 403 无权限错误显示为"无权限访问该资源" - 修复评查点分组批量删除显示"成功删除 undefined 个分组"的问题 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -182,6 +182,9 @@ axiosInstance.interceptors.response.use(
|
||||
if (typeof window !== 'undefined') {
|
||||
toastService.warning('无权限访问该资源');
|
||||
}
|
||||
|
||||
// 修改错误消息为友好提示,避免显示原始的 "Request failed with status code 403"
|
||||
error.message = '无权限访问该资源';
|
||||
}
|
||||
|
||||
return Promise.reject(error);
|
||||
|
||||
@@ -59,6 +59,18 @@ function handleApiResponse<T>(response: ApiResponse<any>): T {
|
||||
|
||||
// ==================== 类型定义 ====================
|
||||
|
||||
/**
|
||||
* API权限信息(关联到路由的权限)
|
||||
* v3.0新增:每个路由可以关联多个API操作权限
|
||||
*/
|
||||
export interface ApiPermission {
|
||||
id: number;
|
||||
permission_key: string; // 权限标识,如 "evaluation_group:create:write"
|
||||
display_name: string; // 显示名称,如 "创建评查点分组"
|
||||
api_method: string; // HTTP方法:GET | POST | PUT | DELETE
|
||||
api_path: string; // API路径,如 "/api/v3/evaluation-point-groups"
|
||||
}
|
||||
|
||||
/**
|
||||
* 路由信息
|
||||
*/
|
||||
@@ -74,6 +86,7 @@ export interface RouteInfo {
|
||||
is_hidden: boolean;
|
||||
is_cache: boolean;
|
||||
status: number;
|
||||
permissions?: ApiPermission[]; // v3.0新增:关联的API权限列表
|
||||
children?: RouteInfo[];
|
||||
}
|
||||
|
||||
@@ -620,6 +633,162 @@ export async function getRoleRoutePermissions(roleId: number): Promise<RoleRoute
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取角色的路由权限(含API权限)- v3.0新增
|
||||
* 返回树形结构的路由,每个路由包含关联的API权限
|
||||
* @param roleId 角色ID
|
||||
*/
|
||||
export async function getRoleRoutesWithPermissions(roleId: number): Promise<{
|
||||
routes: RouteInfo[];
|
||||
selectedRouteIds: number[];
|
||||
selectedPermissionIds: number[];
|
||||
}> {
|
||||
try {
|
||||
const { get } = await import('~/api/axios-client');
|
||||
|
||||
console.log('🔍 [getRoleRoutesWithPermissions] 开始调用后端API:', `/rbac/roles/${roleId}/routes`);
|
||||
|
||||
const response = await get<any>(`/rbac/roles/${roleId}/routes`);
|
||||
console.log('📦 [getRoleRoutesWithPermissions] 后端API完整响应:', JSON.stringify(response, null, 2));
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
|
||||
// 后端响应格式: { code: 200, msg: "success", data: { role_id, routes: [...] } }
|
||||
let routes: any[] = [];
|
||||
if (response.data && response.data.data && Array.isArray(response.data.data.routes)) {
|
||||
routes = response.data.data.routes;
|
||||
} else if (response.data && Array.isArray(response.data.routes)) {
|
||||
routes = response.data.routes;
|
||||
}
|
||||
|
||||
// 递归转换路由数据格式
|
||||
const mapRouteData = (route: any): RouteInfo => ({
|
||||
id: route.id,
|
||||
route_path: route.route_path,
|
||||
route_name: route.route_name,
|
||||
route_title: route.route_title,
|
||||
icon: route.icon || '',
|
||||
sort_order: route.sort_order || 0,
|
||||
is_hidden: route.is_hidden || false,
|
||||
is_cache: route.is_cache !== false,
|
||||
status: route.status || 1,
|
||||
parent_id: route.parent_id || null,
|
||||
component: route.component,
|
||||
// v3.0: 转换permissions数组
|
||||
permissions: Array.isArray(route.permissions) ? route.permissions.map((p: any) => ({
|
||||
id: p.id,
|
||||
permission_key: p.permission_key,
|
||||
display_name: p.display_name,
|
||||
api_method: p.api_method,
|
||||
api_path: p.api_path
|
||||
})) : [],
|
||||
children: route.children ? route.children.map(mapRouteData) : undefined
|
||||
});
|
||||
|
||||
const mappedRoutes = routes.map(mapRouteData);
|
||||
|
||||
// 收集所有已选中的路由ID
|
||||
const collectRouteIds = (routes: RouteInfo[]): number[] => {
|
||||
let ids: number[] = [];
|
||||
routes.forEach(route => {
|
||||
ids.push(route.id);
|
||||
if (route.children) {
|
||||
ids = ids.concat(collectRouteIds(route.children));
|
||||
}
|
||||
});
|
||||
return ids;
|
||||
};
|
||||
|
||||
// 收集所有已选中的权限ID(从当前角色的权限中)
|
||||
const collectPermissionIds = (routes: RouteInfo[]): number[] => {
|
||||
let ids: number[] = [];
|
||||
routes.forEach(route => {
|
||||
if (route.permissions) {
|
||||
ids = ids.concat(route.permissions.map(p => p.id));
|
||||
}
|
||||
if (route.children) {
|
||||
ids = ids.concat(collectPermissionIds(route.children));
|
||||
}
|
||||
});
|
||||
return ids;
|
||||
};
|
||||
|
||||
const selectedRouteIds = collectRouteIds(mappedRoutes);
|
||||
const selectedPermissionIds = collectPermissionIds(mappedRoutes);
|
||||
|
||||
console.log('✅ [getRoleRoutesWithPermissions] 成功获取路由权限数据');
|
||||
console.log(' - 路由数量:', mappedRoutes.length);
|
||||
console.log(' - 已选路由ID:', selectedRouteIds);
|
||||
console.log(' - 已选权限ID:', selectedPermissionIds);
|
||||
|
||||
return {
|
||||
routes: mappedRoutes,
|
||||
selectedRouteIds,
|
||||
selectedPermissionIds
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('❌ [getRoleRoutesWithPermissions] 获取角色路由权限失败:', error);
|
||||
return {
|
||||
routes: [],
|
||||
selectedRouteIds: [],
|
||||
selectedPermissionIds: []
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 保存角色的API权限 - v3.0新增
|
||||
* @param roleId 角色ID
|
||||
* @param permissionIds 权限ID数组
|
||||
*/
|
||||
export async function saveRoleApiPermissions(
|
||||
roleId: number,
|
||||
permissionIds: number[]
|
||||
): Promise<{ success: boolean; message: string }> {
|
||||
try {
|
||||
const { post } = await import('~/api/axios-client');
|
||||
|
||||
console.log('🔍 [saveRoleApiPermissions] 开始调用后端API:', `/api/v3/rbac/roles/${roleId}/permissions`, permissionIds);
|
||||
|
||||
// 构建权限配置
|
||||
const permissions = permissionIds.map(id => ({
|
||||
permission_id: id,
|
||||
grant_type: 'GRANT',
|
||||
data_scope: 'ALL'
|
||||
}));
|
||||
|
||||
const response = await post<any>(`/api/v3/rbac/roles/${roleId}/permissions`, {
|
||||
permissions,
|
||||
replace: true // 替换模式:先删除现有权限,再插入新权限
|
||||
});
|
||||
|
||||
console.log('📦 [saveRoleApiPermissions] 后端API完整响应:', JSON.stringify(response, null, 2));
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
|
||||
let message = 'API权限保存成功';
|
||||
if (response.data && response.data.message) {
|
||||
message = response.data.message;
|
||||
} else if (response.data && response.data.data) {
|
||||
const { assigned_count } = response.data.data;
|
||||
message = `成功分配 ${assigned_count} 个API权限`;
|
||||
}
|
||||
|
||||
console.log('✅ [saveRoleApiPermissions] API权限保存成功');
|
||||
return { success: true, message };
|
||||
} catch (error) {
|
||||
console.error('❌ [saveRoleApiPermissions] 保存API权限失败:', error);
|
||||
return {
|
||||
success: false,
|
||||
message: error instanceof Error ? error.message : '保存API权限失败'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新角色的路由权限
|
||||
* @param roleId 角色ID
|
||||
@@ -1145,24 +1314,46 @@ export async function deletePermission(
|
||||
// ==================== 角色权限关联 API ====================
|
||||
|
||||
/**
|
||||
* 获取角色的所有权限
|
||||
* 获取角色的所有权限(已分配的API权限)
|
||||
* @param roleId 角色ID
|
||||
*/
|
||||
export async function getRolePermissions(roleId: number): Promise<RolePermissionDetail[]> {
|
||||
try {
|
||||
const response = await get<any>(`${RBAC_API_BASE}/roles/${roleId}/permissions`);
|
||||
const data = handleApiResponse<{ permissions: any[] }>(response);
|
||||
const { get } = await import('~/api/axios-client');
|
||||
|
||||
return data.permissions.map(perm => ({
|
||||
console.log('🔍 [getRolePermissions] 开始调用后端API:', `/api/v3/rbac/roles/${roleId}/permissions`);
|
||||
|
||||
const response = await get<any>(`/api/v3/rbac/roles/${roleId}/permissions`);
|
||||
console.log('📦 [getRolePermissions] 后端API完整响应:', JSON.stringify(response, null, 2));
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(response.error);
|
||||
}
|
||||
|
||||
// 解析响应数据
|
||||
let permissions: any[] = [];
|
||||
if (response.data?.data?.permissions) {
|
||||
permissions = response.data.data.permissions;
|
||||
} else if (response.data?.permissions) {
|
||||
permissions = response.data.permissions;
|
||||
} else if (Array.isArray(response.data?.data)) {
|
||||
permissions = response.data.data;
|
||||
} else if (Array.isArray(response.data)) {
|
||||
permissions = response.data;
|
||||
}
|
||||
|
||||
console.log('✅ [getRolePermissions] 解析出的权限数组:', permissions);
|
||||
|
||||
return permissions.map(perm => ({
|
||||
id: perm.id,
|
||||
permission_id: perm.permission_id,
|
||||
permission_id: perm.permission_id || perm.id, // 兼容:如果没有 permission_id,使用 id
|
||||
permission_key: perm.permission_key,
|
||||
display_name: perm.display_name,
|
||||
grant_type: perm.grant_type || 'GRANT',
|
||||
data_scope: perm.data_scope || 'ALL'
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('获取角色权限失败:', error);
|
||||
console.error('❌ [getRolePermissions] 获取角色权限失败:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user