# premise 这是我在展会看到的友商上位机 UI(`assets/paxini.jpg`)的风格参考,希望做一份相近气质的前端界面。前端由你负责实现与迭代。 ## 约束(长期有效) - 使用 `Svelte + TypeScript` - 页面拆分成 `HudPanel`、`CenterStage`、`SignalChart` - 不引入大型 UI 库 - 主色调:近黑背景 + 青蓝 / 荧光绿 / 橙红 - 主界面整体是“一整块 board”,通过渐变过渡,不做强分块 - 不做明显流光/扫光动画(可有静态纹理层次) - 样式集中在组件内或 `theme` 文件 - 布局优先 `grid/flex`,仅在必要位置使用绝对定位 - 后续对接 Tauri,组件层不要把数据源写死 --- # 当前实现基线(已完成) ## Step1:主背景 - 已完成近黑全屏背景、弱渐变、暗角和轻噪声层 - `:root/html/body` 背景已与主界面统一,避免拖动窗口出现黑边断层 ## Step2:TitleBar + ControlBar + 主布局 - 已完成窗口按钮(最小化 / 最大化切换 / 关闭) - 已完成 ControlBar: - 配置菜单组(打开/关闭/校准/参数) - 连接状态卡片 - 串口下拉框 - 中英文切换 - 设备信息行(设备/采样率/通道) - 页面主结构已固定为:`TitleBar + ControlBar + WebGL2 Area` ## Step2-1:Tauri 交互与窗口体验 - TitleBar 已支持拖动(`data-tauri-drag-region`) - 默认窗口大小自动尝试调整到屏幕约 `3/4` - 已连接 Tauri window commands: ```ts invoke("win_minimize"); invoke("win_toggle_maximize"); invoke("win_close"); ``` ## Step3:HUD 面板与数据演示 - 左右两侧悬浮曲线面板已完成 - 有数据时入场并渲染,无数据时退场(当前 demo:5s 开/关周期) - 折线、渐变填充、边框与深色半透明底已完成 - 底部量程条已完成 --- # 当前前端结构(请按此扩展) ## 1) `src/routes/+page.svelte` - 页面编排与状态管理 - 维护 `signalPanels`、语言、连接状态、串口、配置菜单选中态 - 将左右面板分流后传给 `CenterStage` - 处理 Tauri invoke(窗口控制) ## 2) `src/lib/components/HudPanel.svelte` - 只负责顶部控制区 UI 与交互事件分发 - 通过事件向外抛出: - `windowcontrol` - `localechange` - `configlink` - `portchange` ## 3) `src/lib/components/CenterStage.svelte` - 承载 WebGL2 主区域和左右悬浮 rail - 接收 `leftPanels/rightPanels` 并在 rail 内渲染 `SignalChart` - 通过 `ResizeObserver` 动态计算: - `panel-zone` 顶部起始位置 - 左右 rail 缩放比例(低高度窗口可自适应) - 侧边定位策略:贴边但保留 `edge inset` 安全距离 ## 4) `src/lib/components/SignalChart.svelte` - 单个曲线面板渲染组件 - 仅消费结构化数据,不关心数据来源 - 支持 `side`(left/right)与 `active`(入场/退场)状态 --- # 关键布局规则(避免回归) - WebGL2 区域占据下半主容器 - HUD 曲线面板浮在 WebGL2 上方,但贴近左右边缘,不遮挡中间核心区域 - 左右面板必须挂在 `CenterStage` 的 rail 容器内 - 不再使用命名 slot 传曲线面板(曾导致面板落到错误容器) - 调试用彩色边框已移除,保持正式视觉 --- # 数据接入约定(统一结构) ```ts type SignalTone = "cyan" | "lime" | "orange"; type SignalPanelSide = "left" | "right"; interface HudSignalSeries { id: string; tone: SignalTone; points: number[]; } interface HudSignalIcon { id: string; label: string; tone: SignalTone; } interface HudSignalPanel { id: string; code: string; title: string; side: SignalPanelSide; active: boolean; series: HudSignalSeries[]; icons: HudSignalIcon[]; } interface HudPacket { ts: number; panels: HudSignalPanel[]; } ``` 统一入口: ```ts let signalPanels: HudSignalPanel[] = []; function applyPacket(packet: HudPacket) { signalPanels = packet.panels; } ``` --- # Step3 数据源 Demo(可替换) ## Demo A:本地 Mock ```ts function startMockFeed(push: (packet: HudPacket) => void): () => void { let hasData = false; const cycle = setInterval(() => { hasData = !hasData; push({ ts: Date.now(), panels: hasData ? buildRandomPanels() : buildInactivePanels() }); }, 5000); const tick = setInterval(() => { if (hasData) { push({ ts: Date.now(), panels: evolvePanels() }); } }, 1200); return () => { clearInterval(cycle); clearInterval(tick); }; } ``` ## Demo B:Tauri invoke 轮询 ```ts import { invoke } from "@tauri-apps/api/core"; async function fetchHudPacket(): Promise { return await invoke("hud_get_packet"); } ``` ## Demo C:Tauri 事件推送 ```ts import { listen, type UnlistenFn } from "@tauri-apps/api/event"; async function startTauriStream(push: (packet: HudPacket) => void): Promise<() => void> { const unlisten: UnlistenFn = await listen("hud_stream", (event) => { push(event.payload); }); return () => unlisten(); } ``` ## Demo D:WebSocket ```ts function startWebSocket(push: (packet: HudPacket) => void): () => void { const ws = new WebSocket("ws://127.0.0.1:9001/hud"); ws.onmessage = (e) => push(JSON.parse(e.data) as HudPacket); return () => ws.close(); } ``` --- # 其他接口建议(主控区联动) ```ts invoke("sensor_connect", { port: "COM3" }); invoke("sensor_disconnect"); invoke("sensor_set_sample_rate", { hz: 120 }); invoke("sensor_set_channels", { channels: [1, 2, 3, 4] }); invoke("sensor_start_stream"); invoke("sensor_stop_stream"); invoke("sensor_get_status"); ``` --- # step3 前端细节调整 1. 添加对串口连接失败的提醒 2. 连接成功后,连接按钮变成断开按钮点击就断开连接 # step 4 webgl2 1. 黑色背景,科技感 UI 容器 2. 一个 64x64 的压力矩阵 3. 用 InstancedMesh 渲染 4096 个小圆柱或小圆环 4. 每个实例根据 value 更新位置、缩放、颜色 5. 支持 OrbitControls 旋转缩放 6. 内置一个假数据生成器,能模拟脚印从无到有再消失 7. 数据更新用平滑插值,不要跳变 8. 左侧显示总压力、最大值、平均值 9. 支持 2D/3D 模式切换,但第一版 2D 可以只是俯视正交投影 # step4 config panel 我需要一个panel来进行参数配置,有以下配置(后面会加) 1. 点阵数量,目前默认是64*64,我希望可以自定义 2. range上下限,我希望可以自定义max和min来映射颜色