/** * ACT-WORK-05 差异评估的 HTML 报告模版 —— **活动自己持有的资产**,随包版本冻结。 * * 五个局面各一张,版面语言**逐块搬自** `docs/SS02-ACT-WORK-05-html/` 的四份目标件 * (《HTML报告生成规范》§7:有目标件时「搬」,不要「照着做」——样式表整段搬,标记逐块搬, * 只把写死的数据换成绑定): * · `p08-master-create` —— S0d 摆,数据工单(P-08 芯片主数据),搬自「数据工单-01」 * (2026-08-27:补齐目标件里原本就有、之前漏搬的【创建什么 · 表 2】标准化参数子表 `parts.parameter` * —— 用户核对实跑页面后指出「页面结构必须与目标件一致」,目标件本就是两张表); * · `p10-document-create` —— S0d 摆,数据工单(P-10 原厂技术资料),**没有自己的目标件** * (规格里数据工单是「一个统一模板按目标台账实例化」),复用「数据工单-01」的样式表与版面骨架; * · `eval-input-list` —— S0/S1 摆,评估输入清单,搬自「执行工单-01」; * · `difference-review` —— S2/S3/S4 摆,差异评估结论审核,搬自「验收工单-01」; * · `landing-confirm` —— S5/T_release 摆,评审放行与落账确认,搬自「验收工单-02」。 * * 目标件的样式表逐字节存在 `./target-style.ts`(`P08_CSS`/`EXEC_CSS`/`ACC1_CSS`/`ACC2_CSS`, * 抽取与 diff 验证过程见 task-5-report.md)。**这份文件只做取值绑定,不算不判**; * `BIND_COMMON`/`BIND_P08`/`BIND_EXEC`/`BIND_ACC1`/`BIND_ACC2` 是平台绑定层必须新增的东西 * (目标件是写死数据的静态demo,没有「N 行草稿」「编辑态与归档态二选一」这些平台概念): * 缺项红带、编辑闸/增删行按 `html.editable` 收起、序号列换成 CSS 计数器(目标件靠 JS 脚本 * 重排数字,沙箱零脚本改用它)、`data-class-of` 的 `v-*` 别名(枚举列不许摆 ASCII 原值 —— * 有色徽标的地方原样复制目标件自己那几条颜色声明当 `v-` 版,目标件本来是纯文字的地方 * 就只补 `::after` 文案、不额外配色)。 */ import { P08_CSS, EXEC_CSS, ACC1_CSS, ACC2_CSS } from './target-style.ts'; import { D } from '../constants.ts'; /* ==================================================================================== * 绑定层公共开关 —— 五张模版共用,与目标件自己的样式表分开存(那份要保持逐字节可比) * ==================================================================================== */ const BIND_COMMON = ` /* ── 2026-08-27 用户四连指出的件上毛病,病根三处都在「目标件的样式与平台的编辑态没对上」 ── */ /* ① 表宽超出就得能横着滑(E04/I01 08-23 同款):目标件把 overflow-x:auto 写在 .tbl-wrap 上,而平台包装是 .jc-tbl-wrap,谁都没管到 */ .jc-tbl-wrap { overflow-x: auto; overflow-y: hidden; } /* ② 编辑态只留输入格:目标件 #tbl1 .jc-vcell{display:inline}(id 特异度)盖过了 table.on td>.jc-vcell{display:none}, 于是表 1 每格都是「只读字 + 输入框」并排 —— 内部料号那格恰好有字,人看见的就是它 */ #tbl1.on td > .jc-vcell:not(.ro) { display: none; } /* ③ 一行里的 input 与 select 同高同线:select 天生比 input 矮 4px,编辑态里一排格子高低不齐 */ input.jc-cell, select.jc-cell { height: 32px; box-sizing: border-box; padding-top: 0; padding-bottom: 0; vertical-align: middle; } /* ④ 加行 / 删行要看得见(2026-08-27 用户:「加一行后点旁边的叉没有反应」):E04 那套记号样式挂在它自己的 外壳类下,本件按目标件重做后表是 table.on —— 一条都没跟过来,× 按了类是切了、屏上没动静 */ table.on tr.jc-row-new td { background: #f2fbf7; } table.on tr.jc-row-gone td { background: #fff6f6; opacity: .55; } table.on tr.jc-row-gone .jc-cell { text-decoration: line-through; color: var(--ink-3); } table.on tr.jc-row-gone .jc-rowdel { border-color: var(--danger); color: var(--danger); background: #fff; } /* 只在「待处理」这一态露面;归档 / 验收只读态即便 HTML 里还带着 data-edit 也不摆闸和加行钮 (目标件是静态 demo,不区分这两态;html.editable 是平台在 iframe 根节点上真开的关) */ html:not(.editable) .jc-editgate, html:not(.editable) .jc-addrow { display: none !important; } /* 「加一行」钮跟着紧邻的表进不进编辑态现身(目标件靠 JS 脚本的 classList.toggle('show',on), 沙箱零脚本,改用 :has() 认「紧挨着的表是不是 .on」) */ .jc-tbl-wrap:has(table.on) + .jc-addrow { display: flex; align-items: center; gap: 8px; } /* 序号列交给 CSS 计数器 —— 目标件的 JS 脚本在增删行后重写 .jc-rn-num / .num 的文本, 沙箱零脚本,新增的行 / 删掉的行让计数器自己算对,不必比对哪一行手改过数字。 两种列名都要接(.rn 用于 P08/P10/执行工单 —— 目标件里那三份自己就用它; .num 用于两份验收工单 —— 目标件里那两份自己就用它,静态数字,这里换成动态计数)。 */ .tbl-wrap table { counter-reset: rn; } .tbl-wrap table td.rn { counter-increment: rn; } .tbl-wrap table td.rn .jc-rn-num::before { content: counter(rn); } .tbl-wrap table td.num { counter-increment: rn; } .tbl-wrap table td.num::before { content: counter(rn); } /* 缺项红带 —— 目标件没有这个概念(它是静态 demo,不会「缺东西」); 这是本仓的必填提示机制,与提交闸同源,配色用目标件自己 :root 里那两个 --danger 变量 (四份目标件的 :root 对这两个变量给的是同一组值,借用不算改目标件的数值)。 */ .jc-gapbar { display: none; } html.editable .jc-gapbar { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 10px; padding: 9px 12px; border-radius: 9px; background: #fff5f5; border: 1px solid #f3b9b5; color: var(--danger); font-size: 12px; line-height: 1.65; } .jc-gapbar b { font-weight: 800; } .jc-gapbar span { color: #9a3b36; } html.editable .jc-gapbar.v-0 { display: none; } `; /* * ==================== 徽标别名的生成器 ==================== * * **2026-08-27 实测事故**:手写的 `.jc-tag.v-pin_to_pin::after{…}` 永远匹配不上,件上显示「待定」, * 而台账里 `substitution_type` 明明是 `pin_to_pin`。病根是平台有两套互相打架的消毒规则: * `data-class-of` 投影出的 class 走 `view-render.ts` 的 `classToken`(只留 `[a-z0-9-]`, * **吃掉下划线**,`pin_to_pin` → `v-pintopin`,封顶 24 字);基座读数名走 `engines.ts` 的 * `slug`(只留 `[a-z0-9_]`,吃掉连字符)。两套规则不一样,tally 值一旦带下划线,手写的 * `.v-xxx` 选择器就必然是错的。**判据那一侧(读数名)一个字都不碰**——那已经审查通过、 * 与 `slug` 一致;这里改的只是展示层:选择器一律用 `vtok()` 从 tally 值**算**出来,不再手写。 */ /** 必须与 `classToken` 一字不差:小写、只留 `[a-z0-9-]`、封顶 24 字。 */ const vtok = (v: string): string => v.toLowerCase().replace(/[^a-z0-9-]/g, '').slice(0, 24); /** 一族徽标的 CSS:选择器与文案从 `[值, 人读名, 可选颜色声明]` 表算出来,值与文案成对写在一处 */ type BadgeSpec = readonly [value: string, label: string, color?: string]; const badgeCss = (selector: string, specs: readonly BadgeSpec[]): string => specs .map(([v, label, color]) => { const cls = `${selector}.v-${vtok(v)}`; return color === undefined ? `${cls}::after { content: "${label}"; }` : `${cls}::after { content: "${label}"; }\n${cls} { ${color} }`; }) .join('\n'); const OK = 'color: var(--ok); background: var(--ok-soft); border-color: #c9ecc2;'; const DANGER = 'color: var(--danger); background: var(--danger-soft); border-color: #f3b9b5;'; const AMBER = 'color: #8f6a12; background: #fdf3d7; border-color: #ecd9a0;'; const MUTED = 'color: var(--ink-3); background: #eef1f6; border-color: var(--line);'; /* ==================== 数据工单(P08/P10 共用):徽标别名 ==================== */ const BIND_P08 = ` /* 生命周期 / 资料类型 / 资料状态 三个枚举列共用同一个徽标类 —— 取值域互不重叠(active/nrnd/eol/ discontinued/risk 与 datasheet/app_note/… 与 valid/expired/pending/void 三组没有一个词撞车), 同一页内不必再分家。配色借的是本设计语言在别的目标件里已经出现过的那几个色(ok/danger/琥珀), 不是新配的颜色。 */ .stx { display: inline-block; font-size: 11px; font-weight: 700; padding: 1px 8px; border-radius: 999px; border: 1px solid transparent; white-space: nowrap; } ${badgeCss('.stx', [ ['active', 'Active', OK], ['nrnd', 'NRND', AMBER], ['eol', 'EOL', DANGER], ['discontinued', '停产', MUTED], ['risk', '风险', DANGER], ['datasheet', 'Datasheet', 'color: var(--ink-2); background: var(--soft); border-color: var(--line);'], ['app_note', '应用笔记', 'color: var(--ink-2); background: var(--soft); border-color: var(--line);'], ['reference_design', '参考设计', 'color: var(--ink-2); background: var(--soft); border-color: var(--line);'], ['white_paper', '白皮书', 'color: var(--ink-2); background: var(--soft); border-color: var(--line);'], ['sample_doc', '样片资料', 'color: var(--ink-2); background: var(--soft); border-color: var(--line);'], ['commitment_letter', '承诺函', 'color: var(--ink-2); background: var(--soft); border-color: var(--line);'], ['other', '其他', 'color: var(--ink-2); background: var(--soft); border-color: var(--line);'], ['valid', '有效', OK], ['expired', '过期', DANGER], ['pending', '待核实', AMBER], ['void', '作废', MUTED], ])} /* * 表 2(标准化参数集)的约束类型(硬/软)—— 目标件里这一格是字面类名 .ct.hard / .ct.soft * (P08_CSS 已带这两条颜色声明,.ct 基座形状也在);平台的 data-class-of 走的是 * v- 别名机制,不是字面枚举值,这里用 badgeCss 生成 .ct.v-hard / .ct.v-soft, * 颜色原样照抄目标件那两条声明的数值(DANGER/MUTED 与 .ct.hard / .ct.soft 同一份值)。 */ ${badgeCss('.ct', [ ['hard', '硬约束', DANGER], ['soft', '软约束', MUTED], ])} `; /* ==================== 执行工单(评估输入清单):徽标别名 ==================== */ const BIND_EXEC = ` /* 存在性核对的两列(角色 / 在库状态)与评估输入清单的状态列共用目标件自己的 .st 徽标基座 —— 取值域互不重叠(found/absent/created/abandoned 与 gap/default/ok 没有一个词撞车)。 四档配色**原样照抄目标件自己的 .st.bound / .st.ok / .st.miss / .st.def 那四条声明**,只换成 v- 版。 */ ${badgeCss('.st', [ ['found', '在库', OK], ['absent', '不在库', 'color: var(--human); background: var(--human-soft); border-color: var(--human-line);'], ['created', '本单新建', 'color: var(--brand-deep); background: var(--brand-wash); border-color: var(--brand-soft);'], ['abandoned', '已放弃', MUTED], ['gap', '待补*', 'color: var(--human); background: var(--human-soft); border-color: var(--human-line);'], ['default', '默认·可改', 'color: var(--brand-deep); background: var(--brand-wash); border-color: var(--brand-soft);'], ['ok', '齐备', OK], ])} /* 角色(原 PN / 候选 PN)是分类标签不是状态,目标件里这类描述文字从不配色,这里只补 ::after 文案 */ ${badgeCss('.rl', [ ['original', '原内部料号'], ['candidate', '候选内部料号'], ])} /* * **补充内容按行投影必填**(2026-08-27 实测:11 行全摆红「必填」胶囊,而闸只认 3 行)—— * \`sub_eval.input_gap.supplement\` 的必填是 \`requiredWhen:{status:'gap'}\`,按行不按列; * 静态 \`class="req"\` 会把每一行都标必填。改成 \`\` 按本行的 * status 值投影,只在 v-gap 那一档出红胶囊与行首红条,default / ok 两档不出。 */ td.sup.v-gap > .jc-vcell:empty::after { content: "必填"; display: inline-flex; align-items: center; font-style: normal; font-size: 10px; font-weight: 700; line-height: 1; padding: 3px 7px; border-radius: 5px; border: 1px dashed #f3b9b5; color: var(--danger); background: var(--danger-soft); } tbody tr:has(td.sup.v-gap > .jc-vcell:empty) td { background: #fffbfb; } tbody tr:has(td.sup.v-gap > .jc-vcell:empty) td.rn { color: var(--danger); font-weight: 800; } `; /* ==================== 验收工单 01(差异评估结论审核):徽标别名 ==================== */ const BIND_ACC1 = ` /* 差异四态与推荐等级**都**落在这一屏(本活动数据面的既有约定 —— 各候选推荐等级摘要与 逐项差异明细同屏摆),两者取值域都含 "conditional",光认 .st 分不清是哪一列, 加 .dst / .rec 两个字段级修饰类各自配色(**原样照抄目标件 .st.ok/.miss/.unknown/.cond 四条声明**,只是同一份颜色现在要接两条不同的人读文案)。 */ ${badgeCss('.st.dst', [ ['match', 'MATCH', OK], ['mismatch', 'MISMATCH', DANGER], ['unknown', 'UNKNOWN', AMBER], ['conditional', 'CONDITIONAL', 'color: var(--ledger); background: var(--ledger-soft); border-color: var(--ledger-line);'], ])} ${badgeCss('.st.rec', [ ['recommended', '推荐', OK], ['conditional', '条件推荐', 'color: var(--ledger); background: var(--ledger-soft); border-color: var(--ledger-line);'], ['alternate', '备选', AMBER], ['not_recommended', '不推荐', DANGER], ])} /* * 风险等级(原样照抄目标件 .risk.low/.mid/.hi 三条声明)——加 critical 一档:这一屏「各候选 * 推荐等级」摘要读的是 substitution.relation.risk_level(声明了 low/medium/high/critical * 四档),差异明细本身的 risk_level 只有三档,共用同一族选择器,多出的第四档不会撞车。 */ ${badgeCss('.risk', [ ['low', '低', 'color: var(--ok);'], ['medium', '中', 'color: #8f6a12;'], ['high', '高', 'color: var(--danger);'], ['critical', '危急', 'color: var(--danger); font-weight: 900;'], ])} /* * 约束类型(硬/软)—— 目标件的差异明细表里这一列是纯文字,没有配色(数据工单-01 的 .ct.hard/ * .ct.soft 才是这两个值真正的徽标声明,只是它长在另一份目标件里)。这里把那两条颜色声明 * 原样搬过来当 v- 版(数值不改,只换了外壳类名,因为这一页的样式表里从未定义过 .ct)。 */ .con { display: inline-block; font-size: 11px; font-weight: 700; padding: 1px 8px; border-radius: 999px; border: 1px solid transparent; white-space: nowrap; } ${badgeCss('.con', [ ['hard', '硬约束', DANGER], ['soft', '软约束', MUTED], ])} /* 替代类型 —— 目标件里也是纯文字,不配色,只补文案 */ ${badgeCss('.enumtxt', [ ['pin_to_pin', 'Pin-to-Pin'], ['functional', '功能替代'], ['parametric', '参数替代'], ['other', '其他'], ])} `; /* ==================== 验收工单 02(评审放行与落账确认):徽标别名 ==================== */ const BIND_ACC2 = ` /* 这一屏四个枚举列(推荐等级 / 分析状态 / 需求状态 / 当前阶段)取值域两两不重叠,共用 .st 即可。 推荐等级、分析状态目标件里本就是 .st 徽标(原样照抄 .st.ok/.miss/.unknown/.cond 四条声明); 需求状态 / 当前阶段目标件没有这两列,借同一套配色,不新配颜色。 */ ${badgeCss('.st', [ ['recommended', '推荐', OK], ['conditional', '条件推荐', 'color: var(--ledger); background: var(--ledger-soft); border-color: var(--ledger-line);'], ['alternate', '备选', AMBER], ['not_recommended', '不推荐', DANGER], ['ai_draft', 'AI 草稿', AMBER], ['fae_confirmed', 'FAE确认', OK], ['pending_customer', '待客户验证', 'color: var(--ledger); background: var(--ledger-soft); border-color: var(--ledger-line);'], ['customer_approved', '客户已批准', OK], ['rejected', '不批准', DANGER], ['void', '作废', MUTED], ['pending', '待处理', MUTED], ['responded', '已响应', 'color: var(--ledger); background: var(--ledger-soft); border-color: var(--ledger-line);'], ['won', '已成交', OK], ['abandoned', '已放弃', DANGER], ['closed', '已关闭', MUTED], ['assessing', '评估中', MUTED], ['gate03', '提交 ACT-GATE-03', OK], ])} /* 风险等级比验收 01 多一档 critical(P-11 主记录的 risk_level 声明了它,差异明细没有) */ ${badgeCss('.risk', [ ['low', '低', 'color: var(--ok);'], ['medium', '中', 'color: #8f6a12;'], ['high', '高', 'color: var(--danger);'], ['critical', '危急', 'color: var(--danger); font-weight: 900;'], ])} /* 替代类型 —— 目标件里是纯文字,不配色,只补文案(与验收 01 同一套值域,各自页面各存一份) */ ${badgeCss('.enumtxt', [ ['pin_to_pin', 'Pin-to-Pin'], ['functional', '功能替代'], ['parametric', '参数替代'], ['other', '其他'], ])} `; /** 三层壳照目标件:`
`,不再套 `.proto-root`/`.app-shell`/`.jc-stage` * (那是 E04/I01 自己的视觉底约定,不是平台要求 —— ui-kit / view-render 从未引用过这几个类名; * 目标件本身就是直接 `page > card`,搬版面就该原样搬这一层)。 * 收件人 / 关联任务两格由引擎注入;目标件的「最迟交回」(`card.deadline`)本仓没有真数据源,不摆; * 工单编号(`card.id`)换成本次评估案号 `eval_ref`。 */ const doc = (input: { css: string; alias: string; badge: string; hic?: string; tag: string; kicker: string; title: string; step: string; body: string; footTag: string }): string => `
${input.badge}
${input.hic ?? ''}
${input.kicker}

${input.title}

收件人: · 关联任务:
${input.tag} ${input.step}
${input.body}
${input.kicker} · ${input.title} ${input.footTag} ·
`; /* ==================== 内联 SVG 图标 —— 逐字节搬自「数据工单-01」目标件(它们是版面语言的一部分) ==================== */ const HIC = ''; const ZIC_TASK = ''; const ZIC_TABLE = ''; /* ==================== 通用绑定小工具 ==================== */ const zone = (label: string, aside: string, icon = ''): string => `

${icon}【${label}】—— ${aside}

`; const note = (items: readonly string[]): string => `
${items.map((p) => `

${p}

`).join('')}
`; const criteria = (title: string, items: readonly string[]): string => `
${title}
    ${items.map((i) => `
  • ${i}
  • `).join('')}
`; const flow = (text: string): string => `
${text}
`; const editGate = (address: string, label: string): string => `
${label}
`; const gapbar = (object: string, what: string): string => `
还差 行${what}没填 —— 。按「编辑」开表,红框那几格就是要填的。补齐了才确认得了。
`; const addrow = (address: string, label: string, hint: string): string => `
${hint}
`; /** 可改格:只读那一份留空,渲染器自动配孪生 ``(手写 input 会不填值,人一点编辑就是白框) */ const cell = (col: string, cls = ''): string => ``; /** 只读格:即便挂在可改的表里也不许被 `table.on` 藏起来 —— 目标件自己就有这条覆盖(`.jc-vcell.ro`) */ const ro = (col: string, cls = ''): string => ``; const select = (col: string, options: ReadonlyArray, required = false): string => ``; /** * 枚举徽标。可改表里挨着 `