From ecd9973d8ff08285e55b25f19111ffc03f6b74d1 Mon Sep 17 00:00:00 2001 From: jacy <18049821889@163.com> Date: Sun, 16 Aug 2026 12:17:34 +0800 Subject: [PATCH] =?UTF-8?q?feat(admin):=20=E5=A5=97=E9=A4=90=E5=8F=98?= =?UTF-8?q?=E6=9B=B4=E5=AF=B9=E6=AF=94=E9=80=90=E5=AD=97=E5=B7=AE=E5=BC=82?= =?UTF-8?q?=E5=AE=9A=E4=BD=8D=20+=20=E5=BC=80=E5=8F=91=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E5=B9=B6=E5=85=A5v3.4.17?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/pages/StorePackageAuditsPage.tsx | 113 +++++++++++++++--- 杜康好客-v3.4.17-门店套餐审核快捷入口.md | 24 +++- 2 files changed, 120 insertions(+), 17 deletions(-) diff --git a/apps/admin-web/src/pages/StorePackageAuditsPage.tsx b/apps/admin-web/src/pages/StorePackageAuditsPage.tsx index ecdcd9f..5c22607 100644 --- a/apps/admin-web/src/pages/StorePackageAuditsPage.tsx +++ b/apps/admin-web/src/pages/StorePackageAuditsPage.tsx @@ -42,7 +42,44 @@ function imageSignature(pkg: StorePackageItemDto | StorePackageViewDto) { return normalizeStorePackageImageUrls(pkg).join('|'); } -type FieldChange = { label: string; old: string; now: string }; +type FieldChange = { label: string; old: string; now: string; kind: 'text' | 'value' }; + +/** 文本逐字差异:LCS 比对,产出 equal / delete / insert 段落,用于高亮具体变了哪些字 */ +function diffText(a: string, b: string): Array<{ type: 'equal' | 'insert' | 'delete'; text: string }> { + const m = a.length; + const n = b.length; + const dp: number[][] = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0)); + for (let i = m - 1; i >= 0; i--) { + for (let j = n - 1; j >= 0; j--) { + dp[i][j] = a[i] === b[j] ? dp[i + 1][j + 1] + 1 : Math.max(dp[i + 1][j], dp[i][j + 1]); + } + } + const raw: Array<{ type: 'equal' | 'insert' | 'delete'; text: string }> = []; + let i = 0; + let j = 0; + while (i < m && j < n) { + if (a[i] === b[j]) { + raw.push({ type: 'equal', text: a[i] }); + i++; + j++; + } else if (dp[i + 1][j] >= dp[i][j + 1]) { + raw.push({ type: 'delete', text: a[i] }); + i++; + } else { + raw.push({ type: 'insert', text: b[j] }); + j++; + } + } + while (i < m) raw.push({ type: 'delete', text: a[i++] }); + while (j < n) raw.push({ type: 'insert', text: b[j++] }); + const merged: Array<{ type: 'equal' | 'insert' | 'delete'; text: string }> = []; + for (const s of raw) { + const last = merged[merged.length - 1]; + if (last && last.type === s.type) last.text += s.text; + else merged.push({ ...s }); + } + return merged; +} /** 逐字段比较套餐内容,返回发生变化的字段明细(用于“变动的地方详细列出”) */ function fieldChanges( @@ -51,18 +88,21 @@ function fieldChanges( ): FieldChange[] { const changes: FieldChange[] = []; const text = (v: string | number | null | undefined) => (v ?? '').toString().trim(); - const pushIf = (label: string, oldV: string, newV: string) => { - if (oldV !== newV) changes.push({ label, old: oldV || '(空)', now: newV || '(空)' }); + const pushText = (label: string, oldV: string, newV: string) => { + if (oldV !== newV) changes.push({ label, old: oldV, now: newV, kind: 'text' }); }; - pushIf('价格', `¥${text(live.price)}`, `¥${text(proposed.price)}`); - pushIf('套餐名称', text(live.name), text(proposed.name)); - pushIf('菜品内容', text(live.dishes), text(proposed.dishes)); - pushIf('可用时间', text(live.usableTime), text(proposed.usableTime)); - pushIf('其他说明', text(live.otherNotes), text(proposed.otherNotes)); + const pushValue = (label: string, oldV: string, newV: string) => { + if (oldV !== newV) changes.push({ label, old: oldV || '(空)', now: newV || '(空)', kind: 'value' }); + }; + pushValue('价格', `¥${text(live.price)}`, `¥${text(proposed.price)}`); + pushText('套餐名称', text(live.name), text(proposed.name)); + pushText('菜品内容', text(live.dishes), text(proposed.dishes)); + pushText('可用时间', text(live.usableTime), text(proposed.usableTime)); + pushText('其他说明', text(live.otherNotes), text(proposed.otherNotes)); const liveImgs = normalizeStorePackageImageUrls(live); const proposedImgs = normalizeStorePackageImageUrls(proposed); if (imageSignature(live) !== imageSignature(proposed)) { - changes.push({ label: '图片', old: `${liveImgs.length} 张`, now: `${proposedImgs.length} 张` }); + changes.push({ label: '图片', old: `${liveImgs.length} 张`, now: `${proposedImgs.length} 张`, kind: 'value' }); } return changes; } @@ -112,6 +152,43 @@ const CHANGE_LABELS = { unchanged: { text: '未变', color: 'default' }, } as const; +/** 文本逐字差异渲染:原行红色删除线标出被删的字,新行绿色标出新增的字 */ +function TextDiff({ oldText, newText }: { oldText: string; newText: string }) { + const segs = diffText(oldText, newText); + return ( +