Defer canvas edge deletes to Save and cascade orphan Fabric on NE detach.

Queue fabric edge deletes until Save with undo-safe pending state; hard-delete topology/LLDP placeholders from the canvas; purge fully orphaned fabric nodes after inventory detach, with a periodic reconcile GC.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-05 22:43:03 +08:00
parent 2aafb72676
commit 6e2c8517cf
16 changed files with 732 additions and 166 deletions

View file

@ -718,10 +718,12 @@ const en = {
edit: "Edit",
delete: "Delete",
openTerminal: "Terminal",
confirmDelete: "Delete this network element?",
confirmDelete:
"Delete this network element? If its Fabric node is managed-only (no UME binding), that node and its direct links will also be removed.",
batchDelete: "Batch delete",
batchDeleting: "Deleting…",
batchDeleteConfirm: "Delete {{n}} selected network element(s)? This cannot be undone.",
batchDeleteConfirm:
"Delete {{n}} selected network element(s)? Cannot be undone. Managed-only Fabric nodes cascade (node + direct links); nodes still bound to UME keep topology.",
batchDeleteDone: "Deleted {{n}} network element(s)",
batchDeleteSelectRequired: "Select network elements to delete",
keywordPh: "name / IP / user / tags",
@ -784,7 +786,8 @@ const en = {
syncing: "Syncing…",
delete: "Delete UME NEs",
deleting: "Deleting…",
deleteConfirm: "Delete all managed NEs created from UME sync? This cannot be undone.",
deleteConfirm:
"Delete all managed NEs created from UME sync? Cannot be undone. Fabric nodes that become fully orphaned (no UME left) are removed with their direct links.",
done: "UME sync done: {{inserted}} inserted, {{updated}} updated, {{deleted}} deleted, {{total}} inventory total",
deletedDone: "Deleted {{n}} UME-synced NEs",
},
@ -1409,8 +1412,8 @@ const en = {
projectedNeighbors: "Projected {{count}} neighbor(s) onto the map",
localBrowserOnly: "this browser only",
deleteEdgeConfirm:
"Delete selected link(s)? This removes them from Fabric globally; LLDP may recreate them on the next collect.",
edgeDeleted: "Deleted {{count}} link(s)",
"Remove selected link(s) from the canvas? They are deleted from Fabric only when you Save. LLDP may recreate them on the next collect.",
edgeDeleted: "Queued {{count}} link delete(s) — Save to apply",
discoverCancelled: "Discovery cancelled",
truncatedMembership: "Membership cap reached; some neighbors were not placed. Use classify/slices or a new map.",
truncatedFrozen: "This map membership is frozen; neighbors cannot be projected. Unfreeze in classify/slices or create a new map.",
@ -1418,8 +1421,8 @@ const en = {
truncatedEdges: "Too many edges; display truncated. Filter status or create another map.",
truncatedGeneric: "Graph data truncated. Narrow scope or create another map.",
deleteSelectionConfirm:
"Remove {{nodes}} NE(s) from this map and delete {{edges}} Fabric link(s)?",
selectionDeleted: "Removed {{nodes}} NE(s) and deleted {{edges}} link(s)",
"Remove {{nodes}} NE(s) from this map and {{edges}} link(s)? Link deletes apply on Save; NE remove from map applies on Save.",
selectionDeleted: "Queued: remove {{nodes}} NE(s), delete {{edges}} link(s) — Save to apply",
newMap: "New",
newMapName: "New topology",
rename: "Rename",
@ -1498,6 +1501,10 @@ const en = {
openWebcrt: "Open terminal",
openNe: "NE details",
removeNode: "Remove from canvas",
deletePlaceholder: "Delete placeholder",
deletePlaceholderConfirm:
"Permanently delete this placeholder NE (inventory + Fabric links)? This cannot be undone with canvas Undo.",
deletePlaceholderDone: "Placeholder deleted",
removeEdge: "Delete edge",
nodeMenu: "Node",
paneMenu: "Canvas",
@ -1610,8 +1617,9 @@ const en = {
locateOnCanvas: "Click to locate on canvas",
openPortTraffic: "Open port traffic",
removeStale: "Clear missing ({{count}})",
removeStaleHint: "Manually delete red missing edges; scheduled collect also purges after 4 consecutive misses",
staleRemoved: "Removed {{count}} missing edge(s)",
removeStaleHint:
"Queue delete of red missing edges (applies on Save); scheduled collect also purges after 4 consecutive misses",
staleRemoved: "Queued {{count}} missing edge delete(s) — Save to apply",
connectHint: "Connect mode: click source node, then target (Esc or Select to exit)",
},
topoClassify: {

View file

@ -712,10 +712,12 @@ const zh = {
edit: "编辑",
delete: "删除",
openTerminal: "终端",
confirmDelete: "确定删除该网元?",
confirmDelete:
"确定删除该网元?若拓扑中仅绑定此运维网元(无 UME),将同时删除对应 Fabric 节点及其直连链路。",
batchDelete: "批量删除",
batchDeleting: "删除中…",
batchDeleteConfirm: "确定删除选中的 {{n}} 台网元?此操作不可恢复。",
batchDeleteConfirm:
"确定删除选中的 {{n}} 台网元?不可恢复。仅绑定运维侧的节点会级联清除 Fabric 节点与直连链路;仍绑 UME 的会保留拓扑。",
batchDeleteDone: "已删除 {{n}} 台网元",
batchDeleteSelectRequired: "请先勾选要删除的网元",
keywordPh: "名称 / IP / 用户名 / 标签",
@ -777,7 +779,8 @@ const zh = {
syncing: "同步中…",
delete: "删除 UME 网元",
deleting: "删除中…",
deleteConfirm: "确定删除所有通过 UME 同步到网元管理中的网元?此操作不可恢复。",
deleteConfirm:
"确定删除所有通过 UME 同步到网元管理中的网元?不可恢复。仅绑定这些 ManagedNE、且无 UME 投影的 Fabric 节点会连同直连链路一并清除。",
done: "UME 同步完成:新增 {{inserted}},更新 {{updated}},删除 {{deleted}},UME 清单共 {{total}} 台",
deletedDone: "已删除 {{n}} 台 UME 同步网元",
},
@ -1400,16 +1403,17 @@ const zh = {
projectNeighbors: "投影邻居上图",
projectedNeighbors: "已投影 {{count}} 个邻居上图",
localBrowserOnly: "仅本浏览器",
deleteEdgeConfirm: "删除选中链路?将从 Fabric 全局移除;LLDP 下次采集若仍存在可能再次出现。",
edgeDeleted: "已删除 {{count}} 条链路",
deleteEdgeConfirm: "从画布移除选中链路?仅在点击「保存」后才会从 Fabric 删除;LLDP 下次采集若仍存在可能再次出现。",
edgeDeleted: "已标记删除 {{count}} 条链路(保存后生效)",
discoverCancelled: "发现已取消",
truncatedMembership: "视图已达成员上限,部分邻居未上图。可去分类/切片或新建视图。",
truncatedFrozen: "当前视图已冻结成员,无法投影邻居。请在分类/切片中解冻或新建视图。",
truncatedNodes: "画布节点过多已截断显示。建议新建视图或收紧成员范围。",
truncatedEdges: "画布链路过多已截断显示。建议筛选状态或新建视图。",
truncatedGeneric: "图数据已截断显示。建议收紧范围或新建视图。",
deleteSelectionConfirm: "删除选中的 {{nodes}} 个网元(移出画布)和 {{edges}} 条链路(从 Fabric 删除)?",
selectionDeleted: "已移除 {{nodes}} 个网元、删除 {{edges}} 条链路",
deleteSelectionConfirm:
"从本图移除 {{nodes}} 个网元、删除 {{edges}} 条链路?移出与链路删除均在点击「保存」后生效。",
selectionDeleted: "已标记:移除 {{nodes}} 个网元、删除 {{edges}} 条链路(保存后生效)",
newMap: "新建",
newMapName: "新拓扑图",
rename: "重命名",
@ -1488,6 +1492,9 @@ const zh = {
openWebcrt: "打开终端",
openNe: "网元详情",
removeNode: "从画布移除",
deletePlaceholder: "删除占位网元",
deletePlaceholderConfirm: "永久删除此占位网元(含库存记录与 Fabric 直连链路)?画布撤销无法恢复。",
deletePlaceholderDone: "占位网元已删除",
removeEdge: "删除链路",
nodeMenu: "网元",
paneMenu: "画布",
@ -1598,8 +1605,8 @@ const zh = {
locateOnCanvas: "点击定位到画布",
openPortTraffic: "打开端口流量",
removeStale: "清除未发现 ({{count}})",
removeStaleHint: "手动删除红色未发现链路;周期采集连续 4 次未发现也会自动清理",
staleRemoved: "已删除 {{count}} 条未发现链路",
removeStaleHint: "标记删除红色未发现链路(保存后生效);周期采集连续 4 次未发现也会自动清理",
staleRemoved: "已标记删除 {{count}} 条未发现链路(保存后生效)",
connectHint: "连线模式:依次点击源节点 → 目标节点(Esc 或切回选择退出)",
},
topoClassify: {