"use client"; import { useEffect, useMemo, useState } from "react"; type PageKey = | "dashboard" | "waste" | "events" | "floor" | "trace" | "assets" | "alerts" | "settings" | "terminal"; type RoleKey = "admin" | "warehouse" | "operator"; const pageMeta: Record = { dashboard: { title: "运营驾驶舱", sub: "全厂设备与称重业务总览" }, waste: { title: "废料称重", sub: "电子台秤与人脸回收箱原始记录" }, events: { title: "回收事件", sub: "人员进出、投放与视频事件归档" }, floor: { title: "地磅核对", sub: "台秤汇总与地磅增量自动比对" }, trace: { title: "RFID 追溯", sub: "导体称重、批次、标签与 MES 推送" }, assets: { title: "人员与设备", sub: "人员、产线、回收设备统一管理" }, alerts: { title: "告警中心", sub: "偏差、满溢、离线与接口异常" }, settings: { title: "系统设置", sub: "设备接入、权限、字典、MES 与审计" }, terminal: { title: "终端模拟", sub: "回收箱与导体称重操作流程" }, }; const navGroups: Array<{ label: string; items: Array<{ key: PageKey; label: string; icon: string; roles: RoleKey[] }>; }> = [ { label: "总览", items: [ { key: "dashboard", label: "运营驾驶舱", icon: "⌂", roles: ["admin", "warehouse", "operator"] }, ], }, { label: "废料回收", items: [ { key: "waste", label: "废料称重", icon: "◫", roles: ["admin", "warehouse"] }, { key: "events", label: "回收事件", icon: "◎", roles: ["admin", "warehouse"] }, { key: "floor", label: "地磅核对", icon: "⚖", roles: ["admin", "warehouse"] }, ], }, { label: "导体称重", items: [{ key: "trace", label: "RFID 追溯", icon: "◇", roles: ["admin", "operator"] }], }, { label: "运营管理", items: [ { key: "assets", label: "人员与设备", icon: "◉", roles: ["admin", "warehouse", "operator"] }, { key: "alerts", label: "告警中心", icon: "!", roles: ["admin"] }, ], }, { label: "系统", items: [ { key: "settings", label: "系统设置", icon: "≡", roles: ["admin"] }, { key: "terminal", label: "终端模拟", icon: "▣", roles: ["admin", "warehouse", "operator"] }, ], }, ]; const wasteRows = [ { time: "13:33:05", name: "李四", machine: "MC-108", category: "废锡", device: "智能回收箱 B", weight: 19.5, event: "EVT-0722-006", status: "已确认" }, { time: "13:31:12", name: "李四", machine: "MC-105", category: "废铜", device: "电子台秤 CU-01", weight: 9.9, event: "EVT-0722-006", status: "已确认" }, { time: "11:08:59", name: "赵六", machine: "MC-112", category: "废电线", device: "电子台秤 WR-01", weight: 14.5, event: "EVT-0722-005", status: "已确认" }, { time: "11:07:44", name: "赵六", machine: "MC-101", category: "废铝", device: "电子台秤 AL-01", weight: 13.3, event: "EVT-0722-005", status: "已确认" }, { time: "10:25:07", name: "张三", machine: "MC-105", category: "废电线", device: "电子台秤 WR-01", weight: 11.5, event: "EVT-0722-004", status: "偏差预警" }, { time: "09:46:20", name: "王五", machine: "MC-112", category: "废铜", device: "电子台秤 CU-01", weight: 21.2, event: "EVT-0722-003", status: "偏差预警" }, { time: "09:44:03", name: "王五", machine: "MC-112", category: "废铜", device: "电子台秤 CU-01", weight: 11.4, event: "EVT-0722-003", status: "偏差预警" }, { time: "08:16:35", name: "张三", machine: "MC-103", category: "废锡", device: "人脸回收箱 A", weight: 8.7, event: "EVT-0722-001", status: "已确认" }, ]; const eventRows = [ { id: "EVT-20260722-006", name: "李四", span: "13:30:26 → 13:34:02", duration: "3分36秒", count: 2, total: 29.4, result: "正常" }, { id: "EVT-20260722-005", name: "赵六", span: "11:05:12 → 11:09:48", duration: "4分36秒", count: 3, total: 44.6, result: "正常" }, { id: "EVT-20260722-004", name: "张三", span: "10:22:45 → 10:26:10", duration: "3分25秒", count: 2, total: 17.9, result: "超标" }, { id: "EVT-20260722-003", name: "王五", span: "09:41:07 → 09:47:33", duration: "6分26秒", count: 3, total: 51.2, result: "警告" }, { id: "EVT-20260722-002", name: "李四", span: "09:02:18 → 09:05:52", duration: "3分34秒", count: 2, total: 22.8, result: "正常" }, ]; const floorRows = [ { event: "EVT-0722-001", category: "废铜", bench: 19.8, floor: 20.0, diff: 0.2, rate: 1.0, result: "正常", stored: "自动入库" }, { event: "EVT-0722-002", category: "废锡", bench: 8.3, floor: 8.4, diff: 0.1, rate: 1.2, result: "正常", stored: "自动入库" }, { event: "EVT-0722-003", category: "废铜", bench: 51.2, floor: 53.6, diff: 2.4, rate: 4.7, result: "警告", stored: "已入库·预警" }, { event: "EVT-0722-004", category: "废电线", bench: 17.9, floor: 19.35, diff: 1.45, rate: 8.1, result: "超标", stored: "已入库·异常" }, { event: "EVT-0722-005", category: "废铝", bench: 13.3, floor: 13.5, diff: 0.2, rate: 1.5, result: "正常", stored: "自动入库" }, { event: "EVT-0722-006", category: "废锡", bench: 19.5, floor: 19.75, diff: 0.25, rate: 1.3, result: "正常", stored: "自动入库" }, ]; const traceRows = [ { time: "13:32:08", workshop: "六楼车间", task: "WT-0722-07", rfid: "E280-6102-0351", tare: 149.8, gross: 270.5, machine: "MC-205", batch: "B2607-020", name: "赵六", push: "已推送" }, { time: "11:07:52", workshop: "六楼车间", task: "WT-0722-06", rfid: "E280-6102-0318", tare: 143.6, gross: 232.2, machine: "MC-205", batch: "B2607-020", name: "赵六", push: "重试中" }, { time: "10:20:36", workshop: "一楼新能源车间", task: "WT-0722-05", rfid: "E280-6102-0345", tare: 158.4, gross: 289.9, machine: "MC-201", batch: "B2607-018", name: "李四", push: "已推送" }, { time: "09:48:15", workshop: "一楼新能源车间", task: "WT-0722-04", rfid: "E280-6102-0340", tare: 151.9, gross: 247.6, machine: "MC-203", batch: "B2607-019", name: "王五", push: "待推送" }, { time: "09:04:41", workshop: "一楼新能源车间", task: "WT-0722-03", rfid: "E280-6102-0335", tare: 165.2, gross: 301.8, machine: "MC-203", batch: "B2607-019", name: "王五", push: "已推送" }, ]; const tagSeed = [ { id: "E280-6102-0331", name: "1# 导体轴", state: "在用", last: "今天 08:15" }, { id: "E280-6102-0335", name: "3# 导体轴", state: "在用", last: "今天 09:04" }, { id: "E280-6102-0345", name: "5# 导体轴", state: "在用", last: "今天 10:20" }, { id: "E280-6102-0360", name: "备用标签 A", state: "未用", last: "—" }, { id: "E280-6102-0318", name: "7# 导体轴", state: "损坏", last: "昨天 15:03" }, ]; const people = [ { name: "张三", id: "MES-0117", workshop: "一楼新能源车间", role: "仓管员", face: "已录入", sync: "已同步", last: "今日 10:26" }, { name: "李四", id: "MES-0132", workshop: "一楼新能源车间", role: "产线工人", face: "需更新", sync: "已同步", last: "今日 13:34" }, { name: "王五", id: "MES-0098", workshop: "一楼新能源车间", role: "产线工人", face: "未录入", sync: "已同步", last: "今日 09:47" }, { name: "赵六", id: "MES-0201", workshop: "六楼车间", role: "产线工人", face: "已录入", sync: "已同步", last: "今日 11:09" }, { name: "王工", id: "MES-0007", workshop: "一楼新能源车间", role: "管理员", face: "已录入", sync: "已同步", last: "今日 13:40" }, ]; const machines = [ { id: "MC-101", name: "拉丝机 1#", type: "拉丝机", workshop: "一楼新能源车间", state: "运行中", mes: "正常" }, { id: "MC-105", name: "绞线机 1#", type: "绞线机", workshop: "一楼新能源车间", state: "运行中", mes: "正常" }, { id: "MC-112", name: "挤塑机 1#", type: "挤塑机", workshop: "六楼车间", state: "维护中", mes: "正常" }, { id: "MC-201", name: "导体加工线 1#", type: "导体加工线", workshop: "一楼新能源车间", state: "运行中", mes: "正常" }, { id: "MC-205", name: "导体加工线 3#", type: "导体加工线", workshop: "六楼车间", state: "停机", mes: "断连" }, ]; const devices = [ { id: "BIN-FACE-01", name: "人脸识别回收箱 A", type: "智能回收箱", place: "一楼废料库", protocol: "HTTPS / WebSocket", state: "在线", heartbeat: "刚刚", value: "62%" }, { id: "BIN-FACE-02", name: "人脸识别回收箱 B", type: "智能回收箱", place: "六楼车间", protocol: "HTTPS / WebSocket", state: "在线", heartbeat: "2 秒前", value: "86%" }, { id: "SCALE-B-01", name: "废铜电子台秤", type: "电子台秤", place: "废铜区", protocol: "RS232 → 边缘网关", state: "在线", heartbeat: "1 秒前", value: "12.61 kg" }, { id: "SCALE-F-01", name: "3 吨地磅", type: "地磅系统", place: "一楼废料库", protocol: "Modbus TCP", state: "在线", heartbeat: "刚刚", value: "1843.2 kg" }, { id: "RFID-UHF-01", name: "UHF RFID 读写器", type: "RFID 系统", place: "导体称重线", protocol: "TCP / LLRP", state: "在线", heartbeat: "3 秒前", value: "99.1%" }, { id: "EDGE-GW-01", name: "车间边缘网关", type: "边缘网关", place: "工控机房", protocol: "MQTT over TLS", state: "在线", heartbeat: "刚刚", value: "18 ms" }, ]; const alerts = [ { level: "紧急", time: "10:26", type: "双秤偏差", source: "地磅核对", ref: "EVT-20260722-004 · 废电线", detail: "偏差率 8.1%,超过红色阈值 5%", state: "待处理" }, { level: "警告", time: "09:47", type: "双秤偏差", source: "地磅核对", ref: "EVT-20260722-003 · 废铜", detail: "偏差率 4.7%,建议人工复核", state: "待处理" }, { level: "警告", time: "09:12", type: "满溢预警", source: "人脸回收箱 B", ref: "BIN-FACE-02", detail: "满溢率 86%,已通知清运", state: "处理中" }, { level: "提示", time: "07:55", type: "设备离线", source: "电子台秤", ref: "SCALE-B-01", detail: "心跳中断 42 秒后自动恢复", state: "已关闭" }, ]; const logs = [ ["13:40:12", "王工", "修改告警阈值", "双秤偏差警告阈值 3% → 2%", "敏感"], ["11:02:33", "王工", "同步 MES 人员", "增量更新 3 条人员数据", "正常"], ["10:28:01", "王工", "审核告警", "EVT-20260722-003 转人工复核", "正常"], ["09:15:47", "王工", "导出报表", "废料称重记录 · 今日全部", "正常"], ["08:50:22", "王工", "更新人脸", "李四 MES-0132 · 旧特征已归档", "敏感"], ]; const integrationCards = [ { icon: "▦", title: "人脸识别回收箱", protocol: "HTTPS / WebSocket", detail: "人脸身份、门磁、满溢、投放重量、抓拍事件", device: "2 台", status: "待现场联调" }, { icon: "⚖", title: "电子台秤", protocol: "RS232 / Modbus-RTU", detail: "稳定值、毛重/皮重、精度、去皮、设备心跳", device: "4 台", status: "待协议参数" }, { icon: "▰", title: "地磅系统", protocol: "RS485 / Modbus TCP", detail: "进场重量、离场重量、净重、车辆/事件关联", device: "1 套", status: "待协议参数" }, { icon: "◇", title: "RFID 系统", protocol: "TCP / LLRP / 串口", detail: "EPC、读写、标签状态、称重任务和批次绑定", device: "1 套", status: "待协议参数" }, ]; function Badge({ value }: { value: string }) { const kind = /正常|在线|已确认|已同步|已录入|已推送|自动入库|运行中|在用/.test(value) ? "ok" : /超标|紧急|断连|损坏|未录入/.test(value) ? "danger" : /警告|预警|重试|待|维护|需更新|处理中/.test(value) ? "warn" : "muted"; return {value}; } function Empty({ children }: { children: React.ReactNode }) { return
{children}
; } function PageHeaderActions({ onExport, onRefresh, }: { onExport?: () => void; onRefresh?: () => void; }) { return (
{onRefresh && ( )} {onExport && ( )}
); } function Dashboard({ liveWeight, notify, }: { liveWeight: number; notify: (text: string) => void; }) { const metric = [ ["今日回收总量", "386.4", "kg", "+12.6% 较昨日", "mint"], ["今日回收事件", "42", "次", "6 个完整进出事件", "blue"], ["在线设备", "10 / 10", "台", "边缘网关链路正常", "violet"], ["待处理告警", "2", "条", "含 1 条红色告警", "orange"], ]; return ( <>
SMART RECYCLING CONTROL CENTER

今天的设备与业务都在正常运转

所有称重记录、人员身份、RFID 标签和 MES 推送在同一条数据链路上完成追溯。

边缘网关在线 最近心跳:刚刚 · 延迟 18ms
{metric.map(([label, value, unit, hint, tone]) => (
{label}
{value} {unit}
{hint}
))}
LIVE SIGNAL

实时称重

{liveWeight.toFixed(2)} kg
稳定采样中 · 精度 ±10g
当前操作人王工
来源机台MC-101
废料类别废铜
7 DAY TREND

近 7 日回收趋势

单位:kg
{[68, 45, 82, 56, 92, 74, 88].map((height, index) => (
{[301, 246, 362, 278, 418, 344, 386][index]}
{["一", "二", "三", "四", "五", "六", "日"][index]}
))}
废铜 42% 废电线 28% 废铝 19% 废锡 11%
DEVICE HEALTH

设备健康

{[ ["人脸回收箱", "2 / 2", "98%"], ["电子台秤", "4 / 4", "99%"], ["地磅系统", "1 / 1", "97%"], ["RFID 读写器", "1 / 1", "99%"], ["边缘网关", "1 / 1", "100%"], ].map(([name, count, health]) => (
{name}{count} 在线
{health}
))}
RECENT RECORDS

最近称重流水

{wasteRows.slice(0, 5).map((row) => (
{row.name} · {row.machine}{row.category} · {row.device}
{row.weight.toFixed(2)} kg {row.time}
))}
INTEGRATION FLOW

统一设备链路

01现场设备人脸箱 · 台秤 · 地磅 · RFID
→
02边缘网关协议适配 · 断网缓存
→
03管理平台业务规则 · 告警 · 追溯
→
04MES人员 · 机台 · 批次 · 上报
); } function WastePage({ notify }: { notify: (text: string) => void }) { const [query, setQuery] = useState(""); const [category, setCategory] = useState("全部"); const [view, setView] = useState<"list" | "stats">("list"); const filtered = wasteRows.filter( (row) => (category === "全部" || row.category === category) && [row.name, row.machine, row.event, row.device].join(" ").toLowerCase().includes(query.toLowerCase()), ); return (
{["全部", "废铜", "废电线", "废铝", "废锡"].map((item) => ( ))}
setQuery(event.target.value)} placeholder="搜索操作人、机台、事件 ID…" /> notify("称重记录已刷新")} onExport={() => exportCsv("废料称重记录", filtered)} />
{view === "list" ? ( filtered.length ? (
{filtered.map((row) => ( ))}
称重时间操作人关联机台废料类别来源设备精确重量事件 ID状态
{row.time}{row.name}{row.machine}{row.category}{row.device}{row.weight.toFixed(2)} kg{row.event}
) : 没有符合筛选条件的称重记录 ) : (
今日总量386.4 kg较昨日 +12.6%
称重次数42 次原子记录 42 条
本月累计8.52 t自动入库率 100%
日均投放392 kg近 30 日均值
{[["MC-112", 89], ["MC-101", 78], ["MC-108", 63], ["MC-105", 51]].map(([name, width], index) => (
{index + 1}{name}{width} kg
))}
)}
); } function EventsPage({ notify }: { notify: (text: string) => void }) { const [query, setQuery] = useState(""); const [result, setResult] = useState("全部"); const filtered = eventRows.filter((row) => (result === "全部" || row.result === result) && `${row.id}${row.name}`.toLowerCase().includes(query.toLowerCase())); return (
{["全部", "正常", "警告", "超标"].map((item) => )}
setQuery(event.target.value)} placeholder="搜索事件 ID 或操作人…" /> notify("事件列表已刷新")} onExport={() => exportCsv("回收事件", filtered)} />
{filtered.map((row) => )}
事件 ID操作人进入 → 离开历时原子笔数台秤总重判定视频
{row.id}{row.name}{row.span}{row.duration}{row.count} 条{row.total.toFixed(2)} kg
); } function FloorPage({ notify }: { notify: (text: string) => void }) { const [result, setResult] = useState("全部"); const filtered = floorRows.filter((row) => result === "全部" || row.result === result); return ( <>
自动比对规则正常 ≤ 2%警告 2%—5%超标 > 5%
{["全部", "正常", "警告", "超标"].map((item) => )}
exportCsv("地磅核对", filtered)} />
{filtered.map((row) => )}
事件 ID废料类别台秤汇总地磅增量偏差值偏差率判定入库状态操作
{row.event}{row.category}{row.bench.toFixed(2)} kg{row.floor.toFixed(2)} kg{row.diff.toFixed(2)} kg{row.rate.toFixed(1)}%
); } function TracePage({ notify }: { notify: (text: string) => void }) { const [tab, setTab] = useState<"trace" | "tags">("trace"); const [query, setQuery] = useState(""); const [tags, setTags] = useState(tagSeed); const rows = traceRows.filter((row) => Object.values(row).join(" ").toLowerCase().includes(query.toLowerCase())); const addTag = () => { const next = { id: `E280-6102-${String(370 + tags.length)}`, name: `备用标签 ${String.fromCharCode(65 + tags.length)}`, state: "未用", last: "—" }; setTags((current) => [...current, next]); notify(`RFID 标签 ${next.id} 已添加`); }; return (
{tab === "trace" ? ( <>
setQuery(event.target.value)} placeholder="搜索任务号、RFID、批次号、机台…" /> notify("RFID 数据已刷新")} onExport={() => exportCsv("RFID称重追溯", rows)} />
{rows.map((row) => )}
时间所属车间任务号RFID 标签皮重毛重净重机台批次操作人MES
{row.time}{row.workshop}{row.task}{row.rfid}{row.tare.toFixed(2)}{row.gross.toFixed(2)}{(row.gross - row.tare).toFixed(2)} kg{row.machine}{row.batch}{row.name}
) : ( <>
标签写入、检测、报废与称重任务绑定
{tags.map((tag) => )}
标签 EPC名称状态最后检测操作
{tag.id}{tag.name}{tag.last}
)}
); } function AssetsPage({ notify }: { notify: (text: string) => void }) { const [tab, setTab] = useState<"people" | "machines" | "devices">("people"); return (
{tab === "people" && ( <>
人员主数据以 MES 为准,本系统维护人脸与使用权限
{people.map((person) => )}
姓名MES 员工 ID所属车间角色人脸状态同步状态最近操作操作
{person.name}{person.id}{person.workshop}{person.role}{person.last}
)} {tab === "machines" && (
{machines.map((machine) => )}
机台编号机台名称类型所属车间运行状态MES 链路
{machine.id}{machine.name}{machine.type}{machine.workshop}
)} {tab === "devices" && (
{devices.map((device) =>

{device.name}

{device.id} · {device.place}

{device.value}
设备类型
{device.type}
通信方式
{device.protocol}
最后心跳
{device.heartbeat}
)}
)}
); } function AlertsPage({ notify }: { notify: (text: string) => void }) { const [items, setItems] = useState(alerts); return ( <>
紧急1
警告2
处理中1
今日已关闭8
{items.map((item) => (
{item.level}{item.time}

{item.type}

{item.ref}

{item.detail}
{item.state !== "已关闭" && }
))}
); } function SettingsPage({ notify }: { notify: (text: string) => void }) { const [tab, setTab] = useState<"integration" | "accounts" | "workshops" | "mes" | "logs">("integration"); const [testing, setTesting] = useState(""); const test = (title: string) => { setTesting(title); window.setTimeout(() => { setTesting(""); notify(`${title} 接入测试完成:等待现场网络和协议参数`); }, 700); }; return (
{tab === "integration" && (
UNIFIED DEVICE ACCESS

四类硬件统一接入

现场串口、RS485 和局域网设备先连接边缘网关,再通过加密 MQTT/HTTPS 与管理平台通信。断网时本地缓存,恢复后自动补传。

EDGE-GW-01网关服务在线 · 演示配置
{integrationCards.map((item) => (
{item.icon}

{item.title}

{item.device}

{item.detail}

推荐协议
{item.protocol}
接入方式
车间边缘网关
数据保障
签名校验 · 离线补传 · 去重
))}
GATEWAY PROFILE

边缘网关配置

正式接入前需提供每台设备的品牌型号、通信协议、寄存器/数据字典和现场网络条件。
)} {tab === "accounts" && (
{people.map((person) => )}
姓名工号部门系统角色权限范围状态
{person.name}{person.id}{person.workshop}{person.role}{person.role === "管理员" ? "全部模块与系统配置" : person.role === "仓管员" ? "废料回收与设备查看" : "导体称重与 RFID 追溯"}
)} {tab === "workshops" && (
人员和机台统一引用车间主数据
{[["WS-01", "一楼新能源车间", "1F · 东侧", "5 人", "5 台"], ["WS-02", "六楼车间", "6F · 西侧", "2 人", "3 台"]].map((row) =>
{row[0]}
{row[1]}{row[2]}
关联人员{row[3]}
关联机台{row[4]}
)}
)} {tab === "mes" && (
MES CONNECTOR

接口配置

人员、机台、批次向下同步;称重结果向上推送。

最近推送

{traceRows.slice(0, 4).map((row) =>
{row.time}{row.task}
)}
)} {tab === "logs" && ( <>
日志不可修改,建议至少保留 180 天 exportCsv("系统操作日志", logs)} />
{logs.map((row) => )}
时间操作人动作详情级别
{row[0]}{row[1]}{row[2]}{row[3]}
)}
); } function TerminalPage({ notify }: { notify: (text: string) => void }) { const [wasteStep, setWasteStep] = useState(0); const [rfidStep, setRfidStep] = useState(0); const wasteFlow = [ { no: "W1", title: "身份验证", big: "请面向摄像头", detail: "支持人脸、工号、IC 卡三种身份方式", action: "模拟识别:张三" }, { no: "W2", title: "门锁已开启", big: "04:58", detail: "请进入投放,门磁与地磅正在记录", action: "模拟投放完成" }, { no: "W3", title: "投放确认", big: "38.50 kg", detail: "台秤汇总 38.50kg · 地磅增量 39.00kg · 偏差 1.3%", action: "确认并上报 MES" }, { no: "W4", title: "处理完成", big: "数据已归档", detail: "事件 EVT-20260722-001 已生成", action: "重新开始" }, ]; const rfidFlow = [ { no: "C0", title: "导体轴称重", big: "请选择称重类型", detail: "全程追溯并自动上报 MES", action: "一次称重 · 皮重" }, { no: "C1", title: "RFID 读取成功", big: "152.30 kg", detail: "E280-6102-0345 · 历史皮重偏差 < 0.5kg", action: "模拟二次称重" }, { no: "C2", title: "净重计算完成", big: "115.80 kg", detail: "毛重 268.10kg − 皮重 152.30kg", action: "上报 MES 并打印" }, { no: "C3", title: "任务完成", big: "MES I-04 成功", detail: "标签、批次、机台与净重已绑定", action: "重新开始" }, ]; const advance = (kind: "waste" | "rfid") => { if (kind === "waste") { const next = (wasteStep + 1) % wasteFlow.length; setWasteStep(next); if (next === 0) notify("废料回收终端流程已重置"); } else { const next = (rfidStep + 1) % rfidFlow.length; setRfidStep(next); if (next === 0) notify("RFID 称重终端流程已重置"); } }; return (
{[ { label: "人脸识别回收箱终端", color: "green", current: wasteFlow[wasteStep], step: wasteStep, click: () => advance("waste") }, { label: "RFID 导体称重终端", color: "blue", current: rfidFlow[rfidStep], step: rfidStep, click: () => advance("rfid") }, ].map((terminal) => (
{terminal.label}ANDROID INDUSTRIAL HMI
{terminal.current.no}
{terminal.current.title}

{terminal.current.big}

{terminal.current.detail}

{[0, 1, 2, 3].map((dot) => )}
))}
); } function exportCsv(name: string, data: unknown[]) { const rows = data.map((item) => (Array.isArray(item) ? item : Object.values(item as Record))); const headers = data.length && !Array.isArray(data[0]) ? Object.keys(data[0] as Record) : []; const csv = [headers, ...rows].map((row) => row.map((cell) => `"${String(cell ?? "").replaceAll('"', '""')}"`).join(",")).join("\n"); const blob = new Blob([`\ufeff${csv}`], { type: "text/csv;charset=utf-8" }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = `${name}.csv`; link.click(); URL.revokeObjectURL(link.href); } export default function Home() { const [page, setPage] = useState("dashboard"); const [role, setRole] = useState("admin"); const [clock, setClock] = useState("00:00:00"); const [date, setDate] = useState(""); const [sidebarOpen, setSidebarOpen] = useState(false); const [toast, setToast] = useState(""); const [liveWeight, setLiveWeight] = useState(12.61); useEffect(() => { const tick = () => { const now = new Date(); setClock(now.toLocaleTimeString("zh-CN", { hour12: false })); setDate(now.toLocaleDateString("zh-CN", { month: "2-digit", day: "2-digit", weekday: "short" })); }; tick(); const timer = window.setInterval(tick, 1000); return () => window.clearInterval(timer); }, []); useEffect(() => { const timer = window.setInterval(() => setLiveWeight((value) => Math.max(0, value + (Math.random() - 0.5) * 0.03)), 1500); return () => window.clearInterval(timer); }, []); useEffect(() => { const allowed = navGroups.flatMap((group) => group.items).find((item) => item.key === page)?.roles.includes(role); if (!allowed) setPage("dashboard"); }, [page, role]); const notify = (text: string) => { setToast(text); window.setTimeout(() => setToast(""), 2600); }; const current = pageMeta[page]; const roleLabel = { admin: "管理员", warehouse: "仓管员", operator: "产线工人" }[role]; const pageContent = useMemo(() => { if (page === "dashboard") return ; if (page === "waste") return ; if (page === "events") return ; if (page === "floor") return ; if (page === "trace") return ; if (page === "assets") return ; if (page === "alerts") return ; if (page === "settings") return ; return ; // eslint-disable-next-line react-hooks/exhaustive-deps }, [page, liveWeight]); return (

{current.title}

{current.sub}

MES 已连接
{clock}{date}
王
当前视角:{roleLabel}
{pageContent}
{sidebarOpen &&
); }