import { useEffect, useMemo, useRef, useState } from 'react'; import type { InstanceContext } from '../app-shell.js'; import { createOperationClient, type OperationClient } from '../operations/operation-client.js'; import { safeUiError } from '../ui/locale.js'; export type OverviewFieldValue = string | number | boolean | null; export type OverviewSection = Readonly>; export interface OverviewSnapshot { readonly observedAt?: string | undefined; readonly device: OverviewSection; readonly sim: OverviewSection; readonly network: OverviewSection; readonly stats: OverviewSection; readonly cpu: OverviewSection; readonly connectivity: OverviewSection; } export interface OverviewDataSource { /** Implementations are injected by the application owner; this component has no production endpoint. */ load(instanceId: string, signal: AbortSignal): Promise; } export interface OverviewSystemPageProps { readonly instance: InstanceContext; readonly dataSource?: OverviewDataSource | undefined; /** Change this value when an external owner has requested a refresh. */ readonly refreshSignal?: unknown | undefined; readonly operationClient?: OperationClient | undefined; readonly revision?: number | undefined; } type ReadState = | { kind: 'idle' } | { kind: 'loading'; snapshot?: OverviewSnapshot } | { kind: 'ready'; snapshot: OverviewSnapshot } | { kind: 'error'; message: string; snapshot?: OverviewSnapshot }; const SECTIONS = [ ['device', '设备'], ['sim', 'SIM'], ['network', '网络'], ['stats', '统计'], ['cpu', 'CPU'], ['connectivity', '连接状态'], ] as const; const FIELD_LABELS: Readonly> = { Model: '型号', Manufacturer: '制造商', IMEI: 'IMEI', Version: '固件版本', Uptime: '运行时间', Slots: '卡槽数', Active: '活跃数', Operator: '运营商', Technology: '接入制式', Signal: '信号强度', MCC: 'MCC', MNC: 'MNC', Registration: '注册状态', IPv4: 'IPv4 地址', IPv6: 'IPv6 地址', Download: '下行速率', Upload: '上行速率', 'Messages today': '今日消息数', Calls: '通话数', Usage: '使用率', Temperature: '温度', State: '状态', Latency: '延迟', // Device payloads arrive with their native snake_case field names. model: '型号', manufacturer: '制造商', brand: '品牌', imei: 'IMEI', meid: 'MEID', iccid: 'ICCID', imsi: 'IMSI', phone_number: '号码', firmware_version: '固件版本', os_version: '系统版本', version: '版本', baseband_version: '基带版本', uptime: '运行时间', uptime_seconds: '运行时间(秒)', slots: '卡槽数', active_slots: '活跃数', active: '已激活', enabled: '已启用', operator: '运营商', carrier: '运营商', network_type: '接入制式', technology: '技术', radio_mode: '网络模式', signal: '信号强度', signal_dbm: '信号强度(dBm)', signal_percent: '信号质量', rsrp: 'RSRP', rsrq: 'RSRQ', sinr: 'SINR', mcc: 'MCC', mnc: 'MNC', registration: '注册状态', registration_state: '注册状态', roaming: '漫游', airplane_mode: '飞行模式', ipv4: 'IPv4 地址', ipv6: 'IPv6 地址', ip_address: 'IP 地址', gateway: '网关', dns: 'DNS', download: '下行速率', upload: '上行速率', download_bytes: '下行流量', upload_bytes: '上行流量', messages_today: '今日消息数', calls: '通话数', usage: '使用率', temperature: '温度', max_temperature_c: '最高温度', cpu_percent: 'CPU 使用率', cpu_load: 'CPU 负载', memory_percent: '内存使用率', memory_total_mb: '内存总量', memory_available_mb: '可用内存', battery_level: '电量', battery_percent: '电量', latency: '延迟', connected: '连接状态', interface: '网络接口', ssid: '无线网络', apn: 'APN', }; const ENUM_LABELS: Readonly> = { connected: '已连接', not_registered: '未注册', attached: '已附着', detached: '已分离', enabled: '已启用', disabled: '已停用', true: '是', false: '否', lte: 'LTE', nr: 'NR', nr5g: '5G NR', gsm: 'GSM', wcdma: 'WCDMA', td_scdma: 'TD-SCDMA', evdo: 'EVDO', disconnected: '未连接', connecting: '正在连接', registered: '已注册', unregistered: '未注册', searching: '正在搜索', roaming: '漫游中', online: '在线', offline: '离线', unknown: '未知', }; function displayFieldLabel(key: string): string { return FIELD_LABELS[key] ?? key; } function displayFieldValue(value: OverviewFieldValue): string { if (value == null) return '不可用'; if (typeof value === 'boolean') return value ? '是' : '否'; if (typeof value === 'string') return ENUM_LABELS[value.toLocaleLowerCase()] ?? value; return String(value); } function StructuredSection({ label, values }: { label: string; values: OverviewSection }) { const entries = Object.entries(values); return (

{label}

{entries.length ? (
{entries.map(([key, value]) => (
{displayFieldLabel(key)}
{displayFieldValue(value)}
))}
) : (

未提供{label}数据。

)}
); } function ResourceSummary({ instance }: { instance: InstanceContext }) { const resources = instance.resources; const metric = (label: string, value: number | undefined) => (
{label} {value === undefined ? ( 暂未获取 ) : ( <> {value.toFixed(1)}% )}
); return (

运行状态

{instance.resourceSummaryState === 'loading' ? (

正在加载资源摘要…

) : null} {instance.resourceSummaryState === 'unavailable' ? (

资源摘要暂不可用,实例基本信息仍可使用。

) : null}
连接状态 {instance.status === 'online' ? '在线' : instance.status === 'offline' ? '离线' : instance.status === 'auth-required' ? '需要认证' : '未知'}
{metric('CPU', resources?.cpuPercent)} {metric('内存', resources?.memoryPercent)}
最高温度 {resources?.maxTemperatureCelsius === undefined ? '暂未获取' : `${resources.maxTemperatureCelsius.toFixed(1)} °C`}
手机号 {resources?.phoneNumbers?.join('、') || '暂未获取'}
); } export function OverviewSystemPage({ instance, dataSource, refreshSignal, operationClient, revision, }: OverviewSystemPageProps) { const requestOwner = useRef(0); const [retry, setRetry] = useState(0); const [state, setState] = useState({ kind: 'idle' }); const [actionState, setActionState] = useState<{ busy: boolean; message?: string; error?: string; }>({ busy: false }); const client = useMemo(() => operationClient ?? createOperationClient(), [operationClient]); const effectiveRevision = revision ?? instance.revision; async function runOverviewAction(kind: 'service' | 'system'): Promise { const targetRevision = effectiveRevision; if (!targetRevision || targetRevision < 1) { setActionState({ busy: false, error: '缺少配置版本,请刷新后重试。' }); return; } const op = kind === 'service' ? { operationId: 'postServiceRestart', parameterSchemaId: 'simadmin.58e2204.postServiceRestart.parameters.v1', title: '重启服务', fields: [] as const, } : { operationId: 'postSystemReboot', parameterSchemaId: 'simadmin.58e2204.postSystemReboot.parameters.v1', title: '系统重启', fields: [{ fieldId: 'delay_seconds', kind: 'number' as const, value: 3 }], }; if ( typeof window !== 'undefined' && !window.confirm(`将执行「${op.title}」。此操作为高风险,确认继续?`) ) return; setActionState({ busy: true, message: `正在${op.title}…` }); try { // Avoid pageSize=100 first-page truncation for late-sorted operation ids. await client.list({ search: op.operationId, pageSize: 100 }); const prepared = await client.prepare({ operationId: op.operationId, targets: [{ instanceId: instance.id, revision: targetRevision }], parameters: { parameterSchemaId: op.parameterSchemaId, fields: [...op.fields], }, }); const confirmed = typeof window === 'undefined' ? true : window.confirm(`${prepared.confirmationPrompt} 风险等级 ${prepared.risk}。确认继续?`); if (!confirmed) { setActionState({ busy: false, message: '已取消操作。' }); return; } const job = await client.execute(prepared.id); setActionState({ busy: false, message: job.status === 'succeeded' ? `${op.title}已提交成功。` : `${op.title}结果未知或失败。`, ...(job.status === 'succeeded' ? {} : { error: `${op.title}结果未知或失败。` }), }); } catch (error) { setActionState({ busy: false, error: safeUiError(error, `${op.title}失败,请稍后重试。`), }); } } useEffect(() => { const request = ++requestOwner.current; const controller = new AbortController(); if (instance.authentication === 'auth-required') { setState({ kind: 'idle' }); return () => controller.abort(); } if (!dataSource) { setState({ kind: 'idle' }); return () => controller.abort(); } setState((current) => ({ kind: 'loading', ...(current.kind === 'ready' || current.kind === 'error' ? current.snapshot ? { snapshot: current.snapshot } : {} : {}), })); void dataSource.load(instance.id, controller.signal).then( (snapshot) => { if (request === requestOwner.current && !controller.signal.aborted) setState({ kind: 'ready', snapshot }); }, (reason: unknown) => { void reason; if (request === requestOwner.current && !controller.signal.aborted) setState((current) => ({ kind: 'error', message: '无法加载概览数据。', ...(current.kind === 'loading' && current.snapshot ? { snapshot: current.snapshot } : {}), })); }, ); return () => controller.abort(); }, [dataSource, instance.authentication, instance.id, refreshSignal, retry]); const systemOperations = (

系统操作

服务重启与系统重启均为高风险操作,执行前会再次确认。

{!effectiveRevision ? (

缺少配置版本,暂不可执行重启。请从实例列表进入并刷新后重试。

) : null} {actionState.error ? (

{actionState.error}

) : null} {actionState.message ? (

{actionState.message}

) : null}
); if (instance.authentication === 'auth-required') return (
需要先完成认证,才能读取概览数据。
{systemOperations}
); if (!dataSource) return (

未提供安全概览只读数据源,但系统操作仍可使用。

{systemOperations}
); const retainedSnapshot = state.kind === 'loading' || state.kind === 'error' ? state.snapshot : undefined; const snapshot = state.kind === 'ready' ? state.snapshot : retainedSnapshot; if ((state.kind === 'loading' || state.kind === 'idle') && !retainedSnapshot) return (

正在加载概览…

{systemOperations}
); if (state.kind === 'error' && !state.snapshot) return (

无法加载概览: {state.message}

{systemOperations}
); if (!snapshot) return
{systemOperations}
; return (
{state.kind === 'loading' ?

正在刷新概览…

: null} {state.kind === 'error' ? (

刷新失败,正在显示上次已知的概览数据。

) : null} {instance.freshness !== 'fresh' ? (

概览数据可能已过期;使用这些值前请先核实数据新鲜度。

) : null} {snapshot.observedAt ?

观测时间:{snapshot.observedAt}

: null}
{SECTIONS.map(([key, label]) => ( ))}
{systemOperations}
); }