「权限 × 状态 × 审计」联合约束机制

This commit is contained in:
Cheng Zhou
2025-12-17 21:48:29 +08:00
parent 13efb0a3a9
commit 4202ed7922
36 changed files with 714 additions and 85 deletions
+38
View File
@@ -29,6 +29,9 @@ import { computed, reactive, ref, watch } from "vue";
import { ElMessage, type FormInstance, type FormRules } from "element-plus";
import { createSite, updateSite } from "../../api/sites";
import type { Site } from "../../types/api";
import { useAuthStore } from "../../store/auth";
import { evaluateAction } from "../../guards/actionGuard";
import { logAudit } from "../../audit";
const props = defineProps<{
visible: boolean;
@@ -46,6 +49,7 @@ const visibleProxy = computed({
set: (val: boolean) => emit("update:visible", val),
});
const auth = useAuthStore();
const formRef = ref<FormInstance>();
const submitting = ref(false);
const form = reactive({
@@ -86,6 +90,21 @@ watch(
const onSubmit = async () => {
if (!formRef.value) return;
const decision = evaluateAction({
actorRole: auth.user?.role || null,
requiredPermission: "site.manage",
target: { siteId: props.site?.id, studyId: props.studyId },
});
if (!decision.allowed) {
ElMessage.warning(decision.reason || "无权限执行该操作");
logAudit(decision.auditType, {
targetId: props.site?.id || props.studyId,
targetName: props.site?.name,
reason: decision.reason,
severity: decision.severity,
});
return;
}
await formRef.value.validate();
submitting.value = true;
try {
@@ -98,6 +117,12 @@ const onSubmit = async () => {
is_active: form.is_active,
});
ElMessage.success("中心已更新");
logAudit("SITE_STATUS_CHANGED", {
targetId: props.site.id,
targetName: props.site.name,
after: { name: form.name, is_active: form.is_active },
severity: "normal",
});
} else {
await createSite(props.studyId, {
name: form.name,
@@ -106,11 +131,24 @@ const onSubmit = async () => {
contact: form.contact,
});
ElMessage.success("中心已创建");
logAudit("SITE_STATUS_CHANGED", {
targetId: props.studyId,
targetName: form.name,
after: { name: form.name },
severity: "normal",
});
}
emit("saved");
visibleProxy.value = false;
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || "保存失败");
logAudit("SITE_STATUS_CHANGED", {
targetId: props.site?.id || props.studyId,
targetName: props.site?.name || form.name,
after: { name: form.name },
severity: "warning",
reason: e?.response?.data?.message,
});
} finally {
submitting.value = false;
}