mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 05:30:46 +08:00
Make biz compare template-driven with field rules, board UX, and import/export.
Operators pick metrics then Key/Compare/Display rules; seed built-ins only once; fail-first verdict board for cutover review. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
21b254120e
commit
d232044750
11 changed files with 1978 additions and 345 deletions
|
|
@ -254,12 +254,32 @@ const en = {
|
|||
templateDeleted: "Template deleted",
|
||||
confirmDeleteTemplate: "Delete this compare template?",
|
||||
confirmDeleteJob: "Delete this compare job and all runs?",
|
||||
emptyTemplates: "No templates yet. Create one or use built-ins.",
|
||||
emptyTemplates: "No templates yet. Create one or import a JSON file.",
|
||||
exportTemplate: "Export",
|
||||
importTemplate: "Import",
|
||||
templateExported: "Template exported",
|
||||
templateImported: "Template imported",
|
||||
templateImportInvalid: "Invalid file: need name and metrics",
|
||||
templateImportHint: "Import/export JSON to share templates privately (no job/batch data)",
|
||||
templateHint:
|
||||
"A template holds multiple metric sheets. Key: row identity (no separate compare). Interface: port mapping. Compare: value checks after key match. Leave compare empty for presence-only (entry set match).",
|
||||
"Pick metrics, then set Key / interface / compare / display. Compare modes: exact, abs delta, or relative %. Display can include context-only columns. Empty compare = presence-only.",
|
||||
stepPickMetrics: "① Pick metrics",
|
||||
stepConfigRules: "② Configure compare rules",
|
||||
pickMetricsHint: "Check metrics to add. Then configure each sheet below.",
|
||||
noMetricsYet: "Select at least one metric above",
|
||||
selectedCount: "{{n}} selected",
|
||||
colFields: "Fields",
|
||||
keyFields: "Key",
|
||||
ifaceFields: "Interface",
|
||||
compareFields: "Compare",
|
||||
compareMode: "Mode",
|
||||
tolerance: "Tolerance",
|
||||
displayField: "Display",
|
||||
keyAlwaysDisplay: "Key columns are always shown",
|
||||
modeEq: "Exact equal",
|
||||
modeNumeric: "Abs delta",
|
||||
modePercent: "Relative %",
|
||||
normalizeField: "Normalize",
|
||||
ignoreFields: "Ignore",
|
||||
field: "Field",
|
||||
note: "Note",
|
||||
|
|
@ -267,6 +287,23 @@ const en = {
|
|||
keyIsIdentity: "Key aligns rows; not used as a value-compare field",
|
||||
metricsRequired: "Add at least one metric sheet",
|
||||
noMetricFields: "No fields for this metric",
|
||||
rowFilters: "Row filter rules",
|
||||
rowFiltersHint:
|
||||
"Filter rows before compare. Conditions in a group are AND; groups are OR. Selecting ARP seeds editable default rules you can change.",
|
||||
rowFiltersEmpty: "No filters → compare all rows. Add a condition group to start.",
|
||||
filterClear: "Clear rules",
|
||||
filterAddOr: "Add group (OR)",
|
||||
filterAddAnd: "Add condition (AND)",
|
||||
filterRemoveGroup: "Remove group",
|
||||
filterOr: "OR",
|
||||
filterField: "Field",
|
||||
filterOp: "Op",
|
||||
filterValue: "Value",
|
||||
normalizeNone: "None",
|
||||
normalizeLower: "lower",
|
||||
normalizeUpper: "upper",
|
||||
normalizeMac: "MAC",
|
||||
normalizeEmpty: "empty→blank",
|
||||
templateFilterPh: "Filter template / metric",
|
||||
jobFilterPh: "Filter job / template / status",
|
||||
addMetric: "Add metric",
|
||||
|
|
@ -286,9 +323,10 @@ const en = {
|
|||
validateMapping: "Validate",
|
||||
validateNeed: "Select mapping and before/after batches first",
|
||||
mapHint:
|
||||
"Optional. Leave empty to ignore port renames (match on non-interface keys). Or one before_if,after_if pair per line",
|
||||
"One pair per line: before_if,after_if (comma / tab / pipe). Example:\ngei-0/1/1,gei-0/1/2\nxgei-0/1/0,xgei-0/2/0\n# comments start with #. Not slash-grouped A,B/C,D.",
|
||||
mappingSaved: "Mapping saved",
|
||||
mappingOptionalHint: "No port map: ignore local port changes; match on non-interface keys.",
|
||||
mappingOptionalHint:
|
||||
"Optional. Empty = ignore local port renames (match on non-interface keys). Fill to apply port map.",
|
||||
createJob: "Create compare job",
|
||||
defaultJobName: "Cutover compare",
|
||||
jobName: "Name",
|
||||
|
|
@ -322,32 +360,49 @@ const en = {
|
|||
resultEmpty: "No matching diff rows",
|
||||
resultFilterPh: "Filter key / values…",
|
||||
kindAll: "All",
|
||||
kindDiff: "Diffs only",
|
||||
kindAdded: "Added only",
|
||||
kindRemoved: "Removed only",
|
||||
kindChanged: "Changed only",
|
||||
kindUnchanged: "Unchanged only",
|
||||
kindAddedShort: "Added",
|
||||
kindRemovedShort: "Removed",
|
||||
kindChangedShort: "Changed",
|
||||
kindUnchangedShort: "Same",
|
||||
diffCount: "{{n}} diffs",
|
||||
kindDiff: "Failures only",
|
||||
kindAdded: "Extra only",
|
||||
kindRemoved: "Missing only",
|
||||
kindChanged: "Mismatch only",
|
||||
kindUnchanged: "Match only",
|
||||
kindAddedShort: "Extra",
|
||||
kindRemovedShort: "Missing",
|
||||
kindChangedShort: "Mismatch",
|
||||
kindUnchangedShort: "Match",
|
||||
kindFail: "Fail",
|
||||
kindPass: "Pass",
|
||||
verdictPass: "Pass",
|
||||
verdictFail: "Fail",
|
||||
failCount: "Failed rows",
|
||||
missCount: "Missing",
|
||||
extraCount: "Extra",
|
||||
mismatchCount: "Mismatch",
|
||||
matchCount: "Match",
|
||||
failFields: "Failed fields",
|
||||
failFieldsEmpty: "—",
|
||||
failWholeRow: "Whole row",
|
||||
sheetAllPass: "All match",
|
||||
filterFailField: "Failed compare field",
|
||||
filterFailFieldAll: "Any failed field",
|
||||
resultEmpty: "No matching rows",
|
||||
resultFilterPh: "Filter identity / values…",
|
||||
diffCount: "{{n}} failed",
|
||||
passRate: "Pass rate",
|
||||
passOk: "In sync",
|
||||
passWarn: "Has diffs",
|
||||
runOverview: "Run overview",
|
||||
sheetCurrent: "Current sheet",
|
||||
diffCountLabel: "Diff rows",
|
||||
diffRate: "Diff rate",
|
||||
passOk: "All passed",
|
||||
passWarn: "Has failures",
|
||||
runOverview: "Verdict",
|
||||
sheetCurrent: "Current check",
|
||||
diffCountLabel: "Failed rows",
|
||||
diffRate: "Fail rate",
|
||||
beforeCount: "Before rows",
|
||||
afterCount: "After rows",
|
||||
matchedRows: "Matched",
|
||||
totalRows: "Total rows",
|
||||
sheetCount: "Sheets",
|
||||
sheetNavTitle: "Items",
|
||||
sheetNavHint: "{{n}} items · click to switch",
|
||||
sheetFailCount: "{{n}} with diffs",
|
||||
topChangedFields: "Top changed fields",
|
||||
sheetNavTitle: "Checks",
|
||||
sheetNavHint: "{{n}} checks · click to switch",
|
||||
sheetFailCount: "{{n}} failed",
|
||||
topChangedFields: "Top failed fields",
|
||||
exportTables: "Download tables",
|
||||
exportOk: "Compare tables exported",
|
||||
fullscreen: "Fullscreen board",
|
||||
|
|
@ -358,9 +413,10 @@ const en = {
|
|||
colName: "Name",
|
||||
colMode: "Mode",
|
||||
colStatus: "Status",
|
||||
colKind: "Kind",
|
||||
colKind: "Verdict",
|
||||
colFailFields: "Failed fields",
|
||||
colActions: "Actions",
|
||||
colKey: "Key",
|
||||
colKey: "Identity",
|
||||
colChange: "Changes",
|
||||
},
|
||||
portTraffic: {
|
||||
|
|
|
|||
|
|
@ -254,12 +254,32 @@ const zh = {
|
|||
templateDeleted: "模板已删除",
|
||||
confirmDeleteTemplate: "删除该比对模板?",
|
||||
confirmDeleteJob: "删除该比对任务及全部运行记录?",
|
||||
emptyTemplates: "暂无模板。可新建或使用系统默认。",
|
||||
emptyTemplates: "暂无模板。可新建,或从 JSON 文件导入。",
|
||||
exportTemplate: "导出",
|
||||
importTemplate: "导入",
|
||||
templateExported: "模板已导出",
|
||||
templateImported: "模板已导入",
|
||||
templateImportInvalid: "导入文件无效:需要 name 与 metrics",
|
||||
templateImportHint: "导入/导出 JSON,便于私下传递模板(不含任务/批次数据)",
|
||||
templateHint:
|
||||
"一个模板可包含多张监控表。Key:对齐前后条目(选中后即为行身份,无需再勾比对);接口:参与端口映射;比对:勾选具体参数做值检查。若不勾任何比对字段,则只校验条目是否一致(存在性)。",
|
||||
"先勾选监控项,再配置 Key / 接口 / 比对 / 展示。比对可设完全相等、绝对容差或相对偏差%;展示列可含不参与比对的上下文。不勾比对字段则只校验条目存在性。",
|
||||
stepPickMetrics: "① 选择监控项",
|
||||
stepConfigRules: "② 配置比对规则",
|
||||
pickMetricsHint: "勾选后加入模板;可多选。选好后在下方按表配置参数。",
|
||||
noMetricsYet: "请先在上方勾选至少一个监控项",
|
||||
selectedCount: "已选 {{n}} 项",
|
||||
colFields: "字段数",
|
||||
keyFields: "Key",
|
||||
ifaceFields: "接口",
|
||||
compareFields: "比对",
|
||||
compareMode: "比对方式",
|
||||
tolerance: "容差",
|
||||
displayField: "展示",
|
||||
keyAlwaysDisplay: "Key 列始终展示",
|
||||
modeEq: "完全相等",
|
||||
modeNumeric: "绝对偏差",
|
||||
modePercent: "相对偏差 %",
|
||||
normalizeField: "归一化",
|
||||
ignoreFields: "忽略",
|
||||
field: "字段",
|
||||
note: "备注",
|
||||
|
|
@ -267,6 +287,23 @@ const zh = {
|
|||
keyIsIdentity: "Key 用于对齐行,不作为值比对字段",
|
||||
metricsRequired: "请至少添加一张监控表",
|
||||
noMetricFields: "该 metric 无字段定义",
|
||||
rowFilters: "行过滤规则",
|
||||
rowFiltersHint:
|
||||
"比对前按规则筛选行。组内条件为「且」,组与组之间为「或」。勾选 ARP 等监控项时会带上默认可编辑规则,可按需改删。",
|
||||
rowFiltersEmpty: "无过滤则比对全部行。可添加条件组。",
|
||||
filterClear: "清空规则",
|
||||
filterAddOr: "添加条件组(或)",
|
||||
filterAddAnd: "组内加条件(且)",
|
||||
filterRemoveGroup: "删除本组",
|
||||
filterOr: "或",
|
||||
filterField: "字段",
|
||||
filterOp: "运算",
|
||||
filterValue: "值",
|
||||
normalizeNone: "无",
|
||||
normalizeLower: "小写",
|
||||
normalizeUpper: "大写",
|
||||
normalizeMac: "MAC",
|
||||
normalizeEmpty: "空值归一",
|
||||
templateFilterPh: "筛选模板名 / metric",
|
||||
jobFilterPh: "筛选任务名 / 模板 / 状态",
|
||||
addMetric: "添加监控项",
|
||||
|
|
@ -285,9 +322,11 @@ const zh = {
|
|||
saveMapping: "保存映射",
|
||||
validateMapping: "校验映射",
|
||||
validateNeed: "请先选择映射与前后批次",
|
||||
mapHint: "可选。不填表示忽略端口变化(同设备比对按非接口 Key 对齐)。填写时每行 before_if,after_if",
|
||||
mapHint:
|
||||
"每行一对:操作前接口,操作后接口(逗号/Tab/竖线分隔)。例:\ngei-0/1/1,gei-0/1/2\nxgei-0/1/0,xgei-0/2/0\n# 井号开头为注释。不是 A,B/C,D 这种斜杠分组。",
|
||||
mappingSaved: "映射已保存",
|
||||
mappingOptionalHint: "未填端口映射:忽略本端端口变化,仅按邻居等非接口 Key 对齐。",
|
||||
mappingOptionalHint:
|
||||
"可选。留空=忽略本端端口改名,按非接口 Key 对齐。填写后才做端口映射校验。",
|
||||
createJob: "建立比对任务",
|
||||
defaultJobName: "割接比对",
|
||||
jobName: "名称",
|
||||
|
|
@ -318,35 +357,50 @@ const zh = {
|
|||
result: "比对结果",
|
||||
pickBatchRun: "选择比对批次",
|
||||
pickRun: "选择批次…",
|
||||
resultEmpty: "无匹配差异行",
|
||||
resultFilterPh: "筛选 key / 字段值…",
|
||||
resultEmpty: "无匹配失败/结果行",
|
||||
resultFilterPh: "筛选身份 / 字段值…",
|
||||
kindAll: "全部",
|
||||
kindDiff: "仅差异",
|
||||
kindAdded: "仅新增",
|
||||
kindRemoved: "仅删除",
|
||||
kindChanged: "仅变更",
|
||||
kindUnchanged: "仅未变",
|
||||
kindAddedShort: "新增",
|
||||
kindRemovedShort: "删除",
|
||||
kindChangedShort: "变更",
|
||||
kindUnchangedShort: "未变",
|
||||
diffCount: "差异 {{n}}",
|
||||
kindDiff: "仅失败",
|
||||
kindAdded: "仅多余",
|
||||
kindRemoved: "仅缺失",
|
||||
kindChanged: "仅不一致",
|
||||
kindUnchanged: "仅一致",
|
||||
kindAddedShort: "多余",
|
||||
kindRemovedShort: "缺失",
|
||||
kindChangedShort: "不一致",
|
||||
kindUnchangedShort: "一致",
|
||||
kindFail: "失败",
|
||||
kindPass: "通过",
|
||||
verdictPass: "通过",
|
||||
verdictFail: "不通过",
|
||||
failCount: "失败条目",
|
||||
missCount: "缺失",
|
||||
extraCount: "多余",
|
||||
mismatchCount: "不一致",
|
||||
matchCount: "一致",
|
||||
failFields: "失败项",
|
||||
failFieldsEmpty: "—",
|
||||
failWholeRow: "整行",
|
||||
sheetAllPass: "全部一致",
|
||||
filterFailField: "失败比对项",
|
||||
filterFailFieldAll: "全部失败项",
|
||||
diffCount: "失败 {{n}}",
|
||||
passRate: "通过率",
|
||||
passOk: "前后一致",
|
||||
passWarn: "存在差异",
|
||||
runOverview: "本次总览",
|
||||
sheetCurrent: "当前表",
|
||||
diffCountLabel: "差异条目",
|
||||
diffRate: "差异率",
|
||||
passOk: "全部通过",
|
||||
passWarn: "存在失败",
|
||||
runOverview: "总裁决",
|
||||
sheetCurrent: "当前检查项",
|
||||
diffCountLabel: "失败条目",
|
||||
diffRate: "失败率",
|
||||
beforeCount: "操作前行数",
|
||||
afterCount: "操作后行数",
|
||||
matchedRows: "对齐条目",
|
||||
totalRows: "合计条目",
|
||||
sheetCount: "监控表",
|
||||
sheetNavTitle: "对比项",
|
||||
sheetNavTitle: "检查项",
|
||||
sheetNavHint: "共 {{n}} 项 · 点击切换",
|
||||
sheetFailCount: "{{n}} 项有差异",
|
||||
topChangedFields: "变更字段 Top",
|
||||
sheetFailCount: "{{n}} 项失败",
|
||||
topChangedFields: "失败字段 Top",
|
||||
exportTables: "下载表格",
|
||||
exportOk: "已导出比对表格",
|
||||
fullscreen: "大屏全屏",
|
||||
|
|
@ -357,9 +411,10 @@ const zh = {
|
|||
colName: "名称",
|
||||
colMode: "模式",
|
||||
colStatus: "状态",
|
||||
colKind: "类型",
|
||||
colKind: "结论",
|
||||
colFailFields: "失败项",
|
||||
colActions: "操作",
|
||||
colKey: "Key",
|
||||
colKey: "身份",
|
||||
colChange: "变更",
|
||||
},
|
||||
portTraffic: {
|
||||
|
|
|
|||
|
|
@ -10206,12 +10206,12 @@ html.login-page--paused .login-page__flare {
|
|||
.bs-cmp-nav__meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 1px;
|
||||
align-items: flex-start;
|
||||
gap: 2px;
|
||||
font-size: 10px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
color: var(--bs-cmp-muted);
|
||||
line-height: 1.15;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
.bs-cmp-nav__meta em {
|
||||
|
|
@ -10615,9 +10615,53 @@ html.login-page--paused .login-page__flare {
|
|||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bs-cmp-overview__verdict {
|
||||
font-size: 1.05rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.bs-cmp-overview.is-ok .bs-cmp-overview__verdict {
|
||||
color: #4ade80;
|
||||
}
|
||||
.bs-cmp-overview.is-warn .bs-cmp-overview__verdict {
|
||||
color: #f87171;
|
||||
}
|
||||
.bs-cmp-nav__fail {
|
||||
color: #f87171 !important;
|
||||
font-style: normal;
|
||||
}
|
||||
.bs-cmp-nav__pass {
|
||||
color: #4ade80 !important;
|
||||
font-style: normal;
|
||||
}
|
||||
.bs-cmp-badge--fail {
|
||||
background: rgba(248, 113, 113, 0.2);
|
||||
color: #fecaca;
|
||||
}
|
||||
.bs-cmp-badge--pass {
|
||||
background: rgba(74, 222, 128, 0.15);
|
||||
color: #bbf7d0;
|
||||
}
|
||||
.bs-cmp-col-kind {
|
||||
width: 72px;
|
||||
white-space: nowrap;
|
||||
width: 7.5rem;
|
||||
white-space: normal;
|
||||
}
|
||||
.bs-cmp-col-kind .bs-cmp-badge {
|
||||
display: inline-block;
|
||||
margin: 0 4px 4px 0;
|
||||
}
|
||||
.bs-cmp-col-fail,
|
||||
.bs-cmp-fail-cell {
|
||||
max-width: 11rem;
|
||||
font-size: 0.85em;
|
||||
color: var(--bs-cmp-muted, #94a3b8);
|
||||
}
|
||||
.bs-cmp-val-reason {
|
||||
font-size: 0.75em;
|
||||
margin-top: 2px;
|
||||
}
|
||||
.bs-cmp-strip__kind--diff b {
|
||||
color: #f87171;
|
||||
}
|
||||
|
||||
.bs-cmp-badge {
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1854,6 +1854,11 @@ export const bizCompareListMetrics = () =>
|
|||
role: string;
|
||||
}>;
|
||||
}>;
|
||||
row_filter_presets?: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
row_filters: Array<Record<string, unknown>>;
|
||||
}>;
|
||||
}>("/v1/biz-state/compare/metrics");
|
||||
|
||||
export const bizCompareCreateTemplate = (body: Record<string, unknown>) =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue