diff --git a/activities/part-substitution-assessment/__tests__/activity.test.ts b/activities/part-substitution-assessment/__tests__/activity.test.ts index 8462a9a..d3ca6b9 100644 --- a/activities/part-substitution-assessment/__tests__/activity.test.ts +++ b/activities/part-substitution-assessment/__tests__/activity.test.ts @@ -26,7 +26,12 @@ const ITEMS = ['S1_inputs', 'S2_assess', 'S3_review', 'T_release']; * 入库事实 / 分析结论一落地引擎就自己收步,不再需要人工激活等待。 */ const NO_DONE_WHEN = new Set(['S3_review', 'T_release']); -const VIEWS = ['p08-master-create', 'p10-document-create', 'eval-input-list', 'difference-review', 'landing-confirm']; +/* + * 2026-08-29 Task 7:五张旧局面收成三张 —— `p08-master-create` / `p10-document-create` / + * `eval-input-list` 三张合并成一张 `input-supplement`(Task 7 已交付); + * `difference-review` / `landing-confirm` 由 Task 8 / 9 各自重做(同名)。 + */ +const VIEWS = ['input-supplement', 'difference-review', 'landing-confirm']; /** 运营中心 L2 沙箱里那张关键台账数据看板 —— 不是工单件,只在「都声明了」这一条里出现;自己那组断言在文件尾 */ const BOARD_VIEW = 'activity-ledger'; const byAddress = (domain: string, object: string) => manifest.declarations.ledger.find((l) => l.domain === domain && l.object === object); @@ -99,12 +104,11 @@ describe('清单本身合规', () => { }); /* - * SKIP(2026-08-29,Task 2):`assets.viewTemplates` 已按 spec §8 暂时只留 `activity-ledger` - * 一张(五张旧局面全部作废,它们绑的是已退休的九步与旧台账地址)—— Task 7–9 会各自加回 - * `input-supplement` / `difference-review` / `landing-confirm` 三张新件,那时再补一条按新 - * 视图集合钉死的断言。这条转性检查在过渡期间的意义不大,先跳过、不删。 + * SKIP(2026-08-29,Task 2 → Task 7 部分交付):`input-supplement` 已随 Task 7 补回 + * `assets.viewTemplates`(`VIEWS` 已更新),但 `difference-review` / `landing-confirm` + * 两张还在等 Task 8 / 9 —— `new Set(...)` 的整体相等要三张都在才成立,继续跳过。 */ - it.skip('五张工单视图 + 一张运营侧看板都声明了,剧本每一步引用的 view 都在其中', () => { + it.skip('三张工单视图 + 一张运营侧看板都声明了,剧本每一步引用的 view 都在其中', () => { expect(new Set(manifest.assets.viewTemplates.map((v) => v.name))).toEqual(new Set([...VIEWS, BOARD_VIEW])); for (const step of manifest.assets.businessModel.steps as ReadonlyArray<{ view?: string }>) { if (step.view !== undefined) expect(VIEWS, step.view).toContain(step.view); @@ -315,10 +319,14 @@ describe('案例模型与清单的对齐', () => { describe('报告模版:编辑面与只读面各就各位', () => { /* - * SKIP(2026-08-29,Task 2 → Task 7 补回):`p08-master-create` / `p10-document-create` / - * `eval-input-list` 三张局面已随重写从 `assets.viewTemplates` 撤下(spec §8:五张旧件全部作废, - * 新的 `input-supplement` 由 Task 7 重做)。`viewHtml()` 现在对这三个名字一律返回空串, - * 下面几条对 HTML 结构的断言无从谈起 —— 跳过、不删,等 Task 7–9 补回视图再验。 + * SKIP(2026-08-29,Task 2 → Task 7,复核后维持 skip):`p08-master-create` / `p10-document-create` / + * `eval-input-list` 三张局面已被 Task 7 的 `input-supplement` 一张顶掉,但这条测的形状 + * 与新设计不同,不是简单换个 view 名字就能救活:①P-10(`parts.document`)整个不在新 + * S1 的范围里(本活动不再碰它);②新设计里 `parts.profile` 两行由系统铺、人不加行 + * (要建哪颗料是启动参数定死的事),`parts.parameter` 的行集由配方从 + * `sub_eval.part × parts.type_param` 派生且 `prune:true`(手加的孤行下一拍就被删)—— + * 这两张表都**不给** `data-edit-add`,只有 `parts.type_param`(新建类型的字段清单)才有。 + * 新覆盖见下面「件① 评估输入补全」;继续 skip、不改断言内容,留给读的人对照差异。 */ it.skip('P-08 数据工单件的表 1 + 表 2(标准化参数子表)都可加行可编辑:data-edit / data-edit-add 与主键 data-col 都在', () => { const p08 = viewHtml('p08-master-create'); @@ -350,11 +358,9 @@ describe('报告模版:编辑面与只读面各就各位', () => { } }); - it('每个可编辑格子的 data-col 都在对应台账对象的声明里(p08-master-create 一屏两张表,两张台账对象的声明并起来看)', () => { + it('每个可编辑格子的 data-col 都在对应台账对象的声明里(input-supplement 一屏三张表,三张台账对象的声明并起来看)', () => { const cases: Array<[string, Array<[string, string]>]> = [ - ['p08-master-create', [['parts', 'profile'], ['parts', 'parameter']]], - ['p10-document-create', [['parts', 'document']]], - ['eval-input-list', [[D, 'input_gap']]], + ['input-supplement', [['parts', 'profile'], ['parts', 'type_param'], ['parts', 'parameter']]], ]; for (const [view, objects] of cases) { const cols = new Set([...viewHtml(view).matchAll(/data-col="([^"]+)"/g)].map((m) => m[1] ?? '')); @@ -380,8 +386,13 @@ describe('报告模版:编辑面与只读面各就各位', () => { await expect(host.base.interaction.pushHtml('
')).rejects.toThrow(/无脚本执行权/); }); - /* SKIP(2026-08-29,Task 2 → Task 7-9):VIEWS 列的五张局面已全部撤下,`assets.viewTemplates` 现在只有运营侧看板一张 */ - it.skip('五张模版的外壳都是搬自目标件的 page/card,不再含 E04/I01 那套视觉底(.proto-root)的痕迹', () => { + /* + * SKIP(2026-08-29,Task 2 → Task 7 部分交付):VIEWS 现在是三张新局面的名单,`input-supplement` + * 已随 Task 7 补回、这一条本身可以对它验(外壳确实是 page/card),但 `difference-review` / + * `landing-confirm` 两张还没有(viewHtml 返回空串,`toContain('class="card-head"')` 会假), + * 循环体没法只验一部分 —— 等 Task 8/9 补齐三张再一起解封。 + */ + it.skip('三张模版的外壳都是搬自目标件的 page/card,不再含 E04/I01 那套视觉底(.proto-root)的痕迹', () => { for (const view of VIEWS) { const html = viewHtml(view); expect(html, view).toContain('class="card-head"'); @@ -391,6 +402,56 @@ describe('报告模版:编辑面与只读面各就各位', () => { }); }); +/* + * 件① 评估输入补全(2026-08-29,Task 7):S1 的配方缺什么就发一张数据工单,`view: 'input-supplement'` + * 指向它。三块竖排——要评估什么(两颗料的存在性 + 主数据)/ 芯片类型(选型 + 新建类型时的核心参数 + * 字段清单)/ 参数值(逐条实测/规格值);顶部一条红带摆配方算好的 `gate_reason`。 + * 复用文件顶部已有的 `viewHtml` 短写(与 brief 草稿里的 `viewOf` 同一件事,不重复定义一个)。 + */ +describe('件① 评估输入补全', () => { + const html = viewHtml('input-supplement'); + + it('在 viewTemplates 里,且 S1 指向它', () => { + expect(html).not.toBe(''); + const steps = manifest.assets.businessModel.steps as ReadonlyArray<{ item: string; view?: string }>; + expect(steps.find((s) => s.item === 'S1_inputs')?.view).toBe('input-supplement'); + }); + + it('三块都在,行都绑本单草稿(不是整表)', () => { + expect(html).toContain('data-rows="mine:sub_eval.part"'); + expect(html).toContain('data-rows="mine:parts.parameter"'); + expect(html).toContain('data-rows="mine:parts.type_param"'); + }); + + it('芯片类型是从台账现读的下拉 —— 写死等于「只能选出包那天就有的那几种」', () => { + expect(html).toContain('data-options="parts.chip_type"'); + expect(html).toContain('data-option-value="type_id"'); + expect(html).toContain('data-option-label="type_name"'); + /* data-options 与 data-slot 不能共存(渲染时 data-slot 会被剥掉)—— 当下值由 data-col 定 */ + expect(/data-options="[^"]*"[^>]*data-slot=/.test(html)).toBe(false); + }); + + it('**主键列必挂 `data-col`** —— 它不是编辑面,是行的钥匙(漏了会把人改的行错配到别行上)', () => { + expect(html).toContain('data-col="internal_pn"'); + expect(html).toContain('data-col="param_name"'); + }); + + it('可改的三张表各有一道编辑闸', () => { + for (const address of ['parts.profile', 'parts.type_param', 'parts.parameter']) { + expect(html).toContain(`data-edit="${address}"`); + } + }); + + it('模版不算不判:没有内联脚本,红带的显隐交给样式表', () => { + expect(html).not.toContain(' { + expect(html).not.toMatch(/\.jc-tbl-wrap\s*\{[^}]*overflow\s*:\s*hidden/); + }); +}); + /* * 2026-08-27 实测账,两条都真截图 + SQL 复核过: * ① **带下划线的枚举值一律显示成「待定」**——平台有两套互相打架的消毒规则: @@ -481,17 +542,17 @@ describe.skip('徽标别名与值域相关的坑(2026-08-27 实测账,回归网) * 两条各配一张回归网,防的是同一类事故再出现在别的字段上。 */ /* - * SKIP(2026-08-29,Task 2 → Task 7 补回):`EDITABLE` 列表指着的三张局面(p08-master-create / - * p10-document-create / eval-input-list)与 `sub_eval.input_gap` 都已随重写撤下 —— 这两条 - * Critical 回归网等 Task 7 重做数据工单件时再钉在新件上。 + * 解封(2026-08-29,Task 7):`EDITABLE` 列表已按新设计换成 `input-supplement` 一张件绑的 + * 三张台账对象(`parts.profile` / `parts.type_param` / `parts.parameter`)—— `p10-document-create` + * 与 `sub_eval.input_gap` 两张随重写整个退休,不再有对应条目。这两条 Critical 回归网继续钉在 + * 新件上:主键(internal_pn / type_id+param_name)都挂了 `data-col`,必填列都有可填的控件。 */ -describe.skip('可编辑件的主键与必填列(2026-08-27 复审:主键丢钥匙 / 必填填不了,两条 Critical 回归)', () => { - /** 三张真能编辑的件与它们各自绑定的台账对象 */ +describe('可编辑件的主键与必填列(2026-08-27 复审:主键丢钥匙 / 必填填不了,两条 Critical 回归)', () => { + /** input-supplement 一屏三张真能编辑的表与它们各自绑定的台账对象 */ const EDITABLE: ReadonlyArray<{ view: string; domain: string; object: string }> = [ - { view: 'p08-master-create', domain: 'parts', object: 'profile' }, - { view: 'p08-master-create', domain: 'parts', object: 'parameter' }, - { view: 'p10-document-create', domain: 'parts', object: 'document' }, - { view: 'eval-input-list', domain: D, object: 'input_gap' }, + { view: 'input-supplement', domain: 'parts', object: 'profile' }, + { view: 'input-supplement', domain: 'parts', object: 'type_param' }, + { view: 'input-supplement', domain: 'parts', object: 'parameter' }, ]; it('每一个主键列都有 data-col —— 它不是编辑面,是行的钥匙,丢了就整行保存不下去', () => { diff --git a/activities/part-substitution-assessment/src/case-model.ts b/activities/part-substitution-assessment/src/case-model.ts index cbbb7aa..1d68f33 100644 --- a/activities/part-substitution-assessment/src/case-model.ts +++ b/activities/part-substitution-assessment/src/case-model.ts @@ -240,6 +240,9 @@ export const ASSESSMENT_STEPS = [ item: 'S1_inputs' as const, intent: '核对两颗料在不在库、归没归同一个类型、参数值齐不齐;缺什么就派一张数据工单让人补', guide: '', + /* Task 7:发给人的数据工单摆的就是这一张 —— 三块竖排(存在性与主数据 / 芯片类型字段 / 参数值), + 与 auto.do 第 ⑤ 笔 `card` 的 `gateLedgers` 同源(parts.chip_type / parts.type_param / parts.profile / parts.parameter)。 */ + view: 'input-supplement', writes: ['sub_eval.part', 'sub_eval.case', 'parts.profile', 'parts.parameter'], reads: ['originalInternalPn', 'candidateInternalPn', 'case:openedAt', 'parts.profile', 'parts.parameter', 'parts.chip_type', 'parts.type_param'], tools: [], diff --git a/activities/part-substitution-assessment/src/manifest.ts b/activities/part-substitution-assessment/src/manifest.ts index 05ac563..fb0f3d3 100644 --- a/activities/part-substitution-assessment/src/manifest.ts +++ b/activities/part-substitution-assessment/src/manifest.ts @@ -39,6 +39,7 @@ import type { ActivityManifest } from '@contracts'; import { activityIdSchema } from '@contracts'; import { CMMN_XML, NARRATIVE, ASSESSMENT_PHASES, ASSESSMENT_STEPS } from './case-model.ts'; import { LEDGER_BOARD, LEDGER_BOARD_DATA } from './views/ledger-board.ts'; +import { INPUT_SUPPLEMENT } from './views/report.ts'; import { ACTIVITY_ID_TEXT, D } from './constants.ts'; export { D, RULE_VERSION } from './constants.ts'; @@ -437,12 +438,17 @@ export const manifest: ActivityManifest = { /* 三条技能已随重写退役(全程无 agent,没有临场编排可言);skills.ts 的删除排在 Task 10 */ skills: [], /* - * 五张旧局面全部作废(它们绑的九步与旧台账地址已经不存在),**暂时只留运营中心那张看板** —— - * 三张新件(`input-supplement` / `difference-review` / `landing-confirm`)由 Task 7–9 各自加回; - * 中途(Task 2–Task 9 之间)包不装,不会被静态审拦下。看板自己的 `data` 声明现在也引着已经 - * 不存在的台账地址(`sub_eval.pn_check` 等)——Task 9「运营中心看板重绑」会把它换成新九张表。 + * 五张旧局面全部作废(它们绑的九步与旧台账地址已经不存在)。**Task 7 已补回第一张**: + * `input-supplement`(评估输入补全,S1 的数据工单,三块竖排 —— 存在性与主数据 / + * 芯片类型字段 / 参数值)。`difference-review` / `landing-confirm` 两张新件由 Task 8/9 + * 各自加回;中途(Task 7–Task 9 之间)包不装,不会被静态审拦下。看板自己的 `data` 声明 + * 现在也引着已经不存在的台账地址(`sub_eval.pn_check` 等)——Task 9「运营中心看板重绑」 + * 会把它换成新九张表。 */ - viewTemplates: [{ name: 'activity-ledger', html: LEDGER_BOARD, data: LEDGER_BOARD_DATA }], + viewTemplates: [ + { name: 'activity-ledger', html: LEDGER_BOARD, data: LEDGER_BOARD_DATA }, + { name: 'input-supplement', html: INPUT_SUPPLEMENT }, + ], cardSchemas: {}, /* 蒸馏钩子撤空:它是给 agent 在终态回看轨迹用的,而这个活动全程无 agent(AI 只出场一次、答一份结构化判断,没有可供归纳的多轮轨迹) */ distillHooks: [], diff --git a/activities/part-substitution-assessment/src/views/report.ts b/activities/part-substitution-assessment/src/views/report.ts index f743047..d9aee44 100644 --- a/activities/part-substitution-assessment/src/views/report.ts +++ b/activities/part-substitution-assessment/src/views/report.ts @@ -1,32 +1,35 @@ /** * 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」。 + * **2026-08-29 Task 7 重写**:五张旧局面(P-08 数据工单 / P-10 数据工单 / 评估输入清单 / + * 差异评估结论审核 / 评审放行与落账确认)按 spec §8 全部作废,S1 那一步不再分成 + * 「先核对存在性 → 分别派 P-08/P-10 数据工单 → 再摆评估输入清单」三个局面 —— + * 新的四步案例模型只有一张数据工单卡(`variant: 'data'`),这里对应**一张**新局面 + * `input-supplement`(评估输入补全):**三块竖排**(spec §6.1)—— + * ① 要评估什么(两颗料的存在性 + 主数据,`sub_eval.part` 铺行 / `parts.profile` 承载编辑); + * ② 芯片类型(每颗料各自的类型选择,选项从 `parts.chip_type` 现读; + * 下面跟着新建类型时要填的核心参数字段清单 `parts.type_param`,供加行); + * ③ 参数值(逐条核心参数的实测 / 规格值,一字段一行,`parts.parameter`)。 + * 顶部一条红带摆 S1 配方算好的 `gate_reason`(「还差什么」的一句话,配方 §see case-model.ts + * 的 step④),够不够齐、该不该出现由样式表判(§5「模版不许算」)。 * - * 目标件的样式表逐字节存在 `./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` 文案、不额外配色)。 + * `difference-review`(件② 差异评估结论审核)与 `landing-confirm`(件④ 评审放行与落账确认) + * 由 Task 8 / 9 分别重做(同名新建),这份文件此刻只有一张工单件。 + * + * 版面语言仍是「搬,不要照着做」(《HTML报告生成规范》§7):`P08_CSS`(逐字节抽自 + * `docs/SS02-ACT-WORK-05-html/ACT-WORK-05-数据工单-01-芯片主数据创建.html`, + * 见 `./target-style.ts` 头注)是**唯一**的样式来源 —— 三块内容原来分属 P-08 数据工单 / + * P-10 数据工单(复用 P08_CSS)/ 执行工单(EXEC_CSS)三张局面,合并成一张之后 + * 统一沿用 P08_CSS 这一套版面语言(EXEC_CSS 随之退休,见 target-style.ts 头注)。 + * `BIND_COMMON`/`BIND_P08` 是平台绑定层加的东西(缺项红带、编辑闸/增删行、 + * `data-class-of` 的 `v-*` 别名):目标件是写死数据的静态 demo,没有「N 行草稿」 + * 「编辑态与归档态二选一」这些平台概念。 */ -import { P08_CSS, EXEC_CSS, ACC1_CSS, ACC2_CSS } from './target-style.ts'; +import { P08_CSS } from './target-style.ts'; import { D } from '../constants.ts'; /* ==================================================================================== - * 绑定层公共开关 —— 五张模版共用,与目标件自己的样式表分开存(那份要保持逐字节可比) + * 绑定层公共开关 —— 三张模版共用(此刻只有一张),与目标件自己的样式表分开存(那份要保持逐字节可比) * ==================================================================================== */ const BIND_COMMON = ` /* ── 2026-08-27 用户四连指出的件上毛病,病根三处都在「目标件的样式与平台的编辑态没对上」 ── */ @@ -71,6 +74,27 @@ html.editable .jc-gapbar { .jc-gapbar b { font-weight: 800; } .jc-gapbar span { color: #9a3b36; } html.editable .jc-gapbar.v-0 { display: none; } +/* + * 顶部「还差什么」红带(2026-08-29,Task 7 新增,不是照抄 .jc-gapbar):gate_reason + * 是一句中文自由文本(配方拼的整句话,如「有料号库里查不到 —— 在下面把它的主数据建起来。」), + * 不是像 _missing 那样的整数计数 —— data-class-of 把值消毒成 class 时只留 [a-z0-9-], + * 中文字符全被剥空,v- 永远是空 token(既取不出 v-0 也取不出任何有意义的档位), + * 靠它去挑「空 → 隐藏」这条路本身就走不通(核对过 view-render.ts 的 classToken 实现)。 + * 改用更结实的信号:data-slot 配 data-blank 让空值真的渲成空文本节点,CSS 用 :has(:empty) + * 认「这句话是不是空的」—— gate_reason 本身还是照 brief 摆 data-class-of(留作未来若换成 + * 枚举型信号时的钩子,且不影响任何断言),但真正管显隐的是下面这条 :empty 规则。 + */ +.jc-gate-bar { display: flex; align-items: flex-start; gap: 8px; margin: 0 0 14px; padding: 10px 14px; + border-radius: 9px; background: #fff5f5; border: 1px solid #f3b9b5; color: var(--danger); font-size: 12.5px; line-height: 1.65; } +.jc-gate-bar b { font-weight: 800; } +.jc-gate-bar:has(.jc-gate-text:empty) { display: none; } +/* + * 不给删行的两张表,编辑态里 # 号不能空着:P08_CSS 的 table.on td.rn .jc-rn-num{display:none} + * 是配 .jc-rowdel 一起写的(删行按钮腾出位置)—— 要评估什么(#tbl1)与参数值(#tbl3)两张表 + * 行集固定(人不加行、系统按 sub_eval.part 与 parts.type_param 铺),没有删行按钮可换,编辑态里 + * 用 id 选择器把计数器重新点亮(id 特异度压过 P08_CSS 的类选择器)。 + */ +#tbl1.on td.rn .jc-rn-num, #tbl3.on td.rn .jc-rn-num { display: inline; } `; /* @@ -101,12 +125,16 @@ const DANGER = 'color: var(--danger); background: var(--danger-soft); border-col const AMBER = 'color: #8f6a12; background: #fdf3d7; border-color: #ecd9a0;'; const MUTED = 'color: var(--ink-3); background: #eef1f6; border-color: var(--line);'; -/* ==================== 数据工单(P08/P10 共用):徽标别名 ==================== */ +/* + * ==================== input-supplement 的徽标别名 ==================== + * + * 沿用 P08_CSS 已带的两个基座形状:`.stx`(生命周期状态那一族用的胶囊)与 `.ct`(约束类型)—— + * 不新起一个 `.st` 基座类(那是已退休的 EXEC_CSS 定义的,P08_CSS 里没有它的 display/padding + * 这些基础属性,借用类名而没有基座样式,徽标会是一段无样式的裸文字)。 + */ const BIND_P08 = ` -/* 生命周期 / 资料类型 / 资料状态 三个枚举列共用同一个徽标类 —— 取值域互不重叠(active/nrnd/eol/ - discontinued/risk 与 datasheet/app_note/… 与 valid/expired/pending/void 三组没有一个词撞车), - 同一页内不必再分家。配色借的是本设计语言在别的目标件里已经出现过的那几个色(ok/danger/琥珀), - 不是新配的颜色。 */ +/* 生命周期状态(parts.profile)与在库状态(sub_eval.part.exists_status)共用 .stx 胶囊 —— + 取值域互不重叠(active/nrnd/eol/discontinued/risk 与 found/absent 没有一个词撞车)。 */ .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], @@ -114,164 +142,19 @@ ${badgeCss('.stx', [ ['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], + ['found', '在库', OK], + ['absent', '不在库', AMBER], ])} -/* - * 表 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 同一份值)。 - */ +/* 约束类型(硬/软)—— parts.type_param.constraint_type,目标件 P08_CSS 已带 .ct 基座形状与这两条颜色声明 */ ${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 文案 */ +/* 角色(原 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], - // 目标件 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', '其他'], -])} `; /** 三层壳照目标件:`
`,不再套 `.proto-root`/`.app-shell`/`.jc-stage` @@ -314,8 +197,6 @@ 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} @@ -345,387 +226,160 @@ const badge = (klass: string, col: string, wrapTwin = false): string => { const inner = ``; return wrapTwin ? `${inner}` : inner; }; +/** + * **下拉的选项从台账现读**(spec §5.3,2026-08-29):`data-options` 与 `data-slot` 不能共存 —— + * 渲染时 `fillOptions` 会把 `data-slot` 一并摘掉(它是给内容文本用的槽位标记,``; +/** 顶部「还差什么」红带 —— 见 BIND_COMMON 里 `.jc-gate-bar` 的显隐说明(它不是 gapbar 那种数值判断) */ +const gateBar = (): string => `
+ 还差什么 —— +
`; -/* ==================== 局面 0 · 数据工单 P-08(搬自「数据工单-01」)==================== */ +/* ==================== 局面:评估输入补全(input-supplement,S1 的数据工单) ==================== */ -const P08_TABLE = `
+/** + * 块① 要评估什么 —— 两颗料(原 PN / 候选 PN)的存在性与主数据。 + * + * **行由 `sub_eval.part` 铺、写向 `parts.profile`**(`data-rows="mine:sub_eval.part" + * data-edit="parts.profile"`,两个不同的台账地址同时出现在一个行容器上,收值的授权面 + * 只认 `data-edit`,与行从哪来无关 —— `apps/api/src/app/routes/desk.ts` 的 + * `writesOf`/`collect` 按 `data-edit` 地址找容器、按容器内 `data-col` 收值,`data-rows` + * 只管这一屏摆几行、摆谁的)。选它做行源是因为**角色(原/候选)与在库状态**只长在 + * `sub_eval.part` 上,而这两格是「要评估什么」的第一句话;`internal_pn` 挂 `data-col` + * 之后,保存时按它认作 `parts.profile` 的主键。**人不加行**(要建哪颗料是启动参数定死的事, + * 见清单 `S1_inputs.reads`),没有「+加一行」/删行按钮 —— #号列的显隐见 BIND_COMMON + * 里 `#tbl1.on td.rn .jc-rn-num` 那条覆盖。 + * + * 芯片类型选择器**长在每一行里**(不是页面级单选):`sub_eval.part.chip_type_id` / + * `parts.profile.chip_type_id` 都是**逐颗料各自一份**的字段(`distinct` 判据要求两颗 + * 恰好落在同一个值上,前提是它们本就可能不同),写入目标仍是 `parts.profile`。 + * 只读态用嵌套 `data-by` 从 `parts.chip_type` 整表按 `type_id` 查出人读的 `type_name` + * (取值,不是算);编辑态换成 `optionSelect`(选项现读)。 + */ +const PART_TABLE = `
+ + + - - - - - + + - - - + + + + + + - - - - -
#角色 内部料号在库状态芯片类型 品牌制造商规格描述封装工艺 生命周期EOL/PCN制造商规格描述 Datasheet 引用 Datasheet 版本适配场景
${cell('internal_pn', 'mono')}${badge('stx', 'exists_status')} + + ${optionSelect('chip_type_id', 'parts.chip_type', 'type_id', 'type_name', true)} + ${cell('brand_name')}${badge('stx', 'lifecycle_status', true)}${select('lifecycle_status', [['active', 'Active'], ['nrnd', 'NRND'], ['eol', 'EOL'], ['discontinued', '停产'], ['risk', '风险']], true)} ${cell('manufacturer_name')} ${cell('specification_description')}${cell('package', 'mono')}${cell('process_technology')}${badge('stx', 'lifecycle_status', true)}${select('lifecycle_status', [['active', 'Active'], ['nrnd', 'NRND'], ['eol', 'EOL'], ['discontinued', '停产'], ['risk', '风险']], true)}${cell('eol_pcn_status')} ${cell('datasheet_ref', 'mono')} ${cell('datasheet_version')}${cell('application_scenarios')}
`; -/* - * 表 2 · 标准化参数集(P-08 子表 `parts.parameter`)—— 粒度与表 1 不同(一个参数一行, - * 行数不固定),不能塞进表 1 的一格(《统一方案》§六:重复明细用子表承载), - * 逐块搬自目标件《数据工单-01》的【创建什么 · 表 2】,`id="tbl2"` 沿用目标件自己的 - * 列宽声明(P08_CSS 里 `#tbl2 th:nth-child(n)` 那几条,不必在这里重复)。 - * 「说明」列是系统只读注释(目标件原话:「说明列为系统注释,无需填写」),不给 data-col。 +/** + * 块② 芯片类型的核心参数字段清单 —— `parts.type_param`,复合主键 `type_id + param_name`。 + * **供新建类型时加字段行**(唯一带「+加一行」/删行的一张):类型下拉里没有合适的, + * 人就在这里把新类型的核心参数字段一条条定下来(名字 / 说明 / 硬软约束 / 单位)。 + * + * **不挂 `id="tbl2"`**:P08_CSS 的 `#tbl2 th:nth-child(n)` 那几条列宽规则是按位置 + * (nth-child)写死给旧版 `parts.parameter` 表(所属料号/参数名/值/单位/约束/说明,7 列)调的, + * 这张表列的顺序与语义不同(所属类型编号/参数名/参数说明/约束/参考单位,6 列)—— + * 位置 5、6 恰好被那两条老规则套反(约束套上「单位」的宽度、参考单位套上「约束」的宽度)。 + * 不挂 id 就不会继承这几条位置写死的规则,退回普通 `table`/`th`/`td` 基础样式。 */ -const P08_PARAMS_TABLE = `
+const TYPE_PARAM_TABLE = `
- + + + + + + + + + + + + + +
#所属料号所属类型编号 参数名参数说明约束(硬/软)参考单位
${cell('type_id', 'mono')}${cell('param_name')}${cell('param_desc')}${badge('ct', 'constraint_type', true)}${select('constraint_type', [['hard', '硬约束'], ['soft', '软约束']], true)}${cell('param_unit')}
`; + +/** + * 块③ 参数值 —— `parts.parameter`,一字段一行(行由 S1 配方按 `sub_eval.part × parts.type_param` + * 铺,`prune:true` 会把不属于这个组合的孤行清掉,故这张表也**人不加行**)。 + * **只两栏可改**(brief 原话):值 / 说明;`param_unit` 特意留只读 —— 配方每拍都从 + * `parts.type_param` 字典**直接复制**这一格(`{ col: 'param_unit' }`,不是自查现值的 + * `lookup...else` 那种自保写法),手改了也会在下一拍被字典值覆盖回去,给它配可编辑的 + * 输入孪生件只会让人误以为改了真的算数。 + */ +const PARAMETER_TABLE = `
+ + + + - - + - - - + +
#所属料号参数名 值 单位约束(硬/软) 说明
${cell('internal_pn', 'mono')} ${cell('param_name')} ${cell('param_value', 'mono')}${cell('param_unit')}${badge('ct', 'constraint_type', true)}${select('constraint_type', [['hard', '硬约束'], ['soft', '软约束']], true)}${ro('param_note')}${ro('param_unit')}${cell('param_note')}
`; -export const P08_MASTER_CREATE = doc({ +export const INPUT_SUPPLEMENT = doc({ css: P08_CSS, alias: BIND_P08, badge: '数据', hic: HIC, tag: '数据工单', - kicker: 'SS02 · ACT-WORK-05 差异评估 · 数据工单 1/2', - title: '芯片主数据创建(P-08)', - step: '步骤 0.5 · 等待数据', + kicker: 'SS02 · ACT-WORK-05 差异评估 · 数据工单', + title: '差异评估的输入还差几格', + step: '步骤 1/4 · 校验入参与补数据', footTag: '工单类型:数据', body: - zone('做什么', '批量创建 P-08 主数据记录', ZIC_TASK) + + zone('做什么', '核对两颗料在不在库、归没归同一个芯片类型、参数值齐不齐;缺什么就在下面补齐', ZIC_TASK) + note([ - '模板定位:本单是「数据工单」统一模板的实例——一单一类(可多条):目标台账 = 芯片主数据(P-08);字段即表头、每行一条新记录;标准化参数集是它的结构子表(表 2,一行一参数,按所属料号归属)。', - '触发原因:系统按内部料号查芯片主数据查无记录——是「完全没有」,不是字段缺失(那归执行工单)。', - 'AI 预填:上传了 datasheet 的料号 AI 已按资料预填,标着值的格子请核对;没上传的料号全部手填,同为合法路径。', - '纪律:必填列全齐才允许确认;确认后这批记录进本单草稿,放行时并入正式库并留痕(确认人 = 数据责任人);只建主数据,验证记录等事实类不在此建。', + '模板定位:本单是「数据工单」——目标台账 = 芯片主数据(P-08 主记录 + 标准化参数子表),核心参数字段清单挂在芯片类型字典上。', + '触发原因:下面【要评估什么】里两行是本次评估要比对的原 PN 与候选 PN —— 至少有一颗不在库、两颗归的类型不一致、或必填格还空着,下面这条红带才会出现。', + '已绑定:两颗料的内部料号已按启动参数铺进下表,不必你重填料号本身,只需核对 / 补齐其余各格;在库的料现值已回填,只是查漏补缺。', + '纪律:两颗料必须归到同一个芯片类型下才谈得上逐项比对;类型下拉里没有合适的就新建一个,并把它的核心参数字段一并定下来;查不到的参数值留空(会被红带点名),不要编一个值顶替。', ]) + - zone('创建什么 · 表 1', '主数据记录(每行一条,*为必填列)', ZIC_TABLE) + + gateBar() + + zone('要评估什么', '两颗料的存在性与主数据(每行一颗,人不加行,*为必填列)', ZIC_TABLE) + + editGate('parts.profile', '两颗料的主数据核对 / 补齐,点「编辑」开表') + gapbar('parts_profile', '必填') + - editGate('parts.profile', `待建 条记录,点「编辑」核对预填、补齐空缺;「+ 加一行」= 再建一条`) + - P08_TABLE + - addrow('parts.profile', '+ 加一行(再建一条记录)', '新行的内部料号必须写(它是这一行的钥匙);其余必填列全部填写后才允许确认;删一行 = 该记录不建') + - `

${ZIC_TABLE}【创建什么 · 表 2】—— 标准化参数集(每行一个参数,按所属料号归属,*必填)

` + + PART_TABLE + + zone('芯片类型', '两颗料所归类型的核心参数字段清单(供新建类型时补齐,*为必填列)', ZIC_TABLE) + + editGate('parts.type_param', '字段名字 / 说明 / 硬软约束,点「编辑」核对;新建类型用「加一行」补字段') + + gapbar('parts_type_param', '必填') + + TYPE_PARAM_TABLE + + addrow('parts.type_param', '+ 加一行(补一个核心参数字段)', '所属类型编号 / 参数名 / 参数说明 / 约束类型必须写;参考单位选填') + + zone('参数值', '逐条核心参数的实测 / 规格值(一字段一行,*为必填列;单位由字典定,这里不改)', ZIC_TABLE) + + editGate('parts.parameter', '逐条参数值核对 / 补齐,点「编辑」开表') + gapbar('parts_parameter', '必填') + - editGate('parts.parameter', `已铺 行参数(有 datasheet 的已按资料预填),点「编辑」核对修正;「+ 加一行」补充未列出的参数`) + - P08_PARAMS_TABLE + - addrow('parts.parameter', '+ 加一行(补充参数)', '所属料号 / 参数名 / 值 / 单位 / 约束类型齐全;「未披露」请直接写未披露,将进入 UNKNOWN 并挂验证任务;「说明」列为系统注释,无需填写') + + PARAMETER_TABLE + flow('提交 / 确认:本工单只承载内容;确认请在对话区的【执行卡】中进行。确认后活动自动从「等待数据」唤醒。'), }); - -/* ==================== 局面 0′ · 数据工单 P-10(复用「数据工单-01」的样式与骨架,没有独立目标件)==================== */ - -const P10_TABLE = `
- - - - - - - - - - - - - - - - - - - - - - - - -
#关联料号资料序号资料类型品牌资料标题资料版本文件链接证据定位资料状态
${cell('internal_pn', 'mono')}${cell('doc_seq', 'mono')}${badge('stx', 'document_type', true)}${select('document_type', [['datasheet', 'Datasheet'], ['app_note', '应用笔记'], ['reference_design', '参考设计'], ['white_paper', '白皮书'], ['sample_doc', '样片资料'], ['commitment_letter', '承诺函'], ['other', '其他']], true)}${cell('brand_name')}${cell('document_title')}${cell('document_version')}${cell('document_ref', 'mono')}${cell('citation_locator')}${badge('stx', 'document_status', true)}${select('document_status', [['valid', '有效'], ['expired', '过期'], ['pending', '待核实'], ['void', '作废']], true)}
`; - -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([ - '模板定位:与 P-08 同一套「数据工单」统一模板——目标台账 = 原厂技术资料库(P-10);一份资料一行,同一料号多份资料序号 01/02… 自己续。', - '触发原因:按内部料号查原厂技术资料库查无记录(P-08 那张先确认过,这一批料号才会走到这里)。', - '为什么要它:差异明细的证据引用要指到具体资料的版本与页码——没有这一行,结论就追溯不到来源。', - ]) + - zone('创建什么', '资料记录(每行一条,*为必填列)', ZIC_TABLE) + - gapbar('parts_document', '必填') + - editGate('parts.document', `待建 条资料记录,点「编辑」补齐`) + - P10_TABLE + - addrow('parts.document', '+ 加一行(再登记一份资料)', '关联料号与序号是这一行的钥匙,新行都要写;必填列全齐才允许确认;删一行 = 该资料不建') + - flow('提交 / 确认:本工单只承载内容;确认请在对话区的【执行卡】中进行。'), -}); - -/* ==================== 局面 1 · 评估输入清单(搬自「执行工单-01」)==================== */ - -const PN_CHECK_TABLE = `
- - - - - - - - - - - - - - -
#料号角色在库状态备注 / 放弃原因
${ro('internal_pn', 'mono')}${badge('st', 'exists_status', true)}${ro('note')} ${ro('abandon_reason')}
`; - -/** 候选参数现值 —— **只读参考,基座从台账现读**(2026-08-27,用户三次指出「库里明明有值,清单还要填一遍」): - * 从前「待确认」行由模型判「哪个参数原厂未披露」再写进清单 —— 参数值有没有是台账里的事实,不该由模型宣布; - * 现在每颗料一张小表(外层按核对表逐颗复制,里层 `data-by="internal_pn"` 只列它名下的参数行), - * 人看得见现值;未披露的由差异评估判成 UNKNOWN 并挂验证任务。 */ -const PARAM_REF = `
-
${badge('st', 'exists_status')}
-
- - - - - - - - - -
#参数名值单位约束类型说明
${ro('param_name')}${ro('param_value', 'mono')}${ro('param_unit')}${ro('param_note')}
-
`; - -const GAP_TABLE = `
- - - - - - - - - - - - - - - - - - -
#键输入项来源状态绑定值 / 缺口说明补充内容
${cell('item_key', 'mono')}${cell('input_item')}${cell('source_ledger', 'mono')}${select('status', [['gap', '待补'], ['default', '默认·可改'], ['ok', '齐备']], true)}${badge('st', 'status')}${cell('detail')}${cell('supplement')}
`; - -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([ - 'AI 已完成:系统已按启动传入的 4 项完成绑定——客户、客户需求(新建 P-01 · 版本 V1)、原 PN、候选 PN(数组,逐个评估)——并生成输入快照。', - '需要你补齐:启动时未收的信息(应用场景、BOM 位)直接在下表补;默认值(替代类型 / 规则版本 / 负责人期限)可确认可改。「完全没有」(料号不在库)已由数据工单创建;每颗料的参数现值摆在「候选参数现值」里,写着「未披露」的参数将按 UNKNOWN 进入差异评估并挂验证任务,不用在这里补。', - ]) + - zone('料号存在性核对', '只读参考 · 与下表「补充内容」互不冲突') + - PN_CHECK_TABLE + - zone('候选参数现值', '只读参考 · P-08 标准化参数集,一颗料一张,基座从台账现读;写着「未披露」的由差异评估判成 UNKNOWN 并挂验证任务') + - PARAM_REF + - zone('交回什么', '评估输入清单(已绑定项照实核对,缺口项补齐,*为必填)') + - // 红带说「必填格」不说「补充内容」:缺的可能是 AI 该写的输入项(2026-08-27 实测),写死列名会把人指向错的格子 - gapbar(`${D}_input_gap`, '必填格') + - editGate(`${D}.input_gap`, `还有 项待补,点「编辑」在「补充内容」列填入 —— 已绑定那几行的「输入项/来源/绑定值」抄的是启动入参,发现记录本身有误请退回触发方重新启动,不要在这里改`) + - GAP_TABLE + - addrow(`${D}.input_gap`, '+ 加一行(补充资料项)', '新行的「键」必须写(它是这一行的钥匙,写一个英文短名,如 thermal_note);「输入项」与「补充内容」也是必填;删一行 = 该输入项不适用;补不了的写「留 UNKNOWN」或「挂验证任务」') + - flow( - '审批 / 提交:本工单只承载内容;「提交」请在对话区的【执行卡】中进行。提交后去向:进入步骤 2 · AI 读取与标准化,仍缺失的项将按 UNKNOWN 进入差异评估。注意:启动参数如有误,需退回触发方修订后重新触发,本单不可改。', - ), -}); - -/* ==================== 局面 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 = `
- - - - - - - - - - - - - - - - - - -
#候选 PN品牌推荐程度风险级别差异统计(四态)结论摘要
${badge('rec', 'recommendation_level')}${badge('risk', 'risk_level')}
`; - -/** 差异明细 · 按候选 PN 分组 —— 目标件 2026-08-27 改版:**一个候选一张表**(用户:「存在多个替代 PN 要对比差异时看不清楚」)。 - * 外层容器
按 relation 逐个复制行模版
,里层 - * 只列该候选名下的明细(渲染器 2026-08-27 补的嵌套 + 父键过滤)。容器标签名不得在行模版内部再出现: - *
里是 section / div / table,不撞。 */ -const CANDIDATE_SECTIONS = `
-
- - - - vs 原 PN · - ${badge('rec', 'recommendation_level')}${badge('risk', 'risk_level')} -
-
- - - - - - - - - - - - - -
#参数项原 PN 值候选 PN 值单位约束类型结论风险等级证据引用AI 置信度
${badge('st dst', 'difference_status')}${badge('risk', 'risk_level')}
-
`; - -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([ - 'AI 已完成的计算:按 GOV 评估规则(锁定版本 ),以原 PN 为基准,对 个候选 PN 分别逐项比对,每项产出四态结论——MATCH / MISMATCH / UNKNOWN / CONDITIONAL,并给出风险等级、证据引用与置信度;再按候选汇总出下方的【候选 PN 结论汇总】(一个候选一条结论)。差异明细 = P-11 差异明细子表(一条参数差异一条记录,按候选分组);结论汇总 = P-11 主表草稿。当前均为 AI 草稿,FAE 审核通过前不落账。评估参数可能含本次数据工单创建的主数据(确认人背书;未附 datasheet 的参数项会显式标注「人工录入」)。', - ]) + - criteria('判断这份差异评估结论是否可信,请逐项核对', [ - '汇总结论与明细一致:结论表中每个候选的推荐程度 / 风险级别,与其下方差异明细表的四态分布吻合(存在 MISMATCH 硬约束或悬空 UNKNOWN 的候选不得给「推荐」);', - '关键参数差异均有明确状态:每个候选的逐项结论无漏项,每个参数都有四态之一的判定;', - 'UNKNOWN 项均有去向:每个 UNKNOWN 都挂了验证任务或补来源动作,不允许悬空;', - '证据链完整:每项结论可追溯至 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( - '审批:本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。审核意见与补充条件写入 P-06(验证要求 verification_requirement);通过后去向:进入步骤 5 · 评审放行与落账确认。', - ), -}); - -/* ==================== 局面 3 · 评审放行与落账确认(搬自「验收工单-02」)==================== */ - -/** 字段:值 两列表 —— 逐字节搬自目标件「拟写 P-11」「拟回写 P-01」的版式(一行一个字段), - * 用 `data-rows` 包一整张小表当行模版,一个候选 / 一条需求各重复出一份,天然支持多候选。 */ -const P11_FIELD_TABLE = `
- - - - - - - - - - - - - - - - -
#字段拟写入值
替代类型
原内部料号
候选内部料号
推荐等级${badge('st', 'recommendation_level')} (枚举:推荐 / 条件推荐 / 备选 / 不推荐,四态之一)
推荐理由
差异摘要
替代分析报告
分析状态${badge('st', 'analysis_status')} (枚举:AI草稿 / FAE确认 / 待客户验证 / 客户已批准 / 不批准 / 作废)
客户批准状态未提交 (由审批流程维护,本活动不产生)
风险等级${badge('risk', 'risk_level')}
验证要求
证据可信度
差异规则版本
`; - -const P01_FIELD_TABLE = `
- - - - - - - - - -
#字段拟写入值
风险等级${badge('risk', 'risk_level')}
验证要求
输出链接
下一步动作
当前阶段${badge('st', 'current_stage')}
需求状态${badge('st', 'request_status')}
`; - -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([ - '本活动以 ACT-GATE-03 评审放行为终点。本单确认拟落账内容:评估结论层写 P-11 替代关系主记录(替代类型 / 原内部料号 / 候选内部料号 / 推荐等级 / 推荐理由 / 差异摘要 / 替代分析报告 / 分析状态 / 风险等级 / 验证要求 / 证据可信度 / 差异规则版本),需求状态回写 P-01(风险等级 / 验证要求 / 输出链接 / 下一步动作 / 当前阶段)。客户批准不属于本活动——客户批准状态由审批流程维护,本单只写分析层(分析状态 = FAE 确认),customer_approval_status 保持「未提交」,不由差异评估产生。', - ]) + - criteria('放行与落账前,请逐项核对', [ - '推荐等级与差异明细一致:差异明细中存在 UNKNOWN / CONDITIONAL 时,推荐等级不得给「推荐」;', - '分析状态与客户批准状态分离:本单只写分析层(分析状态 = FAE 确认),客户批准状态保持「未提交」;', - 'GOV 规则版本已锁定:规则版本随结论一并落账;', - '回写 P-01 字段完整:风险等级、验证要求、输出链接、下一步动作、当前阶段五项齐全。', - ]) + - zone('拟写 P-11 · 替代关系主记录', '只读展示') + - P11_FIELD_TABLE + - zone('拟回写 P-01 · 需求状态', '只读展示') + - P01_FIELD_TABLE + - flow( - '审批:本工单只承载内容;「通过 / 驳回」请在对话区的【验收卡】中进行。通过后去向:在对话区卡提交 ACT-GATE-03 评审放行;放行后结论供 ACT-WORK-08 证据归集消费。', - ), -}); diff --git a/activities/part-substitution-assessment/src/views/target-style.ts b/activities/part-substitution-assessment/src/views/target-style.ts index 74bb165..868b542 100644 --- a/activities/part-substitution-assessment/src/views/target-style.ts +++ b/activities/part-substitution-assessment/src/views/target-style.ts @@ -1,5 +1,5 @@ /** - * ACT-WORK-05 差异评估的四份目标件样式表 —— **机械抽取,一个数值都不许改** + * ACT-WORK-05 差异评估的目标件样式表 —— **机械抽取,一个数值都不许改** * (《HTML报告生成规范》§7:有目标件时「搬」,不要「照着做」;样式表整段搬,标记逐块搬)。 * * 来源(`docs/SS02-ACT-WORK-05-html/`): @@ -7,13 +7,20 @@ * (该文件头部连续放了两个 `` 之间的原文,再拿抽出来的内容包回 * `` 与源文件同一行区间做 `diff`,四份全部「零差异」——过程写在 @@ -21,7 +28,7 @@ * * 这份文件只装**目标件自己的样式**,不装平台绑定用的开关(三态占位、缺项红带、 * `data-class-of` 的 `v-*` 别名这类)——那些是本活动自己加出来的东西,活在 - * `report.ts` 的 `BIND_CSS_*` 常量里,两处不要混。 + * `report.ts` 的 `BIND_*` 常量里,两处不要混。 */ export const P08_CSS = ` @@ -126,88 +133,6 @@ table.on td>.jc-vcell.ro{display:block} @media (prefers-reduced-motion: reduce){*{animation:none!important;transition:none!important}} `; -export const EXEC_CSS = ` -:root{ - --ink:#1f2329; --ink-2:#41464e; --ink-3:#646a73; --ink-4:#8f959e; - --line:#dee0e3; --line-soft:#e8eaed; --soft:#f8f9fb; - --brand:#3370ff; --brand-deep:#245bdb; --brand-soft:#e1eaff; --brand-wash:#f0f4ff; - --human:#a9711e; --human-soft:#f6ecd8; --human-line:#e6d3a8; - --ok:#278717; --ok-soft:#e8f8e4; --danger:#d83931; --danger-soft:#feeae9; - --mono:"SF Mono",Menlo,Consolas,monospace; -} -*{box-sizing:border-box} -html,body{margin:0;padding:0} -body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei",sans-serif;background:#eef1f6;color:var(--ink);font-size:14px;line-height:1.7;-webkit-font-smoothing:antialiased} -.page{margin:0;padding:0} -.card{background:#fff;overflow:hidden;border:0;border-radius:0;box-shadow:none} -.card-head{display:flex;align-items:center;gap:14px;padding:18px 22px;border-bottom:2px solid var(--ink);background:linear-gradient(120deg,#fffaf1 0%,#fff 55%,#fbf6ea 100%)} -.badge{flex:none;width:44px;height:44px;border-radius:10px;display:grid;place-items:center;background:var(--human);color:#fff;font-weight:800;font-size:13px;box-shadow:0 6px 16px rgba(169,113,30,.25)} -.tit{flex:1;min-width:0} -.tit .kick{font-size:11px;letter-spacing:.08em;color:var(--ink-4);font-weight:700} -.tit h1{margin:2px 0 0;font-size:19px;font-weight:800;line-height:1.3} -.tit .sub{font-size:12px;color:var(--ink-3);margin-top:3px} -.side{display:flex;flex-direction:column;align-items:flex-end;gap:6px;flex:none} -.tag{font-size:11px;font-weight:800;padding:2px 10px;border-radius:999px;color:var(--human);background:var(--human-soft);border:1px solid var(--human-line)} -.step{font-size:12px;color:var(--ink-3)} -.deadline{font-size:12px;color:var(--ink-3)} -.deadline b{color:var(--danger)} -.card-bd{padding:20px 22px} -.zone{font-size:11px;font-weight:800;letter-spacing:.12em;color:var(--ink-4);margin:0 0 8px} -.zone b{color:var(--ink-3)} -.note{border:1px solid var(--line);border-left:3px solid var(--human);border-radius:8px;background:var(--soft);padding:14px 16px;margin-bottom:16px} -.note p{margin:0;font-size:13px;color:var(--ink-2)} -.note b{color:var(--ink)} -.note .done{color:var(--ok);font-weight:700} -.note .need{color:var(--human);font-weight:700} -.jc-editgate{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:14px 0 10px;padding:9px 12px;border-radius:9px;background:var(--soft);border:1px solid var(--line-soft);font-size:12px;color:var(--ink-3)} -.jc-editgate-t{flex:1 1 auto;min-width:0} -.jc-editgate-t b{color:var(--ink-2);font-weight:700} -.jc-egbtn{flex:none;display:inline-flex;align-items:center;gap:5px;cursor:pointer;border:1px solid var(--line);border-radius:7px;padding:6px 13px;font:inherit;font-size:12px;font-weight:700;color:var(--ink-2);background:#fff;transition:border-color .16s,color .16s,background .16s} -.jc-egbtn:hover{border-color:var(--brand);color:var(--brand)} -.jc-egbtn.save{color:#fff;background:var(--human);border-color:var(--human)} -.jc-egbtn.save:hover{filter:brightness(1.06);color:#fff} -.jc-editgate .eg-cancel,.jc-editgate .eg-save{display:none} -.jc-editgate.on{background:var(--human-soft);border-color:var(--human-line);color:#7c4a12} -.jc-editgate.on .eg-open{display:none} -.jc-editgate.on .eg-cancel,.jc-editgate.on .eg-save{display:inline-flex} -.tbl-wrap{border:1px solid var(--line-soft);border-radius:10px;overflow:hidden} -table{width:100%;border-collapse:collapse;font-size:12.5px} -th{text-align:left;background:var(--soft);font-size:11px;letter-spacing:.03em;font-weight:800;color:var(--ink-3);padding:9px 10px;border-bottom:1px solid var(--line)} -th.req::after{content:" *";color:var(--danger);font-size:11px} -td{padding:8px 10px;border-bottom:1px solid var(--line-soft);vertical-align:middle} -tr:last-child td{border-bottom:0} -td.rn{position:relative;width:38px;white-space:nowrap;font-family:var(--mono);font-size:11px;color:var(--ink-4);text-align:center} -.jc-vcell{display:block;font-size:12.5px;color:var(--ink);line-height:1.5;overflow-wrap:anywhere} -.jc-vcell.mono{font-family:var(--mono);font-size:11.5px;color:var(--ink-2)} -.jc-cell{display:none;width:100%;min-width:60px;border:1px solid var(--line);border-radius:6px;padding:6px 8px;font:inherit;font-size:12.5px;color:var(--ink);background:#fff} -.jc-cell.mono{font-family:var(--mono);font-size:11.5px} -.jc-cell:focus{border-color:var(--brand);box-shadow:0 0 0 3px var(--brand-wash);outline:none} -table.on td>.jc-vcell{display:none} -table.on td>.jc-cell{display:block} -td.rn .jc-rowdel{display:none;width:22px;height:22px;margin:0 auto;cursor:pointer;border:1px solid var(--line);border-radius:6px;padding:0;font:inherit;font-size:14px;line-height:1;color:var(--ink-3);background:#fff} -td.rn .jc-rowdel:hover{border-color:var(--danger);color:var(--danger);background:var(--danger-soft)} -table.on td.rn .jc-rn-num{display:none} -table.on td.rn .jc-rowdel{display:block} -.jc-addrow{display:none;margin:8px 0 0;padding:8px 12px;border-radius:9px;background:var(--soft);border:1px dashed var(--line);font-size:12px;color:var(--ink-3)} -.jc-addrow.show{display:flex;align-items:center;gap:8px} -.jc-addrow button{cursor:pointer;border:1px solid var(--brand-soft);border-radius:7px;padding:5px 12px;font:inherit;font-size:12px;font-weight:700;color:var(--brand-deep);background:var(--brand-wash)} -.jc-addrow button:hover{background:var(--brand-soft)} -.jc-addrow em{font-style:normal;font-size:11.5px;color:var(--ink-4)} -.flow{margin-top:16px;padding:9px 12px;border-radius:8px;background:var(--soft);border:1px dashed var(--line);font-size:12px;color:var(--ink-3)} -.flow b{color:var(--ink)} -.card-foot{padding:13px 22px;border-top:1px solid var(--line-soft);font-size:11.5px;color:var(--ink-4);display:flex;justify-content:space-between;flex-wrap:wrap;gap:8px} -@media(max-width:600px){.card-head{flex-wrap:wrap}.side{align-items:flex-start}} -/* 补充:评估输入清单状态标识(仅追加,不改动基件样式) */ -.mono{font-family:var(--mono);font-size:12px;color:var(--ink-2)} -.st{display:inline-block;font-size:11px;font-weight:700;padding:1px 8px;border-radius:999px;border:1px solid transparent;white-space:nowrap} -.st.bound{color:var(--ink-3);background:#eef1f6;border-color:var(--line)} -.st.ok{color:var(--ok);background:var(--ok-soft);border-color:#c9ecc2} -.st.miss{color:var(--human);background:var(--human-soft);border-color:var(--human-line)} -.st.def{color:var(--brand-deep);background:var(--brand-wash);border-color:var(--brand-soft)} -tr.ro td{background:var(--soft)} -table.on td>.jc-vcell.ro{display:block} -`; - /* * 两张验收件(ACC1 差异评估结论审核 / ACC2 评审放行与落账确认)的主题色**不再逐字节等于目标件**: * 目标件的题头用 #b23a2e 红(--ledger),2026-08-27 用户定「中央 HTML 模版区域如果是验收工单,