From 86bf1dd9b382486ed360e8804a4c44fc14c6b147 Mon Sep 17 00:00:00 2001 From: zxr <271055687@qq.com> Date: Thu, 9 Apr 2026 13:52:10 +0800 Subject: [PATCH] fix --- src/api/ops/discovery.ts | 91 +++ src/api/ops/ipScan.ts | 58 ++ src/api/ops/netarchTopo.ts | 11 + src/router/local-menu-flat.ts | 44 +- src/router/local-menu-items.ts | 46 +- .../ops/pages/netarch/auto-topology/index.vue | 632 ++++++++++++------ .../ops/pages/netarch/ip-scan-tasks/index.vue | 387 +++++++++++ .../topo/components/NodeDetailDialog.vue | 9 + .../pages/netarch/topo/components/Toolbar.vue | 11 + src/views/ops/pages/netarch/topo/index.vue | 59 ++ src/views/ops/pages/netarch/topo/types.ts | 4 + 11 files changed, 1128 insertions(+), 224 deletions(-) create mode 100644 src/api/ops/discovery.ts create mode 100644 src/api/ops/ipScan.ts create mode 100644 src/views/ops/pages/netarch/ip-scan-tasks/index.vue diff --git a/src/api/ops/discovery.ts b/src/api/ops/discovery.ts new file mode 100644 index 0000000..0bbf529 --- /dev/null +++ b/src/api/ops/discovery.ts @@ -0,0 +1,91 @@ +import { request } from '@/api/request' + +/** 与后端 discovery/summary 对齐 */ +export interface DiscoverySummary { + device_total_current: number + online_current: number + offline_current: number + other_status_current: number + primary_scan_ids: number[] + last_run?: DiscoveryScanRun | null +} + +export interface DiscoveryScanRun { + id: number + scan_id: number + run_type: string + status: string + started_at: string + finished_at?: string | null + device_total: number + online_count: number + offline_count: number + new_count: number + error_message?: string + trigger_by: string +} + +export interface DiscoveryDeviceRow { + id: number + ip_address: string + scan_id?: number + run_id?: number + status: string + mac_address?: string + hostname?: string + device_type?: string + manufacturer?: string + discovery_methods?: string + last_seen_time?: string + last_scan_time?: string + first_seen_time?: string +} + +export interface DiscoveryDevicesResponse { + scope: string + scan_id?: number + primary_scan_ids?: number[] + total: number + page: number + page_size: number + data: DiscoveryDeviceRow[] +} + +export interface DiscoveryScanRunsResponse { + total: number + page: number + page_size: number + data: DiscoveryScanRun[] +} + +export interface DiscoveryConfig { + id: number + primary_scan_ids: number[] + primary_scan_ids_raw?: string +} + +export const fetchDiscoverySummary = () => + request.get<{ code: number; details?: DiscoverySummary }>('/DC-Control/v1/discovery/summary') + +export const fetchDiscoveryDevices = (params?: { + scope?: 'current' | 'raw' + scan_id?: number + page?: number + size?: number + status?: string + keyword?: string +}) => + request.get<{ code: number; details?: DiscoveryDevicesResponse }>('/DC-Control/v1/discovery/devices', { + params, + }) + +export const fetchDiscoveryScanRuns = (params?: { scan_id?: number; page?: number; size?: number }) => + request.get<{ code: number; details?: DiscoveryScanRunsResponse }>('/DC-Control/v1/discovery/scan-runs', { + params, + }) + +export const fetchDiscoveryConfig = () => + request.get<{ code: number; details?: DiscoveryConfig }>('/DC-Control/v1/discovery/config') + +export const updateDiscoveryConfig = (data: { primary_scan_ids: number[] }) => + request.put<{ code: number; details?: DiscoveryConfig }>('/DC-Control/v1/discovery/config', data) diff --git a/src/api/ops/ipScan.ts b/src/api/ops/ipScan.ts new file mode 100644 index 0000000..80ca156 --- /dev/null +++ b/src/api/ops/ipScan.ts @@ -0,0 +1,58 @@ +import { request } from '@/api/request' + +/** IP 扫描任务(与 dc-control ControlIpScan 对齐) */ +export interface IpScanTask { + id: number + name: string + type: string + description?: string + target_range: string + port_range?: string + config?: string + timeout?: number + concurrency?: number + cron_expr?: string + status?: string + server_id: number + last_scan_time?: string + next_scan_time?: string + last_scan_status?: string + total_count?: number + online_count?: number + scan_count?: number + enable?: boolean + created_at?: string + updated_at?: string +} + +export interface IpScanListResponse { + total: number + page: number + page_size: number + data: IpScanTask[] +} + +/** 扫描任务分页列表 */ +export const fetchIpScanList = (params?: { page?: number; size?: number; keyword?: string }) => + request.get<{ code: number; details?: IpScanListResponse; message?: string }>('/DC-Control/v1/ipscans', { + params, + }) + +export const fetchIpScanDetail = (id: number) => + request.get<{ code: number; details?: IpScanTask; message?: string }>(`/DC-Control/v1/ipscans/${id}`) + +/** 触发一次扫描(会创建 scan_run 并调用 Agent) */ +export const triggerIpScan = (id: number) => + request.post<{ code: number; message?: string }>(`/DC-Control/v1/ipscans/${id}/trigger`) + +/** 创建扫描任务 */ +export const createIpScan = (data: Partial) => + request.post<{ code: number; details?: IpScanTask; message?: string }>('/DC-Control/v1/ipscans', data) + +/** 更新扫描任务 */ +export const updateIpScan = (id: number, data: Partial) => + request.put<{ code: number; message?: string }>(`/DC-Control/v1/ipscans/${id}`, data) + +/** 删除扫描任务 */ +export const deleteIpScan = (id: number) => + request.delete<{ code: number; message?: string }>(`/DC-Control/v1/ipscans/${id}`) diff --git a/src/api/ops/netarchTopo.ts b/src/api/ops/netarchTopo.ts index 3b48e6b..e3ff2d4 100644 --- a/src/api/ops/netarchTopo.ts +++ b/src/api/ops/netarchTopo.ts @@ -75,6 +75,10 @@ export interface TopologyNode { parentId?: string | null level?: number position?: { x: number; y: number } + /** 设计 v2.0:绑定资产 / 子拓扑 */ + ref_type?: string + ref_id?: number + sub_topology_id?: number | null created_at?: string updated_at?: string } @@ -155,6 +159,13 @@ export const deleteNode = (topologyId: number, nodeId: string) => export const updateNodesPositions = (topologyId: number, positions: Array<{ id: string; position: { x: number; y: number } }>) => request.put(`/DC-Control/v1/topologies/${topologyId}/nodes/positions`, { positions }) +/** 按资产 ID 批量导入节点(后端生成 node_id=asset-{id} 与 ref_type=asset) */ +export const batchImportAssetNodes = (topologyId: number, asset_ids: number[]) => + request.post<{ code: number; details?: { message: string; imported: number } }>( + `/DC-Control/v1/topologies/${topologyId}/nodes/batch-import`, + { asset_ids }, + ) + // ==================== 链路管理接口 ==================== /** 获取链路列表 */ diff --git a/src/router/local-menu-flat.ts b/src/router/local-menu-flat.ts index 7a0280a..d1b87d5 100644 --- a/src/router/local-menu-flat.ts +++ b/src/router/local-menu-flat.ts @@ -409,10 +409,26 @@ export const localMenuFlatItems: MenuItem[] = [ app_id: 2, parent_id: 35, menu_path: '/netarch/auto-topo', + component: 'ops/pages/netarch/auto-topology', type: 1, sort_key: 22, created_at: '2026-01-05T22:31:45.684645+08:00', }, + { + id: 12040, + identity: '019c8000-0001-7000-8000-000000000040', + title: 'IP 扫描任务', + title_en: 'IP Scan Tasks', + code: 'ops:网络架构管理:ip扫描任务', + description: '创建与管理 IP 发现扫描任务', + app_id: 2, + parent_id: 35, + menu_path: '/netarch/ip-scan-tasks', + component: 'ops/pages/netarch/ip-scan-tasks', + type: 1, + sort_key: 23, + created_at: '2026-04-09T10:00:00+08:00', + }, { id: 36, identity: '019b591d-0231-7667-a9fc-cfeb05da5aab', @@ -424,10 +440,27 @@ export const localMenuFlatItems: MenuItem[] = [ parent_id: 35, menu_path: '/netarch/topo-group', menu_icon: 'appstore', + component: 'ops/pages/netarch/topo-group', type: 1, - sort_key: 23, + sort_key: 24, created_at: '2025-12-26T13:23:51.985419+08:00', }, + { + id: 12041, + identity: '019c8000-0001-7000-8000-000000000041', + title: '拓扑画布', + title_en: 'Topology Canvas', + code: 'ops:网络架构管理:拓扑画布', + description: '编辑网络拓扑(可带 ?id= 打开指定拓扑)', + app_id: 2, + parent_id: 35, + menu_path: '/netarch/topo', + menu_icon: 'appstore', + component: 'ops/pages/netarch/topo', + type: 1, + sort_key: 25, + created_at: '2026-04-09T10:00:00+08:00', + }, { id: 37, identity: '019b591d-0240-7d6d-90b8-a0a6303665dc', @@ -439,8 +472,9 @@ export const localMenuFlatItems: MenuItem[] = [ parent_id: 35, menu_path: '/netarch/traffic', menu_icon: 'appstore', + component: 'ops/pages/netarch/traffic', type: 1, - sort_key: 24, + sort_key: 26, created_at: '2025-12-26T13:23:52.000879+08:00', }, { @@ -454,8 +488,9 @@ export const localMenuFlatItems: MenuItem[] = [ parent_id: 35, menu_path: '/netarch/ip', menu_icon: 'appstore', + component: 'ops/pages/netarch/ip', type: 1, - sort_key: 25, + sort_key: 27, created_at: '2025-12-26T13:23:52.012353+08:00', }, { @@ -724,6 +759,7 @@ export const localMenuFlatItems: MenuItem[] = [ parent_id: 54, menu_path: '/assets/classify', menu_icon: 'appstore', + component: 'ops/pages/assets/classify', type: 1, sort_key: 41, created_at: '2025-12-26T13:23:52.299831+08:00', @@ -739,6 +775,7 @@ export const localMenuFlatItems: MenuItem[] = [ parent_id: 54, menu_path: '/assets/device', menu_icon: 'appstore', + component: 'ops/pages/assets/device/list', type: 1, sort_key: 42, created_at: '2025-12-26T13:23:52.315149+08:00', @@ -754,6 +791,7 @@ export const localMenuFlatItems: MenuItem[] = [ parent_id: 54, menu_path: '/assets/supplier', menu_icon: 'appstore', + component: 'ops/pages/assets/supplier/list', type: 1, sort_key: 43, created_at: '2025-12-26T13:23:52.283421+08:00', diff --git a/src/router/local-menu-items.ts b/src/router/local-menu-items.ts index 73f600b..5ac42bf 100644 --- a/src/router/local-menu-items.ts +++ b/src/router/local-menu-items.ts @@ -440,11 +440,28 @@ export const localMenuItems: MenuItem[] = [ app_id: 2, parent_id: 35, menu_path: '/netarch/auto-topo', + component: 'ops/pages/netarch/auto-topology', type: 1, sort_key: 6, created_at: '2026-01-05T22:31:45.684645+08:00', children: [], }, + { + id: 12040, + identity: '019c8000-0001-7000-8000-000000000040', + title: 'IP 扫描任务', + title_en: 'IP Scan Tasks', + code: 'ops:网络架构管理:ip扫描任务', + description: '创建与管理 IP 发现扫描任务', + app_id: 2, + parent_id: 35, + menu_path: '/netarch/ip-scan-tasks', + component: 'ops/pages/netarch/ip-scan-tasks', + type: 1, + sort_key: 7, + created_at: '2026-04-09T10:00:00+08:00', + children: [], + }, { id: 36, identity: '019b591d-0231-7667-a9fc-cfeb05da5aab', @@ -456,11 +473,29 @@ export const localMenuItems: MenuItem[] = [ parent_id: 35, menu_path: '/netarch/topo-group', menu_icon: 'appstore', + component: 'ops/pages/netarch/topo-group', type: 1, - sort_key: 6, + sort_key: 8, created_at: '2025-12-26T13:23:51.985419+08:00', children: [], }, + { + id: 12041, + identity: '019c8000-0001-7000-8000-000000000041', + title: '拓扑画布', + title_en: 'Topology Canvas', + code: 'ops:网络架构管理:拓扑画布', + description: '编辑网络拓扑(可通过「拓扑管理」进入或带 ?id= 打开指定拓扑)', + app_id: 2, + parent_id: 35, + menu_path: '/netarch/topo', + menu_icon: 'appstore', + component: 'ops/pages/netarch/topo', + type: 1, + sort_key: 9, + created_at: '2026-04-09T10:00:00+08:00', + children: [], + }, { id: 37, identity: '019b591d-0240-7d6d-90b8-a0a6303665dc', @@ -472,8 +507,9 @@ export const localMenuItems: MenuItem[] = [ parent_id: 35, menu_path: '/netarch/traffic', menu_icon: 'appstore', + component: 'ops/pages/netarch/traffic', type: 1, - sort_key: 6, + sort_key: 10, created_at: '2025-12-26T13:23:52.000879+08:00', children: [], }, @@ -488,8 +524,9 @@ export const localMenuItems: MenuItem[] = [ parent_id: 35, menu_path: '/netarch/ip', menu_icon: 'appstore', + component: 'ops/pages/netarch/ip', type: 1, - sort_key: 6, + sort_key: 11, created_at: '2025-12-26T13:23:52.012353+08:00', children: [], }, @@ -780,6 +817,7 @@ export const localMenuItems: MenuItem[] = [ parent_id: 54, menu_path: '/assets/classify', menu_icon: 'appstore', + component: 'ops/pages/assets/classify', type: 1, sort_key: 10, created_at: '2025-12-26T13:23:52.299831+08:00', @@ -796,6 +834,7 @@ export const localMenuItems: MenuItem[] = [ parent_id: 54, menu_path: '/assets/device', menu_icon: 'appstore', + component: 'ops/pages/assets/device/list', type: 1, sort_key: 10, created_at: '2025-12-26T13:23:52.315149+08:00', @@ -812,6 +851,7 @@ export const localMenuItems: MenuItem[] = [ parent_id: 54, menu_path: '/assets/supplier', menu_icon: 'appstore', + component: 'ops/pages/assets/supplier/list', type: 1, sort_key: 10, created_at: '2025-12-26T13:23:52.283421+08:00', diff --git a/src/views/ops/pages/netarch/auto-topology/index.vue b/src/views/ops/pages/netarch/auto-topology/index.vue index a6c7331..dcfa59a 100644 --- a/src/views/ops/pages/netarch/auto-topology/index.vue +++ b/src/views/ops/pages/netarch/auto-topology/index.vue @@ -1,36 +1,80 @@ @@ -179,6 +196,45 @@ const edgeActionDialogOpen = ref(false); const edgeEditDialogOpen = ref(false); const deleteEdgeDialogOpen = ref(false); +const batchImportAssetsOpen = ref(false); +const batchImportAssetIdsText = ref(''); + +const openBatchImportAssets = () => { + batchImportAssetIdsText.value = ''; + batchImportAssetsOpen.value = true; +}; + +const handleBatchImportAssetsConfirm = async () => { + const id = currentTopologyId.value; + if (!id) { + Message.warning('请先通过路由选择拓扑(?id=)'); + batchImportAssetsOpen.value = false; + return; + } + const raw = batchImportAssetIdsText.value + .split(/[\s,,;;]+/) + .map((s) => s.trim()) + .filter(Boolean); + const assetIds = [...new Set(raw.map((x) => parseInt(x, 10)).filter((n) => !Number.isNaN(n)))]; + if (assetIds.length === 0) { + Message.warning('请输入至少一个有效的资产 ID'); + return; + } + try { + const res: any = await TopoAPI.batchImportAssetNodes(id, assetIds); + if (res?.code === 0) { + Message.success(`已导入 ${res.details?.imported ?? assetIds.length} 个节点请求已提交`); + batchImportAssetsOpen.value = false; + await refreshTopology(); + } else { + Message.error(res?.message || '导入失败'); + } + } catch (e) { + console.error(e); + Message.error('导入请求失败'); + } +}; + // 布局钩子 const { applyLayout } = useTopoLayout(); @@ -269,6 +325,9 @@ const loadData = async () => { parentId: node.parentId, level: node.level ?? 0, position: node.position, + ref_type: node.ref_type, + ref_id: node.ref_id, + sub_topology_id: node.sub_topology_id, }, })); } diff --git a/src/views/ops/pages/netarch/topo/types.ts b/src/views/ops/pages/netarch/topo/types.ts index 7965f47..c85ec24 100644 --- a/src/views/ops/pages/netarch/topo/types.ts +++ b/src/views/ops/pages/netarch/topo/types.ts @@ -17,6 +17,10 @@ export interface NodeData { parentId?: string | null; // 父节点ID,null表示根节点 level?: number; // 层级(0为一级节点) position?: { x: number; y: number }; // 节点位置坐标 + /** 与 dc-control 拓扑节点 ref 一致(资产导入等) */ + ref_type?: string; + ref_id?: number; + sub_topology_id?: number | null; } // 拓扑分组类型(从节点自动生成)