统一业务页面权限与抽屉编辑
1、将 FAQ 分类和问题维护改为抽屉交互,并按分类、问题、回复的细粒度权限控制入口。 2、将注意事项、可行性和伦理记录接入详情页抽屉编辑,创建时支持先保存主记录再上传附件。 3、将参与者基础信息编辑改为抽屉,详情页按可读权限显示病史、访视、AE 和 PD 标签页。 4、补充业务页面权限一致性测试,覆盖停用中心、无权限入口和面包屑上下文。
This commit is contained in:
@@ -134,6 +134,8 @@ const replyFiles = ref<UploadUserFile[]>([]);
|
||||
const replyAttachmentsMap = ref<Record<string, any[]>>({});
|
||||
|
||||
const { can } = usePermission();
|
||||
const canReadCategories = computed(() => can("faq.category.read"));
|
||||
const canReadMembers = computed(() => can("project.members.list"));
|
||||
|
||||
const canReply = computed(() => {
|
||||
if (!item.value) return false;
|
||||
@@ -150,29 +152,24 @@ const canUploadReplyAttachment = computed(() => {
|
||||
|
||||
const canDeleteReply = (reply: any) => {
|
||||
if (reply.is_deleted) return false;
|
||||
if (auth.user?.is_admin) return true;
|
||||
if (reply.created_by === auth.user?.id) return true;
|
||||
return item.value?.study_id ? can("faq.edit") : false;
|
||||
if (!item.value?.study_id) return false;
|
||||
return can("faq.reply.delete");
|
||||
};
|
||||
|
||||
const canSelectBest = computed(() => {
|
||||
if (!item.value) return false;
|
||||
return can("faq.reply");
|
||||
return can("faq.update");
|
||||
});
|
||||
|
||||
const canEditQuestion = computed(() => {
|
||||
if (!item.value) return false;
|
||||
if (auth.user?.is_admin) return true;
|
||||
if (item.value.created_by === auth.user?.id) return true;
|
||||
return can("faq.edit");
|
||||
return can("faq.update");
|
||||
});
|
||||
|
||||
const canConfirmResolved = computed(() => {
|
||||
if (!item.value) return false;
|
||||
if (item.value.status === "RESOLVED") return false;
|
||||
if (auth.user?.is_admin) return true;
|
||||
if (item.value.created_by === auth.user?.id) return true;
|
||||
return can("faq.edit");
|
||||
return can("faq.update");
|
||||
});
|
||||
|
||||
const statusLabel = computed(() => {
|
||||
@@ -218,6 +215,10 @@ const categoryName = computed(() => {
|
||||
|
||||
|
||||
const loadMembers = async (studyId?: string | null) => {
|
||||
if (!canReadMembers.value) {
|
||||
members.value = [];
|
||||
return;
|
||||
}
|
||||
if (!studyId) {
|
||||
members.value = [];
|
||||
return;
|
||||
@@ -250,12 +251,19 @@ const loadData = async () => {
|
||||
try {
|
||||
const { data: faqData } = await fetchFaqItem(id);
|
||||
item.value = faqData;
|
||||
const questionTitle = String(faqData.question || "").trim();
|
||||
study.setViewContext({
|
||||
pageTitle: questionTitle ? questionTitle.slice(0, 24) : TEXT.modules.knowledgeMedicalConsult.detailTitle,
|
||||
});
|
||||
const categoryParams: Record<string, any> = {};
|
||||
if (faqData.study_id) {
|
||||
categoryParams.study_id = faqData.study_id;
|
||||
}
|
||||
const { data: catData } = await fetchFaqCategories(categoryParams);
|
||||
categories.value = catData.items || catData || [];
|
||||
categories.value = [];
|
||||
if (canReadCategories.value) {
|
||||
const { data: catData } = await fetchFaqCategories(categoryParams);
|
||||
categories.value = catData.items || catData || [];
|
||||
}
|
||||
await Promise.all([loadReplies(), loadMembers(faqData.study_id)]);
|
||||
} catch (e: any) {
|
||||
ElMessage.error(e?.response?.data?.message || TEXT.common.messages.loadFailed);
|
||||
@@ -309,6 +317,10 @@ const submitReply = async () => {
|
||||
|
||||
const removeReply = async (reply: any) => {
|
||||
if (!item.value) return;
|
||||
if (!canDeleteReply(reply)) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
const ok = await ElMessageBox.confirm(TEXT.common.confirm.delete, TEXT.common.labels.tips).catch(() => null);
|
||||
if (!ok) return;
|
||||
try {
|
||||
@@ -322,6 +334,10 @@ const removeReply = async (reply: any) => {
|
||||
|
||||
const toggleBest = async (reply: any) => {
|
||||
if (!item.value) return;
|
||||
if (!canSelectBest.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const target = item.value.best_reply_id === reply.id ? null : reply.id;
|
||||
if (target) {
|
||||
@@ -338,6 +354,10 @@ const toggleBest = async (reply: any) => {
|
||||
|
||||
const confirmResolved = async () => {
|
||||
if (!item.value) return;
|
||||
if (!canConfirmResolved.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const { data } = await setFaqStatus(item.value.id, { status: "RESOLVED" });
|
||||
item.value = data;
|
||||
@@ -348,6 +368,10 @@ const confirmResolved = async () => {
|
||||
};
|
||||
|
||||
const openEdit = () => {
|
||||
if (!canEditQuestion.value) {
|
||||
ElMessage.warning("权限不足");
|
||||
return;
|
||||
}
|
||||
editing.value = item.value;
|
||||
showForm.value = true;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user