统一业务页面权限与抽屉编辑

1、将 FAQ 分类和问题维护改为抽屉交互,并按分类、问题、回复的细粒度权限控制入口。

2、将注意事项、可行性和伦理记录接入详情页抽屉编辑,创建时支持先保存主记录再上传附件。

3、将参与者基础信息编辑改为抽屉,详情页按可读权限显示病史、访视、AE 和 PD 标签页。

4、补充业务页面权限一致性测试,覆盖停用中心、无权限入口和面包屑上下文。
This commit is contained in:
Cheng Zhou
2026-06-04 11:10:20 +08:00
parent 6a2e43f829
commit 6e8494abd5
39 changed files with 2334 additions and 337 deletions
@@ -57,14 +57,16 @@ describe("SubjectDetail medication adherence", () => {
expect(source).toContain("isSystemAdmin");
[
"subjects:update",
"subject_histories:read",
"subject_histories:create",
"subject_histories:update",
"subject_histories:delete",
"visits:list",
"visits:create",
"visits:update",
"visits:delete",
"subject_aes:create",
"subject_aes:list",
"subject_aes:read",
"subject_aes:update",
"subject_aes:delete",
"subject_pds:create",
@@ -75,6 +77,10 @@ describe("SubjectDetail medication adherence", () => {
expect(source).toContain(operationKey);
});
expect(source).toContain("v-if=\"currentTabAction.allowed\"");
expect(source).toContain("v-if=\"canReadHistory\"");
expect(source).toContain("v-if=\"canReadVisit\"");
expect(source).toContain("v-if=\"canListAe\"");
expect(source).toContain("v-if=\"canListPd\"");
expect(source).toContain("v-if=\"canUpdateHistory || canDeleteHistory\"");
expect(source).toContain("v-if=\"canUpdateVisit || canDeleteVisit\"");
expect(source).toContain("v-if=\"canUpdateAe || canDeleteAe\"");
@@ -83,10 +89,26 @@ describe("SubjectDetail medication adherence", () => {
expect(source).toContain("const canSaveVisit = computed(() => (visitEditingId.value ? canUpdateVisit.value : canCreateVisit.value));");
expect(source).toContain("const canSaveAe = computed(() => (aeEditingId.value ? canUpdateAe.value : canCreateAe.value));");
expect(source).toContain("const canSavePd = computed(() => (pdEditingId.value ? canUpdatePd.value : canCreatePd.value));");
expect(source).toContain("const canReadHistory = computed(() => canUseApiPermission(\"subject_histories:read\"));");
expect(source).toContain("const canReadVisit = computed(() => canUseApiPermission(\"visits:list\"));");
expect(source).toContain("if (!canReadHistory.value)");
expect(source).toContain("if (!canReadVisit.value)");
expect(source).toContain("const canCreateAe = computed(() => canUseApiPermission(\"subject_aes:create\"));");
expect(source).toContain("const canUpdateAe = computed(() => canUseApiPermission(\"subject_aes:update\"));");
expect(source).toContain("const canDeleteAe = computed(() => canUseApiPermission(\"subject_aes:delete\"));");
expect(source).toContain("const canListAe = computed(() => canUseApiPermission(\"subject_aes:list\"));");
expect(source).toContain("const canListAe = computed(() => canUseApiPermission(\"subject_aes:read\"));");
expect(source).toContain("const canListPd = computed(() => canUseApiPermission(\"subject_pds:list\"));");
});
});
describe("SubjectDetail drawer editor", () => {
it("opens participant basic information edit in the shared drawer editor", () => {
const source = readSubjectDetail();
expect(source).toContain("SubjectEditorDrawer");
expect(source).toContain("subjectEditorVisible");
expect(source).toContain("@success=\"handleSubjectEditorSuccess\"");
expect(source).not.toContain("subjectEditing");
expect(source).not.toContain("saveSubjectEdit");
});
});