完善项目级权限与项目管理入口
- 新增项目成员候选用户接口,避免 PM 读取全局用户列表 - 按项目角色和权限矩阵控制项目管理、成员、中心与审计入口 - 合并侧边栏管理后台入口,统一从项目管理进入成员等模块 - 加强成员管理安全约束,禁止 PM 修改自己、系统管理员或更高权限角色 - 修复多项目角色缓存串用问题,并补充前后端回归测试
This commit is contained in:
@@ -427,7 +427,7 @@ const routes: RouteRecordRaw[] = [
|
||||
path: "projects",
|
||||
name: "AdminProjects",
|
||||
component: AdminProjects,
|
||||
meta: { title: TEXT.menu.projectManagement, requiresAdmin: true },
|
||||
meta: { title: TEXT.menu.projectManagement },
|
||||
},
|
||||
{
|
||||
path: "projects/:projectId",
|
||||
@@ -445,7 +445,7 @@ const routes: RouteRecordRaw[] = [
|
||||
path: "projects/:projectId/permissions",
|
||||
name: "AdminProjectPermissions",
|
||||
component: ProjectPermissions,
|
||||
meta: { title: "权限管理", requiresAdmin: true },
|
||||
meta: { title: "权限管理", adminProjectPermission: { module: "project_members", action: "write" } },
|
||||
},
|
||||
{
|
||||
path: "projects/:projectId/sites",
|
||||
@@ -472,25 +472,6 @@ const router = createRouter({
|
||||
routes,
|
||||
});
|
||||
|
||||
const pmAdminLandingModules: Array<{ module: string; path: (studyId: string) => string }> = [
|
||||
{ module: "project_members", path: (studyId) => `/admin/projects/${studyId}/members` },
|
||||
{ module: "sites", path: (studyId) => `/admin/projects/${studyId}/sites` },
|
||||
{ module: "audit_export", path: () => "/admin/audit-logs" },
|
||||
];
|
||||
|
||||
const findPmAdminLandingPath = async () => {
|
||||
const studyStore = useStudyStore();
|
||||
const studyId = studyStore.currentStudy?.id;
|
||||
if (!studyId) return null;
|
||||
if (!studyStore.currentPermissions) {
|
||||
await studyStore.loadCurrentStudyPermissions();
|
||||
}
|
||||
const role = studyStore.currentStudyRole || (studyStore.currentStudy as any)?.role_in_study || "";
|
||||
if (role !== "PM") return null;
|
||||
const matched = pmAdminLandingModules.find((item) => !!studyStore.currentPermissions?.roles?.PM?.[item.module]?.read);
|
||||
return matched ? matched.path(studyId) : null;
|
||||
};
|
||||
|
||||
const ensureAdminProjectAccess = async (to: any, isAdmin: boolean) => {
|
||||
if (isAdmin) return true;
|
||||
const studyStore = useStudyStore();
|
||||
@@ -511,7 +492,7 @@ const ensureAdminProjectAccess = async (to: any, isAdmin: boolean) => {
|
||||
|
||||
const role = studyStore.currentStudyRole || (studyStore.currentStudy as any)?.role_in_study || "";
|
||||
if (role !== "PM") return false;
|
||||
return !!studyStore.currentPermissions?.roles?.PM?.[permission.module]?.[permission.action];
|
||||
return !!studyStore.currentPermissions?.roles?.[role]?.[permission.module]?.[permission.action];
|
||||
};
|
||||
|
||||
router.beforeEach(async (to, _from, next) => {
|
||||
@@ -549,23 +530,13 @@ router.beforeEach(async (to, _from, next) => {
|
||||
next({ path: studyStore.currentStudy ? "/project/overview" : "/admin/users" });
|
||||
return;
|
||||
}
|
||||
if (!isAdmin && token && (to.path === "/" || to.path === "/workbench")) {
|
||||
const pmAdminLanding = await findPmAdminLandingPath().catch(() => null);
|
||||
if (pmAdminLanding) {
|
||||
next({ path: pmAdminLanding });
|
||||
return;
|
||||
}
|
||||
}
|
||||
if ((to.path === "/login" || to.path === "/register") && token) {
|
||||
if (!auth.forceLogin) {
|
||||
const pmAdminLanding = isAdmin ? null : await findPmAdminLandingPath().catch(() => null);
|
||||
next({
|
||||
path: isAdmin
|
||||
? studyStore.currentStudy
|
||||
? "/project/overview"
|
||||
: "/admin/users"
|
||||
: pmAdminLanding
|
||||
? pmAdminLanding
|
||||
: studyStore.currentStudy
|
||||
? "/project/overview"
|
||||
: "/workbench",
|
||||
|
||||
Reference in New Issue
Block a user