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

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
@@ -0,0 +1,56 @@
import { describe, expect, it } from "vitest";
import { readFileSync } from "node:fs";
import { resolve } from "node:path";
const readSource = () => readFileSync(resolve(__dirname, "./StartupFeasibilityEthics.vue"), "utf8");
describe("StartupFeasibilityEthics project permissions", () => {
it("hides initiation and ethics create/delete actions by their backend operation permissions", () => {
const source = readSource();
expect(source).toContain('canUseApiPermission("startup_initiation:create")');
expect(source).toContain('canUseApiPermission("startup_initiation:read")');
expect(source).toContain('canUseApiPermission("startup_initiation:update")');
expect(source).toContain('canUseApiPermission("startup_initiation:delete")');
expect(source).toContain('canUseApiPermission("startup_ethics:read")');
expect(source).toContain('canUseApiPermission("startup_ethics:create")');
expect(source).toContain('canUseApiPermission("startup_ethics:update")');
expect(source).toContain('canUseApiPermission("startup_ethics:delete")');
expect(source).toContain('v-if="canReadFeasibility"');
expect(source).toContain('v-if="canReadEthics"');
expect(source).toContain("if (!canReadFeasibility.value)");
expect(source).toContain("if (!canReadEthics.value)");
expect(source).toContain('v-if="canCreateFeasibility"');
expect(source).toContain('v-if="canUpdateFeasibility"');
expect(source).toContain('v-if="canDeleteFeasibility"');
expect(source).toContain('v-if="canCreateEthics"');
expect(source).toContain('v-if="canUpdateEthics"');
expect(source).toContain('v-if="canDeleteEthics"');
});
});
describe("StartupFeasibilityEthics drawer editors", () => {
it("opens initiation and ethics create forms in drawers instead of routing to form pages", () => {
const source = readSource();
expect(source).toContain("FeasibilityEditorDrawer");
expect(source).toContain("EthicsEditorDrawer");
expect(source).toContain("feasibilityDrawerVisible");
expect(source).toContain("ethicsDrawerVisible");
expect(source).not.toContain('router.push("/startup/feasibility/new")');
expect(source).not.toContain('router.push("/startup/ethics/new")');
});
it("opens initiation and ethics edit drawers from table action columns", () => {
const source = readSource();
expect(source).toContain(':record-id="editingFeasibilityId"');
expect(source).toContain(':record-id="editingEthicsId"');
expect(source).toContain("const editingFeasibilityId = ref<string | undefined>();");
expect(source).toContain("const editingEthicsId = ref<string | undefined>();");
expect(source).toContain("@click.stop=\"openFeasibilityEditor(scope.row)\"");
expect(source).toContain("@click.stop=\"openEthicsEditor(scope.row)\"");
expect(source).toContain("editingFeasibilityId.value = row?.id;");
expect(source).toContain("editingEthicsId.value = row?.id;");
});
});