Archived
活动包 `part-substitution-assessment`(case-model / manifest / report / target-style / badge / ledger-board 与两份包内测试)、 目标件「验收工单-01-差异评估结论审核」、`docs/ss02-act-05` 关键台账数据两份、端到端测试记录、e2e 脚本。 逐条口径见同批 CLAUDE.md 快照。2026-08-27 另一会话做的,一直留在工作树没入库;与同批另两条提交互相依赖。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167qWjeyBwvfdGovd7FWbCu
732 lines
50 KiB
TypeScript
732 lines
50 KiB
TypeScript
/**
|
||
* 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 同款):两种包装都管 —— 数据工单/执行件用平台的 .jc-tbl-wrap,
|
||
两张验收件照目标件用 .tbl-wrap,而验收版目标件写的是 overflow:hidden(只有数据工单那版补了 overflow-x:auto)。
|
||
2026-08-27 用户:「差异评估结论审核这个页面,表格部分不能左右滑动」—— 上一刀只管了前者 */
|
||
.jc-tbl-wrap, .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-<token> 别名机制,不是字面枚举值,这里用 badgeCss 生成 .ct.v-hard / .ct.v-soft,
|
||
* 颜色原样照抄目标件那两条声明的数值(DANGER/MUTED 与 .ct.hard / .ct.soft 同一份值)。
|
||
*/
|
||
${badgeCss('.ct', [
|
||
['hard', '硬约束', DANGER],
|
||
['soft', '软约束', MUTED],
|
||
])}
|
||
`;
|
||
|
||
/* ==================== 执行工单(评估输入清单):徽标别名 ==================== */
|
||
const BIND_EXEC = `
|
||
/* 候选参数现值(只读参考块):一颗料一张,复用 .jc-tbl-wrap;块头摆料号 / 角色 / 在库状态 */
|
||
.prefs { display: block; }
|
||
.pref { border: 1px solid var(--line-soft); border-radius: 10px; overflow: hidden; margin-bottom: 12px; }
|
||
.pref:last-of-type { margin-bottom: 0; }
|
||
.pref-head { display: flex; align-items: center; gap: 10px; padding: 9px 14px; background: var(--soft); border-bottom: 1px solid var(--line); }
|
||
.pref-head b { font-size: 13.5px; }
|
||
.pref .jc-tbl-wrap { border: 0; border-radius: 0; }
|
||
|
||
/* 存在性核对的两列(角色 / 在库状态)与评估输入清单的状态列共用目标件自己的 .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"\` 会把每一行都标必填。改成 \`<td data-class-of="status">\` 按本行的
|
||
* 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],
|
||
// 目标件 2026-08-27 改版:CONDITIONAL 从题头红改成品牌蓝(.st.cond),原样照抄
|
||
['conditional', 'CONDITIONAL', 'color: var(--brand-deep); background: var(--brand-wash); border-color: var(--brand-soft);'],
|
||
])}
|
||
/* 推荐程度 pill —— 目标件 .rec.yes / .rec.cond / .rec.no 三条声明原样照抄;台账是四档(备选借条件推荐那档琥珀) */
|
||
${badgeCss('.rec', [
|
||
['recommended', '推荐', OK],
|
||
['conditional', '有条件推荐', 'color: var(--amber); background: var(--amber-soft); border-color: var(--amber-line);'],
|
||
['alternate', '备选', 'color: var(--amber); background: var(--amber-soft); border-color: var(--amber-line);'],
|
||
['not_recommended', '不推荐', DANGER],
|
||
])}
|
||
/* 候选块序号(目标件 .cand-idx 写死 1/2)—— 交给 CSS 计数器;块头的风险级别前缀「风险 」也在目标件里 */
|
||
.card-bd { counter-reset: cand; }
|
||
.cand { counter-increment: cand; }
|
||
.cand-idx::before { content: counter(cand); }
|
||
.cand-con .risk::before { content: "风险 "; }
|
||
/*
|
||
* 风险等级(原样照抄目标件 .risk.low/.mid/.hi 三条声明)——加 critical 一档:这一屏「各候选
|
||
* 推荐等级」摘要读的是 substitution.relation.risk_level(声明了 low/medium/high/critical
|
||
* 四档),差异明细本身的 risk_level 只有三档,共用同一族选择器,多出的第四档不会撞车。
|
||
*/
|
||
${badgeCss('.risk', [
|
||
['low', '低', 'color: var(--ok);'],
|
||
['medium', '中', 'color: var(--amber, #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: var(--amber, #8f6a12);'],
|
||
['high', '高', 'color: var(--danger);'],
|
||
['critical', '危急', 'color: var(--danger); font-weight: 900;'],
|
||
])}
|
||
/* 替代类型 —— 目标件里是纯文字,不配色,只补文案(与验收 01 同一套值域,各自页面各存一份) */
|
||
${badgeCss('.enumtxt', [
|
||
['pin_to_pin', 'Pin-to-Pin'],
|
||
['functional', '功能替代'],
|
||
['parametric', '参数替代'],
|
||
['other', '其他'],
|
||
])}
|
||
`;
|
||
|
||
/** 三层壳照目标件:`<div class="page"><div class="card">`,不再套 `.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 =>
|
||
`<style>${input.css}${BIND_COMMON}${input.alias}</style>
|
||
<div class="page"><div class="card">
|
||
<header class="card-head">
|
||
<div class="badge">${input.badge}</div>
|
||
${input.hic ?? ''}
|
||
<div class="tit">
|
||
<div class="kick">${input.kicker}</div>
|
||
<h1>${input.title}</h1>
|
||
<div class="sub">收件人:<span data-slot="case:owner"></span> · 关联任务:<span class="mono" data-slot="case:taskId" data-blank></span></div>
|
||
</div>
|
||
<div class="side">
|
||
<span class="tag">${input.tag}</span>
|
||
<span class="step">${input.step}</span>
|
||
</div>
|
||
</header>
|
||
<div class="card-bd">${input.body}</div>
|
||
<footer class="card-foot">
|
||
<span>${input.kicker} · ${input.title}</span>
|
||
<span style="font-family:'SF Mono',Menlo,Consolas,monospace">${input.footTag} · <span data-slot="eval_ref" data-blank></span></span>
|
||
</footer>
|
||
</div></div>`;
|
||
|
||
/* ==================== 内联 SVG 图标 —— 逐字节搬自「数据工单-01」目标件(它们是版面语言的一部分) ==================== */
|
||
const HIC =
|
||
'<svg class="hic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><ellipse cx="10" cy="5" rx="7" ry="3"/><path d="M3 5v14c0 1.66 3.13 3 7 3 1.13 0 2.21-.09 3.2-.26"/><path d="M3 12c0 1.66 3.13 3 7 3 1.13 0 2.21-.09 3.2-.26"/><circle cx="18" cy="18" r="4.2"/><path d="M18 15.8v4.4M15.8 18h4.4"/></svg>';
|
||
const ZIC_TASK =
|
||
'<svg class="zic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="8" y="2" width="8" height="4" rx="1"/><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><path d="M9 12h6M9 16h4"/></svg>';
|
||
const ZIC_TABLE =
|
||
'<svg class="zic" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M3 10h18M9 10v10"/></svg>';
|
||
|
||
/* ==================== 通用绑定小工具 ==================== */
|
||
const zone = (label: string, aside: string, icon = ''): string => `<p class="zone">${icon}<b>【${label}】</b>—— ${aside}</p>`;
|
||
const note = (items: readonly string[]): string => `<div class="note">${items.map((p) => `<p>${p}</p>`).join('')}</div>`;
|
||
const criteria = (title: string, items: readonly string[]): string =>
|
||
`<div class="criteria"><div class="c-title">${title}</div><ul>${items.map((i) => `<li>${i}</li>`).join('')}</ul></div>`;
|
||
const flow = (text: string): string => `<div class="flow">${text}</div>`;
|
||
const editGate = (address: string, label: string): string => `<div class="jc-editgate" data-edit-for="${address}">
|
||
<span class="jc-editgate-t">${label}</span>
|
||
<button class="jc-egbtn eg-open" type="button" data-edit-open="${address}">编辑</button>
|
||
<button class="jc-egbtn eg-cancel" type="button" data-edit-cancel="${address}">取消</button>
|
||
<button class="jc-egbtn eg-save" type="button" data-edit-save="${address}">保存</button>
|
||
</div>`;
|
||
const gapbar = (object: string, what: string): string => `<div class="jc-gapbar" data-class-of="${object}_missing">
|
||
<span><b>还差 <span data-slot="${object}_missing"></span> 行${what}没填</b> —— <span data-slot="${object}_missing_keys"></span>。按「编辑」开表,红框那几格就是要填的。补齐了才确认得了。</span>
|
||
</div>`;
|
||
const addrow = (address: string, label: string, hint: string): string => `<div class="jc-addrow">
|
||
<button type="button" data-edit-add="${address}">${label}</button>
|
||
<em>${hint}</em>
|
||
</div>`;
|
||
/** 可改格:只读那一份留空,渲染器自动配孪生 `<input>`(手写 input 会不填值,人一点编辑就是白框) */
|
||
const cell = (col: string, cls = ''): string => `<span class="jc-vcell${cls === '' ? '' : ` ${cls}`}" data-slot="${col}" data-col="${col}" data-blank></span>`;
|
||
/** 只读格:即便挂在可改的表里也不许被 `table.on` 藏起来 —— 目标件自己就有这条覆盖(`.jc-vcell.ro`) */
|
||
const ro = (col: string, cls = ''): string => `<span class="jc-vcell ro${cls === '' ? '' : ` ${cls}`}" data-slot="${col}" data-blank></span>`;
|
||
const select = (col: string, options: ReadonlyArray<readonly [string, string]>, required = false): string =>
|
||
`<select class="jc-cell" data-col="${col}"${required ? ' required' : ''}><option value=""></option>${options.map(([v, l]) => `<option value="${v}">${l}</option>`).join('')}</select>`;
|
||
/**
|
||
* 枚举徽标。可改表里挨着 `<select class="jc-cell">` 孪生件时包一层 `.jc-vcell`(不带 `.ro`):
|
||
* 看的时候摆徽标,**编辑态随只读格一起藏起来,只留下拉框** —— 从前包的是 `.ro`(编辑态留着),
|
||
* 徽标压在下拉框头上把那一格顶低 13px,一排格子高低不齐(2026-08-27 用户指出)。
|
||
*/
|
||
const badge = (klass: string, col: string, wrapTwin = false): string => {
|
||
const inner = `<span class="${klass}" data-class-of="${col}"></span>`;
|
||
return wrapTwin ? `<span class="jc-vcell">${inner}</span>` : inner;
|
||
};
|
||
|
||
/* ==================== 局面 0 · 数据工单 P-08(搬自「数据工单-01」)==================== */
|
||
|
||
const P08_TABLE = `<div class="jc-tbl-wrap"><table id="tbl1">
|
||
<thead><tr>
|
||
<th style="width:38px">#</th>
|
||
<th class="req" data-h="内部料号">内部料号</th>
|
||
<th class="req" data-h="品牌名称">品牌</th>
|
||
<th data-h="制造商名称">制造商</th>
|
||
<th class="req" data-h="规格描述">规格描述</th>
|
||
<th class="req" data-h="封装">封装</th>
|
||
<th data-h="工艺">工艺</th>
|
||
<th class="req" data-h="生命周期状态">生命周期</th>
|
||
<th data-h="EOL/PCN状态">EOL/PCN</th>
|
||
<th data-h="Datasheet引用">Datasheet 引用</th>
|
||
<th data-h="Datasheet版本">Datasheet 版本</th>
|
||
<th data-h="适配应用场景">适配场景</th>
|
||
</tr></thead>
|
||
<tbody data-rows="mine:parts.profile" data-edit="parts.profile"><tr>
|
||
<td class="rn"><span class="jc-rn-num"></span><button class="jc-rowdel" type="button" title="删除此行" data-edit-del="parts.profile">×</button></td>
|
||
<td>${cell('internal_pn', 'mono')}</td>
|
||
<td>${cell('brand_name')}</td>
|
||
<td>${cell('manufacturer_name')}</td>
|
||
<td>${cell('specification_description')}</td>
|
||
<td>${cell('package', 'mono')}</td>
|
||
<td>${cell('process_technology')}</td>
|
||
<td>${badge('stx', 'lifecycle_status', true)}${select('lifecycle_status', [['active', 'Active'], ['nrnd', 'NRND'], ['eol', 'EOL'], ['discontinued', '停产'], ['risk', '风险']], true)}</td>
|
||
<td>${cell('eol_pcn_status')}</td>
|
||
<td>${cell('datasheet_ref', 'mono')}</td>
|
||
<td>${cell('datasheet_version')}</td>
|
||
<td>${cell('application_scenarios')}</td>
|
||
</tr></tbody>
|
||
</table></div>`;
|
||
|
||
/*
|
||
* 表 2 · 标准化参数集(P-08 子表 `parts.parameter`)—— 粒度与表 1 不同(一个参数一行,
|
||
* 行数不固定),不能塞进表 1 的一格(《统一方案》§六:重复明细用子表承载),
|
||
* 逐块搬自目标件《数据工单-01》的【创建什么 · 表 2】,`id="tbl2"` 沿用目标件自己的
|
||
* 列宽声明(P08_CSS 里 `#tbl2 th:nth-child(n)` 那几条,不必在这里重复)。
|
||
* 「说明」列是系统只读注释(目标件原话:「说明列为系统注释,无需填写」),不给 data-col。
|
||
*/
|
||
const P08_PARAMS_TABLE = `<div class="jc-tbl-wrap"><table id="tbl2">
|
||
<thead><tr>
|
||
<th style="width:38px">#</th>
|
||
<th class="req" data-h="所属料号">所属料号</th>
|
||
<th class="req" data-h="参数名">参数名</th>
|
||
<th class="req" data-h="值">值</th>
|
||
<th data-h="单位">单位</th>
|
||
<th class="req" data-h="约束类型">约束(硬/软)</th>
|
||
<th data-h="说明">说明</th>
|
||
</tr></thead>
|
||
<tbody data-rows="mine:parts.parameter" data-edit="parts.parameter"><tr>
|
||
<td class="rn"><span class="jc-rn-num"></span><button class="jc-rowdel" type="button" title="删除此行" data-edit-del="parts.parameter">×</button></td>
|
||
<td>${cell('internal_pn', 'mono')}</td>
|
||
<td>${cell('param_name')}</td>
|
||
<td>${cell('param_value', 'mono')}</td>
|
||
<td>${cell('param_unit')}</td>
|
||
<td>${badge('ct', 'constraint_type', true)}${select('constraint_type', [['hard', '硬约束'], ['soft', '软约束']], true)}</td>
|
||
<td class="desc">${ro('param_note')}</td>
|
||
</tr></tbody>
|
||
</table></div>`;
|
||
|
||
export const P08_MASTER_CREATE = doc({
|
||
css: P08_CSS,
|
||
alias: BIND_P08,
|
||
badge: '数据',
|
||
hic: HIC,
|
||
tag: '数据工单',
|
||
kicker: 'SS02 · ACT-WORK-05 差异评估 · 数据工单 1/2',
|
||
title: '芯片主数据创建(P-08)',
|
||
step: '步骤 0.5 · 等待数据',
|
||
footTag: '工单类型:数据',
|
||
body:
|
||
zone('做什么', '批量创建 P-08 主数据记录', ZIC_TASK) +
|
||
note([
|
||
'<span class="need">模板定位</span>:本单是「数据工单」统一模板的实例——<b>一单一类(可多条)</b>:目标台账 = <b>芯片主数据(P-08)</b>;<b>字段即表头、每行一条新记录</b>;标准化参数集是它的结构子表(表 2,一行一参数,按所属料号归属)。',
|
||
'<span class="need">触发原因</span>:系统按内部料号查芯片主数据<b>查无记录</b>——是「完全没有」,不是字段缺失(那归执行工单)。',
|
||
'<span class="done">AI 预填</span>:上传了 datasheet 的料号 AI 已按资料预填,标着值的格子请核对;没上传的料号全部手填,同为合法路径。',
|
||
'<span class="need">纪律</span>:必填列全齐才允许确认;确认后这批记录进本单草稿,放行时并入正式库并留痕(确认人 = 数据责任人);只建主数据,验证记录等事实类不在此建。',
|
||
]) +
|
||
zone('创建什么 · 表 1', '主数据记录(每行一条,*为必填列)', ZIC_TABLE) +
|
||
gapbar('parts_profile', '必填') +
|
||
editGate('parts.profile', `<b>待建 <span data-slot="parts_profile_rows"></span> 条记录</b>,点「编辑」核对预填、补齐空缺;「+ 加一行」= 再建一条`) +
|
||
P08_TABLE +
|
||
addrow('parts.profile', '+ 加一行(再建一条记录)', '新行的内部料号必须写(它是这一行的钥匙);其余必填列全部填写后才允许确认;删一行 = 该记录不建') +
|
||
`<p class="zone tbl-gap">${ZIC_TABLE}<b>【创建什么 · 表 2】</b>—— 标准化参数集(每行一个参数,按所属料号归属,*必填)</p>` +
|
||
gapbar('parts_parameter', '必填') +
|
||
editGate('parts.parameter', `<b>已铺 <span data-slot="parts_parameter_rows"></span> 行参数</b>(有 datasheet 的已按资料预填),点「编辑」核对修正;「+ 加一行」补充未列出的参数`) +
|
||
P08_PARAMS_TABLE +
|
||
addrow('parts.parameter', '+ 加一行(补充参数)', '所属料号 / 参数名 / 值 / 单位 / 约束类型齐全;「未披露」请直接写未披露,将进入 UNKNOWN 并挂验证任务;「说明」列为系统注释,无需填写') +
|
||
flow('<b>提交 / 确认:</b>本工单只承载内容;确认请在对话区的【执行卡】中进行。确认后活动自动从「等待数据」唤醒。'),
|
||
});
|
||
|
||
/* ==================== 局面 0′ · 数据工单 P-10(复用「数据工单-01」的样式与骨架,没有独立目标件)==================== */
|
||
|
||
const P10_TABLE = `<div class="jc-tbl-wrap"><table>
|
||
<thead><tr>
|
||
<th style="width:38px">#</th>
|
||
<th class="req" data-h="关联料号">关联料号</th>
|
||
<th class="req" data-h="资料序号">资料序号</th>
|
||
<th class="req" data-h="资料类型">资料类型</th>
|
||
<th data-h="品牌名称">品牌</th>
|
||
<th class="req" data-h="资料标题">资料标题</th>
|
||
<th class="req" data-h="资料版本">资料版本</th>
|
||
<th class="req" data-h="文件链接">文件链接</th>
|
||
<th data-h="证据定位">证据定位</th>
|
||
<th class="req" data-h="资料状态">资料状态</th>
|
||
</tr></thead>
|
||
<tbody data-rows="mine:parts.document" data-edit="parts.document"><tr>
|
||
<td class="rn"><span class="jc-rn-num"></span><button class="jc-rowdel" type="button" title="删除此行" data-edit-del="parts.document">×</button></td>
|
||
<td>${cell('internal_pn', 'mono')}</td>
|
||
<td>${cell('doc_seq', 'mono')}</td>
|
||
<td>${badge('stx', 'document_type', true)}${select('document_type', [['datasheet', 'Datasheet'], ['app_note', '应用笔记'], ['reference_design', '参考设计'], ['white_paper', '白皮书'], ['sample_doc', '样片资料'], ['commitment_letter', '承诺函'], ['other', '其他']], true)}</td>
|
||
<td>${cell('brand_name')}</td>
|
||
<td>${cell('document_title')}</td>
|
||
<td>${cell('document_version')}</td>
|
||
<td>${cell('document_ref', 'mono')}</td>
|
||
<td>${cell('citation_locator')}</td>
|
||
<td>${badge('stx', 'document_status', true)}${select('document_status', [['valid', '有效'], ['expired', '过期'], ['pending', '待核实'], ['void', '作废']], true)}</td>
|
||
</tr></tbody>
|
||
</table></div>`;
|
||
|
||
export const P10_DOCUMENT_CREATE = doc({
|
||
css: P08_CSS,
|
||
alias: BIND_P08,
|
||
badge: '数据',
|
||
hic: HIC,
|
||
tag: '数据工单',
|
||
kicker: 'SS02 · ACT-WORK-05 差异评估 · 数据工单 2/2',
|
||
title: '原厂技术资料创建(P-10)',
|
||
step: '步骤 0.5 · 等待数据',
|
||
footTag: '工单类型:数据',
|
||
body:
|
||
zone('做什么', '为本单新建的料号登记原厂资料(P-10)', ZIC_TASK) +
|
||
note([
|
||
'<span class="need">模板定位</span>:与 P-08 同一套「数据工单」统一模板——目标台账 = <b>原厂技术资料库(P-10)</b>;一份资料一行,同一料号多份资料序号 01/02… 自己续。',
|
||
'<span class="need">触发原因</span>:按内部料号查原厂技术资料库<b>查无记录</b>(P-08 那张先确认过,这一批料号才会走到这里)。',
|
||
'<span class="need">为什么要它</span>:差异明细的证据引用要指到具体资料的版本与页码——没有这一行,结论就追溯不到来源。',
|
||
]) +
|
||
zone('创建什么', '资料记录(每行一条,*为必填列)', ZIC_TABLE) +
|
||
gapbar('parts_document', '必填') +
|
||
editGate('parts.document', `<b>待建 <span data-slot="parts_document_rows"></span> 条资料记录</b>,点「编辑」补齐`) +
|
||
P10_TABLE +
|
||
addrow('parts.document', '+ 加一行(再登记一份资料)', '关联料号与序号是这一行的钥匙,新行都要写;必填列全齐才允许确认;删一行 = 该资料不建') +
|
||
flow('<b>提交 / 确认:</b>本工单只承载内容;确认请在对话区的【执行卡】中进行。'),
|
||
});
|
||
|
||
/* ==================== 局面 1 · 评估输入清单(搬自「执行工单-01」)==================== */
|
||
|
||
const PN_CHECK_TABLE = `<div class="jc-tbl-wrap"><table>
|
||
<thead><tr>
|
||
<th style="width:38px">#</th>
|
||
<th data-h="料号">料号</th>
|
||
<th data-h="角色">角色</th>
|
||
<th data-h="在库状态">在库状态</th>
|
||
<th data-h="备注/放弃原因">备注 / 放弃原因</th>
|
||
</tr></thead>
|
||
<tbody data-rows="mine:${D}.pn_check"><tr>
|
||
<td class="rn"><span class="jc-rn-num"></span></td>
|
||
<td>${ro('internal_pn', 'mono')}</td>
|
||
<td><span class="jc-vcell ro"><span class="rl" data-class-of="role"></span></span></td>
|
||
<td>${badge('st', 'exists_status', true)}</td>
|
||
<td>${ro('note')} ${ro('abandon_reason')}</td>
|
||
</tr></tbody>
|
||
</table></div>`;
|
||
|
||
/** 候选参数现值 —— **只读参考,基座从台账现读**(2026-08-27,用户三次指出「库里明明有值,清单还要填一遍」):
|
||
* 从前「待确认」行由模型判「哪个参数原厂未披露」再写进清单 —— 参数值有没有是台账里的事实,不该由模型宣布;
|
||
* 现在每颗料一张小表(外层按核对表逐颗复制,里层 `data-by="internal_pn"` 只列它名下的参数行),
|
||
* 人看得见现值;未披露的由差异评估判成 UNKNOWN 并挂验证任务。 */
|
||
const PARAM_REF = `<article class="prefs" data-rows="mine:${D}.pn_check"><section class="pref">
|
||
<div class="pref-head"><b class="mono" data-slot="internal_pn" data-blank></b><span class="rl" data-class-of="role"></span>${badge('st', 'exists_status')}</div>
|
||
<div class="jc-tbl-wrap"><table>
|
||
<thead><tr><th style="width:38px">#</th><th data-h="参数名">参数名</th><th data-h="值">值</th><th data-h="单位">单位</th><th data-h="约束类型">约束类型</th><th data-h="说明">说明</th></tr></thead>
|
||
<tbody data-rows="parts.parameter" data-by="internal_pn"><tr>
|
||
<td class="rn"><span class="jc-rn-num"></span></td>
|
||
<td>${ro('param_name')}</td>
|
||
<td>${ro('param_value', 'mono')}</td>
|
||
<td>${ro('param_unit')}</td>
|
||
<td><span class="jc-vcell ro"><span class="ct" data-class-of="constraint_type"></span></span></td>
|
||
<td>${ro('param_note')}</td>
|
||
</tr></tbody>
|
||
</table></div>
|
||
</section></article>`;
|
||
|
||
const GAP_TABLE = `<div class="jc-tbl-wrap"><table id="tbl">
|
||
<thead><tr>
|
||
<th style="width:38px">#</th>
|
||
<th style="width:120px" data-h="键">键</th>
|
||
<th data-h="输入项">输入项</th>
|
||
<th data-h="来源">来源</th>
|
||
<th data-h="状态">状态</th>
|
||
<th data-h="绑定值/缺口说明">绑定值 / 缺口说明</th>
|
||
<th data-h="补充内容">补充内容</th>
|
||
</tr></thead>
|
||
<tbody data-rows="mine:${D}.input_gap" data-edit="${D}.input_gap"><tr>
|
||
<td class="rn"><span class="jc-rn-num"></span><button class="jc-rowdel" type="button" title="删除此行" data-edit-del="${D}.input_gap">×</button></td>
|
||
<td>${cell('item_key', 'mono')}</td>
|
||
<td>${cell('input_item')}</td>
|
||
<td>${cell('source_ledger', 'mono')}</td>
|
||
<td>${select('status', [['gap', '待补'], ['default', '默认·可改'], ['ok', '齐备']], true)}${badge('st', 'status')}</td>
|
||
<td>${cell('detail')}</td>
|
||
<td class="sup" data-class-of="status">${cell('supplement')}</td>
|
||
</tr></tbody>
|
||
</table></div>`;
|
||
|
||
export const EVAL_INPUT_LIST = doc({
|
||
css: EXEC_CSS,
|
||
alias: BIND_EXEC,
|
||
badge: '执行',
|
||
tag: '执行工单',
|
||
kicker: 'SS02 · ACT-WORK-05 差异评估 · 执行工单 1/1',
|
||
title: '评估输入与资料补齐确认',
|
||
step: '步骤 1/5 · 建立评估上下文',
|
||
footTag: '工单类型:执行',
|
||
body:
|
||
zone('做什么', '启动 4 项已绑定,其余信息在工单里补齐') +
|
||
note([
|
||
'<span class="done">AI 已完成</span>:系统已按启动传入的 <b>4 项</b>完成绑定——客户、客户需求(新建 P-01 · 版本 V1)、原 PN、候选 PN(数组,逐个评估)——并生成输入快照。',
|
||
'<span class="need">需要你补齐</span>:启动时未收的信息(应用场景、BOM 位)<b>直接在下表补</b>;默认值(替代类型 / 规则版本 / 负责人期限)可确认可改。「完全没有」(料号不在库)已由数据工单创建;每颗料的参数现值摆在「候选参数现值」里,写着「未披露」的参数将按 <b>UNKNOWN</b> 进入差异评估并挂验证任务,不用在这里补。',
|
||
]) +
|
||
zone('料号存在性核对', '只读参考 · 与下表「补充内容」互不冲突') +
|
||
PN_CHECK_TABLE +
|
||
zone('候选参数现值', '只读参考 · P-08 标准化参数集,一颗料一张,基座从台账现读;写着「未披露」的由差异评估判成 UNKNOWN 并挂验证任务') +
|
||
PARAM_REF +
|
||
zone('交回什么', '评估输入清单(已绑定项照实核对,缺口项补齐,*为必填)') +
|
||
// 红带说「必填格」不说「补充内容」:缺的可能是 AI 该写的输入项(2026-08-27 实测),写死列名会把人指向错的格子
|
||
gapbar(`${D}_input_gap`, '必填格') +
|
||
editGate(`${D}.input_gap`, `<b>还有 <span data-slot="${D}_input_gap_missing"></span> 项待补</b>,点「编辑」在「补充内容」列填入 —— 已绑定那几行的「输入项/来源/绑定值」抄的是启动入参,发现记录本身有误请退回触发方重新启动,不要在这里改`) +
|
||
GAP_TABLE +
|
||
addrow(`${D}.input_gap`, '+ 加一行(补充资料项)', '新行的「键」必须写(它是这一行的钥匙,写一个英文短名,如 thermal_note);「输入项」与「补充内容」也是必填;删一行 = 该输入项不适用;补不了的写「留 UNKNOWN」或「挂验证任务」') +
|
||
flow(
|
||
'<b>审批 / 提交:</b>本工单只承载内容;「提交」请在对话区的【执行卡】中进行。<b>提交后去向:</b>进入步骤 2 · AI 读取与标准化,仍缺失的项将按 UNKNOWN 进入差异评估。<b>注意:</b>启动参数如有误,需退回触发方修订后重新触发,本单不可改。',
|
||
),
|
||
});
|
||
|
||
/* ==================== 局面 2 · 差异评估结论审核(搬自「验收工单-01」)==================== */
|
||
|
||
/** 候选 PN 结论汇总 —— 目标件 2026-08-27 改版:一个候选一条结论(P-11 主表草稿)。
|
||
* 品牌不在 relation 上:用嵌套行模版 `data-by` 从 `parts.profile` 整表里挑这一颗料那一行(取值,不是算)。
|
||
* 「差异统计(四态)」绑 difference_summary —— S3 剧本让模型按「7 项比对:4 MATCH / 1 MISMATCH / …」的形写它,
|
||
* 模版不数四态(数出来的画面一律不做假)。 */
|
||
const RELATION_SUMMARY_TABLE = `<div class="tbl-wrap" style="margin-bottom:20px"><table>
|
||
<thead><tr>
|
||
<th style="width:40px">#</th>
|
||
<th data-h="候选PN">候选 PN</th>
|
||
<th data-h="品牌">品牌</th>
|
||
<th data-h="推荐程度">推荐程度</th>
|
||
<th data-h="风险级别">风险级别</th>
|
||
<th data-h="差异统计">差异统计(四态)</th>
|
||
<th data-h="结论摘要">结论摘要</th>
|
||
</tr></thead>
|
||
<tbody data-rows="mine:substitution.relation"><tr>
|
||
<td class="num"></td>
|
||
<td class="cust mono" data-slot="candidate_internal_pn" data-blank></td>
|
||
<td><span data-rows="parts.profile" data-by="internal_pn=candidate_internal_pn"><i data-slot="brand_name" data-blank></i></span></td>
|
||
<td>${badge('rec', 'recommendation_level')}</td>
|
||
<td>${badge('risk', 'risk_level')}</td>
|
||
<td class="stat" data-slot="difference_summary" data-blank></td>
|
||
<td class="reason" data-slot="recommendation_reason" data-blank></td>
|
||
</tr></tbody>
|
||
</table></div>`;
|
||
|
||
/** 差异明细 · 按候选 PN 分组 —— 目标件 2026-08-27 改版:**一个候选一张表**(用户:「存在多个替代 PN 要对比差异时看不清楚」)。
|
||
* 外层容器 <article> 按 relation 逐个复制行模版 <section class="cand">,里层 <tbody data-by="substitution_relation_id">
|
||
* 只列该候选名下的明细(渲染器 2026-08-27 补的嵌套 + 父键过滤)。容器标签名不得在行模版内部再出现:
|
||
* <article> 里是 section / div / table,不撞。 */
|
||
const CANDIDATE_SECTIONS = `<article class="cands" data-rows="mine:substitution.relation"><section class="cand">
|
||
<div class="cand-head">
|
||
<span class="cand-idx"></span>
|
||
<span class="cand-name mono" data-slot="candidate_internal_pn" data-blank></span>
|
||
<span class="cand-brand" data-rows="parts.profile" data-by="internal_pn=candidate_internal_pn"><i data-slot="brand_name" data-blank></i></span>
|
||
<span class="cand-vs">vs 原 PN · <i data-slot="original_internal_pn" data-blank></i></span>
|
||
<span class="cand-con">${badge('rec', 'recommendation_level')}${badge('risk', 'risk_level')}</span>
|
||
</div>
|
||
<div class="tbl-wrap"><table>
|
||
<thead><tr><th style="width:40px">#</th><th data-h="参数项">参数项</th><th data-h="原PN值">原 PN 值</th><th data-h="候选PN值">候选 PN 值</th><th data-h="单位">单位</th><th data-h="约束类型">约束类型</th><th data-h="结论">结论</th><th data-h="风险等级">风险等级</th><th data-h="证据引用">证据引用</th><th data-h="AI置信度">AI 置信度</th></tr></thead>
|
||
<tbody data-rows="mine:substitution.difference_item" data-by="substitution_relation_id"><tr>
|
||
<td class="num"></td>
|
||
<td class="cust" data-slot="parameter_name" data-blank></td>
|
||
<td class="mono" data-slot="original_value" data-blank></td>
|
||
<td class="mono" data-slot="candidate_value" data-blank></td>
|
||
<td data-slot="parameter_unit" data-blank></td>
|
||
<td><span class="con" data-class-of="constraint_type"></span></td>
|
||
<td>${badge('st dst', 'difference_status')}</td>
|
||
<td>${badge('risk', 'risk_level')}</td>
|
||
<td class="mono" data-slot="evidence_ref" data-blank></td>
|
||
<td class="mono" data-slot="confidence" data-blank></td>
|
||
</tr></tbody>
|
||
</table></div>
|
||
</section></article>`;
|
||
|
||
export const DIFFERENCE_REVIEW = doc({
|
||
css: ACC1_CSS,
|
||
alias: BIND_ACC1,
|
||
badge: '验收',
|
||
tag: '验收工单',
|
||
kicker: 'SS02 · ACT-WORK-05 差异评估 · 验收工单 1/2',
|
||
title: '差异评估结论审核',
|
||
step: '步骤 3~4/5 · 逐项差异评估 + FAE/RI 审核',
|
||
footTag: '工单类型:验收',
|
||
body:
|
||
zone('AI 结论说明', '结论是怎么算出来的') +
|
||
note([
|
||
'<b>AI 已完成的计算</b>:按 GOV 评估规则(锁定版本 <b class="mono" data-slot="rule_version" data-blank></b>),以原 PN <b class="mono" data-slot="original_internal_pn" data-blank></b> 为基准,对 <b data-slot="substitution_relation_rows"></b> 个候选 PN 分别逐项比对,每项产出四态结论——<b>MATCH / MISMATCH / UNKNOWN / CONDITIONAL</b>,并给出风险等级、证据引用与置信度;再按候选汇总出下方的【候选 PN 结论汇总】(一个候选一条结论)。差异明细 = <b>P-11 差异明细子表</b>(一条参数差异一条记录,按候选分组);结论汇总 = <b>P-11 主表草稿</b>。当前均为 AI 草稿,<b>FAE 审核通过前不落账</b>。评估参数可能含本次数据工单创建的主数据(确认人背书;未附 datasheet 的参数项会显式标注「人工录入」)。',
|
||
]) +
|
||
criteria('判断这份差异评估结论是否可信,请逐项核对', [
|
||
'<b>汇总结论与明细一致</b>:结论表中每个候选的推荐程度 / 风险级别,与其下方差异明细表的四态分布吻合(存在 MISMATCH 硬约束或悬空 UNKNOWN 的候选不得给「推荐」);',
|
||
'<b>关键参数差异均有明确状态</b>:每个候选的逐项结论无漏项,每个参数都有四态之一的判定;',
|
||
'<b>UNKNOWN 项均有去向</b>:每个 UNKNOWN 都挂了验证任务或补来源动作,不允许悬空;',
|
||
'<b>证据链完整</b>:每项结论可追溯至 P-08 / P-10 / P-06 的具体记录,或本单数据工单的创建记录(标注:人工录入);硬约束项未经证据支持不得判 MATCH。',
|
||
]) +
|
||
zone('候选 PN 结论汇总', '每个候选一条结论,内容 = 对应下方明细表的汇总判定(P-11 主表草稿)') +
|
||
RELATION_SUMMARY_TABLE +
|
||
zone('差异明细 · 按候选 PN 分组', '每个候选一张表,与原 PN 逐项对比(P-11 差异明细子表草稿 · 一条参数一条记录 difference_item_id)') +
|
||
CANDIDATE_SECTIONS +
|
||
flow(
|
||
'<b>审批:</b>本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。审核意见与补充条件写入 P-06(验证要求 verification_requirement);<b>通过后去向:</b>进入步骤 5 · 评审放行与落账确认。',
|
||
),
|
||
});
|
||
|
||
/* ==================== 局面 3 · 评审放行与落账确认(搬自「验收工单-02」)==================== */
|
||
|
||
/** 字段:值 两列表 —— 逐字节搬自目标件「拟写 P-11」「拟回写 P-01」的版式(一行一个字段),
|
||
* 用 `data-rows` 包一整张小表当行模版,一个候选 / 一条需求各重复出一份,天然支持多候选。 */
|
||
const P11_FIELD_TABLE = `<div class="tbl-wrap"><table>
|
||
<thead><tr><th style="width:40px">#</th><th style="width:180px" data-h="字段">字段</th><th data-h="拟写入值">拟写入值</th></tr></thead>
|
||
<tbody data-rows="mine:substitution.relation">
|
||
<tr><td class="num"></td><td class="cust">替代类型</td><td><span class="enumtxt" data-class-of="substitution_type"></span></td></tr>
|
||
<tr><td class="num"></td><td class="cust">原内部料号</td><td class="mono" data-slot="original_internal_pn" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">候选内部料号</td><td class="mono" data-slot="candidate_internal_pn" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">推荐等级</td><td>${badge('st', 'recommendation_level')} <span class="reason">(枚举:推荐 / 条件推荐 / 备选 / 不推荐,四态之一)</span></td></tr>
|
||
<tr><td class="num"></td><td class="cust">推荐理由</td><td data-slot="recommendation_reason" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">差异摘要</td><td data-slot="difference_summary" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">替代分析报告</td><td class="mono" data-slot="analysis_report_ref" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">分析状态</td><td>${badge('st', 'analysis_status')} <span class="reason">(枚举:AI草稿 / FAE确认 / 待客户验证 / 客户已批准 / 不批准 / 作废)</span></td></tr>
|
||
<tr><td class="num"></td><td class="cust">客户批准状态</td><td>未提交 <span class="reason">(由审批流程维护,本活动不产生)</span></td></tr>
|
||
<tr><td class="num"></td><td class="cust">风险等级</td><td>${badge('risk', 'risk_level')}</td></tr>
|
||
<tr><td class="num"></td><td class="cust">验证要求</td><td class="mono" data-slot="verification_requirement" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">证据可信度</td><td class="mono" data-slot="confidence_score" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">差异规则版本</td><td class="mono" data-slot="rule_version" data-blank></td></tr>
|
||
</tbody>
|
||
</table></div>`;
|
||
|
||
const P01_FIELD_TABLE = `<div class="tbl-wrap"><table>
|
||
<thead><tr><th style="width:40px">#</th><th style="width:180px" data-h="字段">字段</th><th data-h="拟写入值">拟写入值</th></tr></thead>
|
||
<tbody data-rows="mine:demand.request">
|
||
<tr><td class="num"></td><td class="cust">风险等级</td><td>${badge('risk', 'risk_level')}</td></tr>
|
||
<tr><td class="num"></td><td class="cust">验证要求</td><td class="mono" data-slot="verification_requirement" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">输出链接</td><td class="mono" data-slot="output_ref" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">下一步动作</td><td data-slot="next_action" data-blank></td></tr>
|
||
<tr><td class="num"></td><td class="cust">当前阶段</td><td>${badge('st', 'current_stage')}</td></tr>
|
||
<tr><td class="num"></td><td class="cust">需求状态</td><td>${badge('st', 'request_status')}</td></tr>
|
||
</tbody>
|
||
</table></div>`;
|
||
|
||
export const LANDING_CONFIRM = doc({
|
||
css: ACC2_CSS,
|
||
alias: BIND_ACC2,
|
||
badge: '验收',
|
||
tag: '验收工单',
|
||
kicker: 'SS02 · ACT-WORK-05 差异评估 · 验收工单 2/2',
|
||
title: '评审放行与落账确认',
|
||
step: '步骤 5/5 · 提交 ACT-GATE-03',
|
||
footTag: '工单类型:验收',
|
||
body:
|
||
zone('AI 结论说明', '本单确认什么') +
|
||
note([
|
||
'<b>本活动以 ACT-GATE-03 评审放行为终点。</b>本单确认拟落账内容:评估结论层写 <b>P-11 替代关系主记录</b>(替代类型 / 原内部料号 / 候选内部料号 / 推荐等级 / 推荐理由 / 差异摘要 / 替代分析报告 / 分析状态 / 风险等级 / 验证要求 / 证据可信度 / 差异规则版本),需求状态回写 <b>P-01</b>(风险等级 / 验证要求 / 输出链接 / 下一步动作 / 当前阶段)。<b>客户批准不属于本活动</b>——客户批准状态由审批流程维护,本单只写分析层(分析状态 = FAE 确认),customer_approval_status 保持「未提交」,不由差异评估产生。',
|
||
]) +
|
||
criteria('放行与落账前,请逐项核对', [
|
||
'<b>推荐等级与差异明细一致</b>:差异明细中存在 UNKNOWN / CONDITIONAL 时,推荐等级不得给「推荐」;',
|
||
'<b>分析状态与客户批准状态分离</b>:本单只写分析层(分析状态 = FAE 确认),客户批准状态保持「未提交」;',
|
||
'<b>GOV 规则版本已锁定</b>:规则版本随结论一并落账;',
|
||
'<b>回写 P-01 字段完整</b>:风险等级、验证要求、输出链接、下一步动作、当前阶段五项齐全。',
|
||
]) +
|
||
zone('拟写 P-11 · 替代关系主记录', '只读展示') +
|
||
P11_FIELD_TABLE +
|
||
zone('拟回写 P-01 · 需求状态', '只读展示') +
|
||
P01_FIELD_TABLE +
|
||
flow(
|
||
'<b>审批:</b>本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。<b>通过后去向:</b>在对话区卡提交 ACT-GATE-03 评审放行;放行后结论供 ACT-WORK-08 证据归集消费。',
|
||
),
|
||
});
|