人手改的那一手真正落得下去;界面不撒谎那几处补齐

手改是**兜 AI 的错**的那一手 —— 这一轮把它从「摆在那儿但落不了地」做成真的:

件上就地改
· 保存**按地址收**,不看当下那张卡:载荷从前借道工单的 `blocks[i]` 取列名,
  于是熔断卡(没有 blocks)在屏上时,人补的那一格一个字都送不出去 ——
  而他要补的恰恰是把这一单卡住的那个必填格。授权面改由活动清单的台账声明把关。
· **能增删行**:错不止「这一格填错了」,还有「这一行不该在」与「漏了一行」。
  删挂记号不当场抹掉(看得见、可后悔),且**删要说出来**(`drops`)——
  「没回传就是删了」会把一次不完整的回传变成一次删表。
· **必填没填按不下提交**:与放行门 L1 同一个数同一套算法;闸上摆出差在哪一行哪一格。
· 只写真改了的那几行 —— 改一格却留「修订 4 行」的痕,那句话是假的。

判据与门
· 裁量判据带谓词时**谓词说了算**:「选填缺口已补齐」基座数得出来,
  人填好的目标价它照样判「客户未提供」的毛病由此消掉。
· 验收卡判据**读时现算** —— 人在成果件上补一格,判据跟着变。
· 放行门认「**办完了没有**」不认「有人写过」:从前写个 `false` 就把门推开,
  于是一单没打标就进了放行门。三个数全由基座现数。
· 拒回把「活没干完」说成「数或行对不上」的毛病:判据点名的表一行都没写时直说那一步没办。
  **指向两扇打不开的门,比不指还糟。**

界面不撒谎
· 日志只有三色:负责人绿 · 智能体蓝 · 系统灰(归侧五种是度量的分法,不是给人看的)。
· 验收收口答两件事:落了什么(按**交付物**点名,一份成果的几张表并成一行)、
  往哪儿去(转出哪一口、带过去哪些字段);一个口都没发就直说到此为止。
· 签完到收尾之间那一段有自己的话「字已签下,正在落地」—— 中间态本来就该说出来,只是要说对。
· 消息不再被内部事件刷屏:只有从通道进来的件才谈得上「无人认领」。
· 建客户公司与联系人**一次确认落两行**;联系人编码自增不问人;卡面摆表的人读名。

A1 三案例端到端全 released:全 A 类直通 12 拍 / 客户点名跳一条 10 拍 / 新客户缺必填 14 拍。
五道门禁全绿(971 passed)。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JsTDjoHovERXyy6Wmw1ALp
This commit is contained in:
2026-08-02 13:41:44 +08:00
co-authored by Claude Opus 5
parent 94f1c7a4ef
commit 259192f736
228 changed files with 27850 additions and 2847 deletions
+654 -66
View File
@@ -1,77 +1,665 @@
/**
* A1 视图模版 —— 三种局面:事态全景 / 打标判据 / 成果预览(AM 07)。
* 外壳与卡片件来自 `@sdk`:六个活动共用一套面貌,不各自发明。
* A1 询价受理的 HTML 报告模版 —— **从 PRD 目标件搬过来的那两份件**。
*
* 出处(逐块照搬,样式表见 `prd-style.ts`,一个数值都没改):
* · `~/coding/JCAI_PRD/docs/活动执行页/A1-询价受理需求.html` → `INTAKE_OVERVIEW`
* (阶段2 · 待处理页里嵌的那份);
* · `~/coding/JCAI_PRD/docs/活动执行页/A1-询价受理记录.html` → `INTAKE_RELEASE`
* (阶段3 · 工作成果里定稿归档的那份)。
*
* ── 为什么是搬,不是照着做 ─────────────────────────────────────────────
* 前三轮我拿自己那套 SDK 外壳「照着做」:块搬齐了,版面却是两套 —— 章节头成了
* 彩色方块编号、多出一段摘要与一条元信息带、字号用 clamp 响应式。三次报「25 块到位」,
* 而那是**结构断言**(某个 class 在不在),对版面一点约束都没有。人一眼就看出不是那份件。
* 目标件是像素级的答案:搬过来,才不会在复述里走样。
*
* ── 搬的时候,哪些东西必须换掉 ─────────────────────────────────────────
* 原型是**带脚本的演示页**:数据写死在 script 里,由 JS 造表、算计数、切编辑态。
* 而这里的容器是 sandbox iframe,**一行脚本也跑不了**;填数只有三件事 ——
* `data-slot` 取值、`data-rows` 逐行展开、`data-class-of` 按值上色。所以:
* · 写死的行 → `data-rows` 模版,一行一条从台账草稿展开;
* · JS 算的计数 → 台账里的汇总列,`data-slot` 取;
* · JS 造的「必填/选填」占位胶囊 → CSS `:empty::after`(基座对空值留空,见 `data-blank`);
* · JS 算的**命中行**与结论横幅 → 基座按契约的出口谓词求值后注入 `case:route*`
* (与放行时发口的是同一次判定,不是这屏自己又算了一遍);
* · 编辑态 → **就长在件里**(2026-08-01 改)。每个可改的格子渲两份:只读那份与
* `<input class="jc-cell">` 孪生件,由根节点上的 `editing` 二选一显示。
* 待处理那一屏摆的**只有这份件**,所以改数据也只能在件上改;
* 工作成果不加那个 class,同一份 HTML 就是归档件。
* 收值在外层(`allow-same-origin` 读得到 DOM),iframe 里仍然一行脚本都没有。
* · 「修改 / 确认」按钮 → 不做:提交那一下在**对话区的卡**上,
* 判据与作业指导书也在那儿 —— 工作区只放件本身。
*
* ── 搬不过来的两处,如实空着 ───────────────────────────────────────────
* · 圆标里的首字母(IT / EV / 冯)—— 得算,而模版不许算;换成同形状里的图标。
* · 「近 6 笔成交」迷你柱 —— `market.price_history` 有列没数据(种子不写、CRM 是桩),
* 没有写入方。画六根编出来的柱子是撒谎,画一条空轨像组件坏了,所以那一格不画,
* 把「为什么没有」写在卡脚。
*
* ── 取值路径三纪律(编错一个字,屏上就是一片「—」)────────────────────
* ① 顶层只取汇总列与单值列;逐行列摊到顶层是「后写的赢」,摆到题头上是抽样不是概览。
* ② 逐条数据一律走 `mine:域.对象` —— `域.对象` 是正式版全表,别单的行也在里面。
* ③ 列名逐字照 `manifest.ts` 的 `declarations.ledger` 抄,不照记忆写。
*/
import { kvCard, noteCard, tableCard, viewShell } from '@sdk';
import { PRD_CSS, PRD_SPRITE } from './prd-style.ts';
export const INTAKE_OVERVIEW = viewShell({
title: 'RFQ 受理全景',
kicker: 'A1 · 统一受理',
tone: 'brand',
/* ==================== 搬运期补的几条样式 ==================== */
/**
* 原型用 JS 干、这里只能用 CSS 干的那几件 —— **补在原样式表之后,不改原值**。
*
* ① 空格子的「必填 / 选填」胶囊:原型是 JS 判空后插一个 `jc-ph`;
* 这里改成 `:empty::after`,靠基座对空值留空(`data-blank`)触发,胶囊样式仍用原型的数值。
* ② 缺必填的整行浅红 + 行首红条:原型给 `tr.miss`,这里用 `:has()` 认。
* ③ 类徽 / 分布条 / 判定表命中行按值上色:原型由 JS 拼 class,
* 这里由基座把值消毒成 `v-*` 追加进 class(`data-class-of`)—— 判断在样式表里,不在填数里。
*/
const BIND_CSS = `
/* ① 空格子自己说话 —— 值真的空才轮到这两条(基座 data-blank) */
.proto-root .jc-tbl-in td.req > .jc-vcell:empty::after,
.proto-root .jc-tbl-in td.opt > .jc-vcell:empty::after {
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;
}
.proto-root .jc-tbl-in td.req > .jc-vcell:empty::after { content: "必填"; color: var(--danger); background: var(--danger-soft); border-color: #f3b6b3; }
.proto-root .jc-tbl-in td.opt > .jc-vcell:empty::after { content: "选填"; color: var(--ink-4); background: var(--soft); border-color: var(--line); }
/* 归档件里空格子就是「客户没给」,不再摆「你还能填」的胶囊 */
.proto-root .jc-tbl-in td.dimempty > .jc-vcell:empty::after { content: "—"; color: var(--ink-4); }
/* ② 缺必填的整行 —— 原型是 tr.miss,这里由 :has() 认 */
.proto-root .jc-tbl-in tbody tr:has(td.req > .jc-vcell:empty) td { background: #fffbfb; }
.proto-root .jc-tbl-in tbody tr:has(td.req > .jc-vcell:empty):hover td { background: #fff5f5; }
.proto-root .jc-tbl-in tbody tr:has(td.req > .jc-vcell:empty) td.jc-rn { color: var(--danger); font-weight: 800; position: relative; }
@media (min-width: 1001px) {
.proto-root .jc-tbl-in tbody tr:has(td.req > .jc-vcell:empty) td.jc-rn::after {
content: ""; position: absolute; left: 0; top: 4px; bottom: 4px; width: 3px; border-radius: 0 3px 3px 0; background: var(--danger);
}
}
/* 序号由 CSS 计数器排 —— 模版里没有循环变量,行是基座复制出来的 */
.proto-root .jc-tbl-in tbody { counter-reset: rn; }
.proto-root .jc-tbl-in td.jc-rn::before { counter-increment: rn; content: counter(rn); }
/* 窄屏原型给的是 ::before「第 」+ ::after「 条」;这里 ::before 已被计数器占用,
把两句合进 ::before,并把原型那句 ::after 收掉 —— 否则窄屏上写着「第 9 条 条」 */
@media (max-width: 1000px) {
.proto-root .jc-tbl-in td.jc-rn::before { content: "第 " counter(rn) " 条"; }
.proto-root .jc-tbl-in td.jc-rn::after { content: none; }
}
/* ④ 就地改:每个可改的格子渲了两份 —— 只读那份与输入那份,这里二选一。
**默认锁着**:开关是那张表上的 on(外层按下「编辑」才加),
一屏两张表各管各的。判断在样式表里,不在填数里,件本身仍是同一份。 */
/* 默认锁着:只读那份在,输入孪生件收起来 —— **件首先是给人看的** */
.proto-root .jc-cell { display: none; }
/* 开闸是给那张表的容器加 「on」(外层按下「编辑」才加)—— 一屏两张表各管各的 */
.proto-root .jc-tbl-in.on td > .jc-vcell,
.proto-root .jc-tbl-in.on td > .jc-tbadge { display: none; }
.proto-root .jc-tbl-in.on td > .jc-cell { display: block; }
/* 类那一列平时只有一枚 24px 的徽,编辑态要放得下一个字 */
.proto-root .jc-tbl-in.on td.jc-ty, .proto-root .jc-tbl-in.on th:nth-child(2) { width: 56px; min-width: 56px; }
.proto-root .jc-tbl-in.on td.num > .jc-cell, .proto-root .jc-tbl-in.on .jc-cell.num { text-align: right; }
/* 编辑态下必填空格子:红框 + 红 placeholder(原型的 req-empty 同款,只是由 :placeholder-shown 认) */
.proto-root .jc-tbl-in.on td.req > .jc-cell:placeholder-shown { border-color: #f3b6b3; background: #fffafa; }
.proto-root .jc-tbl-in.on tbody tr:has(td.req > .jc-cell:placeholder-shown) td { background: #fffbfb; }
/* ⑥ **铺满工作区**(2026-08-02 用户定:「报告不要做成单独的嵌入层」)。
原型那份是独立网页,jc-stage 给了 1160px 上限与 22/16/52px 的四周留白 ——
摆进工作区就成了「画布上浮着的一张小卡」:左右各空一条,底下还坠着 52px。
件在这里**就是这一屏的正文**,不是嵌在里面的另一个页面:去掉上限与横向留白,
外框那圈圆角边也一并卸掉(工作区自己已经是一张卡,两层框叠着像贴纸)。 */
.jc-stage { max-width: none; padding: 0 0 4px; }
.proto-root .jc-report { border: 0; border-radius: 0; box-shadow: none; }
/* ⑤ 那道闸 —— 默认「已锁定 + 编辑」,开了变成「取消 / 保存」。
整块只在这一屏可改时才出现(外层给根节点加 「editable」);
归档件里它一个字都不该有 —— 那时件是件,不是表单。 */
.proto-root .jc-editgate { display: none; }
html.editable .proto-root .jc-editgate {
display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
margin-bottom: 10px; padding: 8px 12px; border-radius: 9px;
background: var(--soft); border: 1px solid var(--line-soft);
font-size: 11.5px; color: var(--ink-3);
}
.proto-root .jc-editgate-t { display: inline-flex; align-items: center; gap: 6px; flex: 1 1 auto; min-width: 0; }
.proto-root .jc-editgate-t b { color: var(--ink-2); font-weight: 700; }
.proto-root .jc-egbtn {
flex: none; display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
border: 1px solid var(--line); border-radius: 7px; padding: 5px 11px;
font: inherit; font-size: 11.5px; font-weight: 700; color: var(--ink-2); background: #fff;
transition: border-color var(--t-fast), color var(--t-fast), background var(--t-fast);
}
.proto-root .jc-egbtn:hover { border-color: var(--brand); color: var(--brand); }
.proto-root .jc-egbtn.save { color: #fff; background: var(--brand); border-color: var(--brand); }
.proto-root .jc-egbtn.save:hover { background: var(--brand-deep); border-color: var(--brand-deep); }
/* 锁着的时候只有「编辑」;开了换成「取消 / 保存」—— 三个钮同时摆着,人不知道现在算哪一态 */
.proto-root .jc-editgate .jc-egbtn.ghost, .proto-root .jc-editgate .jc-egbtn.save { display: none; }
.proto-root .jc-editgate.on { background: var(--brand-wash); border-color: var(--brand-soft); color: var(--brand-deep); }
.proto-root .jc-editgate.on .jc-egbtn.open { display: none; }
.proto-root .jc-editgate.on .jc-egbtn.ghost, .proto-root .jc-editgate.on .jc-egbtn.save { display: inline-flex; }
.proto-root .jc-editgate.on .jc-editgate-t { visibility: hidden; }
/* 那条「怎么改」的提示只在开了闸之后说 —— 锁着的时候它是句废话 */
.proto-root .jc-editbar { display: none; }
.proto-root .jc-editgate.on + .jc-editbar { display: flex; }
/* ⑦ 增删行 —— 手改是兜 AI 的错那一手,而错不止「这一格填错了」:
「这一行根本不该在」与「漏了一行」是同一类错,只给改格子就没有出口。 */
/* 序号那一格一列两用:只读时是行号(计数器画的),编辑时是「撤掉这一行」的 × */
.proto-root .jc-rowdel { display: none; }
.proto-root .jc-tbl-in.on td.jc-rn::before { content: none; }
.proto-root .jc-tbl-in.on .jc-rowdel {
display: block; width: 20px; height: 20px; margin: 0 auto; cursor: pointer;
border: 1px solid var(--line); border-radius: 6px; padding: 0;
font: inherit; font-size: 13px; line-height: 1; color: var(--ink-3); background: #fff;
transition: border-color var(--t-fast), color var(--t-fast), background var(--t-fast);
}
.proto-root .jc-tbl-in.on .jc-rowdel:hover { border-color: var(--danger); color: var(--danger); background: #fff6f6; }
/* 撤掉的行**不当场抹掉** —— 划着摆在那儿,人看得见自己删了哪一行,也才有得后悔 */
.proto-root .jc-tbl-in.on tr.jc-row-gone td { background: #fff6f6; opacity: .55; }
.proto-root .jc-tbl-in.on tr.jc-row-gone .jc-cell { text-decoration: line-through; color: var(--ink-3); }
.proto-root .jc-tbl-in.on tr.jc-row-gone .jc-rowdel { border-color: var(--danger); color: var(--danger); background: #fff; }
.proto-root .jc-tbl-in.on tr.jc-row-new td { background: #f2fbf7; }
/* 表尾那条「加一行」—— 与闸同款,只在这一屏可改且开了闸时现身 */
.proto-root .jc-addrow { display: none; }
html.editable .proto-root .jc-tbl-wrap:has(table.on) + .jc-addrow {
display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
margin: -2px 0 12px; padding: 7px 12px; border-radius: 9px;
background: var(--soft); border: 1px dashed var(--line);
}
.proto-root .jc-addrow em { font-style: normal; font-size: 11px; color: var(--ink-3); }
/* ③ 按值上色:基座把值消毒成 v-* 追加进 class */
/* 刻度条按缺口:missing_key_fields 是条数,0 = 完整(绿,原型的默认色),非 0 = 缺必填(红) */
.proto-root .jc-segmeter > i.v-1, .proto-root .jc-segmeter > i.v-2, .proto-root .jc-segmeter > i.v-3 { background: var(--danger); }
/* 刻度条按状况标:归档屏那张卡上,一格即一条料的类别 */
.proto-root .jc-segmeter > i.v-a { background: var(--ok); }
.proto-root .jc-segmeter > i.v-b { background: var(--brand); }
.proto-root .jc-segmeter > i.v-c { background: var(--warn-accent); }
.proto-root .jc-tbadge { background: var(--ink-4); }
.proto-root .jc-tbadge.v-a { background: var(--ok); }
.proto-root .jc-tbadge.v-b { background: var(--brand); }
.proto-root .jc-tbadge.v-c { background: var(--warn-accent); }
/* 分布条:三段按条数分宽。原型是 JS 写 inline style="flex:N",而模版不许算 ——
改由 data-class-of 把**条数本身**投影成 v-<N>,这里备一把梯子让 CSS 认。
条数 0 的那段整段不出:摆一条零宽的彩带不如不摆。 */
/* 原型那段是 grid + place-items:center;这里段里多了个 ::before 前缀,
grid 会把前缀和数字排成上下两行 —— 改成一行居中的 flex */
.proto-root .jc-distbar > i { flex: 24; min-width: 34px; display: inline-flex; align-items: center; justify-content: center; }
.proto-root .jc-distbar > i.v-0, .proto-root .jc-distbar > i:not([class*=" v-"]) { display: none; }
.proto-root .jc-distbar > i.v-1 { flex: 1 } .proto-root .jc-distbar > i.v-2 { flex: 2 }
.proto-root .jc-distbar > i.v-3 { flex: 3 } .proto-root .jc-distbar > i.v-4 { flex: 4 }
.proto-root .jc-distbar > i.v-5 { flex: 5 } .proto-root .jc-distbar > i.v-6 { flex: 6 }
.proto-root .jc-distbar > i.v-7 { flex: 7 } .proto-root .jc-distbar > i.v-8 { flex: 8 }
.proto-root .jc-distbar > i.v-9 { flex: 9 } .proto-root .jc-distbar > i.v-10 { flex: 10 }
.proto-root .jc-distbar > i.v-11 { flex: 11 } .proto-root .jc-distbar > i.v-12 { flex: 12 }
.proto-root .jc-distbar > i.v-13 { flex: 13 } .proto-root .jc-distbar > i.v-14 { flex: 14 }
.proto-root .jc-distbar > i.v-15 { flex: 15 } .proto-root .jc-distbar > i.v-16 { flex: 16 }
.proto-root .jc-distbar > i.v-17 { flex: 17 } .proto-root .jc-distbar > i.v-18 { flex: 18 }
.proto-root .jc-distbar > i.v-19 { flex: 19 } .proto-root .jc-distbar > i.v-20 { flex: 20 }
/* 段里那句「A · 5」的前缀 —— 类别是这一段的固有属性,不必从数据里取 */
.proto-root .jc-distbar > i.a::before { content: "A · "; }
.proto-root .jc-distbar > i.b::before { content: "B · "; }
.proto-root .jc-distbar > i.c::before { content: "C · "; }
/* 判定表命中行:基座把命中的那一条注入成 v-r1/r2/r3,壳上带 class,行自己认 */
.proto-root .jc-dmn.v-r1 { --tone: var(--warn-accent); --tone-soft: rgba(255,136,0,.11); }
.proto-root .jc-dmn.v-r2 { --tone: var(--brand); --tone-soft: rgba(51,112,255,.09); }
.proto-root .jc-dmn.v-r3 { --tone: var(--ok); --tone-soft: rgba(39,135,23,.10); }
.proto-root .jc-dmn.v-r1 .jc-dmn-r.r1, .proto-root .jc-dmn.v-r2 .jc-dmn-r.r2, .proto-root .jc-dmn.v-r3 .jc-dmn-r.r3 {
position: relative; background: var(--tone-soft); color: var(--ink-2);
}
.proto-root .jc-dmn.v-r1 .jc-dmn-r.r1::before, .proto-root .jc-dmn.v-r2 .jc-dmn-r.r2::before, .proto-root .jc-dmn.v-r3 .jc-dmn-r.r3::before {
content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 4px; background: var(--tone);
}
.proto-root .jc-dmn.v-r1 .jc-dmn-r.r1 .jc-dmn-c, .proto-root .jc-dmn.v-r2 .jc-dmn-r.r2 .jc-dmn-c, .proto-root .jc-dmn.v-r3 .jc-dmn-r.r3 .jc-dmn-c { color: var(--ink); }
.proto-root .jc-dmn.v-r1 .jc-dmn-r.r1 .jc-dmn-i, .proto-root .jc-dmn.v-r2 .jc-dmn-r.r2 .jc-dmn-i, .proto-root .jc-dmn.v-r3 .jc-dmn-r.r3 .jc-dmn-i { color: var(--tone); }
.proto-root .jc-dmn.v-r1 .jc-dmn-r.r1 .jc-dmn-c b, .proto-root .jc-dmn.v-r2 .jc-dmn-r.r2 .jc-dmn-c b, .proto-root .jc-dmn.v-r3 .jc-dmn-r.r3 .jc-dmn-c b { color: var(--tone); font-weight: 800; }
.proto-root .jc-dmn.v-r1 .jc-dmn-r.r1 .jc-dmn-t, .proto-root .jc-dmn.v-r2 .jc-dmn-r.r2 .jc-dmn-t, .proto-root .jc-dmn.v-r3 .jc-dmn-r.r3 .jc-dmn-t { color: var(--ink); }
.proto-root .jc-dmn.v-r1 .jc-dmn-r.r1 .jc-dmn-t code, .proto-root .jc-dmn.v-r2 .jc-dmn-r.r2 .jc-dmn-t code, .proto-root .jc-dmn.v-r3 .jc-dmn-r.r3 .jc-dmn-t code {
color: #fff; background: var(--tone); border-color: var(--tone);
}
/* 判定那一列:两句话都写在模版里,哪一句现身**由壳上那个 v-rN 决定** ——
模版仍然不许算,而「命中 / 未求值」这层判断留在样式表。
一条都没命中(算不出去向)时壳上没有 v-rN,两句都不出:不冒充判过。 */
.proto-root .jc-dmn .jc-dmn-v > .y, .proto-root .jc-dmn .jc-dmn-v > .n { display: none; }
.proto-root .jc-dmn.v-r1 .r1 .jc-dmn-v > .y, .proto-root .jc-dmn.v-r2 .r2 .jc-dmn-v > .y, .proto-root .jc-dmn.v-r3 .r3 .jc-dmn-v > .y {
display: inline-flex; align-items: center; gap: 5px; font-weight: 800; color: var(--tone);
}
.proto-root .jc-dmn.v-r1 .r2 .jc-dmn-v > .n, .proto-root .jc-dmn.v-r1 .r3 .jc-dmn-v > .n,
.proto-root .jc-dmn.v-r2 .r1 .jc-dmn-v > .n, .proto-root .jc-dmn.v-r2 .r3 .jc-dmn-v > .n,
.proto-root .jc-dmn.v-r3 .r1 .jc-dmn-v > .n, .proto-root .jc-dmn.v-r3 .r2 .jc-dmn-v > .n { display: inline; color: var(--ink-4); }
.proto-root .jc-verdict.v-r1 { --tone: var(--warn-accent); --tone-soft: rgba(255,136,0,.14); --tone-line: #ffd9a8; }
.proto-root .jc-verdict.v-r2 { --tone: var(--brand); --tone-soft: rgba(51,112,255,.13); --tone-line: var(--brand-soft); }
.proto-root .jc-verdict.v-r3 { --tone: var(--ok); --tone-soft: rgba(39,135,23,.13); --tone-line: #bfedb4; }
/* 圆标里放图标不放首字母 —— 首字母得算,而画一个算错的缩写不如画一个说得清类别的图标 */
.proto-root .jc-logo svg, .proto-root .jc-face svg, .proto-root .jc-rp-ava svg { width: 17px; height: 17px; }
`;
/** 一枚图标 —— 与原型同一写法:`use` 引精灵里的 symbol */
const ico = (name: string, size = 11): string =>
`<svg width="${size}" height="${size}" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><use href="#i-${name}"/></svg>`;
/**
* 一份件的外壳 —— **照原型的三层壳**:`.proto-root > .app-shell > .jc-stage > article.jc-report`。
* 少一层,整张样式表(选择器带 `.proto-root .app-shell` 前缀)一条都不生效。
*/
const doc = (input: {
badge: string;
kicker: string;
title: string;
titleRef: string;
/** 题头右上:在办亮灯(jc-rp-wait);办结那一枚印章由归档态自己带 */
state: string;
body: string;
foot: string;
}): string => `<style>${PRD_CSS}${BIND_CSS}</style>
${PRD_SPRITE}
<div class="proto-root"><div class="app-shell"><div class="jc-stage">
<article class="jc-report">
<header class="jc-rp-head">
<div class="jc-rp-badge">${ico(input.badge, 17)}</div>
<div class="jc-rp-tit">
<em>${input.kicker}</em>
<h3>${input.title} <span data-slot="${input.titleRef}"></span></h3>
</div>
${input.state}
</header>
<div class="jc-rp-bd">${input.body}</div>
<footer class="jc-rp-foot">${input.foot}</footer>
</article>
</div></div></div>`;
/** 章节头 —— 原型的 `jc-rp-part`:小标签 + 细线 + 右端小签 */
const part = (label: string, aside = '', tone = ''): string =>
`<div class="jc-rp-part"><b>${label}</b><i class="ln"></i>${aside === '' ? '' : `<em class="${tone}">${aside}</em>`}</div>`;
/** 落款 —— 署名取任务负责人(基座填);右端那枚签写「这份件在流里的位置」,不写状态 */
const sign = (note: string, tag = ''): string =>
`<div class="jc-rp-sign">
<span class="jc-rp-ava">${ico('user', 15)}</span>
<div><b data-slot="case:owner"></b><span>${note}</span></div>
</div>${tag === '' ? '' : `<span class="jc-done-tag">${ico('git-branch', 13)}${tag}</span>`}`;
/* ==================== 共用件 ==================== */
/**
* 来源信件 —— 原型的 `jc-letter`。正文 `pre-wrap` 原样照录:摘要过的原文就不是原文了。
* 收件签旁那枚归属签(通道入站 → 归了谁)与右端入站时刻都由基座填 ——
* 让活动自报「归属解析 → 冯兵」,等于让被指派的人自己说自己是谁。
*/
const LETTER = `<article class="jc-letter">
<div class="jc-letter-h">
<span class="jc-letter-dir">${ico('mail-open', 12)}收件</span>
<span class="jc-chip"><b data-slot="case:origin"></b> · 归属解析 → <b data-slot="case:owner"></b></span>
<code data-slot="case:openedAt"></code>
</div>
<div class="jc-letter-meta">
<p><i>发件人</i><span data-slot="contact_name"></span><code data-slot="customerEmail"></code></p>
</div>
<h4 class="jc-letter-sub" data-slot="subject"></h4>
<p class="jc-letter-body" data-slot="body"></p>
<div class="jc-atts" data-rows="attachments">
<span class="jc-att">${ico('sheet', 13)}<code data-slot="name"></code><span data-slot="note"></span></span>
</div>
</article>`;
/**
* 受理要素四卡 —— 原型的 `jc-four`。
* `kind` 决定第四张卡下面那条刻度条按什么上色:过程屏按缺口(红格 = 必填未补),
* 归档屏按状况标(A 绿 / B 蓝 / C 橙)。**一格一条料**,每格都对得上清单里的一行。
*/
const four = (kind: 'gap' | 'type'): string => `<div class="jc-four">
<div class="jc-fcard">
<span class="jc-fcard-k"><i>${ico('building')}</i>客户公司</span>
<div class="jc-fcard-b">
<span class="jc-logo">${ico('building', 17)}</span>
<div class="jc-fcard-t"><b data-slot="customer_name"></b><span data-slot="customer_kind"></span></div>
</div>
<span class="jc-fcard-note">已命中客户资料库 <code class="jc-mono" data-slot="customer_code"></code></span>
</div>
<div class="jc-fcard t-teal">
<span class="jc-fcard-k"><i>${ico('user')}</i>联系人</span>
<div class="jc-fcard-b">
<span class="jc-face">${ico('user', 17)}</span>
<div class="jc-fcard-t">
<b data-slot="contact_name"></b>
<div class="jc-mailrow"><code data-slot="customerEmail"></code></div>
</div>
</div>
<span class="jc-fcard-note">采购口 · 本单对外发件收件人</span>
</div>
<div class="jc-fcard t-ok">
<span class="jc-fcard-k"><i>${ico('history')}</i>客户属性</span>
<span class="jc-tagbig">${ico('shield-check', 13)}<span data-slot="customer_kind"></span></span>
<span class="jc-fcard-note">
成交台账尚未接入 —— 历史价由下游定价环节现查;
新客户走首单口径,<b>不得取锚点</b>
</span>
</div>
<div class="jc-fcard${kind === 'type' ? ' t-ok' : ''}">
<span class="jc-fcard-k"><i>${ico('boxes')}</i>有效 PN</span>
<div class="jc-count"><b data-slot="line_total"></b><span>/ <b data-slot="bom_total"></b> 行 BOM</span></div>
<div class="jc-segmeter" data-rows="mine:quote_intake.record">
<i data-class-of="${kind === 'type' ? 'type_tag' : 'missing_key_fields'}"></i>
</div>
<span class="jc-fcard-note">
${
kind === 'type'
? '一格一条料 · 按状况标着色 · <b data-slot="line_total"></b> 条已交下游'
: '一格一条料 · 红格是必填未补 <b data-slot="missing_key_total"></b> 条'
}
</span>
</div>
</div>`;
/**
* 有效 PN 清单 —— 原型那六列,一列不多一列不少。
*
* 「客户料号」原型明写不收(『Itelma Code 不是本记录的字段』),这里也不摆:
* 报告是给报价这条线看的,客户自己的编码在 A1 出口的载荷里,不占版面。
* `req`/`opt` 只决定空格子出哪种胶囊;归档屏一律 `dimempty`(那时空就是「客户没给」,
* 再摆「你还能填」的红胶囊就是在催一件已经办完的事)。
*/
const listTable = (mode: 'draft' | 'final'): string => {
/*
* `data-col` 一挂,这一格就多渲一个输入孪生件(基座 `twinInputs`),
* 待处理那一屏可就地改;写的是**台账列名**,不是表头文字 ——
* 表头改个说法不该让写回错位。归档件不挂:那时它是件,不是表单。
*/
const cell = (key: string, head: string, kind: 'req' | 'opt', style = ''): string => {
const cls = mode === 'final' ? 'dimempty' : kind;
return `<td class="${cls}" data-h="${head}"><span class="jc-vcell ${style}" data-slot="${key}" data-col="${key}" data-blank></span></td>`;
};
const req = mode === 'final' ? '' : 'req';
/*
* 序号那一格在编辑态换成「撤掉这一行」的 × —— 一列两用,不为它单开一列。
* 只读时它是行号(CSS 计数器画的),编辑时人要的是「这一行不该在」的那个动作。
*/
const del = `<td class="jc-rn"><button type="button" class="jc-rowdel"
data-edit-del="quote_intake.record" title="撤掉这一行">×</button></td>`;
return `<div class="jc-tbl-wrap"><table class="jc-tbl-in">
<thead><tr>
<th style="width:36px;min-width:36px"></th>
${mode === 'final' ? '<th style="width:44px;min-width:44px">类</th>' : ''}
<th class="${req}" style="min-width:178px">PN</th>
<th style="min-width:88px">品类</th>
<th class="${req}" style="min-width:78px">年用量</th>
<th style="min-width:140px">制造商</th>
<th style="min-width:84px">目标价</th>
<th style="min-width:200px;width:100%">备注</th>
</tr></thead>
<tbody data-rows="mine:quote_intake.record" data-edit="quote_intake.record"><tr>
${del}
${mode === 'final' ? '<td class="jc-ty" data-h="分类"><span class="jc-tbadge" data-class-of="type_tag" data-slot="type_tag" data-col="type_tag"></span></td>' : ''}
${cell('pn', 'PN', 'req', 'mono')}
${cell('category', '品类', 'opt')}
${cell('eau', '年用量', 'req', 'mono')}
${cell('brand', '制造商', 'opt')}
${cell('target_price', '目标价', 'opt', 'num')}
${cell('remark', '备注', 'opt')}
</tr></tbody>
</table></div>`;
};
/** 编辑态那条提示带 —— 原型的 `jc-editbar`,只在待处理那一屏现身 */
/**
* 一张可改的表的**闸** —— 默认锁着,按「编辑」才开(2026-08-01 用户定)。
*
* 从前进这一屏整份件就是编辑态:54 个输入框一起亮着,人还没决定要改什么,
* 屏上已经是一张待填的表 —— **看**与**改**是两件事,件首先是给人看的。
*
* 三个按钮由外层挂事件(容器 `allow-same-origin`,读得到也改得动 iframe 的 DOM);
* **iframe 里仍然一行脚本都没有**。`data-edit-*` 是外层认它们的钩子:
* · `open` → 给根节点加 `editing`,输入孪生件现身;
* · `cancel` → 把每个输入还原成进编辑态那一刻的值,再关掉;
* · `save` → 收值交给外层缓冲(提交那一下才真落库),再关掉。
* 一屏可能有两张表(需求表 / 分类表),所以闸带 `data-edit-for="域.对象"` —— 各管各的。
*/
const editGate = (address: string, what: string): string => `<div class="jc-editgate" data-edit-for="${address}">
<span class="jc-editgate-t">${ico('lock', 12)}${what}<b>已锁定</b> —— 看的时候不误碰,要改按右边</span>
<button type="button" class="jc-egbtn open" data-edit-open="${address}">${ico('pen', 12)}编辑</button>
<button type="button" class="jc-egbtn ghost" data-edit-cancel="${address}">取消</button>
<button type="button" class="jc-egbtn save" data-edit-save="${address}">${ico('check', 12)}保存</button>
</div>
<div class="jc-editbar">${ico('pen', 14)}
<span>改哪一格点哪一格:<b>整份清单每一格都能覆盖</b> —— AI 抽错的直接改,缺的补上;
多出来的行按右边的 × 撤掉,漏了的用底下的「加一行」补。
红框是必填未补。改动只落任务草稿层,<b>保存不等于放行</b>。</span>
</div>`;
/**
* 表尾那条「加一行」—— **只在编辑态现身**(样式表按根节点的 `editing` 决定)。
*
* 为什么人得能加行:手改本来就是**兜 AI 的错**的那一手,而错不止「这一格填错了」——
* 「BOM 里有而它没抽出来」是同一类错,只给改格子就没有出口。
*/
const addRowBar = (address: string): string => `<div class="jc-addrow">
<button type="button" class="jc-egbtn ghost" data-edit-add="${address}">${ico('plus', 12)}加一行</button>
<em>照末行的样子添一行空的;主键(型号 PN)填了才存得下,不填这一行不落库。</em>
</div>`;
/*
* ── 这里**去掉了「本轮不报」那条横幅**(2026-08-02 用户当场指出:「也删了」)────
*
* 原文:「本轮不报 N 行:… —— 客户补齐规格后可另起一单。
* 有效条目数 = 来件条目 − 本轮不报,两个数对不上就是拆漏了」。
*
* 绝大多数单子它是「0 行:—」——**一条永远摆在那儿、九成时候说「没有」的横幅**,
* 读的人每次都得扫过它才知道没事。而后半句是讲给拆料那一步听的对账口诀,
* 不是给验收的人看的:拆漏没拆漏由收步判据 `record_rows = line_total` 当场拦着,
* 拦不住的时候摆一句口诀也没用。
*
* 题头那一行仍如实写着「N 条 · 已剔除 M」——「剔除了几条」这个事实没有丢。
*/
/**
* 料号分类:分布条 + 图例 —— 原型的 `jc-dist`。
*
* 三段按条数分宽:段宽在原型里是 JS 写的 `style="flex:N"`,模版不许算,
* 于是把**条数本身**投影成 class(`data-class-of` → `v-5`),让样式表按梯子取宽。
* 段里印的数字与图例读的是同三个汇总列 —— 一处数据两处呈现,对不上就是台账自己错了。
*/
const DIST = `<div class="jc-dist">
<div class="jc-distbar">
<i class="a" data-class-of="quote_intake_record_type_tag_a" data-slot="quote_intake_record_type_tag_a"></i>
<i class="b" data-class-of="quote_intake_record_type_tag_b" data-slot="quote_intake_record_type_tag_b"></i>
<i class="c" data-class-of="quote_intake_record_type_tag_c" data-slot="quote_intake_record_type_tag_c"></i>
</div>
<div class="jc-distlg">
<span><em class="a"></em>A 类 · 行情完备 <b data-slot="quote_intake_record_type_tag_a"></b></span>
<span><em class="b"></em>B 类 · 需寻源询价 <b data-slot="quote_intake_record_type_tag_b"></b></span>
<span><em class="c"></em>C 类 · 需替代选型 <b data-slot="quote_intake_record_type_tag_c"></b></span>
</div>
</div>`;
/* ==================== 局面一 · 受理中(阶段2 待处理页嵌的那份)==================== */
export const INTAKE_OVERVIEW = doc({
badge: 'file-text',
kicker: '统一报价 · A1 受理归一 · 过程成果',
title: '询价受理需求',
titleRef: 'rfq_ref',
state:
'<span class="jc-rp-wait warn">缺必填 <b data-slot="missing_key_total"></b> · 缺选填 <b data-slot="missing_minor_total"></b></span>',
body:
kvCard('询价来源', [
['客户编码', 'customer_code', 'mono'],
['询价单号', 'rfqRef', 'mono'],
['主题', 'subject'],
]) +
kvCard('归一结果', [
['型号 PN', 'pn', 'mono'],
['厂牌', 'brand'],
['年用量 EAU', 'eau'],
['目标价', 'target_price'],
['交期', 'lead_time'],
['缺关键字段', 'missing_key_fields'],
]) +
noteCard('这一屏在说什么', '归一由模型从询价原文抽取,逐条落<b>任务级草稿层</b>:本任务可见、正式版不可见、不产生数据事件。放行前的一切都还改得动。'),
part('来源 · 客户 RFQ 邮件', '<span data-slot="case:openedAt"></span> 入站', 'jc-mono') +
LETTER +
`<div class="jc-derive">${ico('info', 14)}
<span>下方受理要素与 PN 清单,均由 AI 从这封来函与附件 BOM 抽取归一而来 —— 正文里的口径要求
(币种 / 贸易条款 / 交期单位 / 认证 / 预授权替代 / 本轮跳哪条)已逐条落到对应字段与备注;
附件那一行写的是<b>基座读取的结果</b>,读得出文本才作数。</span>
</div>` +
part('受理要素', 'F-01 完整度自检') +
four('gap') +
part('有效 PN 清单', '<b data-slot="line_total"></b> 条 · 已剔除 <b data-slot="skipped_total"></b>') +
editGate('quote_intake.record', '有效 PN 清单') +
listTable('draft') +
addRowBar('quote_intake.record') +
`<div class="jc-drop">${ico('info', 13)}
<span>带 * 的两列是<b>必填</b>(PN / 年用量):缺任一按 F-01 退回补全,不许放行;
其余列缺了标灰「选填」—— 先受理、后追问,<b>不为它们拦单</b>,但那一问不能省。
<b>原文没写的一律不补</b>:受理阶段编出来的数会一路流到定价和报价单上,越往后越难发现。</span>
</div>`,
foot: sign('AI 拟稿,你审定 —— 要改哪一格,在「待处理」那张单的需求表上就地改', '统一报价 · A1 受理归一'),
});
export const INTAKE_CLASSIFIED = viewShell({
title: '料号分类与判据',
kicker: 'A1 · 三分类 P-03',
tone: 'ok',
/* ==================== 局面三 · 已分类 ==================== */
export const INTAKE_CLASSIFIED = doc({
badge: 'shield-check',
kicker: '统一报价 · A1 受理归一 · 三分类 P-03',
title: '料号分类表',
titleRef: 'rfq_ref',
state: '<span class="jc-rp-wait">已分级 · 未打标 <b data-slot="untyped_total"></b></span>',
body:
kvCard('分类结论', [
['型号 PN', 'pn', 'mono'],
['Type 标', 'type_tag', 'tag'],
['分级依据', 'basis'],
]) +
kvCard('打标判据(DMN 入参)', [
['距上次报价', 'history_days'],
['行情稳定', 'market_stable'],
['生命周期', 'lifecycle'],
['历史价命中', 'history_hit'],
]) +
noteCard(
'为什么是确定性的',
'三分类规则 P-03 是<b>决策表</b>不是模型判断:同样的判据必得同样的结论,零方差零 token,命中的规则行随出参留痕。' +
'A = 直接快答;B = 重估后报(转寻源);C = 转案处理(转替代选型)。',
),
part('料号分类', '三分类规则 P-03 · 逐条判定') +
DIST +
part('逐条分级(判据 → 结论)', '按序求值 · 首个命中即出') +
editGate('quote_intake.type_table', '料号分类表') +
`<div class="jc-tbl-wrap"><table class="jc-tbl-in">
<thead><tr>
<th style="width:36px;min-width:36px"></th>
<th style="min-width:178px">型号 PN</th>
<th style="min-width:120px">距上次报价(天)</th>
<th style="min-width:88px">行情稳定</th>
<th style="min-width:96px">生命周期</th>
<th style="width:44px;min-width:44px">类</th>
<th style="min-width:200px;width:100%">分级依据</th>
</tr></thead>
<tbody data-rows="mine:quote_intake.type_table" data-edit="quote_intake.type_table"><tr>
<td class="jc-rn"></td>
<td data-h="型号 PN"><span class="jc-vcell mono" data-slot="pn" data-col="pn"></span></td>
<td data-h="距上次报价"><span class="jc-vcell num" data-slot="history_days" data-col="history_days"></span></td>
<td data-h="行情稳定"><span class="jc-vcell" data-slot="market_stable" data-col="market_stable"></span></td>
<td data-h="生命周期"><span class="jc-vcell" data-slot="lifecycle" data-col="lifecycle"></span></td>
<td class="jc-ty" data-h="分类"><span class="jc-tbadge" data-class-of="type_tag" data-slot="type_tag" data-col="type_tag"></span></td>
<td data-h="分级依据"><span class="jc-vcell" data-slot="basis" data-col="basis"></span></td>
</tr></tbody>
</table></div>` +
`<div class="jc-drop">${ico('info', 13)}
<span>三分类规则 P-03 是<b>决策表</b>,不是临场判断:同样的输入必得同样的结论,凭哪一条命中都留了痕。
<b>A</b> = 历史价 ≤ 90 天且行情稳;<b>B</b> = 无历史 / 过期 / 波动;<b>C</b> = EOL / 缺货 / 客户要求替代。
规则从上往下第一条命中即出 —— C 那一行排在最前,EOL 的料哪怕历史价很新也不判 A。
<b>拿不准的一律降 B</b>:错标一个 A,就是让一条本该重新核价的料带着旧价走到报价单上。</span>
</div>`,
foot: sign('状况标由判定表 P-03 求值 —— 判据与结论逐条可回核', '统一报价 · A1 受理归一'),
});
export const INTAKE_RELEASE = viewShell({
title: '统一受理记录 · 待放行',
kicker: 'A1 · 成果预览',
tone: 'case',
/* ==================== 局面四 · 待放行(阶段3 工作成果里的那份)==================== */
/**
* G2 分支网关 —— 三条互斥条件按序求值,只命中一条、只落一个活动。
*
* **命中哪一条不是这屏自己算的**:契约给每个出口写了判据,基座在放行那一刻按序求值,
* 把结果注入 `case:route*`(与发口用的是同一次判定)。这里只把它摆出来:
* 壳上带 `v-r1/r2/r3`,对应那一行自己上色。
* 摆成三张并列的去向卡就丢了「按序求值、只落一个」那层意思,所以照原型做成判定表。
*/
const dmnRow = (n: 1 | 2 | 3, cond: string, node: string, to: string, hit: string): string =>
`<div class="jc-dmn-r r${n}">
<span class="jc-dmn-i">${n}</span>
<span class="jc-dmn-c">${cond}</span>
<span class="jc-dmn-t"><code>${node}</code>${to}</span>
<span class="jc-dmn-v"><b class="y">${ico('check', 13)}命中 · ${hit}</b><b class="n">未命中 · 排他网关只落一口</b></span>
</div>`;
const G2 = `<div class="jc-dmn" data-class-of="case:routeKey">
<div class="jc-dmn-h"><span>序</span><span>条件(按序求值)</span><span>去向</span><span>判定</span></div>
${dmnRow(1, '存在 <b>C 类</b>(需替代选型)', 'A2', '替代选型', 'C 类 <b data-slot="quote_intake_record_type_tag_c"></b> 条')}
${dmnRow(2, '无 C 类、存在 <b>B 类</b>(需寻源询价)', 'A3', '渠道寻源', 'B 类 <b data-slot="quote_intake_record_type_tag_b"></b> 条')}
${dmnRow(3, '全部为 <b>A 类</b>(行情完备)', 'A4', '成本定价', 'A 类 <b data-slot="quote_intake_record_type_tag_a"></b> 条')}
</div>
<div class="jc-verdict" data-class-of="case:routeKey">
<div class="jc-vd-l">
<span class="jc-vd-k">本次判定 · 按序求值,首个命中即出</span>
<div class="jc-vd-cond"><span data-slot="case:routeTo"></span></div>
<p class="jc-vd-why">去向是<b>分类表算出来的</b>:三条规则按序求值、首个命中即出,
在你签字那一刻现算。所以它不是谁点出来的一次手感 ——
<b>要改去向,就在上面的清单里改料号的类,这一格会跟着变。</b></p>
</div>
<span class="jc-vd-arrow">${ico('arrow-right', 26)}</span>
<div class="jc-vd-r">
<span class="jc-vd-k">转入活动</span>
<div class="jc-vd-node"><span data-slot="case:routeTo"></span></div>
<span class="jc-vd-who"><i>${ico('user', 11)}</i>下游负责人按运营分派落定</span>
<p class="jc-vd-next">放行即落地:草稿层合并进正式版、intake 口的数据事件此刻才发射,
这一口的载荷随之交给下游。<b>门前的都还改得动,门后的都算数了。</b></p>
</div>
</div>`;
export const INTAKE_RELEASE = doc({
badge: 'file-check',
kicker: '统一报价 · A1 受理归一 · 交付成果',
title: '询价受理记录',
titleRef: 'rfq_ref',
/*
* 题头这枚灯**在办与归档同一句话才算数**:这份件放行前嵌在放行卡上、放行后原样进
* 「工作成果」。写「待放行」进了档案就成了谎,写「受理完成」在放行前就是替人签了字 ——
* 所以只写这份件本身是什么:归一了几条、三分类已定。
*/
state: '<span class="jc-rp-wait">受理记录 · <b data-slot="line_total"></b> 条 · 三分类已定</span>',
body:
tableCard(
'本单交付',
'mine:quote_intake.record',
[
['型号', 'pn', 'mono'],
['厂牌', 'brand'],
['年用量', 'eau'],
['目标价', 'target_price'],
['交期', 'lead_time'],
],
'只列<b>本单</b>写下的行 —— 别单放行过的记录不混进来。',
) +
kvCard('分流去向', [
['Type 标', 'type_tag', 'tag'],
['依据', 'basis'],
]) +
noteCard(
'放行意味着什么',
'放行通过<b>即刻落地</b>:草稿层合并入正式版、数据事件此刻才发射,G2 网关按 Type 分流。' +
'放行是草稿态与事实态之间的那道门 —— 门前的都还改得动,门后的都算数了。',
),
/*
* **来件原文留在归档件里**(2026-08-02 用户指出:「成果里为啥没有邮箱原文了」)。
*
* 待处理与工作成果本来就是**同一份件**,差别只在编辑态开不开。
* 归档件少一块来件,就等于把「凭什么这么受理」的出处从档案里抽走了 ——
* 三个月后回看这一单,九条料是怎么来的、客户那句「本轮跳过」在哪儿说的,
* 全都无从对起。件要自己说得清自己。
*/
part('来源 · 客户 RFQ 邮件', '<span data-slot="case:openedAt"></span> 入站', 'jc-mono') +
LETTER +
part('受理要素', 'F-01 完整度自检 · 通过', 'ok') +
four('type') +
part('料号分类', '三分类规则 P-03 · 逐条判定') +
DIST +
part('有效 PN 清单', '<b data-slot="line_total"></b> 条 · 已剔除 <b data-slot="skipped_total"></b>') +
editGate('quote_intake.record', '有效 PN 清单') +
listTable('final') +
addRowBar('quote_intake.record') +
part('结论与去向', '分支判定 · 起单 ×1', 'ok') +
G2 +
/*
* ── 这里**去掉了两张请求单表与那条对账说明**(2026-08-02 用户当场指出)──────
*
* 原来摆的是「开出的替代评估请求(交 A2)」「开出的询价请求(交 A3)」两张明细表,
* 外加一句「这两条等式在你签字前由系统当场对账,对不上直接拒收」。
*
* 两个理由:
* · 那句话**已经不成立** —— `route-alt` / `route-rfq` 两条对账判据同日删掉了
* (它们是内部一致性,该由引擎自己看住,不占放行卡)。件上还写着「系统当场对账」,
* 就是在说一件不再发生的事;
* · 这份件是**受理记录**:它答的是「这一单受理成了什么样」。派出去的两类单是
* **下游的输入**,不是这一单的成果 —— 要看它们,去 A2 / A3 那两条流水线上看。
* 摆在这儿只是把同一份数据在两处各画一遍。
*
* 「这一签下去开出了几张单」仍看得见:放行之后验收卡那一屏(落地结果)按表点名,
* 询价请求 / 替代评估请求各几行都在上面 —— 那才是它该出现的时刻与位置。
*/
part('放行后即执行的落地动作', '门前 → 门后') +
`<div class="jc-acts">
<div class="jc-act"><i>${ico('check', 11)}</i>
<div><b>草稿层合并进正式版</b>&#12288;<span>本任务写下的受理记录、分类表、结论与两类请求单一并落账;
人工补全与就地修订的那几处随之落定。</span></div>
</div>
<div class="jc-act"><i>${ico('check', 11)}</i>
<div><b>受理结果此刻才对外发出</b>&#12288;<span>连同
<b data-slot="line_total"></b> 条有效料号与它们的分类一并交出 —— 放行之前,世界上什么都没有发生。</span></div>
</div>
<div class="jc-act"><i>${ico('check', 11)}</i>
<div><b>按分类算出的去向落下游一单</b>&#12288;<span>本次转入
<b data-slot="case:routeTo"></b>,已进对方的任务清单;另两条去向本次不起单。</span></div>
</div>
</div>`,
/*
* ── 这里**去掉了「受理结论」那张表**(2026-08-02 用户当场指出:「这个也多余」)──
*
* 摆的是 `bom_total / skipped_total / skipped_note / summary` 四格,而
* 前三格与同日删掉的那条「本轮不报」横幅说的是同一件事,
* 第四格是一句 AI 复述的总结 —— 底下的清单、分布条、去向横幅把该说的都说了,
* 再加一句转述只是让人多读一行。
*
* 一份件里同一个数出现两次,人得停下来对一遍两处一不一样 —— 那是白花的注意力。
*/
foot: sign('AI 拟稿 · 待审定放行 —— 要改哪一格,在这张放行单的成果表上就地改', '统一报价 · A1 受理归一 → 交下游'),
});