WuYongJie ff41839238 feat: 实现Apple风格UI系统和产品上传功能
- 完整实现Apple HIG 2025设计系统
- 添加Modal弹窗组件(毛玻璃效果、流畅动画)
- 添加FileUpload文件上传组件(拖拽、批量上传、进度显示)
- 实现产品上传功能(支持DWG/GLB/GLTF文件)
- 优化产品列表展示(可折叠的产品+SKU结构)
- 实现Tab切换功能(3D模型管理、五金件、BOM、文档)
- 修复主题系统hydration错误
- 优化按钮和间距,符合Apple设计规范
- 添加分享页面的Apple风格重设计
- 创建测试数据脚本
2025-12-12 20:16:41 +08:00

360 lines
9.7 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.

/**
* 硬件检测引擎
*
* 检测设备的 CPU、内存、磁盘和 Docker 环境
*/
import {
DeviceConnection,
DeviceProfile,
HardwareDetectionResult,
DeploymentMode,
CpuInfo,
CpuArchitecture,
MemoryInfo,
DiskInfo,
DockerInfo,
} from './types'
import { executeRemoteCommand } from './connection'
/**
* 检测设备硬件配置
*/
export async function detectHardware(
connection: DeviceConnection
): Promise<HardwareDetectionResult> {
try {
// 检测各项硬件信息
const cpu = await detectCPU(connection)
const memory = await detectMemory(connection)
const disk = await detectDisk(connection)
const docker = await detectDocker(connection)
const profile: DeviceProfile = {
deviceType: connection.type,
cpu,
memory,
disk,
docker,
}
// 分析硬件配置并给出建议
const analysis = analyzeHardware(profile)
return {
profile,
...analysis,
}
} catch (error) {
throw new Error(`硬件检测失败: ${error instanceof Error ? error.message : String(error)}`)
}
}
/**
* 检测 CPU 信息
*/
async function detectCPU(connection: DeviceConnection): Promise<CpuInfo> {
if (connection.type === 'windows') {
// Windows: 使用 wmic
const result = await executeRemoteCommand(
connection,
'wmic cpu get name,numberofcores /format:csv'
)
if (result.exitCode === 0) {
const lines = result.stdout.trim().split('\n').filter(line => line.trim())
if (lines.length >= 2) {
const data = lines[1].split(',')
const model = data[1]?.trim() || 'Unknown'
const cores = parseInt(data[2]?.trim() || '0', 10)
return {
cores,
model,
isN100OrLower: model.toLowerCase().includes('n100') || cores <= 4,
architecture: 'x86_64',
}
}
}
} else {
// Linux/NAS: 使用 lscpu
const result = await executeRemoteCommand(connection, 'lscpu')
if (result.exitCode === 0) {
const lines = result.stdout.split('\n')
let cores = 0
let model = 'Unknown'
let architecture = 'x86_64'
for (const line of lines) {
if (line.includes('CPU(s):')) {
const match = line.match(/CPU\(s\):\s+(\d+)/)
if (match) cores = parseInt(match[1], 10)
} else if (line.includes('Model name:')) {
model = line.split(':')[1]?.trim() || 'Unknown'
} else if (line.includes('Architecture:')) {
const arch = line.split(':')[1]?.trim() || 'x86_64'
architecture = arch === 'x86_64' || arch === 'aarch64' ? arch : 'x86_64'
}
}
return {
cores,
model,
isN100OrLower: model.toLowerCase().includes('n100') || cores <= 4,
architecture: architecture as CpuArchitecture,
}
}
}
// 默认返回值
return {
cores: 4,
model: 'Unknown',
isN100OrLower: true,
architecture: 'x86_64',
}
}
/**
* 检测内存信息
*/
async function detectMemory(connection: DeviceConnection): Promise<MemoryInfo> {
if (connection.type === 'windows') {
// Windows: 使用 wmic
const result = await executeRemoteCommand(
connection,
'wmic OS get TotalVisibleMemorySize,FreePhysicalMemory /format:csv'
)
if (result.exitCode === 0) {
const lines = result.stdout.trim().split('\n').filter(line => line.trim())
if (lines.length >= 2) {
const data = lines[1].split(',')
const freeKB = parseInt(data[1]?.trim() || '0', 10)
const totalKB = parseInt(data[2]?.trim() || '0', 10)
const total = Math.round(totalKB / 1024 / 1024) // KB to GB
const available = Math.round(freeKB / 1024 / 1024)
const used = total - available
return { total, available, used }
}
}
} else {
// Linux/NAS: 使用 free
const result = await executeRemoteCommand(connection, 'free -g')
if (result.exitCode === 0) {
const lines = result.stdout.split('\n')
const memLine = lines.find(line => line.startsWith('Mem:'))
if (memLine) {
const parts = memLine.split(/\s+/).filter(p => p)
const total = parseInt(parts[1] || '0', 10)
const used = parseInt(parts[2] || '0', 10)
const available = parseInt(parts[6] || '0', 10)
return { total, available, used }
}
}
}
// 默认返回值
return {
total: 4,
available: 3,
used: 1,
}
}
/**
* 检测磁盘信息
*/
async function detectDisk(connection: DeviceConnection): Promise<DiskInfo> {
if (connection.type === 'windows') {
// Windows: 使用 wmic (检测 C: 盘)
const result = await executeRemoteCommand(
connection,
'wmic logicaldisk where "DeviceID=\'C:\'" get Size,FreeSpace /format:csv'
)
if (result.exitCode === 0) {
const lines = result.stdout.trim().split('\n').filter(line => line.trim())
if (lines.length >= 2) {
const data = lines[1].split(',')
const freeBytes = parseInt(data[1]?.trim() || '0', 10)
const totalBytes = parseInt(data[2]?.trim() || '0', 10)
const total = Math.round(totalBytes / 1024 / 1024 / 1024) // Bytes to GB
const available = Math.round(freeBytes / 1024 / 1024 / 1024)
const used = total - available
return { total, available, used, type: 'unknown' }
}
}
} else {
// Linux/NAS: 使用 df
const result = await executeRemoteCommand(connection, 'df -BG /')
if (result.exitCode === 0) {
const lines = result.stdout.split('\n')
const dataLine = lines[1]
if (dataLine) {
const parts = dataLine.split(/\s+/).filter(p => p)
const total = parseInt(parts[1]?.replace('G', '') || '0', 10)
const used = parseInt(parts[2]?.replace('G', '') || '0', 10)
const available = parseInt(parts[3]?.replace('G', '') || '0', 10)
// 尝试检测磁盘类型
const lsblkResult = await executeRemoteCommand(connection, 'lsblk -d -o name,rota')
let type: 'ssd' | 'hdd' | 'unknown' = 'unknown'
if (lsblkResult.exitCode === 0 && lsblkResult.stdout.includes('0')) {
type = 'ssd'
} else if (lsblkResult.exitCode === 0 && lsblkResult.stdout.includes('1')) {
type = 'hdd'
}
return { total, available, used, type }
}
}
}
// 默认返回值
return {
total: 100,
available: 80,
used: 20,
type: 'ssd',
}
}
/**
* 检测 Docker 环境
*/
async function detectDocker(connection: DeviceConnection): Promise<DockerInfo> {
let version = 'unknown'
let composeVersion = 'unknown'
let running = false
let rootless = false
// 检测 Docker 版本
try {
const versionResult = await executeRemoteCommand(connection, 'docker --version')
if (versionResult.exitCode === 0) {
const match = versionResult.stdout.match(/Docker version ([\d.]+)/)
if (match) {
version = match[1]
running = true
}
}
} catch {
// Docker 未安装或未运行
}
// 检测 Docker Compose 版本
try {
const composeResult = await executeRemoteCommand(connection, 'docker-compose --version')
if (composeResult.exitCode === 0) {
const match = composeResult.stdout.match(/version ([\d.]+)/)
if (match) {
composeVersion = match[1]
}
}
} catch {
// Docker Compose 未安装
}
// 检测是否为 rootless 模式
if (running) {
try {
const infoResult = await executeRemoteCommand(connection, 'docker info')
if (infoResult.exitCode === 0) {
rootless = infoResult.stdout.toLowerCase().includes('rootless')
}
} catch {
// 无法获取 Docker 信息
}
}
return {
version,
composeVersion,
rootless,
running,
}
}
/**
* 分析硬件配置
*/
function analyzeHardware(profile: DeviceProfile): {
recommendedMode: DeploymentMode
warnings: string[]
canDeploy: boolean
reason?: string
} {
const warnings: string[] = []
// 检查最低内存要求
if (profile.memory.total < 4) {
return {
recommendedMode: 'lightweight',
warnings: [],
canDeploy: false,
reason: `内存不足。系统要求最低 4GB 内存,当前仅有 ${profile.memory.total}GB。`,
}
}
// 检查磁盘空间
if (profile.disk.available < 50) {
warnings.push(`磁盘可用空间不足 50GB,当前仅有 ${profile.disk.available}GB。建议清理磁盘空间。`)
}
// 检查 Docker 环境
if (!profile.docker.running) {
return {
recommendedMode: 'lightweight',
warnings: [],
canDeploy: false,
reason: 'Docker 未运行。请先启动 Docker 服务。',
}
}
// 根据内存大小推荐部署模式
let recommendedMode: DeploymentMode
if (profile.memory.total < 8) {
recommendedMode = 'lightweight'
warnings.push(`内存为 ${profile.memory.total}GB,将使用轻量级部署模式。推荐升级到 8GB 以获得更好性能。`)
} else {
recommendedMode = 'standard'
}
// N100 芯片提示
if (profile.cpu.isN100OrLower) {
warnings.push('检测到 Intel N100 或类似低功耗处理器,系统已针对此配置优化。')
}
return {
recommendedMode,
warnings,
canDeploy: true,
}
}
/**
* 获取系统信息摘要
*/
export function getSystemSummary(profile: DeviceProfile): string {
return `
设备类型: ${profile.deviceType}
处理器: ${profile.cpu.model} (${profile.cpu.cores} 核心)
内存: ${profile.memory.total}GB (可用 ${profile.memory.available}GB)
磁盘: ${profile.disk.total}GB (可用 ${profile.disk.available}GB, ${profile.disk.type.toUpperCase()})
Docker: ${profile.docker.version} (Compose ${profile.docker.composeVersion})
`.trim()
}