# HTML 报告生成规范(活动视图模版)
> 2026-08-02 初版,从《活动开发规范》拆出成册。基准:`activities/quote-intake@42` 的三张模版
> (`intake-overview` / `intake-classified` / `intake-release`)—— 用户验收通过的那套。
> 渲染引擎:`apps/api/src/modules/interaction/internal/domain/view-render.ts`;
> 容器:`packages/ui-kit/src/ViewContainer.tsx`;保存链路:`apps/api/src/app/routes/desk.ts`。
> **本文与代码冲突时以代码为准。**
---
## 1. 报告是什么:同一份 HTML 的三个身份
| 身份 | 在哪 | 状态 |
|---|---|---|
| **件**(给人看) | 工作区 · 待处理 | 锁定态 —— 进屏先是一份能读的报告,不是一张待填的表 |
| **表单**(给人改) | 工作区 · 待处理,按「编辑」后 | 可改格子现出输入件,能增删行 |
| **归档件**(存档) | 工作区 · 工作成果 | 同一份 HTML 原样定稿,零输入框零外壳 |
**为什么必须是同一份**:渲成两三套就是存两三份,存多份就会分叉,分叉之后没人看得出
以哪份为准。**看与改是两件事**(整份件一进屏就是编辑态,人还没决定改什么,
眼前已经是 54 个亮着的输入框 —— 踩过),但**看的与改的必须是同一份东西**。
三条硬规矩:
1. **一单一模版贯穿始终**:同一个局面,在办与归档用同一张;局面变了换张
(A1 三张:受理需求 → 料号分类 → 受理记录),不在一张里堆条件分支。
2. **剧本每步的 `view` 定推哪张**,写清单时定死,不归模型临场挑;放行那一步的
`view` 必须指向验收目标件 —— 最后摆上去的那张就是归档件。
3. **数据变了基座重渲**(repush):人在件上保存、在卡上回填,落草稿层后服务端拿
新数据重渲同一张 —— 由数据算出来的东西(类徽、分布条、去向)跟着一起变。
**手改的全部意义就是纠正 AI 的错,改了数而画面不动,这个兜底功能就是假的**(用户原话级教训)。
## 2. 报告与卡(表单)的关系 —— 这套里最容易做错的一章
三个区各归各位(2026-08-01 用户定,别再混):
| 区 | 放什么 | 不放什么 |
|---|---|---|
| **工作区 · 待处理** | **只放那份件**(活动的 HTML 模版),整篇铺满工作区宽度、可就地改 | 题头 / 编号带 / 依据带 / 判据表 / 指导书 / 提交钮 —— 一样都不要 |
| **工作区 · 工作成果** | 同一份件,定稿归档 | 同上 |
| **对话区的卡** | 作业指导书(steps/standard)· 依据 · 判据 · **整单级表单字段** · **提交与裁决** · 落地结果 | 件本身(给一枚「执行工单/验收工单」四字钮跳过去就够) |
分工的原则:**逐行数据归件,整单级归卡;编辑在件,提交在卡。**
- 一张 9 行的清单,人要逐格核、逐格改 —— 那是件的事(报告即表单);
- 「结果说明」这种整单一格的字段、「先受理还是退回」这种裁决 —— 那是卡的事;
- 卡上那枚提交钮是**唯一的交卷动作**;件上的保存只落草稿层,**工单不办结、
放行门的位置不动**。
**同一个数管三处(同源纪律)**:台账声明 `required` 产出的 `域_对象_missing`——
```
件上的缺项红带(gapBar) ←┐
卡上的提交闸(gate) ←┼── 同一个基座读数、同一套算法
放行门的 L1 机检 ←┘
```
三处各算各的,就会出现「件上说齐了、卡上说不能交、放行门又说行」——
那种矛盾人是判不出对错的。所以**模版与卡都不算这个数,只摆基座算好的**。
**保存链路(件 → 库 → 屏)**:
```
人按「保存」→ 前端按件自己说的话收值(data-edit 地址 + data-col 列名 + drops)
→ POST /tasks/:id/table {tables, drops}
→ 服务端按【活动清单的台账声明】过授权门,归型,只写真改了的行,
自增主键补号,draftDrop 撤显式点名的行
→ 留一条人痕(改了几行/新增几行/撤掉哪几行 —— 那条痕是下一拍模型的行动指南)
→ repush 重渲 → 闸、判据、类徽、分布条、去向全部跟着变
```
三条实测教训,每条都别再犯:
- **授权面是台账声明,不是「当下那张卡长什么样」** —— 从前保存借道工单 `blocks[i]`
取列名,熔断卡(没有 blocks)开着时,人补的那一格一个字都送不出去,
而他要补的恰恰是把这一单卡住的必填格;
- **删要说出来**(载荷 `drops` 点名主键),不靠「没回传就是删了」—— 那种口径会把
一次不完整的回传变成一次删表;
- **只写真改了的行** —— 件上按一次保存整张表都回传,照单全写会留「修订 4 行」的痕,
而人只改了一格;日志是给人看的,那句话就成了假的。
**卡侧还有一条老路**(与件侧并存,别混):卡面 `blocks` 里绑了 `ledger`+`fields` 的表,
由**基座铺行**(发卡那一刻从草稿层铺,活动别自己抄行 —— 回复有长度上限,铺长了截断
整拍作废)、人提交时由**基座写回**(`table:N` 载荷)。验收卡的 `editableAtRelease`
成果表走的就是它。两条路的授权面是同一份台账声明。
## 3. 沙箱与安全
- 容器:`sandbox="allow-same-origin"` 的 iframe,**永不给 `allow-scripts`**
(两者同给才是破沙箱)。父文档读得到也改得动里面的 DOM:开编辑 = 加 class、
收值 = 读 input,全在父文档侧;**iframe 里始终一行脚本都没有**。
- 模版含 `