diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 7c13f59..4d09b69 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -335,7 +335,9 @@ const en = { policyTitle: "Collect policy", policySaved: "Policy saved", enabled: "Enable scheduled collect", - autoAddUnmatched: "Create SSH placeholder NEs for unmatched neighbors", + autoAddUnmatched: "Auto-add unmatched neighbors as placeholders on discover", + autoAddUnmatchedHint: + "Shared by scheduled collect and canvas discover. Off = record unmatched only, no placeholder NE. Default on.", intervalDays: "Interval (days)", interval: "Collect interval", intervalUnitDays: "days", @@ -1444,7 +1446,7 @@ const en = { alignNeedSelection: "Select at least two nodes", snapGrid: "Snap to grid", autoLayoutDiscover: "Auto-layout after discover", - discoverAutoAddUnmatched: "Auto-add unmatched NEs to fabric on discover", + discoverAutoAddUnmatched: "Auto-add unmatched NEs as placeholders on discover", discoverProjectNeighbors: "Project direct neighbors onto canvas after discover", undo: "Undo", redo: "Redo", diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 8975414..eb453c2 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -332,7 +332,8 @@ const zh = { policyTitle: "采集策略", policySaved: "策略已保存", enabled: "启用周期调度", - autoAddUnmatched: "未纳管邻居建 SSH 占位网元", + autoAddUnmatched: "发现时自动入库未匹配网元(占位)", + autoAddUnmatchedHint: "周期采集与画布单点发现共用;关闭则未匹配邻居只记未匹配、不建占位网元。默认开启。", intervalDays: "周期(天)", interval: "采集周期", intervalUnitDays: "天", @@ -1437,7 +1438,7 @@ const zh = { alignNeedSelection: "请至少选中两个节点", snapGrid: "网格吸附", autoLayoutDiscover: "发现后自动布局", - discoverAutoAddUnmatched: "发现时自动入库未匹配网元", + discoverAutoAddUnmatched: "发现时自动入库未匹配网元(占位)", discoverProjectNeighbors: "发现后将直连邻居加入画布", undo: "撤销", redo: "重做", diff --git a/web/src/pages/TopologyPage.tsx b/web/src/pages/TopologyPage.tsx index 6679c07..6b3a7a9 100644 --- a/web/src/pages/TopologyPage.tsx +++ b/web/src/pages/TopologyPage.tsx @@ -40,6 +40,7 @@ import { createTopologyView, deleteTopologyFolder, deleteTopologyMap, + fetchLldpCollectDashboard, fetchLldpDiscoverJob, fetchManagedNe, fetchManagedNeById, @@ -52,6 +53,7 @@ import { removeTopologyViewNodes, searchFabricNodes, startLldpDiscover, + updateLldpCollectPolicy, updateTopologyFolder, updateTopologyMap, } from "../services/api"; @@ -458,8 +460,8 @@ type EdgeDefaults = Record; const EDGE_DEFAULTS_KEY = "netx.topology.edgeDefaults"; const AUTO_LAYOUT_DISCOVER_KEY = "netx.topology.autoLayoutAfterDiscover"; -const DISCOVER_AUTO_ADD_KEY = "netx.topology.discoverAutoAddUnmatched"; -const DISCOVER_PROJECT_NEIGHBORS_KEY = "netx.topology.discoverProjectNeighbors"; +const DISCOVER_AUTO_ADD_KEY = "netx.topology.discoverAutoAddUnmatched.v2"; +const DISCOVER_PROJECT_NEIGHBORS_KEY = "netx.topology.discoverProjectNeighbors.v2"; const SCALE_BUNDLE_WIDTH_KEY = "netx.topology.scaleBundleWidth"; const CANVAS_BG_KEY = "netx.topology.canvasBg"; const DEFAULT_CANVAS_BG = "#0f172a"; @@ -845,10 +847,10 @@ export function TopologyPage() { const [snapToGrid, setSnapToGrid] = useState(true); const [autoLayoutAfterDiscover, setAutoLayoutAfterDiscover] = useState(loadAutoLayoutAfterDiscover); const [discoverAutoAddUnmatched, setDiscoverAutoAddUnmatched] = useState(() => - loadBoolFlag(DISCOVER_AUTO_ADD_KEY, false), + loadBoolFlag(DISCOVER_AUTO_ADD_KEY, true), ); const [discoverProjectNeighbors, setDiscoverProjectNeighbors] = useState(() => - loadBoolFlag(DISCOVER_PROJECT_NEIGHBORS_KEY, false), + loadBoolFlag(DISCOVER_PROJECT_NEIGHBORS_KEY, true), ); const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const [addNeOpen, setAddNeOpen] = useState(false); @@ -1049,6 +1051,24 @@ export function TopologyPage() { } }, [mapId]); + // Keep canvas "auto placeholder" in sync with topology LLDP collect policy (shared strategy). + useEffect(() => { + let alive = true; + void fetchLldpCollectDashboard() + .then((dash) => { + if (!alive || !dash?.policy) return; + const next = Boolean(dash.policy.auto_add_unmatched); + setDiscoverAutoAddUnmatched(next); + persistBoolFlag(DISCOVER_AUTO_ADD_KEY, next); + }) + .catch(() => { + /* keep local default */ + }); + return () => { + alive = false; + }; + }, []); + const neQuery = useQuery({ queryKey: [...queryKeys.managedNeAll, "topology-add-ne", debouncedKeyword], queryFn: () => @@ -3097,6 +3117,15 @@ export function TopologyPage() { const next = e.target.checked; setDiscoverAutoAddUnmatched(next); persistBoolFlag(DISCOVER_AUTO_ADD_KEY, next); + void updateLldpCollectPolicy({ auto_add_unmatched: next }) + .then(() => { + void queryClient.invalidateQueries({ + queryKey: queryKeys.lldpCollectDashboard, + }); + }) + .catch(() => { + /* local toggle still applies to this canvas discover */ + }); }} /> {t("topology.discoverAutoAddUnmatched")} diff --git a/web/src/pages/network/LldpLinksPage.tsx b/web/src/pages/network/LldpLinksPage.tsx index 56eb072..1781a6d 100644 --- a/web/src/pages/network/LldpLinksPage.tsx +++ b/web/src/pages/network/LldpLinksPage.tsx @@ -338,6 +338,9 @@ export function LldpLinksPage() { setAutoAdd(e.target.checked)} /> {t("lldpLinks.autoAddUnmatched")} +

+ {t("lldpLinks.autoAddUnmatchedHint")} +