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

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
+16 -12
View File
@@ -27,16 +27,16 @@
<el-tag :type="statusTagType(scope.row.status)" size="small">{{ statusLabel(scope.row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column :label="TEXT.common.labels.actions" width="140">
<el-table-column v-if="canUpdate || canDelete" :label="TEXT.common.labels.actions" width="140">
<template #default="scope">
<el-button v-if="canDelete(scope.row)" type="text" size="small" class="danger" @click="remove(scope.row)">
<el-button v-if="canDelete" type="text" size="small" class="danger" @click.stop="remove(scope.row)">
{{ TEXT.common.actions.delete }}
</el-button>
<PermissionAction v-if="canEdit" action="faq.edit">
<PermissionAction v-if="canUpdate" action="faq.update">
<el-button
type="text"
size="small"
@click="toggle(scope.row)"
@click.stop="toggle(scope.row)"
:style="{ color: scope.row.is_active ? '#f56c6c' : '#67c23a' }"
>
{{ scope.row.is_active ? TEXT.common.actions.disable : TEXT.common.actions.enable }}
@@ -55,19 +55,18 @@ import { deleteFaqItem, updateFaqItem } from "../api/faqs";
import type { FaqItem, FaqCategory } from "../api/faqs";
import PermissionAction from "./PermissionAction.vue";
import { displayDateTime, displayEnum } from "../utils/display";
import { useAuthStore } from "../store/auth";
import { TEXT } from "../locales";
const props = defineProps<{
items: FaqItem[];
categories: FaqCategory[];
loading: boolean;
canEdit: boolean;
canUpdate: boolean;
canDelete: boolean;
}>();
const emit = defineEmits(["refresh"]);
const router = useRouter();
const auth = useAuthStore();
const categoryMap = computed(() =>
props.categories.reduce<Record<string, string>>((acc, cur) => {
@@ -95,10 +94,11 @@ const statusTagType = (status?: string) => {
return "info";
};
const canDelete = (row: FaqItem) => props.canEdit || row.created_by === auth.user?.id;
const toggle = async (row: FaqItem) => {
if (!props.canUpdate) {
ElMessage.warning("权限不足");
return;
}
const target = !row.is_active;
const ok = await ElMessageBox.confirm(
TEXT.modules.knowledgeMedicalConsult.confirmToggleItem.replace("{action}", target ? TEXT.common.actions.enable : TEXT.common.actions.disable),
@@ -106,7 +106,7 @@ const toggle = async (row: FaqItem) => {
).catch(() => null);
if (!ok) return;
try {
await updateFaqItem(row.id, { is_active: target });
await updateFaqItem(row.id, { study_id: row.study_id, is_active: target });
ElMessage.success(TEXT.common.messages.updateSuccess);
emit("refresh");
} catch (e: any) {
@@ -115,13 +115,17 @@ const toggle = async (row: FaqItem) => {
};
const remove = async (row: FaqItem) => {
if (!props.canDelete) {
ElMessage.warning("权限不足");
return;
}
const ok = await ElMessageBox.confirm(
TEXT.modules.knowledgeMedicalConsult.confirmDeleteItem.replace("{name}", row.question),
TEXT.common.labels.tips
).catch(() => null);
if (!ok) return;
try {
await deleteFaqItem(row.id);
await deleteFaqItem(row.id, row.study_id);
ElMessage.success(TEXT.common.messages.deleteSuccess);
emit("refresh");
} catch (e: any) {