Archived
revert(w05): 数据工单退回换版面之前的旧版面(2.3.0),新版面存在 feat/w05-view-swap
用户实测新版面「感觉有点问题」,先退回稳定的旧版面,新版面原样留在分支上随时可切。
退法不是 reset —— 历史与素材都留着:report.ts / target-style.ts / activity.test.ts
三个文件逐字节恢复到 45cec82 的样子(已 diff 核对为零差异),平台侧的探针测试
(view-container-collect-div-rows / -nested / view-render-nested-editable)、目标件 html、
交接件文档全部留在主支 —— 它们钉的是平台行为与素材,不随这次退回作废。
版本号 2.2.2 -> 2.3.0 而不是退回 2.1.9:store.manifest 不传版本时按 installedAt 取
**最近安装**的那一条(不是版本号最大的),库里 2.1.9 那行的 installedAt 比 2.2.2 早,
同版重装又是 no-op —— 只有拿一个新号把旧版面装进去,注册表才真的换回旧版面。
CLAUDE.md 那条战果记录改口标注「已退回」并指向分支,原文留着当索引。
pnpm gates 退出码 0(2487 测;比换版面那版少的 10 条正是随 UI 一起退掉的新版面钉子)。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -1696,7 +1696,15 @@
|
||||
写出来的数就得排在后面)/ 方向相反的依赖拆两笔写、中间夹子表 / `gateLedgers` 同时管
|
||||
「闸要求非空」与「确认时并进正式表」两件事,漏想一半就会悬空引用或永久卡死。
|
||||
`pnpm gates` 退出码 0(2447 passed | 34 skipped)。
|
||||
- **W05 数据工单换版面 + 两条渲染机制的实锚**(2026-08-30,`part-substitution-assessment@2.2.2`,
|
||||
- **W05 数据工单换版面已退回**(2026-08-31 用户:「改了 ui 之后这个版本我感觉有点问题」):
|
||||
`report.ts` / `target-style.ts` / `activity.test.ts` 三个文件**逐字节恢复**到换版面之前
|
||||
(45cec82 的样子),包版本 **2.3.0**(内容是旧版面 —— `store.manifest` 不传版本时取
|
||||
**最近安装**的那一条,不是版本号最大的,所以退源码必须配一个新号装进去才真生效)。
|
||||
**换版面那一版原样活在分支 `feat/w05-view-swap`**(HEAD `1373eb4`),随时切得回去;
|
||||
下面这条记的是它的账,留着当索引 —— 里面三条渲染机制的实锚(宿主闭合标签 / 行内
|
||||
class-of 只认行作用域 / data-by 回落)与平台侧探针测试**不随退回作废**,它们钉的是
|
||||
平台行为,测试与目标件 html 都还在主支上。
|
||||
- **(已退回,见上条)W05 数据工单换版面 + 两条渲染机制的实锚**(2026-08-30,`part-substitution-assessment@2.2.2`,
|
||||
用户给了新目标件 `docs/ss02-act-05-new/ACT-WORK-05-数据工单-PN主数据创建.html`):
|
||||
`input-supplement` 整张照它重排 —— **两条并列轨道**(每颗料一条:档案卡 + 主数据 + 类型选择)
|
||||
+ `<details>` 新增类型面板(chip_type / type_param 两张表原样搬入,独立「核心参数字段」区退役)
|
||||
|
||||
@@ -527,11 +527,10 @@ describe('报告模版:编辑面与只读面各就各位', () => {
|
||||
});
|
||||
|
||||
/*
|
||||
* 件① 评估输入补全(2026-08-29 Task 7/7b;**2026-08-30 照新目标件换版面**):S1 的配方
|
||||
* 缺什么就发一张数据工单,`view: 'input-supplement'` 指向它。新版面 —— 两条并列轨道
|
||||
* (每颗料一条:档案卡 + 主数据 + 类型选择)/ `<details>` 新增类型面板(chip_type +
|
||||
* type_param 两张表,即「每行四格」的核心参数字段清单)/ 核心参数值瓷贴(单块平铺);
|
||||
* 顶部一条红带摆配方算好的 `gate_reason`。台账绑定面与换版面前一个不差。
|
||||
* 件① 评估输入补全(2026-08-29,Task 7;Task 7b 补②a 块):S1 的配方缺什么就发一张数据工单,
|
||||
* `view: 'input-supplement'` 指向它。四块竖排——要评估什么(两颗料的存在性 + 主数据)/
|
||||
* 芯片类型(选型 + 新建类型)/ 核心参数字段清单(新建类型时要填)/ 参数值(逐条实测/规格值);
|
||||
* 顶部一条红带摆配方算好的 `gate_reason`。
|
||||
* 复用文件顶部已有的 `viewHtml` 短写(与 brief 草稿里的 `viewOf` 同一件事,不重复定义一个)。
|
||||
*/
|
||||
describe('件① 评估输入补全', () => {
|
||||
@@ -621,11 +620,9 @@ describe('件① 评估输入补全', () => {
|
||||
expect(html).toContain('data-col="param_name"');
|
||||
});
|
||||
|
||||
it('可改的四张表各有一道编辑闸 —— 查的是闸([data-edit-for] + 开钮),不是宿主:2026-08-30 换版面第一版恰好漏掉 profile 的闸,宿主在而闸不在,轨道打不开编辑,XL 的必填永远补不上(Task 7 那颗「按不下去的确认钮」同款),旧断言只查宿主没逮到', () => {
|
||||
it('可改的四张表各有一道编辑闸', () => {
|
||||
for (const address of ['parts.profile', 'parts.chip_type', 'parts.type_param', 'parts.parameter']) {
|
||||
expect(html).toContain(`data-edit="${address}"`);
|
||||
expect(html).toContain(`data-edit-for="${address}"`);
|
||||
expect(html).toContain(`data-edit-open="${address}"`);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -702,108 +699,20 @@ describe('件① 评估输入补全', () => {
|
||||
});
|
||||
|
||||
/*
|
||||
* 2026-08-29 评审 Important:BIND_COMMON 的序号计数器规则原来只认 `.tbl-wrap`,而工单件的表
|
||||
* 走平台的 `.jc-tbl-wrap` —— 两个 class 对不上,计数器永不触发,「#」列逐行空白。
|
||||
* 2026-08-30 换版面后这张件只剩面板里两张表(chip_type / type_param),包装是
|
||||
* `jc-tbl-wrap tbl-wrap` 双类(平台钩子 + 目标件的边框圆角);轨道与瓷贴没有 # 列,
|
||||
* 不走计数器。钉死:五条计数器规则还在,且两张表的双类包装恰好两处。
|
||||
* 2026-08-29 评审 Important:BIND_COMMON 的序号计数器规则原来只认 `.tbl-wrap`
|
||||
* (继承自已删除的旧 P08_MASTER_CREATE,同一个 wrapper class,同一个坑),
|
||||
* 而这张件的三张表全用平台的 `.jc-tbl-wrap` —— 两个 class 对不上,计数器永不触发,
|
||||
* 只读态与编辑态下「#」列逐行都是空白。钉死 `.jc-tbl-wrap` 现在也接了这五条规则,
|
||||
* 且这张件确实只用 `.jc-tbl-wrap`(没有表在用没打这条补丁就会数不出来的 `.tbl-wrap`)。
|
||||
*/
|
||||
it('面板两张表的序号列真的会数:计数器规则在,双类包装恰两处', () => {
|
||||
it('四张表的序号列真的会数:.jc-tbl-wrap 也接了 CSS 计数器(不再只认 .tbl-wrap)', () => {
|
||||
expect(html).toMatch(/\.jc-tbl-wrap\s+table\s*,\s*\.tbl-wrap\s+table\s*\{\s*counter-reset:\s*rn;?\s*\}/);
|
||||
expect(html).toMatch(/\.jc-tbl-wrap\s+table\s+td\.rn\s*,\s*\.tbl-wrap\s+table\s+td\.rn\s*\{\s*counter-increment:\s*rn;?\s*\}/);
|
||||
expect(html).toMatch(/\.jc-tbl-wrap\s+table\s+td\.rn\s+\.jc-rn-num::before\s*,\s*\.tbl-wrap\s+table\s+td\.rn\s+\.jc-rn-num::before\s*\{\s*content:\s*counter\(rn\);?\s*\}/);
|
||||
const wrapCount = html.match(/class="jc-tbl-wrap tbl-wrap"/g)?.length ?? 0;
|
||||
expect(wrapCount).toBe(2);
|
||||
// 不许再有单类的表包装 —— 单 .jc-tbl-wrap 缺目标件的边框,单 .tbl-wrap 缺平台的 :has 加行钩子
|
||||
expect(html).not.toMatch(/class="jc-tbl-wrap"/);
|
||||
expect(html).not.toMatch(/class="tbl-wrap"/);
|
||||
});
|
||||
|
||||
/* ==================== 2026-08-30 换版面(照 docs/ss02-act-05-new 新目标件)专属的钉子 ==================== */
|
||||
|
||||
it('**每个台账地址恰一个收值宿主**(chip_type/type_param 的两处是「外层 table + 内层 tbody」同宿主写法):bodyOf 是单 querySelector,同一地址挂两个各自独立的宿主,第二个的值一格都收不上去且不报错(终审 C1 / Task 7b 的族坑)', () => {
|
||||
const countOf = (addr: string): number => (html.match(new RegExp(`data-edit="${addr.replace('.', '\\.')}"`, 'g')) ?? []).length;
|
||||
expect(countOf('parts.profile')).toBe(1);
|
||||
expect(countOf('parts.parameter')).toBe(1);
|
||||
expect(countOf('parts.chip_type')).toBe(2);
|
||||
expect(countOf('parts.type_param')).toBe(2);
|
||||
});
|
||||
|
||||
it('**两条轨道:行源与写向同一元素,行模版是宿主直接子元素**(rowOf 认宿主直接子元素 —— 一条 lane = 一行;行模版里不许再有 <section>,行展开按第一个同名闭合标签截行内容)', () => {
|
||||
expect(html).toMatch(/<article class="lanes" data-rows="mine:sub_eval\.part" data-edit="parts\.profile">\s*<section class="lane">/);
|
||||
const laneBody = /<section class="lane">([\s\S]*?)<\/section>/.exec(html)?.[1] ?? '';
|
||||
expect(laneBody).not.toBe('');
|
||||
expect(laneBody).not.toContain('<section');
|
||||
});
|
||||
|
||||
it('**参数瓷贴单块平铺,瓷贴是宿主直接子元素** —— 按料嵌一层分栏会把一颗料的所有参数格并成一条记录(ui-kit view-container-collect-div-rows.test.ts 用真实 renderView+collect 钉死的「必然归并」)', () => {
|
||||
expect(html).toMatch(/<article class="pvgrid" data-rows="mine:parts\.parameter" data-edit="parts\.parameter"><section class="pv">/);
|
||||
const pvBody = /<section class="pv">([\s\S]*?)<\/section>/.exec(html)?.[1] ?? '';
|
||||
expect(pvBody).not.toBe('');
|
||||
expect(pvBody).not.toContain('<section');
|
||||
/* 瓷贴的主键钥匙(internal_pn / param_name)收在 .rowkey 里 —— 藏起来但必须真挂 data-col */
|
||||
expect(pvBody).toContain('class="rowkey"');
|
||||
expect(pvBody).toContain('data-col="internal_pn"');
|
||||
expect(pvBody).toContain('data-col="param_name"');
|
||||
expect(html).toContain('.pv .rowkey{display:none}');
|
||||
});
|
||||
|
||||
it('**data-rows 宿主的闭合标签不许出现在自己的行模版里** —— renderRows 的正则按宿主闭合标签非贪婪截取,撞上就把行模版截半、后一行嵌进前一行没闭合的标签里(2026-08-30 浏览器走查逮到:<div class="lanes"> 被 lab 那格的 </div> 截断,档案卡串行)。判法:照 ROWS 正则同款截取,截出来的行模版必须**标签配平**(截断必然留下没闭合的标签 —— 直接查「不含闭合标签」是空话,非贪婪截取本来就停在闭合标签之前)', () => {
|
||||
const hosts = [...html.matchAll(/<([a-zA-Z][\w-]*)\b[^>]*\bdata-rows="[^"]+"[^>]*>([\s\S]*?)<\/\1>/g)];
|
||||
expect(hosts.length).toBeGreaterThanOrEqual(2);
|
||||
const VOID = new Set(['input', 'br', 'img', 'hr', 'meta', 'link']);
|
||||
for (const m of hosts) {
|
||||
const inner = m[2] ?? '';
|
||||
const opens = new Map<string, number>();
|
||||
for (const tm of inner.matchAll(/<(\/?)([a-zA-Z][\w-]*)\b/g)) {
|
||||
const tag = (tm[2] ?? '').toLowerCase();
|
||||
if (VOID.has(tag)) continue;
|
||||
opens.set(tag, (opens.get(tag) ?? 0) + (tm[1] === '/' ? -1 : 1));
|
||||
}
|
||||
for (const [tag, n] of opens) {
|
||||
expect(n, `data-rows 宿主 <${m[1]}> 截出的行模版里 <${tag}> 开 ${n} 个没闭合 —— 宿主的闭合标签把模版截半了`).toBe(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('**类型不一致护栏**:distinct 是基座数,红带只在 v-2 档现身,模版不算不判', () => {
|
||||
expect(html).toMatch(/class="jc-typewarn" data-class-of="sub_eval_part_distinct_chip_type_id"/);
|
||||
expect(html).toContain('.jc-typewarn{display:none}');
|
||||
expect(html).toContain('.jc-typewarn.v-2{display:flex');
|
||||
});
|
||||
|
||||
it('**步骤圆点只按基座数点亮,投影挂在行外的包裹层** —— 行内 data-class-of 只认行作用域,作用域级读数在行内投不出(实测圆点永远不亮);圆点自己不许再挂 data-class-of(挂了也是死属性)', () => {
|
||||
expect(html).toContain('class="pj pj-profile" data-class-of="parts_profile_missing"');
|
||||
expect(html).toContain('class="pj pj-type" data-class-of="sub_eval_part_distinct_chip_type_id"');
|
||||
expect(html).toContain('class="pj pj-param" data-class-of="parts_parameter_missing"');
|
||||
expect(html).toContain('.pj-profile.v-0 .sdot.sd-1,.pj-type.v-1 .sdot.sd-2,.pj-param.v-0 .sdot.sd-3{background:var(--data)');
|
||||
expect(html).not.toMatch(/class="sdot[^"]*" data-class-of=/);
|
||||
});
|
||||
|
||||
it('**类型查名有诚实守卫**(data-by 行缺键落回外层同名字段,终审 I3):行里没真选上类型(selectCurrent 没写下 option[selected]),查名 pill 与字段行的名字都不摆', () => {
|
||||
expect(html).toContain('.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .idcard .tpill{display:none}');
|
||||
expect(html).toContain('.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .tywrap span{display:none}');
|
||||
expect(html).toContain('class="jc-vcell tywrap"');
|
||||
});
|
||||
|
||||
it('**新增类型面板是 <details>,且绑定层把抽取样式的 display:none 让开** —— 少了这条让位规则,整个面板(含两道闸与两张表)永远不可见', () => {
|
||||
expect(html).toContain('<details class="newtype">');
|
||||
expect(html).toContain('details.newtype{display:block');
|
||||
});
|
||||
|
||||
it('**目标件里靠脚本驱动的两样,搬过来时如实退掉**:进度条 gateprog 的分母不是基座现有的数(编一个就是界面撒谎);viewarea/editarea 双区靠 body.editing 切换,沙箱零脚本切不动 —— 抽取的 CSS 里留着它们的规则,但标记一处都不许用', () => {
|
||||
const body = html.slice(html.indexOf('</style>'));
|
||||
expect(body).not.toContain('gateprog');
|
||||
expect(body).not.toContain('class="viewarea"');
|
||||
expect(body).not.toContain('class="editarea"');
|
||||
/* .editarea{display:none} 还在 CSS 里 —— 谁把内容包进它,那块就永远消失 */
|
||||
expect(html).toContain('.editarea{display:none}');
|
||||
});
|
||||
|
||||
it('**保存即铺格之前的空态**:pvnull 按 parts_parameter_rows 的 v-0 档现身(零行才摆提示,有行不摆 —— 界面不撒谎)', () => {
|
||||
expect(html).toContain('class="pv-empty pvnull" data-class-of="parts_parameter_rows"');
|
||||
expect(html).toContain('.pvnull{display:none}');
|
||||
expect(html).toContain('.pv-empty.pvnull.v-0{display:block}');
|
||||
// 这张件的四张表都走 .jc-tbl-wrap(没有 .tbl-wrap)—— 补丁真的接在会用到的类上(Task 7b 新增 parts.chip_type 这一张,3→4)
|
||||
expect(html).not.toContain('class="tbl-wrap"');
|
||||
const jcTblWrapCount = html.match(/class="jc-tbl-wrap"/g)?.length ?? 0;
|
||||
expect(jcTblWrapCount).toBe(4);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -54,7 +54,7 @@ export const manifest: ActivityManifest = {
|
||||
activityId: ACTIVITY_ID,
|
||||
name: 'ACT-WORK-05 · 差异评估',
|
||||
/* 重写不是改 —— 同版重装是 no-op(静默丢失,踩过) */
|
||||
packageVersion: '2.2.2',
|
||||
packageVersion: '2.3.0',
|
||||
category: '需求分析与方案匹配',
|
||||
sdkVersion: '0.1.0-m5',
|
||||
checksum: 'dev-checksum-part-substitution-assessment',
|
||||
|
||||
@@ -5,11 +5,12 @@
|
||||
* 差异评估结论审核 / 评审放行与落账确认)按 spec §8 全部作废,S1 那一步不再分成
|
||||
* 「先核对存在性 → 分别派 P-08/P-10 数据工单 → 再摆评估输入清单」三个局面 ——
|
||||
* 新的四步案例模型只有一张数据工单卡(`variant: 'data'`),这里对应**一张**新局面
|
||||
* `input-supplement`(评估输入补全)。**2026-08-30 照新目标件换版面**(详见下方
|
||||
* 「局面:评估输入补全」一节的大注释):两条并列轨道(每颗料一条:档案卡 + 主数据 +
|
||||
* 类型选择)→ 新增类型 `<details>` 面板(`parts.chip_type` + `parts.type_param`,
|
||||
* 后者即「每行四格」的核心参数字段清单)→ 核心参数值瓷贴(`parts.parameter`,单块平铺)。
|
||||
* 台账绑定面与 Task 7/7b 交付的一个不差,变的只有版面。
|
||||
* `input-supplement`(评估输入补全):**四块竖排**(spec §6.1,Task 7b 补第②a 块)——
|
||||
* ① 要评估什么(两颗料的存在性 + 主数据,`sub_eval.part` 铺行 / `parts.profile` 承载编辑);
|
||||
* ②a 芯片类型(每颗料各自的类型选择,选项从 `parts.chip_type` 现读;类型下拉里没有合适的
|
||||
* 就在这里现新建一个,`parts.chip_type` 承载编辑);
|
||||
* ②b 核心参数字段清单(新建类型时要填的字段清单,`parts.type_param` 承载编辑,供加行);
|
||||
* ③ 参数值(逐条核心参数的实测 / 规格值,一字段一行,`parts.parameter`)。
|
||||
* 顶部一条红带摆 S1 配方算好的 `gate_reason`(「还差什么」的一句话,配方 §see case-model.ts
|
||||
* 的 step④),够不够齐、该不该出现由样式表判(§5「模版不许算」)。
|
||||
*
|
||||
@@ -28,9 +29,11 @@
|
||||
* 是本仓**唯一**「auto:true 又是 integer」的主键,改声明成 `text`(与其六处同一身份链路的
|
||||
* 引用列一起改)后两处坑自然绕开,详见 manifest.ts 该字段的大注释。
|
||||
*
|
||||
* 版面语言仍是「搬,不要照着做」(《HTML报告生成规范》§7):`P08_CSS`(2026-08-30 起
|
||||
* 逐字节抽自 `docs/ss02-act-05-new/ACT-WORK-05-数据工单-PN主数据创建.html`,
|
||||
* 见 `./target-style.ts` 头注)是**唯一**的样式来源。
|
||||
* 版面语言仍是「搬,不要照着做」(《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 行草稿」
|
||||
* 「编辑态与归档态二选一」这些平台概念。
|
||||
@@ -47,8 +50,9 @@ const BIND_COMMON = `
|
||||
两张验收件照目标件用 .tbl-wrap,而验收版目标件写的是 overflow:hidden(只有数据工单那版补了 overflow-x:auto)。
|
||||
2026-08-27 用户:「差异评估结论审核这个页面,表格部分不能左右滑动」—— 上一刀只管了前者 */
|
||||
.jc-tbl-wrap, .tbl-wrap { overflow-x: auto; overflow-y: hidden; }
|
||||
/* ②(退役,2026-08-30 换版面):旧目标件 #tbl1 那条 id 特异度覆盖已随 #tbl1 一起消失,
|
||||
新版面的轨道显隐规则在 BIND_P08 的「编辑机制」一节里 */
|
||||
/* ② 编辑态只留输入格:目标件 #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 那套记号样式挂在它自己的
|
||||
@@ -105,8 +109,13 @@ html.editable .jc-gapbar.v-0 { display: none; }
|
||||
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; }
|
||||
/* (#tbl1/#tbl3 的计数器点亮规则已随 2026-08-30 换版面退役:两张固定行集的表
|
||||
改成了轨道 .lanes 与瓷贴 .pvgrid,不再有 # 号列;面板里两张表仍有删行按钮,走通用规则) */
|
||||
/*
|
||||
* 不给删行的两张表,编辑态里 # 号不能空着: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; }
|
||||
/*
|
||||
* **新建行(Task 7b,件① 补「新建芯片类型」)**:parts.chip_type / parts.type_param
|
||||
* 两张表新增一条**字面量静态行**(不走 data-rows,不受行数所限,ViewContainer.addRow
|
||||
@@ -176,82 +185,6 @@ ${badgeCss('.rl', [
|
||||
['original', '原内部料号'],
|
||||
['candidate', '候选内部料号'],
|
||||
])}
|
||||
/* ==================== 编辑机制(孪生件显隐)————————————————————
|
||||
旧 P08_CSS(旧目标件)自带这一套;新目标件走 viewarea/editarea 双区 + 脚本,
|
||||
没有孪生件概念,平台要的这套规则改由绑定层承载(数值照旧目标件那份搬;
|
||||
focus 光环的变量名换成新 :root 里存在的 --data-wash —— 旧文件叫 --brand-wash,
|
||||
新 :root 没有这个名字,留着整条声明会被浏览器丢弃)。 */
|
||||
.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(--data);box-shadow:0 0 0 3px var(--data-wash);outline:none}
|
||||
table.on td>.jc-vcell{display:none}
|
||||
table.on td>.jc-cell{display:block}
|
||||
table.on td>.jc-vcell.ro{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 button{cursor:pointer;border:1px solid var(--data-line);border-radius:7px;padding:5px 12px;font:inherit;font-size:12px;font-weight:700;color:var(--data-deep);background:var(--data-wash)}
|
||||
.jc-addrow button:hover{background:var(--data-soft)}
|
||||
.jc-addrow em{font-style:normal;font-size:11.5px;color:var(--ink-4)}
|
||||
th.req::after{content:" *";color:var(--danger);font-size:11px}
|
||||
th{white-space:nowrap}
|
||||
/* ==================== 两条并列轨道(2026-08-30 用户定「每颗料一条轨道」)————
|
||||
.lanes 是唯一的 parts.profile 宿主(bodyOf 单 querySelector),每条 .lane 是宿主
|
||||
直接子元素 = collect 眼里的一行;.on 落在宿主 .lanes 自身(非表格容器,setOpen 规矩)。 */
|
||||
.lanes{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:var(--s2)}
|
||||
.lane{border:1px solid var(--line-soft);border-radius:var(--r-md);padding:14px;background:#fff}
|
||||
.lane .idcard{grid-template-columns:1fr;margin-bottom:14px}
|
||||
.lane .idcard .cell+.cell{border-left:0;border-top:1px solid var(--line-soft)}
|
||||
.lane .idcard .val{font-size:19px}
|
||||
.idcard .val .stx{margin-left:8px;vertical-align:3px}
|
||||
.idcard .pnv:empty::before{content:"(未填写)";font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:14px;font-weight:400;color:var(--ink-4);letter-spacing:0}
|
||||
.idcard .val:has(.tpill>span:empty) .tpill{display:none}
|
||||
.idcard .val:has(.tpill>span:empty)::after{content:"(未选择)";font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:14px;font-weight:400;color:var(--ink-4);letter-spacing:0}
|
||||
.lanes .fld{margin-top:8px}
|
||||
.lanes .fld .jc-vcell{padding-top:9px}
|
||||
.lanes.on .fld .jc-vcell:not(.ro){display:none}
|
||||
.lanes.on .fld .jc-cell{display:block}
|
||||
/* 目标件 .fld 的输入是 9px 竖内边距的高个子;BIND_COMMON 那条统一 32px 的规则(为表格里
|
||||
input/select 同高写的)排在 P08_CSS 之后会盖过它 —— 这里(alias 排最后)按目标件的高度要回来 */
|
||||
.fld input.jc-cell,.fld select.jc-cell,.pv input.jc-cell{height:36px}
|
||||
@media(max-width:900px){.lanes{grid-template-columns:1fr}}
|
||||
.pj{display:block}
|
||||
/* 步骤圆点点亮 —— 「办完了没有」全是基座数:①③ 缺格数为 0(v-0)、② 不同类型恰 1 种(v-1)。
|
||||
投影在行外的 .pj 包裹层上(行内 data-class-of 只认行作用域,读数投不出 —— 见 body 那段注释) */
|
||||
.pj-profile.v-0 .sdot.sd-1,.pj-type.v-1 .sdot.sd-2,.pj-param.v-0 .sdot.sd-3{background:var(--data);border-color:var(--data);color:#fff}
|
||||
/* 类型查名的诚实守卫 —— data-by 在行缺键时落回外层同名字段(终审 I3 已记档的平台缺陷):
|
||||
XL 还没选类型时,sub_eval.case 摊平出的 chip_type_id 会顶进来,查名 pill 摆出一个它没有的类型。
|
||||
守卫认 selectCurrent 按**这一行自己的值**写下的 option[selected]:行里没真选上,名字不摆、摆「(未选择)」。
|
||||
人在编辑态刚选了还没保存时守卫仍按旧值(与「基座算的数等回声」同一条纪律);保存重渲后跟上。 */
|
||||
.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .idcard .tpill{display:none}
|
||||
.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .idcard .val:has(.tpill)::after{content:"(未选择)";font-family:Inter,ui-sans-serif,system-ui,sans-serif;font-size:14px;font-weight:400;color:var(--ink-4);letter-spacing:0}
|
||||
.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .tywrap span{display:none}
|
||||
.lane:not(:has(select[data-col="chip_type_id"] option[selected])) .tywrap::after{content:"(未选择)";color:var(--ink-4)}
|
||||
/* 类型不一致护栏 —— 只在 v-2 档现身(sub_eval.part 恰两行,distinct 封顶 2;0=都没选,1=一致) */
|
||||
.jc-typewarn{display:none}
|
||||
.jc-typewarn.v-2{display:flex;gap:8px;margin:0 0 var(--s2);padding:10px 14px;border-radius:9px;background:var(--danger-soft);border:1px solid #f3b9b5;color:var(--danger);font-size:12.5px;line-height:1.65}
|
||||
.jc-typewarn b{font-weight:800}
|
||||
/* 新增类型面板:抽取样式的 display:none 是给脚本挂 .show 用的,<details> 自己管折叠 */
|
||||
details.newtype{display:block;margin:0 0 var(--s2)}
|
||||
details.newtype>summary{cursor:pointer;font-size:12.5px;font-weight:700;color:var(--data-deep)}
|
||||
details.newtype[open]>.ntbody{animation:rise .25s cubic-bezier(.2,.7,.3,1)}
|
||||
.ntbody{margin-top:12px}
|
||||
.ntbody h4{margin:14px 0 10px;font-size:12.5px;color:var(--data-deep)}
|
||||
/* 参数瓷贴:钥匙格藏起来(主键必挂 data-col,但那是行的钥匙不是编辑面);.on 落在 .pvgrid 自身 */
|
||||
.pv .rowkey{display:none}
|
||||
.pv .pvsrc{font-size:10.5px;color:var(--ink-4);margin-bottom:2px}
|
||||
.pv .pvu{font-weight:400;color:var(--ink-4);font-size:11px}
|
||||
.pv .pvrow{margin-top:6px}
|
||||
.pv .pvlab{display:block;font-size:11px;color:var(--ink-4);font-weight:700;margin-bottom:2px}
|
||||
.pvgrid.on .pv .jc-vcell:not(.ro){display:none}
|
||||
.pvgrid.on .pv .jc-cell{display:block}
|
||||
/* 保存即铺格之前的空态 —— parts_parameter_rows 是基座数,v-0 才现身 */
|
||||
.pvnull{display:none}
|
||||
.pv-empty.pvnull.v-0{display:block}
|
||||
`;
|
||||
|
||||
/** 三层壳照目标件:`<div class="page"><div class="card">`,不再套 `.proto-root`/`.app-shell`/`.jc-stage`
|
||||
@@ -260,11 +193,12 @@ details.newtype[open]>.ntbody{animation:rise .25s cubic-bezier(.2,.7,.3,1)}
|
||||
* 收件人 / 关联任务两格由引擎注入;目标件的「最迟交回」(`card.deadline`)本仓没有真数据源,不摆;
|
||||
* 工单编号(`card.id`)换成本次评估案号 `eval_ref`。
|
||||
*/
|
||||
const doc = (input: { css: string; alias: string; badge: string; tag: string; kicker: string; title: string; step: string; body: string; footTag: string }): string =>
|
||||
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>
|
||||
@@ -282,9 +216,9 @@ const doc = (input: { css: string; alias: string; badge: string; tag: string; ki
|
||||
</footer>
|
||||
</div></div>`;
|
||||
|
||||
/* ==================== 内联 SVG 图标 —— 逐字节搬自旧目标件,两张验收件仍在用(.zic 尺寸
|
||||
由 ACC1_CSS/ACC2_CSS 各自定义)。旧的 HIC 题头图标已删:新数据工单目标件没有这个图标,
|
||||
新 P08_CSS 也没有 .hic 尺寸规则 —— 无尺寸的内联 SVG 会按默认 300×150 撑爆版面(踩过)。 ==================== */
|
||||
/* ==================== 内联 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 =
|
||||
@@ -343,112 +277,55 @@ const gateBar = (): string => `<div class="jc-gate-bar">
|
||||
/* ==================== 局面:评估输入补全(input-supplement,S1 的数据工单) ==================== */
|
||||
|
||||
/**
|
||||
* **2026-08-30 换版面**:用户给了新目标件
|
||||
* `docs/ss02-act-05-new/ACT-WORK-05-数据工单-PN主数据创建.html`(单颗 PN 的三步向导:
|
||||
* ① PN 名称 → ② PN 类型(下拉首行「+新增类型」+ 面板)→ ③ 核心参数瓷贴;
|
||||
* 状态全靠尾部 `<script>` 驱动),这张件照它重排,**绑定面与功能一个不动**
|
||||
* (四张台账、四道闸、两处加行、终审 C1 的修复、主键钥匙全部原样带走)。用户定的三件事:
|
||||
* 1. **每颗料一条轨道(两条并列)** —— 目标件是单颗一列,W05 天生两颗料;
|
||||
* `.lanes`(`data-rows="mine:sub_eval.part" data-edit="parts.profile"`)每颗复制一条
|
||||
* `<section class="lane">`,网格排两栏。**一条 lane = collect 眼里的一行**
|
||||
* (`rowOf` 认宿主直接子元素;div 行形状钉在 ui-kit 的
|
||||
* `view-container-collect-div-rows.test.ts`)。类型要在两条轨道各选一次而必须相同 ——
|
||||
* 护栏是 `.jc-typewarn`(`data-class-of="sub_eval_part_distinct_chip_type_id"`,
|
||||
* 基座数,v-2 档才现身,模版不算不判),不必等提交才被判据拦;
|
||||
* 2. **新增类型面板每行四格**(参数名/说明/硬软约束/单位),独立的「核心参数字段」区退役 ——
|
||||
* `parts.type_param` 那张表(本就是四格一行)原样搬进 `<details class="newtype">` 面板;
|
||||
* 面板对**已有类型**也开放:「在库但当年字段没录全 → 给已有类型补一个字段」这条路
|
||||
* 不能因换版面而丢(下拉选已有类型即可,不必新建);
|
||||
* 3. **不动平台:每个步骤卡自带一道闸** —— 四道 editGate 原样,位置跟着新版面走
|
||||
* (profile 闸在轨道上方管两条 lane;chip_type / type_param 两道在面板里;
|
||||
* parameter 闸在③的步骤卡里)。
|
||||
* 块① 要评估什么 —— 两颗料(原 PN / 候选 PN)的存在性与主数据。
|
||||
*
|
||||
* **步骤③ 为什么不按轨道分两栏**(与决定 1 的自然延伸有一处偏差,是机制不是偏好):
|
||||
* `bodyOf` 是单个 querySelector,一个 `data-edit` 地址只有一个宿主;而 `rowOf` 把「行」
|
||||
* 认成**宿主的直接子元素** —— 若在唯一宿主里再按料嵌一层分栏容器,一颗料的所有参数格
|
||||
* 会被归并成一条记录(后写的列覆盖先写的、且不报错),终审 C1 / Task 7b 的同族坑。
|
||||
* 所以③是**单块平铺**:瓷贴直接是宿主子元素,每格自带所属料号小字;嵌套形状的
|
||||
* 「必然归并」也钉在上面那份 ui-kit 测试里(文档性反例)。
|
||||
* **行由 `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` 那条覆盖。
|
||||
*
|
||||
* 目标件脚本驱动的状态,无脚本翻译(模版不算不判,分母全是基座数):
|
||||
* · 选类型即时加载参数框 → **保存即铺格**(S1 配方按类型字段清单铺 `parts.parameter` 行,
|
||||
* `.pvnull` 空态按 `parts_parameter_rows` 的 v-0 档现身);
|
||||
* · 步骤圆点 `.done` 点亮 → `.sdot` 挂 `data-class-of`(①③ 用 `_missing` 的 v-0 档、
|
||||
* ② 用 distinct 的 v-1 档 —— 「办完了没有」全是基座数得出来的数);
|
||||
* · 「+新增类型」选中才显示面板 → `<details class="newtype">` 常驻折叠条(A2 方法论条同款);
|
||||
* · 闸上的进度条 `.gateprog`(n/m 项)→ **不做**:分母「总项数」不是基座现有的数,
|
||||
* 编一个就是界面撒谎;缺什么由同源的 `.jc-gapbar`/`.jc-gate-bar` 红带点名,数是真的;
|
||||
* · `body.editing` 双区整页切换 → 平台的 `html.editable` + 各宿主 `.on`
|
||||
* (渲染态即结构,编辑只是点亮其中的输入件 —— A2 对决卡先立此法)。
|
||||
* 芯片类型选择器**长在每一行里**(不是页面级单选):`sub_eval.part.chip_type_id` /
|
||||
* `parts.profile.chip_type_id` 都是**逐颗料各自一份**的字段(`distinct` 判据要求两颗
|
||||
* 恰好落在同一个值上,前提是它们本就可能不同),写入目标仍是 `parts.profile`。
|
||||
* 只读态用嵌套 `data-by` 从 `parts.chip_type` 整表按 `type_id` 查出人读的 `type_name`
|
||||
* (取值,不是算);编辑态换成 `optionSelect`(选项现读)。
|
||||
*/
|
||||
|
||||
/**
|
||||
* 两条轨道 —— 行由 `sub_eval.part` 铺、写向 `parts.profile`(两个台账地址同时出现在行容器上,
|
||||
* 收值的授权面只认 `data-edit`,行从哪来只管这一屏摆几行摆谁的 —— 与旧 PART_TABLE 同一条道理)。
|
||||
* 选 `sub_eval.part` 做行源是因为**角色(原/候选)与在库状态**只长在它身上;`internal_pn`
|
||||
* 挂 `data-col` 之后,保存时按它认作 `parts.profile` 的主键。**人不加行**(要建哪颗料是
|
||||
* 启动参数定死的事)。**宿主是 `<article>` 不是 `<div>`** —— `renderRows` 的正则按
|
||||
* **宿主自己的闭合标签**非贪婪截取行模版(`<div>` 宿主会在行模版里第一个 `</div>`(lab 那格)
|
||||
* 处截断,后一行的展开被浏览器塞进前一行没闭合的标签里,档案卡当场串行 —— 浏览器走查
|
||||
* 逮到的真事故,A2 立的「行容器用行内不会再出现的标签」说的正是宿主);行模版里
|
||||
* 同理不许再出现 `<article>`/`<section>`;档案卡 `.idcard` 是纯投影(`data-slot`,编辑态照常摆着),
|
||||
* 字段行 `.fld` 里才是「只读孪生 + 输入件」二选一。
|
||||
* 芯片类型选择器长在每条轨道里(`chip_type_id` 逐颗料各自一份,`distinct` 判据要求两颗
|
||||
* 恰好同值);只读态用嵌套 `data-by` 从 `parts.chip_type` 整表查人读的 `type_name`,
|
||||
* 编辑态换 `optionSelect`(选项现读)。
|
||||
*/
|
||||
const LANES = `<article class="lanes" data-rows="mine:${D}.part" data-edit="parts.profile">
|
||||
<section class="lane">
|
||||
<div class="idcard">
|
||||
<div class="cell">
|
||||
<div class="lab"><span class="rl" data-class-of="role"></span></div>
|
||||
<div class="val"><span class="pnv" data-slot="internal_pn" data-blank></span><span class="stx" data-class-of="exists_status"></span></div>
|
||||
</div>
|
||||
<div class="cell">
|
||||
<div class="lab">芯片类型</div>
|
||||
<div class="val"><span class="tpill"><span data-rows="parts.chip_type" data-by="type_id=chip_type_id"><i data-slot="type_name" data-blank></i></span></span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="steps">
|
||||
<div class="stepbox">
|
||||
<span class="sdot sd-1">1</span>
|
||||
<div class="scard">
|
||||
<div class="sh">主数据<span class="req-note">*为必填 · 在库的料现值已回填,只查漏补缺</span></div>
|
||||
<div class="sb">
|
||||
<div class="fld"><span class="fk req">内部料号</span><div class="fv">${cell('internal_pn', 'mono')}</div></div>
|
||||
<div class="fld"><span class="fk">在库状态</span><div class="fv"><span class="jc-vcell ro">${badge('stx', 'exists_status')}</span></div></div>
|
||||
<div class="fld"><span class="fk req">品牌</span><div class="fv">${cell('brand_name')}</div></div>
|
||||
<div class="fld"><span class="fk req">生命周期</span><div class="fv">${badge('stx', 'lifecycle_status', true)}${select('lifecycle_status', [['active', 'Active'], ['nrnd', 'NRND'], ['eol', 'EOL'], ['discontinued', '停产'], ['risk', '风险']], true)}</div></div>
|
||||
<div class="fld"><span class="fk">制造商</span><div class="fv">${cell('manufacturer_name')}</div></div>
|
||||
<div class="fld"><span class="fk">规格描述</span><div class="fv">${cell('specification_description')}</div></div>
|
||||
<div class="fld"><span class="fk">Datasheet 引用</span><div class="fv">${cell('datasheet_ref', 'mono')}</div></div>
|
||||
<div class="fld"><span class="fk">Datasheet 版本</span><div class="fv">${cell('datasheet_version')}</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="stepbox">
|
||||
<span class="sdot sd-2">2</span>
|
||||
<div class="scard">
|
||||
<div class="sh">芯片类型<span class="req-note">必填 · 决定第③步比哪些参数;两颗料必须同一个类型</span></div>
|
||||
<div class="sb">
|
||||
<div class="fld"><span class="fk req">芯片类型</span><div class="fv">
|
||||
<span class="jc-vcell tywrap"><span data-rows="parts.chip_type" data-by="type_id=chip_type_id"><i data-slot="type_name" data-blank></i></span></span>
|
||||
${optionSelect('chip_type_id', 'parts.chip_type', 'type_id', 'type_name', true)}
|
||||
<div class="hint">类型库里没有?展开下面「新增类型」面板现场定义,保存后立刻出现在这个下拉里。</div>
|
||||
</div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</article>`;
|
||||
|
||||
/** 类型不一致的护栏红带 —— 与放行判据同源(`sub_eval_part_distinct_chip_type_id = 1`),
|
||||
* 人在两条轨道选岔了,保存那一刻这条带就现身点名,不必等提交才被判据拦。显隐全在样式表
|
||||
* (v-2 档才亮),模版不算不判。 */
|
||||
const TYPE_WARN = `<div class="jc-typewarn" data-class-of="sub_eval_part_distinct_chip_type_id">
|
||||
<span><b>两条轨道选的芯片类型不一致</b> —— 逐项比对要求两颗料归同一个类型;回到上面把类型改成同一个再保存。</span>
|
||||
</div>`;
|
||||
const PART_TABLE = `<div class="jc-tbl-wrap"><table id="tbl1">
|
||||
<thead><tr>
|
||||
<th style="width:38px">#</th>
|
||||
<th 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 data-h="规格描述">规格描述</th>
|
||||
<th data-h="Datasheet引用">Datasheet 引用</th>
|
||||
<th data-h="Datasheet版本">Datasheet 版本</th>
|
||||
</tr></thead>
|
||||
<tbody data-rows="mine:${D}.part" data-edit="parts.profile"><tr>
|
||||
<td class="rn"><span class="jc-rn-num"></span></td>
|
||||
<td><span class="jc-vcell ro"><span class="rl" data-class-of="role"></span></span></td>
|
||||
<td>${cell('internal_pn', 'mono')}</td>
|
||||
<td><span class="jc-vcell ro">${badge('stx', 'exists_status')}</span></td>
|
||||
<td>
|
||||
<span class="jc-vcell"><span data-rows="parts.chip_type" data-by="type_id=chip_type_id"><i data-slot="type_name" data-blank></i></span></span>
|
||||
${optionSelect('chip_type_id', 'parts.chip_type', 'type_id', 'type_name', true)}
|
||||
</td>
|
||||
<td>${cell('brand_name')}</td>
|
||||
<td>${badge('stx', 'lifecycle_status', true)}${select('lifecycle_status', [['active', 'Active'], ['nrnd', 'NRND'], ['eol', 'EOL'], ['discontinued', '停产'], ['risk', '风险']], true)}</td>
|
||||
<td>${cell('manufacturer_name')}</td>
|
||||
<td>${cell('specification_description')}</td>
|
||||
<td>${cell('datasheet_ref', 'mono')}</td>
|
||||
<td>${cell('datasheet_version')}</td>
|
||||
</tr></tbody>
|
||||
</table></div>`;
|
||||
|
||||
/**
|
||||
* 块②a 新建芯片类型 —— `parts.chip_type`(Task 7b,2026-08-29 补齐)。类型下拉(块①)里
|
||||
@@ -518,7 +395,7 @@ const TYPE_WARN = `<div class="jc-typewarn" data-class-of="sub_eval_part_distinc
|
||||
* `apps/api/src/modules/interaction/__tests__/view-render-addrow-thead.test.ts`;
|
||||
* 这份源码本身的结构安全网见 `__tests__/activity.test.ts` 里那条「回归安全网」。
|
||||
*/
|
||||
const CHIP_TYPE_TABLE = `<div class="jc-tbl-wrap tbl-wrap"><table data-edit="parts.chip_type">
|
||||
const CHIP_TYPE_TABLE = `<div class="jc-tbl-wrap"><table data-edit="parts.chip_type">
|
||||
<tbody data-rows="mine:parts.chip_type" data-edit="parts.chip_type"><tr>
|
||||
<td class="rn"><span class="jc-rn-num"></span></td>
|
||||
<td>${ro('type_id', 'mono')}</td>
|
||||
@@ -561,14 +438,13 @@ const CHIP_TYPE_TABLE = `<div class="jc-tbl-wrap tbl-wrap"><table data-edit="par
|
||||
* 这张表列的顺序与语义不同(所属类型编号/参数名/参数说明/约束/参考单位,6 列)——
|
||||
* 位置 5、6 恰好被那两条老规则套反(约束套上「单位」的宽度、参考单位套上「约束」的宽度)。
|
||||
* 不挂 id 就不会继承这几条位置写死的规则,退回普通 `table`/`th`/`td` 基础样式。
|
||||
* (2026-08-30 换版面后新 P08_CSS 已无 #tbl2 规则;这段留作当时不挂 id 的原因。)
|
||||
*
|
||||
* **`<thead>` 排在两个 `<tbody>` 之后,同 `CHIP_TYPE_TABLE` 那条大注释**(评审逮到的 Critical 回归修复,2026-08-30)——
|
||||
* 这张表比 `parts.chip_type` 更要紧:它的「加一行」在这次改动**之前本来是好的**(`data-edit`
|
||||
* 只挂 `<tbody>` 时,`querySelectorAll('tr:last-of-type')` 搜索范围本就不含 `<thead>`),
|
||||
* 外层 `data-edit` 一挂就撞上了同一个坑,是一次真回归。理由与做法一字不差,不重复贴。
|
||||
*/
|
||||
const TYPE_PARAM_TABLE = `<div class="jc-tbl-wrap tbl-wrap"><table data-edit="parts.type_param">
|
||||
const TYPE_PARAM_TABLE = `<div class="jc-tbl-wrap"><table data-edit="parts.type_param">
|
||||
<tbody data-rows="mine:parts.type_param" data-edit="parts.type_param"><tr>
|
||||
<td class="rn"><span class="jc-rn-num"></span><button class="jc-rowdel" type="button" title="删除此行" data-edit-del="parts.type_param">×</button></td>
|
||||
<td>
|
||||
@@ -599,90 +475,80 @@ const TYPE_PARAM_TABLE = `<div class="jc-tbl-wrap tbl-wrap"><table data-edit="pa
|
||||
</table></div>`;
|
||||
|
||||
/**
|
||||
* 新增类型面板 —— 目标件的 `.newtype` 靠脚本挂 `.show`,这里换 `<details>` 常驻折叠条
|
||||
* (零脚本;`display:none` 的抽取样式由 BIND_P08 里 `details.newtype{display:block}` 让位,
|
||||
* 折叠交给 `<details>` 自己)。面板兼两用:新建类型,或给**已有**类型补核心参数字段
|
||||
* (`parts.type_param` 的所属类型下拉选已有的就是补字段)—— 独立的「核心参数字段」区
|
||||
* 退役后,这条能力不能丢。
|
||||
* 块③ 参数值 —— `parts.parameter`,一字段一行(行由 S1 配方按 `sub_eval.part × parts.type_param`
|
||||
* 铺,`prune:true` 会把不属于这个组合的孤行清掉,故这张表也**人不加行**)。
|
||||
*
|
||||
* **2026-08-29 评审 Minor 修正这条注释**:「只两栏可改」这句话本身没错,但没说全 ——
|
||||
* `internal_pn` / `param_name` 也走 `cell()`(带 `data-col`,会渲出可改的输入孪生件),
|
||||
* 所以严格数起来编辑态是 4/5 列都有孪生输入框,不是字面的「两栏」。这是**故意的**、
|
||||
* 不是漏改:**主键格必挂 `data-col`**是本仓的既定约定(它不是编辑面,是行的钥匙 ——
|
||||
* 漏了 `data-col`,保存时这一行凑不出主键,`resolveRowKey` 认不出它是哪一行,人改的
|
||||
* 值可能被错配到别的行上,2026-08-02 踩过)。「只两栏可改」说的是**这张表业务上真正
|
||||
* 要人填的信息**只有值与说明,`internal_pn`/`param_name` 虽然技术上可编辑,但那两格
|
||||
* 的内容由 S1 配方按启动参数与字典铺定,人正常操作不会去改它们(与 `cell()` 在别处
|
||||
* 承载主键的用法完全一致,如块①的 `internal_pn`)。
|
||||
*
|
||||
* `param_unit` 是唯一**真正**只读、连孪生输入框都不给的一列:配方每拍都从
|
||||
* `parts.type_param` 字典**直接复制**这一格(`{ col: 'param_unit' }`,不是自查现值的
|
||||
* `lookup...else` 那种自保写法),手改了也会在下一拍被字典值覆盖回去,给它配可编辑的
|
||||
* 输入孪生件只会让人误以为改了真的算数。
|
||||
*/
|
||||
const NEWTYPE_PANEL = `<details class="newtype">
|
||||
<summary>类型库里没有合适的?在这里新建一个类型;已有类型当年字段没录全,也在这里补</summary>
|
||||
<div class="ntbody">
|
||||
<h4>新增 PN 类型</h4>
|
||||
${editGate('parts.chip_type', '新建一个类型:表尾那一行填类型名称与说明,保存后立刻出现在轨道的下拉里')}
|
||||
${gapbar('parts_chip_type', '必填')}
|
||||
${CHIP_TYPE_TABLE}
|
||||
${addrow('parts.chip_type', '+ 再建一个类型', '两颗料若都要新建、且类型不同,先各自保存,「加一行」能再建一个')}
|
||||
<h4>核心参数字段(参数名 / 说明 / 硬软约束 / 单位)</h4>
|
||||
${editGate('parts.type_param', '这个类型逐条比对时要看哪些参数,一条一行;表尾那一行 / 「加一行」补字段')}
|
||||
${gapbar('parts_type_param', '必填')}
|
||||
${TYPE_PARAM_TABLE}
|
||||
${addrow('parts.type_param', '+ 加一行(补一个核心参数字段)', '所属类型 / 参数名 / 参数说明 / 约束类型必须写;参考单位选填')}
|
||||
</div>
|
||||
</details>`;
|
||||
|
||||
/**
|
||||
* 步骤③ 核心参数值 —— `parts.parameter`,一字段一格(行由 S1 配方按
|
||||
* `sub_eval.part × parts.type_param` 铺,`prune:true` 清孤行,人不加行不删行)。
|
||||
* **单块平铺**(不按轨道分两栏的机制原因见上面的大注释):瓷贴 `<section class="pv">`
|
||||
* 直接是宿主 `.pvgrid` 的子元素 = collect 的一行;宿主是 `<article>`(同 LANES:
|
||||
* `<div>` 宿主会被瓷贴里的第一个 `</div>` 截断行模版);所属料号以小字摆在每格头上,
|
||||
* 配方按料连排,同一颗料的格子天然聚在一起。
|
||||
* 主键钥匙(`internal_pn`/`param_name`)收进 `.rowkey`(display:none,BIND_ACC1_EDIT
|
||||
* 先立此法):不给人看,但必须真挂 `data-col` —— 漏了钥匙,保存的行凑不出主键,
|
||||
* 人改的值会被错配到别的行上(2026-08-02 踩过)。`param_unit` 真只读(配方每拍从字典
|
||||
* 复制,手改会被覆盖,给输入件就是让人误以为改了算数)。
|
||||
*/
|
||||
const PARAM_TILES = `<div class="steps"><div class="stepbox">
|
||||
<span class="sdot sd-3">3</span>
|
||||
<div class="scard">
|
||||
<div class="sh">核心参数值<span class="req-note">一参数一格 · 值必填;查不到就留空并在说明里写明,别编一个数顶替</span></div>
|
||||
<div class="sb">
|
||||
${editGate('parts.parameter', '逐格参数值核对 / 补齐,点「编辑」开格')}
|
||||
${gapbar('parts_parameter', '必填')}
|
||||
<article class="pvgrid" data-rows="mine:parts.parameter" data-edit="parts.parameter"><section class="pv">
|
||||
<div class="pvsrc mono" data-slot="internal_pn" data-blank></div>
|
||||
<div class="pn2"><span data-slot="param_name" data-blank></span><span class="pvu" data-slot="param_unit" data-blank></span></div>
|
||||
<span class="rowkey">${cell('internal_pn', 'mono')}${cell('param_name')}</span>
|
||||
<div class="pvrow">${cell('param_value', 'mono')}</div>
|
||||
<div class="pvrow"><span class="pvlab">说明</span>${cell('param_note')}</div>
|
||||
</section></article>
|
||||
<div class="pv-empty pvnull" data-class-of="parts_parameter_rows">参数格还没铺出来 —— 先在上面把两颗料的芯片类型定下来并保存,系统会按类型自动铺出这里的参数格。</div>
|
||||
</div>
|
||||
</div>
|
||||
</div></div>`;
|
||||
const PARAMETER_TABLE = `<div class="jc-tbl-wrap"><table id="tbl3">
|
||||
<thead><tr>
|
||||
<th style="width:38px">#</th>
|
||||
<th data-h="所属料号">所属料号</th>
|
||||
<th data-h="参数名">参数名</th>
|
||||
<th class="req" data-h="值">值</th>
|
||||
<th 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></td>
|
||||
<td>${cell('internal_pn', 'mono')}</td>
|
||||
<td>${cell('param_name')}</td>
|
||||
<td>${cell('param_value', 'mono')}</td>
|
||||
<td>${ro('param_unit')}</td>
|
||||
<td>${cell('param_note')}</td>
|
||||
</tr></tbody>
|
||||
</table></div>`;
|
||||
|
||||
export const INPUT_SUPPLEMENT = doc({
|
||||
css: P08_CSS,
|
||||
alias: BIND_P08,
|
||||
badge: '数据',
|
||||
hic: HIC,
|
||||
tag: '数据工单',
|
||||
kicker: 'SS02 · ACT-WORK-05 差异评估 · 数据工单',
|
||||
title: '差异评估的输入还差几格',
|
||||
step: '步骤 1/4 · 校验入参与补数据',
|
||||
footTag: '工单类型:数据',
|
||||
body:
|
||||
zone('触发原因', '两颗料至少有一颗不在库、类型不一致、或必填格还空着,这张工单才会派到你手上') +
|
||||
zone('做什么', '核对两颗料在不在库、归没归同一个芯片类型、参数值齐不齐;缺什么就在下面补齐', ZIC_TASK) +
|
||||
note([
|
||||
'<b>流程</b>:两条轨道各一颗料(原 PN / 候选)—— 核对主数据 → 各选芯片类型(必须同一个;下拉里没有就展开「新增类型」面板现场定义)→ 保存后系统按类型铺出第③步的核心参数格 → 逐格填值。',
|
||||
'<span class="need">AI 预填情况</span>:在库的料现值已回填,只需查漏补缺;料号本身已按启动参数铺好,不必重填。<b>纪律</b>:查不到的参数值留空(红带会点名),不要编一个值顶替;数据工单确认后即入正式库,确认人留痕。',
|
||||
'<span class="need">模板定位</span>:本单是「数据工单」——目标台账 = 芯片主数据(P-08 主记录 + 标准化参数子表),核心参数字段清单挂在芯片类型字典上。',
|
||||
'<span class="need">触发原因</span>:下面【要评估什么】里两行是本次评估要比对的原 PN 与候选 PN —— 至少有一颗不在库、两颗归的类型不一致、或必填格还空着,下面这条红带才会出现。',
|
||||
'<span class="done">已绑定</span>:两颗料的内部料号已按启动参数铺进下表,不必你重填料号本身,只需核对 / 补齐其余各格;在库的料现值已回填,只是查漏补缺。',
|
||||
'<span class="need">纪律</span>:两颗料必须归到同一个芯片类型下才谈得上逐项比对;类型下拉里没有合适的就在【芯片类型】区新建一个,并把它的核心参数字段一并定下来;查不到的参数值留空(会被红带点名),不要编一个值顶替。',
|
||||
]) +
|
||||
gateBar() +
|
||||
editGate('parts.profile', '两条轨道的主数据核对 / 补齐(缺类型的在②的下拉里选),点「编辑」开卡') +
|
||||
zone('要评估什么', '两颗料的存在性与主数据(每行一颗,人不加行,*为必填列)', ZIC_TABLE) +
|
||||
editGate('parts.profile', '两颗料的主数据核对 / 补齐,点「编辑」开表') +
|
||||
gapbar('parts_profile', '必填') +
|
||||
/*
|
||||
* 三层投影包裹(pj-*):步骤圆点「办完没有」的三个基座数(profile 缺格 / 类型 distinct /
|
||||
* 参数缺格)都是**作用域级读数**,而行内的 data-class-of 只认行自己的键
|
||||
* (renderRows 末段 renderTemplate(…, one) 拿的是行对象,不是合并作用域 —— 实测投不出)。
|
||||
* 投影挂在行外的包裹层上,样式表用后代选择器点亮圆点;代价是两条轨道的圆点一起亮
|
||||
* (基座数本来就是表级的 —— 分到每颗料头上的数基座没有,不编)。
|
||||
*/
|
||||
`<div class="pj pj-profile" data-class-of="parts_profile_missing"><div class="pj pj-type" data-class-of="sub_eval_part_distinct_chip_type_id"><div class="pj pj-param" data-class-of="parts_parameter_missing">` +
|
||||
LANES +
|
||||
TYPE_WARN +
|
||||
NEWTYPE_PANEL +
|
||||
PARAM_TILES +
|
||||
`</div></div></div>` +
|
||||
PART_TABLE +
|
||||
zone('芯片类型', '上面下拉选不到合适的类型,就在这里新建一个(选填,*为必填列)', ZIC_TABLE) +
|
||||
editGate('parts.chip_type', '新建一个类型:表尾那一行填类型名称与说明,保存后立刻出现在上面的下拉里') +
|
||||
gapbar('parts_chip_type', '必填') +
|
||||
CHIP_TYPE_TABLE +
|
||||
addrow('parts.chip_type', '+ 再建一个类型', '两颗料若都要新建、且类型不同,先各自保存,「加一行」能再建一个') +
|
||||
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', '必填') +
|
||||
PARAMETER_TABLE +
|
||||
flow('<b>提交 / 确认:</b>本工单只承载内容;确认请在对话区的【执行卡】中进行。确认后活动自动从「等待数据」唤醒。'),
|
||||
});
|
||||
|
||||
|
||||
@@ -2,15 +2,14 @@
|
||||
* ACT-WORK-05 差异评估的目标件样式表 —— **机械抽取,一个数值都不许改**
|
||||
* (《HTML报告生成规范》§7:有目标件时「搬」,不要「照着做」;样式表整段搬,标记逐块搬)。
|
||||
*
|
||||
* 来源:
|
||||
* · `P08_CSS` ← `docs/ss02-act-05-new/ACT-WORK-05-数据工单-PN主数据创建.html` 的
|
||||
* 整段 `<style>`(**2026-08-30 换版面**:用户给了新目标件,`input-supplement`
|
||||
* 整张照它重排 —— 步骤轨道 `.steps`/`.stepbox`、档案卡 `.idcard`、参数瓷贴
|
||||
* `.pvgrid`/`.pv`、新增类型面板 `.newtype`。旧来源
|
||||
* `docs/SS02-ACT-WORK-05-html/ACT-WORK-05-数据工单-01-芯片主数据创建.html`
|
||||
* 的两段 `<style>` 随之整段退役 —— 那一版带的 `.jc-vcell`/`.jc-cell` 孪生件
|
||||
* 显隐规则不在新目标件里,平台绑定所需的那部分改由 `report.ts` 的 `BIND_*`
|
||||
* 承载,见那边「编辑机制」一节);抽取与源文件 `<style>` 区间 diff 零差异;
|
||||
* 来源(`docs/SS02-ACT-WORK-05-html/`):
|
||||
* · `P08_CSS` ← `ACT-WORK-05-数据工单-01-芯片主数据创建.html` 的两段 `<style>`
|
||||
* (该文件头部连续放了两个 `<style>` 元素:第一段是基座壳,第二段标着
|
||||
* 「视觉补充段 v3」——`.hic`/`.zic`/`.thd`/`#tbl1`/`#tbl2` 等数据工单专属样式。
|
||||
* 两段按文档原有顺序首尾相接存成一个常量;等价于浏览器解析两个 `<style>` 标签,
|
||||
* 级联结果与原文件逐字节一致,已用 diff 验证,见 task-5-report.md)——
|
||||
* 2026-08-29 Task 7 起同时是唯一的工单件样式来源:`input-supplement` 一张件
|
||||
* 顶掉了原来分开的 P-08/P-10/执行工单三张局面,一并沿用这套壳;
|
||||
* · `ACC1_CSS` ← `ACT-WORK-05-验收工单-01-差异评估结论审核.html` 的 `<style>`;
|
||||
* · `ACC2_CSS` ← `ACT-WORK-05-验收工单-02-评审放行与落账确认.html` 的 `<style>`。
|
||||
*
|
||||
@@ -23,9 +22,9 @@
|
||||
* `p10-document-create`(P-10 原厂技术资料创建)原来**没有自己的目标件**、复用 `P08_CSS` ——
|
||||
* Task 7 重写后 P-10 不再是这个活动的数据工单范围(本活动不碰 P-10),这句历史说明留作背景。
|
||||
*
|
||||
* 抽取用 `sed -n` 按行号截取 `<style>`/`</style>` 之间的原文,再拿抽出来的内容与
|
||||
* 源文件同一行区间做 `diff` 验「零差异」(ACC1/ACC2 的过程在 task-5-report.md;
|
||||
* P08 新版 2026-08-30 换版面时同法重验)。
|
||||
* 抽取用 `sed -n` 按行号截取 `<style>`/`</style>` 之间的原文,再拿抽出来的内容包回
|
||||
* `<style>…</style>` 与源文件同一行区间做 `diff`,四份全部「零差异」——过程写在
|
||||
* task-5-report.md,这里不重复贴证据。
|
||||
*
|
||||
* 这份文件只装**目标件自己的样式**,不装平台绑定用的开关(三态占位、缺项红带、
|
||||
* `data-class-of` 的 `v-*` 别名这类)——那些是本活动自己加出来的东西,活在
|
||||
@@ -36,148 +35,102 @@ export const P08_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;
|
||||
--data:#3370ff; --data-deep:#245bdb; --data-soft:#e1eaff; --data-line:#c3d6ff; --data-wash:#f0f4ff;
|
||||
--brand:#3370ff; --brand-deep:#245bdb; --brand-soft:#e1eaff; --brand-wash:#f0f4ff;
|
||||
--data:#3370ff; --data-soft:#e1eaff; --data-line:#c3d6ff;
|
||||
--ok:#278717; --ok-soft:#e8f8e4; --danger:#d83931; --danger-soft:#feeae9;
|
||||
--amber:#8f6a12; --amber-soft:#fdf3d7; --amber-line:#ecd9a0;
|
||||
--mono:"SF Mono",Menlo,Consolas,monospace;
|
||||
/* 8px 间距节奏(dense) */
|
||||
--s1:8px; --s2:16px; --s3:24px; --s4:32px;
|
||||
--r-sm:8px; --r-md:12px;
|
||||
--tr:.22s cubic-bezier(.2,.7,.3,1);
|
||||
}
|
||||
*{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:#fff;color:var(--ink);font-size:14px;line-height:1.7;-webkit-font-smoothing:antialiased}
|
||||
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:20px 24px;border-bottom:1px solid var(--line-soft);background:linear-gradient(120deg,#f0f4ff 0%,#fff 55%,#eef3ff 100%)}
|
||||
.badge{flex:none;width:44px;height:44px;border-radius:11px;display:grid;place-items:center;background:var(--data);color:#fff;font-weight:800;font-size:13px;box-shadow:0 6px 16px rgba(51,112,255,.28)}
|
||||
.card-head{display:flex;align-items:center;gap:14px;padding:18px 22px;border-bottom:2px solid var(--ink);background:linear-gradient(120deg,#f0f5ff 0%,#fff 55%,#eef4ff 100%)}
|
||||
.badge{flex:none;width:44px;height:44px;border-radius:10px;display:grid;place-items:center;background:var(--data);color:#fff;font-weight:800;font-size:13px;box-shadow:0 6px 16px rgba(51,112,255,.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:20px;font-weight:800;line-height:1.3;letter-spacing:-.01em}
|
||||
.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(--data-deep);background:var(--data-soft);border:1px solid var(--data-line)}
|
||||
.tag{font-size:11px;font-weight:800;padding:2px 10px;border-radius:999px;color:var(--data);background:var(--data-soft);border:1px solid var(--data-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:var(--s3) 24px var(--s4)}
|
||||
|
||||
/* ===== 区标 / 说明带 ===== */
|
||||
.zone{font-size:11px;font-weight:800;letter-spacing:.12em;color:var(--ink-4);margin:0 0 var(--s1)}
|
||||
.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(--data);border-radius:var(--r-sm);background:var(--soft);padding:14px 16px;margin-bottom:var(--s2)}
|
||||
.note p{margin:0;font-size:13px;color:var(--ink-2)}
|
||||
.note p+p{margin-top:6px}
|
||||
.note{border:1px solid var(--line);border-left:3px solid var(--data);border-radius:8px;background:var(--soft);padding:14px 16px;margin-bottom:16px}
|
||||
.note p{margin:0 0 6px;font-size:13px;color:var(--ink-2)}
|
||||
.note p:last-child{margin-bottom:0}
|
||||
.note b{color:var(--ink)}
|
||||
.note .need{color:var(--data-deep);font-weight:700}
|
||||
.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.ok{color:var(--ok);background:var(--ok-soft);border-color:#c9ecc2}
|
||||
.st.new{color:var(--data-deep);background:var(--data-soft);border-color:var(--data-line)}
|
||||
.st.lock{color:var(--ink-3);background:#eef1f6;border-color:var(--line)}
|
||||
|
||||
/* ===== 编辑闸 + 必填进度 ===== */
|
||||
.jc-editgate{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:0 0 var(--s2);padding:10px 14px;border-radius:10px;background:var(--soft);border:1px solid var(--line-soft);font-size:12px;color:var(--ink-3);transition:background var(--tr),border-color var(--tr)}
|
||||
.note .done{color:var(--ok);font-weight:700}
|
||||
.note .need{color:var(--data);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}
|
||||
.gateprog{flex:1 1 100%;display:flex;align-items:center;gap:10px;margin-top:6px}
|
||||
.gateprog .track{flex:1;height:5px;border-radius:3px;background:var(--line-soft);overflow:hidden}
|
||||
.gateprog .fill{display:block;height:100%;width:0;border-radius:3px;background:linear-gradient(90deg,var(--data),var(--data-deep));transition:width .35s cubic-bezier(.2,.7,.3,1)}
|
||||
.gateprog .pct{font-family:var(--mono);font-size:11px;color:var(--ink-4);min-width:64px;text-align:right}
|
||||
.jc-egbtn{flex:none;display:inline-flex;align-items:center;gap:5px;cursor:pointer;border:1px solid var(--line);border-radius:8px;padding:7px 14px;font:inherit;font-size:12px;font-weight:700;color:var(--ink-2);background:#fff;transition:border-color var(--tr),color var(--tr),background var(--tr),transform .12s}
|
||||
.jc-egbtn:hover{border-color:var(--data);color:var(--data)}
|
||||
.jc-egbtn:active{transform:scale(.97)}
|
||||
.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(--data);border-color:var(--data)}
|
||||
.jc-egbtn.save:hover{filter:brightness(1.06);color:#fff}
|
||||
.jc-egbtn:focus-visible{outline:2px solid var(--data-deep);outline-offset:2px}
|
||||
.jc-editgate .eg-cancel,.jc-editgate .eg-save{display:none}
|
||||
.jc-editgate.on{background:var(--data-wash);border-color:var(--data-line);color:#1e40af}
|
||||
.jc-editgate.on{background:var(--brand-wash);border-color:var(--data-line);color:#1e40af}
|
||||
.jc-editgate.on .eg-open{display:none}
|
||||
.jc-editgate.on .eg-cancel,.jc-editgate.on .eg-save{display:inline-flex}
|
||||
|
||||
/* ===== 渲染态:档案卡 ===== */
|
||||
.idcard{border:1px solid var(--line-soft);border-radius:var(--r-md);margin-bottom:var(--s2);background:linear-gradient(150deg,#fff 60%,var(--data-wash));display:grid;grid-template-columns:1.2fr 1fr;overflow:hidden}
|
||||
.idcard .cell{padding:18px 22px}
|
||||
.idcard .cell+.cell{border-left:1px solid var(--line-soft);background:rgba(255,255,255,.55)}
|
||||
.idcard .lab{font-size:11.5px;font-weight:800;color:var(--ink-3);letter-spacing:.08em;display:flex;align-items:center;gap:6px}
|
||||
.idcard .lab::before{content:"";width:8px;height:8px;border-radius:2.5px;background:var(--data)}
|
||||
.idcard .val{margin-top:8px;font-size:24px;font-weight:800;font-family:var(--mono);letter-spacing:-.01em;line-height:1.25;color:var(--ink)}
|
||||
.idcard .val.empty{font-size:14px;font-family:inherit;font-weight:400;color:var(--ink-4)}
|
||||
.idcard .val .tpill{display:inline-block;font-family:inherit;font-size:15px;font-weight:800;color:var(--data-deep);background:var(--data-soft);border:1px solid var(--data-line);border-radius:999px;padding:4px 14px;vertical-align:2px}
|
||||
.idcard .val .st{margin-left:8px;vertical-align:3px}
|
||||
|
||||
/* ===== 参数表(渲染态) ===== */
|
||||
.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:.04em;font-weight:800;color:var(--ink-3);padding:10px 14px;border-bottom:1px solid var(--line)}
|
||||
td{padding:10px 14px;border-bottom:1px solid var(--line-soft);vertical-align:middle}
|
||||
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}
|
||||
tbody tr{transition:background .15s}
|
||||
tbody tr:hover{background:var(--data-wash)}
|
||||
.empty-row td{color:var(--ink-4);text-align:center;padding:22px}
|
||||
.empty-row td:hover{background:#fff}
|
||||
|
||||
/* ===== 编辑态:步骤轨道 ===== */
|
||||
.editarea{display:none}
|
||||
body.editing .editarea{display:block}
|
||||
body.editing .viewarea{display:none}
|
||||
.steps{position:relative}
|
||||
.steps::before{content:"";position:absolute;left:12px;top:20px;bottom:20px;width:2px;background:var(--line-soft);border-radius:1px}
|
||||
.stepbox{position:relative;padding-left:36px;margin-bottom:var(--s3)}
|
||||
.stepbox:last-child{margin-bottom:0}
|
||||
.sdot{position:absolute;left:0;top:0;width:26px;height:26px;border-radius:50%;background:#fff;border:2px solid var(--data-line);color:var(--data-deep);font-size:12px;font-weight:800;display:grid;place-items:center;transition:background var(--tr),border-color var(--tr),color var(--tr)}
|
||||
.stepbox.done .sdot{background:var(--data);border-color:var(--data);color:#fff}
|
||||
.scard{border:1px solid var(--line-soft);border-radius:var(--r-md);background:#fff;transition:border-color var(--tr),box-shadow var(--tr)}
|
||||
.stepbox.done .scard{border-color:var(--data-line)}
|
||||
.scard .sh{display:flex;align-items:center;gap:10px;padding:12px 16px;border-bottom:1px solid var(--line-soft);font-size:13.5px;font-weight:800;letter-spacing:-.01em}
|
||||
.scard .sh .req-note{margin-left:auto;font-size:11px;color:var(--ink-4);font-weight:400}
|
||||
.scard .sb{padding:16px}
|
||||
.fld{display:flex;align-items:flex-start;gap:12px;font-size:12.5px}
|
||||
.fld .fk{width:88px;flex:none;color:var(--ink-3);font-weight:700;font-size:12px;padding-top:9px}
|
||||
.fld .fk.req::after{content:" *";color:var(--danger)}
|
||||
.fld .fv{flex:1;min-width:0}
|
||||
.fld input,.fld select{width:100%;border:1px solid var(--line);border-radius:var(--r-sm);padding:9px 11px;font:inherit;font-size:13px;color:var(--ink);background:#fff;transition:border-color var(--tr),box-shadow var(--tr)}
|
||||
.fld input:focus,.fld select:focus{border-color:var(--data);box-shadow:0 0 0 3px var(--data-wash);outline:none}
|
||||
.hint{font-size:11.5px;color:var(--ink-4);margin-top:6px}
|
||||
|
||||
/* ===== 新增类型面板 ===== */
|
||||
.newtype{display:none;margin-top:12px;border:1px dashed var(--data-line);border-radius:10px;background:var(--data-wash);padding:14px 16px}
|
||||
.newtype.show{display:block;animation:rise .25s cubic-bezier(.2,.7,.3,1)}
|
||||
@keyframes rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
|
||||
.newtype h4{margin:0 0 10px;font-size:12.5px;color:var(--data-deep)}
|
||||
.prow{display:flex;gap:8px;align-items:center;margin-top:8px;animation:rise .2s cubic-bezier(.2,.7,.3,1)}
|
||||
.prow input{flex:1;border:1px solid var(--line);border-radius:7px;padding:8px 10px;font:inherit;font-size:12.5px;background:#fff;transition:border-color var(--tr),box-shadow var(--tr)}
|
||||
.prow input:focus{border-color:var(--data);box-shadow:0 0 0 3px rgba(51,112,255,.12);outline:none}
|
||||
.prow .del{flex:none;width:28px;height:28px;cursor:pointer;border:1px solid var(--line);border-radius:7px;background:#fff;color:var(--ink-3);font-size:14px;line-height:1;transition:border-color var(--tr),color var(--tr),background var(--tr)}
|
||||
.prow .del:hover{border-color:var(--danger);color:var(--danger);background:var(--danger-soft)}
|
||||
.nt-actions{display:flex;gap:8px;margin-top:12px;align-items:center}
|
||||
.btn{cursor:pointer;border:1px solid var(--data-line);border-radius:8px;padding:7px 14px;font:inherit;font-size:12px;font-weight:700;color:var(--data-deep);background:#fff;transition:background var(--tr),transform .12s}
|
||||
.btn:hover{background:var(--data-soft)}
|
||||
.btn:active{transform:scale(.97)}
|
||||
.btn.primary{color:#fff;background:var(--data);border-color:var(--data)}
|
||||
.btn.primary:hover{filter:brightness(1.07)}
|
||||
.btn:focus-visible{outline:2px solid var(--data-deep);outline-offset:2px}
|
||||
|
||||
/* ===== 参数值瓷贴 ===== */
|
||||
.pvgrid{display:grid;grid-template-columns:repeat(2,1fr);gap:10px}
|
||||
.pv{border:1px solid var(--line-soft);border-radius:10px;padding:10px 12px;background:#fff;transition:border-color var(--tr),box-shadow var(--tr)}
|
||||
.pv:focus-within{border-color:var(--data-line);box-shadow:0 0 0 3px rgba(51,112,255,.08)}
|
||||
.pv .pn2{font-size:12px;font-weight:800;color:var(--ink-2);margin-bottom:6px;display:flex;align-items:baseline;gap:6px}
|
||||
.pv .pn2::after{content:"*";color:var(--danger);font-size:11px}
|
||||
.pv input{width:100%;border:1px solid var(--line);border-radius:7px;padding:7px 10px;font:inherit;font-size:12.5px;background:#fff;transition:border-color var(--tr),box-shadow var(--tr)}
|
||||
.pv input:focus{border-color:var(--data);box-shadow:0 0 0 3px rgba(51,112,255,.12);outline:none}
|
||||
.pv-empty{border:1px dashed var(--line);border-radius:10px;padding:20px;text-align:center;color:var(--ink-4);font-size:12px}
|
||||
|
||||
/* ===== 底 ===== */
|
||||
.flow{margin-top:var(--s2);padding:10px 14px;border-radius:var(--r-sm);background:var(--soft);border:1px dashed var(--line);font-size:12px;color:var(--ink-3)}
|
||||
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:14px 24px;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:760px){.pvgrid{grid-template-columns:1fr}.card-head{flex-wrap:wrap}.side{align-items:flex-start}.idcard{grid-template-columns:1fr}.idcard .cell+.cell{border-left:0;border-top:1px solid var(--line-soft)}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}
|
||||
.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)}
|
||||
.pre{font-style:normal;font-size:10.5px;font-weight:800;color:var(--data);background:var(--data-soft);border:1px solid var(--data-line);border-radius:999px;padding:0 7px;margin-left:6px;white-space:nowrap}
|
||||
table.on td>.pre{display:none}
|
||||
.ct{display:inline-block;font-size:11px;font-weight:700;padding:1px 8px;border-radius:999px;border:1px solid transparent;white-space:nowrap}
|
||||
.ct.hard{color:var(--danger);background:var(--danger-soft);border-color:#f3b9b5}
|
||||
.ct.soft{color:var(--ink-3);background:#eef1f6;border-color:var(--line)}
|
||||
.tbl-gap{margin-top:18px}
|
||||
/* ===== 视觉补充段 v3(通用批量模板:字段即表头、每行一条;不改基件样式与 JS 钩子) ===== */
|
||||
.hic{flex:none;width:26px;height:26px;color:var(--data)}
|
||||
.zic{width:14px;height:14px;color:var(--ink-3);vertical-align:-2px;margin-right:3px}
|
||||
/* 表头内字段说明(第二行小字) */
|
||||
.thd{display:block;font-size:10px;font-weight:600;color:var(--ink-4);letter-spacing:0;margin-top:1px;line-height:1.4}
|
||||
/* 表 1:字段即表头,内容多列,允许横向滚动 */
|
||||
#tbl1{min-width:1240px}
|
||||
#tbl1 .jc-vcell{display:inline}
|
||||
/* 表 2 列宽 */
|
||||
#tbl2 th:nth-child(2){width:150px}
|
||||
#tbl2 th:nth-child(3){width:18%}
|
||||
#tbl2 th:nth-child(4){width:14%}
|
||||
#tbl2 th:nth-child(5){width:8%;text-align:center;white-space:nowrap}
|
||||
#tbl2 th:nth-child(6){width:13%;text-align:center;white-space:nowrap}
|
||||
#tbl2 td:nth-child(5),#tbl2 td:nth-child(6){text-align:center;white-space:nowrap}
|
||||
/* 说明列(只读注释,编辑态保持可见) */
|
||||
.desc{color:var(--ink-3);font-size:12px;line-height:1.5}
|
||||
table.on td>.jc-vcell.ro{display:block}
|
||||
.tbl-wrap{overflow-x:auto}
|
||||
@media (prefers-reduced-motion: reduce){*{animation:none!important;transition:none!important}}
|
||||
`;
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user