mirror of
https://github.com/hansjone/netx.git
synced 2026-10-09 03:10:46 +08:00
Require vendor when completing unmanaged NEs from topology.
Persist vendor on create/update and refresh the canvas node so icon tone matches inventory. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
57f45fdaaf
commit
bee86eea0d
3 changed files with 56 additions and 4 deletions
|
|
@ -1398,8 +1398,9 @@ const en = {
|
|||
termConnect: {
|
||||
title: "Complete login",
|
||||
titleCreate: "Create NE and connect",
|
||||
hint: "This node is not managed or lacks credentials. Enter IP and protocol to connect (same as creating/completing an NE).",
|
||||
hint: "This node is not managed or lacks credentials. Enter IP, vendor, and protocol to connect (same as creating/completing an NE).",
|
||||
name: "Name",
|
||||
vendor: "Vendor",
|
||||
ip: "IP address",
|
||||
port: "Port",
|
||||
protocol: "Protocol",
|
||||
|
|
|
|||
|
|
@ -1392,8 +1392,9 @@ const zh = {
|
|||
termConnect: {
|
||||
title: "补全登录信息",
|
||||
titleCreate: "新建网元并登录",
|
||||
hint: "该网元尚未纳管或缺少登录信息,请填写 IP 与协议后连接(等同新建/补全网元)。",
|
||||
hint: "该网元尚未纳管或缺少登录信息,请填写 IP、厂家与协议后连接(等同新建/补全网元)。",
|
||||
name: "名称",
|
||||
vendor: "厂家",
|
||||
ip: "IP 地址",
|
||||
port: "端口",
|
||||
protocol: "协议",
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ import {
|
|||
fetchLldpDiscoverJob,
|
||||
fetchManagedNe,
|
||||
fetchManagedNeById,
|
||||
fetchManagedNeMeta,
|
||||
fetchTopologyGraph,
|
||||
fetchTopologyTree,
|
||||
fetchUmeNe,
|
||||
|
|
@ -298,6 +299,7 @@ type CtxMenu =
|
|||
type TermConnectDialog = {
|
||||
neId: string;
|
||||
name: string;
|
||||
vendor: string;
|
||||
ip_address: string;
|
||||
port: number;
|
||||
protocol: "ssh" | "telnet";
|
||||
|
|
@ -836,6 +838,16 @@ export function TopologyPage() {
|
|||
});
|
||||
}, [edges, expandPhysicalLinks, hidePorts, edgeFlow, edgeDefaults, selectedEdgeId]);
|
||||
|
||||
const neMetaQuery = useQuery({
|
||||
queryKey: queryKeys.managedNeMeta,
|
||||
queryFn: fetchManagedNeMeta,
|
||||
enabled: Boolean(termDialog),
|
||||
staleTime: 60_000,
|
||||
});
|
||||
const termVendors = neMetaQuery.data?.vendors?.length
|
||||
? neMetaQuery.data.vendors
|
||||
: ["ZTE", "Huawei", "Cisco", "H3C", "Juniper", "Nokia", "Other"];
|
||||
|
||||
const treeQuery = useQuery({
|
||||
queryKey: queryKeys.topologyTree,
|
||||
queryFn: fetchTopologyTree,
|
||||
|
|
@ -1893,6 +1905,7 @@ export function TopologyPage() {
|
|||
setTermDialog({
|
||||
neId: managedId,
|
||||
name: String(ne.name || node.data.label || "").trim(),
|
||||
vendor: String(ne.vendor || node.data.vendor || "Other").trim() || "Other",
|
||||
ip_address: hintIp,
|
||||
port: Number(ne.port) || (proto === "telnet" ? 23 : 22),
|
||||
protocol: proto,
|
||||
|
|
@ -1918,6 +1931,7 @@ export function TopologyPage() {
|
|||
setTermDialog({
|
||||
neId: "",
|
||||
name: String(node.data.label || "").trim(),
|
||||
vendor: String(node.data.vendor || "Other").trim() || "Other",
|
||||
ip_address: String(node.data.ne_ip || "").trim(),
|
||||
port: 22,
|
||||
protocol: "ssh",
|
||||
|
|
@ -1945,12 +1959,14 @@ export function TopologyPage() {
|
|||
}
|
||||
const port = Number(termDialog.port) || (protocol === "telnet" ? 23 : 22);
|
||||
const name = termDialog.name.trim() || ip;
|
||||
const vendor = String(termDialog.vendor || "Other").trim() || "Other";
|
||||
setTermBusy(true);
|
||||
try {
|
||||
let neId = termDialog.neId;
|
||||
if (neId) {
|
||||
await updateManagedNe(neId, {
|
||||
name,
|
||||
vendor,
|
||||
ip_address: ip,
|
||||
port,
|
||||
protocol,
|
||||
|
|
@ -1960,14 +1976,22 @@ export function TopologyPage() {
|
|||
setNodes((ns) =>
|
||||
ns.map((n) =>
|
||||
String(n.data.managed_ne_id || "") === neId
|
||||
? { ...n, data: { ...n.data, ne_ip: ip, label: name || n.data.label } }
|
||||
? {
|
||||
...n,
|
||||
data: {
|
||||
...n.data,
|
||||
ne_ip: ip,
|
||||
vendor,
|
||||
label: name || n.data.label,
|
||||
},
|
||||
}
|
||||
: n,
|
||||
),
|
||||
);
|
||||
} else {
|
||||
const created = await createManagedNe({
|
||||
name,
|
||||
vendor: "Other",
|
||||
vendor,
|
||||
device_type: "generic",
|
||||
ip_address: ip,
|
||||
port,
|
||||
|
|
@ -1979,6 +2003,7 @@ export function TopologyPage() {
|
|||
}
|
||||
setTermDialog(null);
|
||||
showOk(t("topology.termConnect.saved"));
|
||||
void queryClient.invalidateQueries({ queryKey: queryKeys.managedNeAll });
|
||||
openOrFocusModule({
|
||||
moduleId: "webcrt",
|
||||
path: `/webcrt?ne_id=${encodeURIComponent(neId)}`,
|
||||
|
|
@ -3909,6 +3934,31 @@ export function TopologyPage() {
|
|||
onChange={(e) => setTermDialog({ ...termDialog, name: e.target.value })}
|
||||
/>
|
||||
</label>
|
||||
<label>
|
||||
<span className="form-label">
|
||||
{t("topology.termConnect.vendor")}
|
||||
<span className="form-label__required" aria-hidden="true">
|
||||
{" "}
|
||||
*
|
||||
</span>
|
||||
</span>
|
||||
<select
|
||||
value={
|
||||
termVendors.includes(termDialog.vendor) ? termDialog.vendor : termDialog.vendor || "Other"
|
||||
}
|
||||
disabled={termBusy}
|
||||
onChange={(e) => setTermDialog({ ...termDialog, vendor: e.target.value })}
|
||||
>
|
||||
{!termVendors.includes(termDialog.vendor) && termDialog.vendor ? (
|
||||
<option value={termDialog.vendor}>{termDialog.vendor}</option>
|
||||
) : null}
|
||||
{termVendors.map((v) => (
|
||||
<option key={v} value={v}>
|
||||
{v}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
<span className="form-label">
|
||||
{t("topology.termConnect.protocol")}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue