mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 04:20:45 +08:00
Show before/after values for all non-key compare result columns.
Key stays single-value; other fields always render a paired 前/后 cell so cutover diffs are readable at a glance. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
157188bd3e
commit
97796478b0
4 changed files with 102 additions and 50 deletions
|
|
@ -381,6 +381,8 @@ const en = {
|
|||
failFields: "Failed fields",
|
||||
failFieldsEmpty: "—",
|
||||
failWholeRow: "Whole row",
|
||||
pairBefore: "Before",
|
||||
pairAfter: "After",
|
||||
sheetAllPass: "All match",
|
||||
filterFailField: "Failed compare field",
|
||||
filterFailFieldAll: "Any failed field",
|
||||
|
|
|
|||
|
|
@ -381,6 +381,8 @@ const zh = {
|
|||
failFields: "失败项",
|
||||
failFieldsEmpty: "—",
|
||||
failWholeRow: "整行",
|
||||
pairBefore: "前",
|
||||
pairAfter: "后",
|
||||
sheetAllPass: "全部一致",
|
||||
filterFailField: "失败比对项",
|
||||
filterFailFieldAll: "全部失败项",
|
||||
|
|
|
|||
|
|
@ -10735,8 +10735,47 @@ html.login-page--paused .login-page__flare {
|
|||
background: rgba(245, 158, 11, 0.08);
|
||||
}
|
||||
|
||||
.bs-cmp-pair {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 3px;
|
||||
min-width: 4.5rem;
|
||||
}
|
||||
|
||||
.bs-cmp-pair__row {
|
||||
display: grid;
|
||||
grid-template-columns: 1.6em minmax(0, 1fr);
|
||||
gap: 6px;
|
||||
align-items: baseline;
|
||||
}
|
||||
|
||||
.bs-cmp-pair__tag {
|
||||
flex: 0 0 auto;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
color: #64748b;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.bs-cmp-val-cell--diff .bs-cmp-pair__row:first-child .bs-cmp-pair__tag {
|
||||
color: #fca5a5;
|
||||
}
|
||||
|
||||
.bs-cmp-val-cell--diff .bs-cmp-pair__row:last-child .bs-cmp-pair__tag {
|
||||
color: #86efac;
|
||||
}
|
||||
|
||||
.bs-cmp-val {
|
||||
font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
|
||||
color: #e2e8f0;
|
||||
word-break: break-all;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.bs-cmp-val.is-empty {
|
||||
color: #64748b;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.bs-cmp-val--pre {
|
||||
|
|
|
|||
|
|
@ -138,6 +138,51 @@ function failFieldNames(d: DiffRow): string[] {
|
|||
return Object.keys(d.changes || {});
|
||||
}
|
||||
|
||||
/** Non-key fields: always show before/after pair for cutover review. */
|
||||
function PairCell(props: {
|
||||
beforeText: string;
|
||||
afterText: string;
|
||||
mismatch: boolean;
|
||||
reason?: string;
|
||||
beforeLabel: string;
|
||||
afterLabel: string;
|
||||
}) {
|
||||
const { beforeText, afterText, mismatch, reason, beforeLabel, afterLabel } = props;
|
||||
const pre = beforeText || "—";
|
||||
const post = afterText || "—";
|
||||
return (
|
||||
<td
|
||||
className={`bs-cmp-val-cell bs-cmp-val-cell--pair${
|
||||
mismatch ? " bs-cmp-val-cell--diff" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="bs-cmp-pair">
|
||||
<div className="bs-cmp-pair__row">
|
||||
<span className="bs-cmp-pair__tag">{beforeLabel}</span>
|
||||
<span
|
||||
className={`bs-cmp-val${mismatch ? " bs-cmp-val--pre" : ""}${
|
||||
!beforeText ? " is-empty" : ""
|
||||
}`}
|
||||
>
|
||||
{pre}
|
||||
</span>
|
||||
</div>
|
||||
<div className="bs-cmp-pair__row">
|
||||
<span className="bs-cmp-pair__tag">{afterLabel}</span>
|
||||
<span
|
||||
className={`bs-cmp-val${mismatch ? " bs-cmp-val--post" : ""}${
|
||||
!afterText ? " is-empty" : ""
|
||||
}`}
|
||||
>
|
||||
{post}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{reason ? <div className="bs-cmp-val-reason muted">{reason}</div> : null}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
|
||||
function toggleInList(list: string[], name: string, on: boolean): string[] {
|
||||
if (on) return list.includes(name) ? list : [...list, name];
|
||||
return list.filter((x) => x !== name);
|
||||
|
|
@ -2263,58 +2308,22 @@ export function BizComparePage() {
|
|||
{resultColumns.extras.map((f) => {
|
||||
const pv = cellText(pre[f]);
|
||||
const av = cellText(post[f]);
|
||||
const isCmp = resultColumns.compareSet.has(f);
|
||||
const ch = d.changes?.[f];
|
||||
if (!isCmp) {
|
||||
const show =
|
||||
d.kind === "removed" ? pv || "—" : av || pv || "—";
|
||||
return (
|
||||
<td key={f} className="bs-cmp-val-cell bs-cmp-val-cell--ctx">
|
||||
<span className="bs-cmp-val">{show}</span>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
if (d.kind === "added") {
|
||||
return (
|
||||
<td key={f} className="bs-cmp-val-cell">
|
||||
<span className="bs-cmp-val bs-cmp-val--post">
|
||||
{av || "—"}
|
||||
</span>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
if (d.kind === "removed") {
|
||||
return (
|
||||
<td key={f} className="bs-cmp-val-cell">
|
||||
<span className="bs-cmp-val bs-cmp-val--pre">
|
||||
{pv || "—"}
|
||||
</span>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
const mismatch = Boolean(ch);
|
||||
if (!mismatch) {
|
||||
return (
|
||||
<td key={f} className="bs-cmp-val-cell">
|
||||
<span className="bs-cmp-val">{pv || av || "—"}</span>
|
||||
</td>
|
||||
);
|
||||
}
|
||||
const isCmp = resultColumns.compareSet.has(f);
|
||||
// Compare fields: engine mismatch; display fields: value differ / side missing
|
||||
const mismatch = isCmp
|
||||
? Boolean(ch) || d.kind === "added" || d.kind === "removed"
|
||||
: pv !== av;
|
||||
return (
|
||||
<td key={f} className="bs-cmp-val-cell bs-cmp-val-cell--diff">
|
||||
<span className="bs-cmp-val bs-cmp-val--pre">
|
||||
{pv || "—"}
|
||||
</span>
|
||||
<span className="bs-cmp-val-arrow" aria-hidden>
|
||||
→
|
||||
</span>
|
||||
<span className="bs-cmp-val bs-cmp-val--post">
|
||||
{av || "—"}
|
||||
</span>
|
||||
{ch?.reason ? (
|
||||
<div className="bs-cmp-val-reason muted">{ch.reason}</div>
|
||||
) : null}
|
||||
</td>
|
||||
<PairCell
|
||||
key={f}
|
||||
beforeText={pv}
|
||||
afterText={av}
|
||||
mismatch={mismatch}
|
||||
reason={ch?.reason}
|
||||
beforeLabel={t("bizCompare.pairBefore")}
|
||||
afterLabel={t("bizCompare.pairAfter")}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</tr>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue