汇报流:活动按步序位置推视图,容器沙箱渲染

「flow:按**步序位置**推送声明的模版」(AM 07)。原先这条链只有写面没有读面,
活动的 viewTemplates 是一行占位字符串,工作台视图区永远只显示一份硬编码概览。

补齐整条链:
  · 步声明新增 `view`:这一步呈现哪张模版 —— 推什么由声明给定,不由临场决定。
  · 新增模版填数的纯函数(interaction/domain):`data-slot` 取值、`data-rows` 逐条展开。
    **填数只做取值与转义**,没有表达式、没有分支、没有循环 —— 一旦模版能算,
    它就成了代码,容器的「无脚本执行权」也就形同虚设。填充**消费槽位**,
    交出去的是干净 HTML,不是带模板属性的半成品。
  · interaction 补读面 `viewOf`;desk 路由把推送的 HTML 交给前端;
    ViewRegion 经 `@ui` 的 ViewContainer(sandbox="" 的 iframe)渲染 —— feature 不碰 innerHTML。
  · A1 写了三张真模版(事态全景 / 打标判据 / 成果预览),内联自带样式
    (沙箱页面不继承工作台 CSS),按步序位置挂在 S3 / S5 / S6 上。

沿路两处修:
① 取值路径遇到**本身带点号的键**(台账地址 `域.对象`)会被拆成两级、永远取不到。
   改为整键优先、找不到再下行;`view-render` 与 `step-sequence` 两处同修。
② 「本单交付」不能画成台账全表:任务视图是正式版 + 我的草稿的合并结果,
   直接拿来列会把**别单**放行过的记录一并列出。本单自己写下的行单独放在
   `mine:域.对象` 下(整键,取值不歧义)。

另:视图推送失败原先静默吞掉 —— 会让「视图区一直空着」变成查不出原因的怪事;
改为留痕(仍不影响步序推进,呈现是投影)。

实测:一封 RFQ 入站 → 放行前那一屏列出本单交付 LM358DR / TI / 12000 / 8.2 / 10周,
以及 Type A「直接快答」的分类结论与四项打标判据。

gates: tsc / eslint / depcruise(321 模块)/ api-report / 423 测试 全绿

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-27 10:47:20 +08:00
co-authored by Claude Opus 5
parent abd371b51c
commit ac2fecd005
29 changed files with 574 additions and 10 deletions
@@ -9,7 +9,7 @@ import type { TaskDetail, ViewBlock, WorkOrder } from '@contracts';
import { ACTION_EFFECT_LABEL, WORK_ORDER_MODE_LABEL } from '@contracts';
import { Ico } from '@web-shared/Ico.tsx';
import ScrollArea from '@web-shared/ScrollArea.tsx';
import { ViewBlocks } from '@ui';
import { ViewBlocks, ViewContainer } from '@ui';
type Tab = 'todo' | 'result' | 'detail' | 'log';
@@ -65,7 +65,16 @@ export function ViewRegion({ detail, onReply }: ViewRegionProps) {
open.map((order) => <WorkOrderCard key={order.id} order={order} onReply={onReply} />)
))}
{tab === 'result' && <ViewBlocks blocks={detail.view as ViewBlock[]} />}
{/*
活动推的 HTML 优先:那是它按当下局面组织的**完整一屏**;
没推 HTML 时退回结构化块。两者都经 ViewContainer 渲染 —— feature 不碰 innerHTML。
*/}
{tab === 'result' &&
(detail.viewHtml === undefined ? (
<ViewBlocks blocks={detail.view as ViewBlock[]} />
) : (
<ViewContainer html={detail.viewHtml} title={`${detail.card.activity} · 视图`} />
))}
{tab === 'detail' && (
<div className="jc-vr-detail">