权限监控与管理界面全面美化

重新设计权限系统所有 UI 组件的视觉风格,统一配色、圆角、阴影和交互动效:
- 实时概览:统计卡片加图标和渐变色条,健康评分改为环形进度,告警改为卡片式
- 趋势分析:图表卡片加彩色图标标识,ECharts 配色升级为渐变面积填充
- 访问日志:指标卡片带图标,日志审计改为卡片式入口,IP排行前三高亮
- IP属地:工具栏重设计,排行列表前三渐变高亮,指标卡片统一新风格
- 系统级权限:从 el-table 改为自定义卡片列表,模块块独立圆角卡片
- 项目权限配置:空状态引导优化,成员表格加头像,工具栏加背景容器
- 角色概览卡片:加进度条可视化,hover 微动效
- 接口权限矩阵:工具栏分离布局,表格圆角包裹
- 角色管理抽屉:侧边栏选中态渐变,操作行 hover 高亮

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Cheng Zhou
2026-05-20 14:36:35 +08:00
parent e95eeed90e
commit 6cefa620e4
68 changed files with 6927 additions and 984 deletions
+11 -3
View File
@@ -2,6 +2,7 @@ import { createRouter, createWebHistory, RouteRecordRaw } from "vue-router";
import { useAuthStore } from "../store/auth";
import { useStudyStore } from "../store/study";
import { isSystemAdmin } from "../utils/roles";
import { isApiPermissionAllowed } from "../utils/apiPermissionValue";
import { findFirstAccessibleProjectPath, getProjectRoutePermission, hasProjectPermission } from "../utils/projectRoutePermissions";
import { fetchStudyDetail } from "../api/studies";
import { fetchApiEndpointPermissions } from "../api/projectPermissions";
@@ -489,6 +490,12 @@ const router = createRouter({
routes,
});
const ADMIN_PROJECT_OPERATION_KEYS: Record<string, Record<"read" | "write", string>> = {
audit_export: { read: "audit_logs:read", write: "audit_logs:read" },
project_members: { read: "project_members:list", write: "project_members:update" },
sites: { read: "sites:read", write: "sites:update" },
};
const ensureAdminProjectAccess = async (to: any, isAdmin: boolean) => {
if (isAdmin) return true;
const studyStore = useStudyStore();
@@ -509,8 +516,9 @@ const ensureAdminProjectAccess = async (to: any, isAdmin: boolean) => {
const role = studyStore.currentStudyRole || (studyStore.currentStudy as any)?.role_in_study || "";
if (role !== "PM") return false;
// 管理员项目权限检查:PM 角色默认有访问权限
return true;
const operationKey = ADMIN_PROJECT_OPERATION_KEYS[permission.module]?.[permission.action];
if (!operationKey) return false;
return isApiPermissionAllowed(studyStore.currentPermissions?.[role]?.[operationKey]);
};
router.beforeEach(async (to, _from, next) => {
@@ -578,7 +586,7 @@ router.beforeEach(async (to, _from, next) => {
await studyStore.loadCurrentStudyPermissions().catch(() => {});
}
const role = studyStore.currentStudyRole || (studyStore.currentStudy as any)?.role_in_study || "";
const canReadAudit = !!studyStore.currentPermissions?.[role]?.["audit_logs:read"]?.allowed;
const canReadAudit = isApiPermissionAllowed(studyStore.currentPermissions?.[role]?.["audit_logs:read"]);
if (!(isAdmin || (role === "PM" && canReadAudit))) {
next({ path: "/project/overview" });
return;