feat: 新增设备巡检模块实现完整的巡检告警功能
新增设备巡检模块核心功能,包括: - 巡检数据存储与调度系统 - 华为/H3C交换机及Linux服务器采集器 - 设备扩展配置与告警规则管理 - 主机详情页巡检配置入口 - 产品名称自定义设置功能 - 网络设备SSH exec通道兼容处理
This commit is contained in:
637
components/monitoring/deviceModels.ts
Normal file
637
components/monitoring/deviceModels.ts
Normal file
@@ -0,0 +1,637 @@
|
||||
/**
|
||||
* 精细 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user