立项配置数据入库、审计日志数据入库、编辑页UI界面美化、设备管理内容补充、审计日志显示优化
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
import type { ProjectPublishSnapshot, SetupConfigDraft } from "../types/setupConfig";
|
||||
import type { SetupDiffRow } from "./setupDiffRows";
|
||||
|
||||
export type DraftSyncStatus = "SYNCED" | "LOCAL_ONLY" | "DIRTY_UNSAVED";
|
||||
export type SetupWorkflowStatus = "UNSAVED_DRAFT" | "SAVED_DRAFT" | "PUBLISHED";
|
||||
export type SetupWorkflowTagMeta = { label: string; type: "success" | "warning" | "info" };
|
||||
|
||||
export const SETUP_WORKFLOW_TAG_META: Record<SetupWorkflowStatus, SetupWorkflowTagMeta> = {
|
||||
UNSAVED_DRAFT: { label: "草稿未保存", type: "warning" },
|
||||
SAVED_DRAFT: { label: "草稿已保存(未发布)", type: "info" },
|
||||
PUBLISHED: { label: "已发布", type: "success" },
|
||||
};
|
||||
|
||||
export const resolveSetupWorkflowStatus = (
|
||||
draftSyncStatus: DraftSyncStatus,
|
||||
options: {
|
||||
canStrictlyDetectNoDiff: boolean;
|
||||
hasPublishableDiff: boolean;
|
||||
}
|
||||
): SetupWorkflowStatus => {
|
||||
const { canStrictlyDetectNoDiff, hasPublishableDiff } = options;
|
||||
if (draftSyncStatus !== "SYNCED") return "UNSAVED_DRAFT";
|
||||
if (canStrictlyDetectNoDiff && !hasPublishableDiff) return "PUBLISHED";
|
||||
return "SAVED_DRAFT";
|
||||
};
|
||||
|
||||
export const buildSetupModuleDiffLines = (
|
||||
localDraft: SetupConfigDraft | null,
|
||||
compareBase: SetupConfigDraft | null,
|
||||
moduleLabels: Array<{ key: keyof SetupConfigDraft; label: string }>
|
||||
): string[] => {
|
||||
if (!localDraft || !compareBase) return [];
|
||||
const lines: string[] = [];
|
||||
moduleLabels.forEach((item) => {
|
||||
const localChunk = JSON.stringify(localDraft[item.key] ?? null);
|
||||
const baseChunk = JSON.stringify(compareBase[item.key] ?? null);
|
||||
if (localChunk !== baseChunk) {
|
||||
lines.push(`${item.label} 存在差异`);
|
||||
}
|
||||
});
|
||||
return lines;
|
||||
};
|
||||
|
||||
export const resolveProjectPublishCompareBase = (
|
||||
publishedSnapshot: ProjectPublishSnapshot | null,
|
||||
fallbackSnapshot: ProjectPublishSnapshot | null
|
||||
): ProjectPublishSnapshot | null => {
|
||||
return publishedSnapshot || fallbackSnapshot || null;
|
||||
};
|
||||
|
||||
export const hasProjectSnapshotDiff = (
|
||||
currentSnapshot: ProjectPublishSnapshot,
|
||||
baseSnapshot: ProjectPublishSnapshot | null
|
||||
): boolean => {
|
||||
if (!baseSnapshot) return false;
|
||||
return JSON.stringify(currentSnapshot) !== JSON.stringify(baseSnapshot);
|
||||
};
|
||||
|
||||
type ResolvePublishedProjectSnapshotInput = {
|
||||
publishStatus: string;
|
||||
apiProjectSnapshot: ProjectPublishSnapshot | null;
|
||||
previousSnapshot: ProjectPublishSnapshot | null;
|
||||
projectSnapshotAtLoad: ProjectPublishSnapshot | null;
|
||||
currentProjectSnapshot: ProjectPublishSnapshot;
|
||||
};
|
||||
|
||||
type ResolvePublishedProjectSnapshotResult = {
|
||||
snapshot: ProjectPublishSnapshot | null;
|
||||
clearFallback: boolean;
|
||||
};
|
||||
|
||||
export const resolvePublishedProjectSnapshot = (
|
||||
input: ResolvePublishedProjectSnapshotInput
|
||||
): ResolvePublishedProjectSnapshotResult => {
|
||||
if (input.apiProjectSnapshot) {
|
||||
return { snapshot: input.apiProjectSnapshot, clearFallback: true };
|
||||
}
|
||||
if (input.publishStatus === "PUBLISHED") {
|
||||
return {
|
||||
snapshot: input.previousSnapshot || input.projectSnapshotAtLoad || input.currentProjectSnapshot,
|
||||
clearFallback: true,
|
||||
};
|
||||
}
|
||||
return {
|
||||
// Draft state keeps the previous published baseline when available.
|
||||
snapshot: input.previousSnapshot,
|
||||
clearFallback: false,
|
||||
};
|
||||
};
|
||||
|
||||
export const shouldCaptureProjectCompareFallback = (params: {
|
||||
hasProjectPendingChanges: boolean;
|
||||
hasPublishedProjectSnapshot: boolean;
|
||||
isFirstPublish: boolean;
|
||||
}): boolean => {
|
||||
if (!params.hasProjectPendingChanges) return false;
|
||||
if (params.hasPublishedProjectSnapshot) return false;
|
||||
if (params.isFirstPublish) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
const PROJECT_FIELD_LABEL_MAP: Record<keyof ProjectPublishSnapshot, string> = {
|
||||
code: "项目编号",
|
||||
name: "项目名称",
|
||||
project_full_name: "项目全称",
|
||||
sponsor: "申办方",
|
||||
protocol_no: "方案号",
|
||||
lead_unit: "组长单位",
|
||||
principal_investigator: "主要研究者",
|
||||
main_pm: "主PM",
|
||||
research_analysis: "研究分期",
|
||||
research_product: "研究产品",
|
||||
control_product: "对照产品",
|
||||
indication: "适应症",
|
||||
research_population: "研究人群",
|
||||
research_design: "研究设计",
|
||||
plan_start_date: "计划开始日期",
|
||||
plan_end_date: "计划结束日期",
|
||||
planned_site_count: "计划中心数",
|
||||
planned_enrollment_count: "计划入组数",
|
||||
summary_note: "方案摘要说明",
|
||||
objective_note: "研究目标摘要",
|
||||
status: "项目状态",
|
||||
visit_interval_days: "访视间隔(天)",
|
||||
visit_total: "访视总数",
|
||||
visit_window_start_offset: "窗口期起始偏移",
|
||||
visit_window_end_offset: "窗口期结束偏移",
|
||||
};
|
||||
|
||||
export const buildProjectDiffRows = (
|
||||
currentSnapshot: ProjectPublishSnapshot,
|
||||
baseSnapshot: ProjectPublishSnapshot | null,
|
||||
serializeValue: (value: unknown) => string
|
||||
): SetupDiffRow[] => {
|
||||
if (!baseSnapshot) return [];
|
||||
const rows: SetupDiffRow[] = [];
|
||||
(Object.keys(PROJECT_FIELD_LABEL_MAP) as Array<keyof ProjectPublishSnapshot>).forEach((key) => {
|
||||
if (currentSnapshot[key] === baseSnapshot[key]) return;
|
||||
rows.push({
|
||||
moduleLabel: "项目信息",
|
||||
path: PROJECT_FIELD_LABEL_MAP[key],
|
||||
changeType: "修改",
|
||||
localValue: serializeValue(currentSnapshot[key]),
|
||||
serverValue: serializeValue(baseSnapshot[key]),
|
||||
});
|
||||
});
|
||||
return rows;
|
||||
};
|
||||
Reference in New Issue
Block a user