From 8d061a152010424634c64073e9a20aa178270163 Mon Sep 17 00:00:00 2001 From: Cheng Zhou Date: Mon, 11 May 2026 09:02:14 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=97=A7=E8=8D=89=E7=A8=BF?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E9=A1=B9=E7=9B=AE=E4=BF=A1=E6=81=AF=E8=A2=AB?= =?UTF-8?q?=E6=B8=85=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/views/admin/ProjectDetail.test.ts | 11 +++++++++++ frontend/src/views/admin/ProjectDetail.vue | 9 ++++++++- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/admin/ProjectDetail.test.ts b/frontend/src/views/admin/ProjectDetail.test.ts index bbaadc9e..c40f5e6c 100644 --- a/frontend/src/views/admin/ProjectDetail.test.ts +++ b/frontend/src/views/admin/ProjectDetail.test.ts @@ -50,6 +50,17 @@ describe("ProjectDetail setup draft publish workflow", () => { expect(source.indexOf("applySetupDraft(createDefaultSetupDraft(siteOptions.value));", functionIndex)).toBe(-1); }); + it("does not let legacy empty projectInfo overwrite project master data", () => { + const source = readProjectDetail(); + const functionIndex = source.indexOf("const applySetupDraft = (draft: SetupConfigDraft) => {"); + const emptyGuardIndex = source.indexOf("!isProjectPublishSnapshotEmpty(projectInfo) || isSetupDraftEffectivelyEmpty(setupDraft)", functionIndex); + const fallbackIndex = source.indexOf("setupDraft.projectInfo = buildProjectPublishSnapshot();", emptyGuardIndex); + + expect(functionIndex).toBeGreaterThan(-1); + expect(emptyGuardIndex).toBeGreaterThan(functionIndex); + expect(fallbackIndex).toBeGreaterThan(emptyGuardIndex); + }); + it("keeps early termination out of regular visit schedule options", () => { const source = readProjectDetail(); diff --git a/frontend/src/views/admin/ProjectDetail.vue b/frontend/src/views/admin/ProjectDetail.vue index 615aed33..643fa6fb 100644 --- a/frontend/src/views/admin/ProjectDetail.vue +++ b/frontend/src/views/admin/ProjectDetail.vue @@ -4009,6 +4009,9 @@ const isProjectPublishSnapshotShape = (value: unknown): value is ProjectPublishS ); }; +const isProjectPublishSnapshotEmpty = (snapshot: ProjectPublishSnapshot): boolean => + JSON.stringify(snapshot) === JSON.stringify(emptyProjectInfoDraft()); + const applyProjectInfoDraft = (snapshot: ProjectPublishSnapshot) => { suppressEnrollmentFieldSync.value = true; form.value.code = snapshot.code || ""; @@ -4044,7 +4047,11 @@ const applySetupDraft = (draft: SetupConfigDraft) => { setupDraft.siteEnrollmentPlans = normalizeSiteEnrollmentPlans(clone(draft.siteEnrollmentPlans)); setupDraft.monitoringStrategies = clone(draft.monitoringStrategies); setupDraft.centerConfirm = clone(draft.centerConfirm); - applyProjectInfoDraft(projectInfo); + if (!isProjectPublishSnapshotEmpty(projectInfo) || isSetupDraftEffectivelyEmpty(setupDraft)) { + applyProjectInfoDraft(projectInfo); + } else { + setupDraft.projectInfo = buildProjectPublishSnapshot(); + } formBaselineSnapshot.value = serializeFormForCompare(); suppressDraftWatch.value = false; };