Files
NetMesh/components/monitoring/deviceModels.ts
zhaolei c64e00d835 feat: 新增设备巡检模块实现完整的巡检告警功能
新增设备巡检模块核心功能,包括:
- 巡检数据存储与调度系统
- 华为/H3C交换机及Linux服务器采集器
- 设备扩展配置与告警规则管理
- 主机详情页巡检配置入口
- 产品名称自定义设置功能
- 网络设备SSH exec通道兼容处理
2026-10-09 10:37:48 +08:00

638 lines
20 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 精细 3D 设备模型工厂
* 参考 ITYUNWEI modelFactory.ts 设计
*/
import * as THREE from 'three';
export type DeviceStatus = 'normal' | 'warning' | 'critical' | 'offline';
export type DeviceType = 'server' | 'switch' | 'router' | 'firewall' | 'storage' | 'workstation';
const statusColors: Record<DeviceStatus, number> = {
normal: 0x10b981,
warning: 0xf59e0b,
critical: 0xef4444,
offline: 0x6b7280,
};
// 不同设备类型的默认发光颜色
const typeColors: Record<DeviceType, number> = {
server: 0x60a5fa, // 蓝色 - 服务器
switch: 0x22d3ee, // 青色 - 交换机
router: 0xa78bfa, // 紫色 - 路由器
firewall: 0xf87171, // 红色 - 防火墙
storage: 0xfbbf24, // 黄色 - 存储
workstation: 0x34d399, // 绿色 - 工作站
};
/** 创建设备基础材质:金属外壳 + 状态发光 */
function createBodyMaterial(status: DeviceStatus, type?: DeviceType): THREE.MeshStandardMaterial {
// 根据状态和类型决定发光颜色
let emissiveColor = statusColors[status];
if (status === 'normal' && type) {
emissiveColor = typeColors[type] || emissiveColor;
}
const intensity = status === 'normal' ? 0.5 : status === 'warning' ? 0.7 : status === 'critical' ? 1.0 : 0.2;
return new THREE.MeshStandardMaterial({
color: 0x2c3e5a,
metalness: 0.55,
roughness: 0.35,
emissive: emissiveColor,
emissiveIntensity: intensity,
side: THREE.DoubleSide,
});
}
/** LED 指示灯材质 */
function createLedMaterial(color: number, intensity = 0.8): THREE.MeshStandardMaterial {
return new THREE.MeshStandardMaterial({
color,
emissive: color,
emissiveIntensity: intensity,
metalness: 0.2,
roughness: 0.3,
side: THREE.DoubleSide,
});
}
/** 前面板塑料材质 */
function createPanelMaterial(): THREE.MeshStandardMaterial {
return new THREE.MeshStandardMaterial({
color: 0x334155,
metalness: 0.3,
roughness: 0.6,
side: THREE.DoubleSide,
});
}
/** 端口金属材质 */
function createPortMaterial(): THREE.MeshStandardMaterial {
return new THREE.MeshStandardMaterial({
color: 0x0f172a,
metalness: 0.9,
roughness: 0.2,
side: THREE.DoubleSide,
});
}
// ============================================================
// 1. 服务器 Server - 机架式服务器
// ============================================================
export function createServerModel(status: DeviceStatus, scale = 1.5): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'server');
const panelMat = createPanelMaterial();
// 主体机箱(扁平长方体)
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.6, scale * 0.5, scale * 1.0),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.62, scale * 0.46, scale * 0.04),
panelMat
);
frontPanel.position.z = scale * 0.52;
group.add(frontPanel);
// 前面板 - 硬盘槽位(4个)
for (let i = 0; i < 4; i++) {
const tray = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.3, scale * 0.35, scale * 0.02),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.5, roughness: 0.5 })
);
tray.position.set(-scale * 0.55 + i * scale * 0.37, 0, scale * 0.54);
group.add(tray);
// 硬盘槽位 LED 灯
const ledColor = i === 0 ? statusColors.normal : i === 1 ? statusColors.warning : 0x475569;
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.02, 8, 8),
createLedMaterial(ledColor, i < 2 ? 1.0 : 0.5)
);
led.position.set(-scale * 0.55 + i * scale * 0.37, scale * 0.12, scale * 0.56);
group.add(led);
}
// 服务器状态 LED 灯(右上角)
const powerLed = new THREE.Mesh(
new THREE.SphereGeometry(0.025, 10, 10),
createLedMaterial(statusColors[status], 1.2)
);
powerLed.position.set(scale * 0.68, scale * 0.18, scale * 0.56);
group.add(powerLed);
const statusLed = new THREE.Mesh(
new THREE.SphereGeometry(0.02, 10, 10),
createLedMaterial(0x22d3ee, 1.0)
);
statusLed.position.set(scale * 0.72, scale * 0.18, scale * 0.56);
group.add(statusLed);
// 顶部散热格栅
for (let i = 0; i < 6; i++) {
const fin = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.2, 0.01, 0.03),
new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 })
);
fin.position.set(0, scale * 0.26, -scale * 0.3 + i * scale * 0.12);
group.add(fin);
}
return group;
}
// ============================================================
// 2. 交换机 Switch - 扁平机架式 + 端口阵列
// ============================================================
export function createSwitchModel(status: DeviceStatus, scale = 1.8): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'switch');
const panelMat = createPanelMaterial();
const portMat = createPortMaterial();
const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 });
// 主体机箱(非常扁平)
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.8, scale * 0.28, scale * 0.8),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.82, scale * 0.24, scale * 0.03),
panelMat
);
frontPanel.position.z = scale * 0.41;
group.add(frontPanel);
// 机架耳
const leftEar = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.12, scale * 0.22, scale * 0.15),
earMat
);
leftEar.position.set(-scale * 0.97, 0, 0);
group.add(leftEar);
const rightEar = leftEar.clone();
rightEar.position.x = scale * 0.97;
group.add(rightEar);
// 端口阵列(24口,2行 x 12列)
const portSize = scale * 0.06;
const portGap = scale * 0.08;
for (let row = 0; row < 2; row++) {
for (let col = 0; col < 12; col++) {
const port = new THREE.Mesh(
new THREE.BoxGeometry(portSize, portSize, scale * 0.05),
portMat
);
const startX = -scale * 0.72 + col * portGap;
const y = row === 0 ? scale * 0.06 : -scale * 0.06;
port.position.set(startX, y, scale * 0.44);
group.add(port);
// 端口上方的小 LED(亮绿色)
const portLed = new THREE.Mesh(
new THREE.SphereGeometry(0.012, 6, 6),
createLedMaterial(0x22c55e, 0.8)
);
portLed.position.set(startX, y + scale * 0.05, scale * 0.47);
group.add(portLed);
}
}
// 品牌区域
const brandArea = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.3, scale * 0.08, 0.035),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.5, roughness: 0.5 })
);
brandArea.position.set(-scale * 0.82, scale * 0.12, scale * 0.44);
group.add(brandArea);
// 状态 LED 灯组
const ledPositions = [
{ color: statusColors[status], x: scale * 0.82, y: scale * 0.08 },
{ color: 0x22d3ee, x: scale * 0.87, y: scale * 0.08 },
{ color: 0x22c55e, x: scale * 0.82, y: -scale * 0.08 },
{ color: 0xf59e0b, x: scale * 0.87, y: -scale * 0.08 },
];
ledPositions.forEach(({ color, x, y }) => {
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.018, 8, 8),
createLedMaterial(color, 1.0)
);
led.position.set(x, y, scale * 0.44);
group.add(led);
});
return group;
}
// ============================================================
// 3. 路由器 Router - 1U 机架式 + 天线 + 端口阵列
// ============================================================
export function createRouterModel(status: DeviceStatus, scale = 1.8): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'router');
const panelMat = createPanelMaterial();
const portMat = createPortMaterial();
const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 });
// 1U 主体机箱
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.6, scale * 0.35, scale * 0.9),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.62, scale * 0.31, scale * 0.03),
panelMat
);
frontPanel.position.z = scale * 0.47;
group.add(frontPanel);
// 机架耳
const leftEar = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.1, scale * 0.28, scale * 0.12),
earMat
);
leftEar.position.set(-scale * 0.86, 0, 0);
group.add(leftEar);
const rightEar = leftEar.clone();
rightEar.position.x = scale * 0.86;
group.add(rightEar);
// 品牌区域 + 状态 LED
const brandArea = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.22, scale * 0.07, 0.035),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 })
);
brandArea.position.set(-scale * 0.65, scale * 0.1, scale * 0.49);
group.add(brandArea);
// 状态 LED 组
const routerLeds = [
{ color: statusColors[status], x: -scale * 0.38 },
{ color: 0x22d3ee, x: -scale * 0.33 },
{ color: 0x22c55e, x: -scale * 0.28 },
{ color: 0xf59e0b, x: -scale * 0.23 },
];
routerLeds.forEach(({ color, x }) => {
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.018, 8, 8),
createLedMaterial(color, 1.0)
);
led.position.set(x, scale * 0.1, scale * 0.49);
group.add(led);
});
// WAN 口(光纤口样式)
const sfpMat = new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.7, roughness: 0.3 });
const wanPort = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.12, scale * 0.14, scale * 0.06),
sfpMat
);
wanPort.position.set(-scale * 0.05, -scale * 0.02, scale * 0.5);
group.add(wanPort);
const wanRing = new THREE.Mesh(
new THREE.TorusGeometry(scale * 0.045, 0.008, 6, 16),
new THREE.MeshStandardMaterial({ color: 0xfbbf24, emissive: 0xfbbf24, emissiveIntensity: 0.6, metalness: 0.5, roughness: 0.3 })
);
wanRing.rotation.x = Math.PI / 2;
wanRing.position.set(-scale * 0.05, scale * 0.08, scale * 0.53);
group.add(wanRing);
// LAN 口阵列(4个)
for (let i = 0; i < 4; i++) {
const port = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.1, scale * 0.13, scale * 0.05),
portMat
);
port.position.set(scale * 0.2 + i * scale * 0.13, -scale * 0.02, scale * 0.5);
group.add(port);
const lanLed = new THREE.Mesh(
new THREE.SphereGeometry(0.012, 6, 6),
createLedMaterial(0x22c55e, 0.9)
);
lanLed.position.set(scale * 0.2 + i * scale * 0.13, -scale * 0.11, scale * 0.52);
group.add(lanLed);
}
// 天线
const antennaMat = new THREE.MeshStandardMaterial({ color: 0x64748b, metalness: 0.9, roughness: 0.2 });
// 天线 1 - 左侧直立
const ant1 = new THREE.Mesh(
new THREE.CylinderGeometry(0.012, 0.015, scale * 0.7, 8),
antennaMat
);
ant1.position.set(-scale * 0.55, scale * 0.5, -scale * 0.1);
group.add(ant1);
const ant1Tip = new THREE.Mesh(
new THREE.SphereGeometry(0.022, 10, 10),
createLedMaterial(0x22d3ee, 1.2)
);
ant1Tip.position.set(-scale * 0.55, scale * 0.87, -scale * 0.1);
group.add(ant1Tip);
// 天线 2 - 右侧略向后倾斜
const ant2 = new THREE.Mesh(
new THREE.CylinderGeometry(0.012, 0.015, scale * 0.65, 8),
antennaMat
);
ant2.position.set(scale * 0.55, scale * 0.48, -scale * 0.15);
ant2.rotation.x = 0.15;
ant2.rotation.z = -0.1;
group.add(ant2);
const ant2Tip = new THREE.Mesh(
new THREE.SphereGeometry(0.022, 10, 10),
createLedMaterial(0x22d3ee, 1.2)
);
ant2Tip.position.set(scale * 0.52, scale * 0.83, -scale * 0.2);
group.add(ant2Tip);
return group;
}
// ============================================================
// 4. 防火墙 Firewall - 1U 机架式 + LCD 屏 + 盾形浮雕
// ============================================================
export function createFirewallModel(status: DeviceStatus, scale = 1.8): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'firewall');
const panelMat = createPanelMaterial();
const portMat = createPortMaterial();
const earMat = new THREE.MeshStandardMaterial({ color: 0x475569, metalness: 0.8, roughness: 0.3 });
// 1U 主体机箱
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.8, scale * 0.36, scale * 0.9),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.82, scale * 0.32, scale * 0.03),
panelMat
);
frontPanel.position.z = scale * 0.48;
group.add(frontPanel);
// 机架耳
const leftEar = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.1, scale * 0.29, scale * 0.12),
earMat
);
leftEar.position.set(-scale * 0.97, 0, 0);
group.add(leftEar);
const rightEar = leftEar.clone();
rightEar.position.x = scale * 0.97;
group.add(rightEar);
// 红色安全装饰条
const redStrip = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.55, 0.018, 0.035),
new THREE.MeshStandardMaterial({
color: 0xef4444,
emissive: 0xef4444,
emissiveIntensity: 0.4,
metalness: 0.3,
roughness: 0.5,
})
);
redStrip.position.set(0, scale * 0.15, scale * 0.5);
group.add(redStrip);
// LCD 状态显示屏
const lcdFrame = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.32, scale * 0.2, scale * 0.04),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 })
);
lcdFrame.position.set(-scale * 0.55, -scale * 0.02, scale * 0.49);
group.add(lcdFrame);
// LCD 显示面(发光蓝绿色)
const lcdScreen = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.28, scale * 0.16, 0.01),
new THREE.MeshStandardMaterial({
color: 0x0d9488,
emissive: 0x14b8a6,
emissiveIntensity: 0.7,
metalness: 0.2,
roughness: 0.3,
})
);
lcdScreen.position.set(-scale * 0.55, -scale * 0.02, scale * 0.52);
group.add(lcdScreen);
// 盾形浮雕
const shieldEmbossMat = new THREE.MeshStandardMaterial({
color: 0x6366f1,
metalness: 0.7,
roughness: 0.3,
emissive: 0x6366f1,
emissiveIntensity: 0.35,
});
const shieldTop = new THREE.Mesh(
new THREE.ConeGeometry(scale * 0.08, scale * 0.14, 4),
shieldEmbossMat
);
shieldTop.rotation.y = Math.PI / 4;
shieldTop.position.set(-scale * 0.18, -scale * 0.03, scale * 0.5);
group.add(shieldTop);
const shieldBody = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.12, scale * 0.14, 0.02),
shieldEmbossMat
);
shieldBody.position.set(-scale * 0.18, -scale * 0.13, scale * 0.5);
group.add(shieldBody);
// 盾心
const shieldCore = new THREE.Mesh(
new THREE.SphereGeometry(0.012, 8, 8),
createLedMaterial(0x22d3ee, 1.2)
);
shieldCore.position.set(-scale * 0.18, -scale * 0.07, scale * 0.52);
group.add(shieldCore);
// 状态 LED 组
const fwLeds = [
{ color: statusColors[status], x: scale * 0.02 },
{ color: 0xef4444, x: scale * 0.08 },
{ color: 0xf59e0b, x: scale * 0.14 },
{ color: 0x22c55e, x: scale * 0.2 },
];
fwLeds.forEach(({ color, x }) => {
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.018, 8, 8),
createLedMaterial(color, 1.0)
);
led.position.set(x, scale * 0.08, scale * 0.5);
group.add(led);
});
// SFP 口
const sfpMat = new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.7, roughness: 0.3 });
for (let i = 0; i < 2; i++) {
const sfp = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.1, scale * 0.12, scale * 0.06),
sfpMat
);
sfp.position.set(scale * 0.45 + i * scale * 0.15, -scale * 0.02, scale * 0.5);
group.add(sfp);
const sfpLed = new THREE.Mesh(
new THREE.SphereGeometry(0.012, 6, 6),
createLedMaterial(0x22d3ee, 1.0)
);
sfpLed.position.set(scale * 0.45 + i * scale * 0.15, -scale * 0.12, scale * 0.52);
group.add(sfpLed);
}
// 网口
for (let i = 0; i < 2; i++) {
const port = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.08, scale * 0.1, scale * 0.05),
portMat
);
port.position.set(scale * 0.78 + i * scale * 0.11, -scale * 0.03, scale * 0.5);
group.add(port);
const portLed = new THREE.Mesh(
new THREE.SphereGeometry(0.01, 6, 6),
createLedMaterial(0x22c55e, 0.9)
);
portLed.position.set(scale * 0.78 + i * scale * 0.11, -scale * 0.11, scale * 0.52);
group.add(portLed);
}
return group;
}
// ============================================================
// 5. 存储 Storage - 磁盘阵列
// ============================================================
export function createStorageModel(status: DeviceStatus, scale = 1.5): THREE.Group {
const group = new THREE.Group();
const bodyMat = createBodyMaterial(status, 'storage');
const panelMat = createPanelMaterial();
// 主体大型方体
const body = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.4, scale * 0.9, scale * 1.1),
bodyMat
);
body.userData.isBody = true;
group.add(body);
// 前面板
const frontPanel = new THREE.Mesh(
new THREE.BoxGeometry(scale * 1.42, scale * 0.82, scale * 0.04),
panelMat
);
frontPanel.position.z = scale * 0.57;
group.add(frontPanel);
// 磁盘槽位(3行 x 4列)
const driveW = scale * 0.28;
const driveH = scale * 0.18;
const startX = -scale * 0.55;
const startY = scale * 0.32;
for (let row = 0; row < 3; row++) {
for (let col = 0; col < 4; col++) {
const drive = new THREE.Mesh(
new THREE.BoxGeometry(driveW, driveH, scale * 0.025),
new THREE.MeshStandardMaterial({
color: row === 2 && col >= 2 ? 0x0f172a : 0x1e293b,
metalness: 0.4,
roughness: 0.5,
})
);
drive.position.set(
startX + col * (driveW + 0.02),
startY - row * (driveH + 0.04),
scale * 0.6
);
group.add(drive);
// 磁盘 LED
const isActive = !(row === 2 && col >= 2);
const ledColor = isActive
? row === 0 && col === 0
? statusColors[status]
: 0x22c55e
: 0x475569;
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.015, 8, 8),
createLedMaterial(ledColor, isActive ? 0.8 : 0.3)
);
led.position.set(
startX + col * (driveW + 0.02) - driveW * 0.35,
startY - row * (driveH + 0.04),
scale * 0.62
);
group.add(led);
}
}
// 状态区域
const statusArea = new THREE.Mesh(
new THREE.BoxGeometry(scale * 0.3, scale * 0.1, scale * 0.045),
new THREE.MeshStandardMaterial({ color: 0x0f172a, metalness: 0.6, roughness: 0.4 })
);
statusArea.position.set(-scale * 0.5, -scale * 0.32, scale * 0.59);
group.add(statusArea);
const stLeds = [
{ color: statusColors[status], x: -scale * 0.58 },
{ color: 0x22d3ee, x: -scale * 0.53 },
{ color: 0x22c55e, x: -scale * 0.48 },
];
stLeds.forEach(({ color, x }) => {
const led = new THREE.Mesh(
new THREE.SphereGeometry(0.02, 10, 10),
createLedMaterial(color, 1.0)
);
led.position.set(x, -scale * 0.32, scale * 0.61);
group.add(led);
});
return group;
}
// ============================================================
// 统一的设备模型创建入口
// ============================================================
export function createDeviceModel(type: DeviceType, status: DeviceStatus, scale?: number): THREE.Group {
switch (type) {
case 'server':
return createServerModel(status, scale ?? 1.5);
case 'switch':
return createSwitchModel(status, scale ?? 1.8);
case 'router':
return createRouterModel(status, scale ?? 2.0);
case 'firewall':
return createFirewallModel(status, scale ?? 2.0);
case 'storage':
return createStorageModel(status, scale ?? 1.6);
case 'workstation':
// 工作站用服务器模型代替(简化版)
return createServerModel(status, scale ?? 1.4);
default:
return createServerModel(status, scale ?? 1.5);
}
}