---
title: 玉衡平台复现 — 总纲 Spec
author: 朱奕樟
date: 2026-04-29
canonical: https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec
---

# 玉衡平台 (Yuhen / Alioth) 复现 — 总纲 Spec v2

> 目标：基于两份 PPT 复现「玉衡 工业智能 / AI 设备管理 SaaS」全平台。**演示 / 招标用**，所有数据仿真模拟。最终部署到 **Cloudflare Pages + Workers**，主入口由用户后续指派域名；规格文档（本 spec 及子 spec）发布到 lab blog (`blog.sysu-sam.com`)。

---

## 1. 来源与边界

| 来源 | 内容 |
|------|------|
| `玉衡系统截图 目标页面 20250908.pptx` | 4 张幻灯片，14 张目标 UI 截图（PC + Mobile + 单机） |
| `20230607_玉衡工业智能产品介绍.pptx` | 产品架构、TDO 框架、3 大产品模块、品牌色板 |

**复现策略**：
- 演示用 → 数据仿真模拟（mock + 预生成时序）
- 真实工程结构（不是 prototype）→ 用 Cloudflare 全栈，便于迭代到真实接入
- 跳过 image18（3D 组态看板，未来用 Three.js 单独做）

**最终部署**：
- 应用 → Cloudflare Pages（前端） + Cloudflare Workers（API）
- Spec → lab blog (`blog.sysu-sam.com/@zhuyizhang/yuhen-spec-*`)

---

## 2. 技术栈决策

### 2.1 为什么用 Cloudflare-native（而不是静态 HTML 内嵌 JSON）

| 维度 | 静态 HTML（已弃） | Cloudflare 全栈（采用） |
|------|------------------|--------------------------|
| 真实性 | mock 全 inline，没有架构层级 | 前后端分离 + 真 DB 表，更接近生产形态 |
| 可演示能力 | 只能看死页面 | 可演示"查询时延"、"多设备数据切换" |
| 扩展性 | 改一处要重发 14 个文件 | 改 worker 或 D1 即可 |
| 域名/Auth | blog 子路径 | 自定义域名 + 可选 Cloudflare Access |
| 维护成本 | 短期低，长期高 | 一次配 worker，长期低 |
| 复杂度 | ★ | ★★ |

→ **采用 Cloudflare 全栈**。复杂度可控，技能栈用户已具备权限。

### 2.2 选型

| 层 | 技术 | 理由 |
|----|------|------|
| **前端框架** | Vite + React 19 + TypeScript | 主流、组件复用最高、ECharts 生态好 |
| **路由** | React Router v6 (data router) | SPA 多页路由，适合 14 页结构 |
| **样式** | Tailwind v4 (CSS-first) + 少量自写 CSS variables | 快速 + frontend-design skill 推荐 |
| **图表** | ECharts 5（核心）+ 偶用 visx 处理特殊图 | 工业 SaaS 标配 |
| **图标** | Lucide React + 部分手绘 SVG | 不堆"圆角图标"，节制使用 |
| **状态** | Zustand（局部）+ React Query（fetch 缓存） | 比 Redux 轻 |
| **后端框架** | Hono on Cloudflare Workers | 极小、TS 原生、与 Pages Functions 兼容 |
| **数据库** | Cloudflare D1（关系数据） + KV（缓存聚合） + R2（图片资产） | 全部 Cloudflare-native |
| **ORM** | Drizzle ORM（D1 schema） | 类型安全，迁移友好 |
| **时序数据** | D1 表 `metrics_timeseries`（少量天数）+ 服务端聚合 | 无需 Analytics Engine，演示足够 |
| **部署** | Cloudflare Pages（前端，自动 build）+ Workers（API） | wrangler deploy 一键 |
| **域名** | 用户后续指定（暂用 `*.pages.dev`） | — |
| **CI** | GitHub Actions → Cloudflare Pages | 后续接 |

### 2.3 仓库结构（monorepo）
```
yuhen/
├── apps/
│   ├── web/                  # Vite + React 前端 → Pages
│   │   ├── src/pages/        # 14 个页面组件
│   │   ├── src/components/   # 公共 UI
│   │   ├── src/lib/api.ts    # fetch wrapper
│   │   └── src/styles/       # tailwind.css
│   └── api/                  # Hono Worker → Workers
│       ├── src/routes/       # /api/devices, /api/tasks, ...
│       ├── src/schema/       # Drizzle schema
│       └── src/seed/         # 仿真数据生成器（一次性）
├── packages/
│   ├── shared/               # 共享类型 / 工具
│   └── ui/                   # 共享组件 (可选)
├── data/                     # 预生成的 seed CSV / SQL
├── spec/                     # 本目录：规格文档
│   ├── https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec
│   ├── https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-01-frontend
│   ├── https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-02-backend
│   ├── https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-03-data
│   ├── https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-04-model
│   ├── https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-05-display
│   └── https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-06-deploy
├── extracted/                # PPT 原始资产（已生成）
├── pnpm-workspace.yaml
└── README.md
```

---

## 3. 模块拆分 → 6 份 sub-spec

| # | 模块 | Sub-spec | 关注点 | 决策权 |
|---|------|----------|--------|--------|
| 01 | **前端 / UI** | `https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-01-frontend` | 视觉系统、14 页结构、组件库、路由、状态、设计系统、frontend-design 反 AI-slop | UI 决策权 |
| 02 | **后端 / API** | `https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-02-backend` | Hono 路由、middleware、auth mock、API 契约、JSON Schema | API 决策权 |
| 03 | **数据 / Schema & Seed** | `https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-03-data` | D1 schema (Drizzle)、KV 聚合 key、仿真数据生成算法、设备拓扑 | Schema 决策权 |
| 04 | **算法 / 智能引擎** | `https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-04-model` | OEE / SPC / PHM / APC 的数学定义、可解释 mock 实现、引擎位置（Worker 还是浏览器） | 算法决策权 |
| 05 | **大屏 / Mobile / 单机** | `https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-05-display` | 大屏看板 1920x1080、Mobile A 端 5 页、单机 MachineProphet | 特殊形态决策 |
| 06 | **部署 / 发布** | `https://blog.sysu-sam.com/@zhuyizhang/yuhen-spec-06-deploy` | wrangler 配置、D1 migration、KV namespace、域名、Pages 配置、Spec 发布到 lab blog | 运维决策权 |

每个 sub-spec 由独立 planner agent 写。

---

## 4. 视觉规范 — 中大绿主题（不变）

### 4.1 主色（中山大学官方 VI）
- 中山大学标准色 CMYK(100, 0, 100, 60) → RGB(0, 102, 0) → **`#006633` 中大绿**
- 辅红 `#990033` (CMYK 30/100/100/50)

### 4.2 调色板（OKLCH，避免纯黑/纯白）
| Token | Hex | OKLCH | 用途 |
|-------|------|-------|------|
| `--brand-700` | `#004D26` | oklch(34% 0.10 152) | 顶栏深色、Hover |
| `--brand-600` | `#006633` | oklch(43% 0.13 152) | **主品牌色 中大绿** |
| `--brand-500` | `#0F8A4A` | oklch(56% 0.13 152) | 链接、激活态 |
| `--brand-300` | `#7DC9A2` | oklch(78% 0.10 152) | 浅色装饰、tag |
| `--brand-100` | `#E6F4EC` | oklch(95% 0.03 152) | Side bar 选中 |
| `--brand-50`  | `#F2F9F5` | oklch(97% 0.02 152) | 页面背景 |
| `--accent`    | `#990033` | oklch(40% 0.15 17)  | 异常/告警 |
| `--accent-amber` | `#C2933E` | oklch(67% 0.10 80) | 警示黄（饱和降） |
| `--ink-900` | `#0F1A14` | oklch(18% 0.01 152) | 主文字 |
| `--ink-700` | `#33453B` | oklch(36% 0.02 152) | 副文字 |
| `--ink-500` | `#5C6B62` | oklch(52% 0.01 152) | 占位 |
| `--ink-200` | `#D2DBD5` | oklch(86% 0.01 152) | 分割线 |
| `--paper`   | `#FBFCFB` | oklch(99% 0 0) | 卡片背景 |

中性灰**全部带 152 色相微染**，与主色协同。

### 4.3 图表色序列
```
['#006633', '#0F8A4A', '#7DC9A2', '#C2933E', '#990033', '#5C6B62']
```
正向 = 绿系，负向 = 红，警示 = 琥珀。**禁止** cyan/purple/neon。

### 4.4 排版（依 frontend-design skill）
- **Display**：`'Noto Serif SC', 'Source Han Serif SC', serif`（中文衬线，学术调）— 标题、KPI 数字
- **Body**：`'Inter Variable', 'PingFang SC', system-ui` — 正文（Inter 用 Variable 版本，避免太"AI 标准化"）
- **Mono**：`'JetBrains Mono'` — 设备 ID / 编号

字号梯度（fluid clamp）：
```css
--fs-h1: clamp(28px, 2.6vw, 36px);
--fs-h2: clamp(20px, 1.8vw, 24px);
--fs-h3: 16px;
--fs-body: 14px;
--fs-caption: 12px;
--fs-kpi: clamp(32px, 3.2vw, 44px);
```

### 4.5 间距 / 圆角 / 阴影
- 顶栏 56 / 侧栏 208 / 主区 padding 20
- 卡片：`radius: 10px`，阴影 `0 1px 2px rgba(15,26,20,.06), 0 4px 12px rgba(15,26,20,.04)`
- 间距节奏：8 / 12 / 16 / 24 / 32 / 48 — 不要每处都 20

### 4.6 反 AI-slop 自检
✗ cyan-on-dark / purple-blue gradient
✗ 大圆角图标矩阵堆在标题旁
✗ KPI 渐变文字
✗ 滥用 glassmorphism
✗ 装饰性 sparkline
✓ 中大绿 + 暖灰 + 暗红警示，三色克制
✓ 学术气：衬线大标题 + 紧凑表格 + 真实数据密度
✓ 工业 SaaS：信息密度 ≥ 美观度

---

## 5. 全局架构

```
                    ┌──────────────┐
                    │ Cloudflare   │
                    │ Pages        │  React SPA (web/)
                    │  (yuhen.app) │
                    └──────┬───────┘
                           │ /api/*
                           ▼
                    ┌──────────────┐
                    │ Workers      │  Hono Router (api/)
                    │  /api/...    │
                    └──┬───────┬───┘
                       │       │
                  ┌────▼──┐  ┌─▼────┐
                  │  D1   │  │ KV   │
                  │ 主数据 │  │ 聚合 │
                  └───┬───┘  └──────┘
                      │
                  ┌───▼───┐
                  │  R2   │  设备图片、报告 PDF
                  └───────┘
```

调用链路：浏览器 → Pages SPA → fetch /api/* → Workers Hono → D1 查询 + KV 缓存 → 返回 JSON。

---

## 6. 数据流向（横切）

| 类型 | 存储 | 大小预估 |
|------|------|---------|
| 组织树（车间/产线/工段/设备） | D1 表 `org_tree` | ~50 行 |
| 设备主数据 | D1 表 `devices` | ~30 台 |
| 时序（健康分/OEE/振动） | D1 表 `metrics_ts`（90 天 × 30 设备 × 3 指标 × 24 小时点 = ~190k 行） | < 50 MB |
| 告警事件 | D1 表 `alerts` | ~500 行 |
| 任务（维修/保养/点检） | D1 表 `tasks` | ~200 行 |
| 报告 | D1 表 `reports` | ~50 行 |
| 备件/库存/费用 | D1 表 `assets`/`costs` | < 100 行 |
| 聚合 KPI 缓存 | KV，key=`agg:group:{id}` | < 1 MB |

**初始化**：`pnpm seed`脚本读 `data/seed.sql` → wrangler d1 execute。

---

## 7. 模块边界与契约

### 7.1 前端 → API
- 全部走 `fetch('/api/...')`（同源，无需 CORS）
- 错误统一 `{ ok: false, code, message }`
- React Query 缓存所有 GET，5 分钟 stale

### 7.2 数据 → 算法
- 算法逻辑放在 Worker 端（`apps/api/src/lib/metrics.ts`）
- 健康分、OEE 等聚合在 KV 缓存，每次写入新告警时失效

### 7.3 大屏 / Mobile / 单机
- 大屏 → 同 API，独立路由 `/big-screen`，独立布局
- Mobile → 复用 API，路由 `/m/*`，独立 layout（无侧栏）
- 单机 → 独立 brand 主题（白底红绿），路由 `/standalone`，复用部分组件

### 7.4 Spec 发布
- 6 份 markdown sub-spec → 通过 md-blog skill 发布到 lab blog
- 每份独立 slug：`yuhen-spec-frontend`、`yuhen-spec-backend` 等
- 总纲 `yuhen-spec`（visible: true），子 spec（visible: false 但可访问）

---

## 8. 验收里程碑

| 里程碑 | 内容 | 产出 |
|--------|------|------|
| **M0** ✅ | 总纲 + 6 份 sub-spec | `spec/*.md` |
| **M1** | 项目脚手架 + D1 schema + 仿真数据 seed | `apps/`, `data/seed.sql` |
| **M2** | 前端公共组件 + 设计系统 | `apps/web/src/components/`, `tailwind.css` |
| **M3** | 14 个 PC 页 | `apps/web/src/pages/*.tsx` |
| **M4** | API 完整覆盖 | `apps/api/src/routes/*.ts` |
| **M5** | Mobile + 大屏 + 单机 | `/m/*`, `/big-screen`, `/standalone` |
| **M6** | 部署 Pages + Workers，spec 发布 blog | URL 列表 |

---

## 9. 反共识 / 反 AI-slop 立场（项目约定）

- **不要 dashboard 套餐图**：14 张同尺寸卡片堆在一起 = 失败
- **表格行高 40-44px**，不是 56+
- 「大数字 + 小说明」招牌指标只用一次，其他用其它范式
- 衬线标题 + 中大绿 + 暖灰，整体偏「学院 / 院所」调性，不是「创业 SaaS」调性
- 真实工业 SaaS：信息密度 ≥ 美观度，不要过度留白
- 不放空状态插图占整页，要么不加，要么用极简 Lucide 图标 + 一行文案

---

## 10. Sub-spec 写作规范（给 6 个 planner agent）

每份 sub-spec **必须**包含：
1. **模块边界**：「我做 / 我不做 / 与谁交接」
2. **决策与 trade-off**：每个选型都要有 1-2 句理由
3. **可执行精度**：executor agent 拿了能直接 code，不要 "TODO / 后续"
4. **依赖于其他 sub-spec 的部分**：明确引用文件名
5. **验收标准**：3-5 条 testable 条件

字数：1500-3500，中文为主，code/schema 用英文。
