Archived
fix(w05): 五张模版整份重做 —— 逐块搬自四份目标件,不再照 E04/I01 那套版面语言做
用户实跑截图指出:芯片主数据创建的模版样式与给的目标件对不上。病根是 brief 一边说
「版面语言逐字量自目标件」,一边让照 I01 的 prd-style.ts 当视觉底 —— 两句互相矛盾,
《HTML报告生成规范》§7 写的是「搬」不是「照着做」。
- 新增 src/views/target-style.ts:四份目标件的 <style> 块逐字节机械抽取(P08 两段拼接、
EXEC/ACC1/ACC2 各一段),抽取用 sed 按行号截取、diff 校验零差异;P10 无独立目标件,
复用 P08 那份壳(规格里数据工单是同一模板按台账实例化)。删掉 src/views/prd-style.ts
(I01 的视觉底,搬完之后没有任何模版再引用它)。
- 整份重写 src/views/report.ts:<div class="page"><div class="card">…</div></div> 外壳
逐字节搬自目标件(不再套 .proto-root/.app-shell/.jc-stage 那套 E04/I01 自造的壳),
内联 SVG 图标(.hic/.zic)原样搬;数据面(哪张表可编辑、哪几列可改、只读件零 data-edit)
与上一版审查通过的绑定面保持不变。
- 修两处实跑逼出的坑(并入这次重做,未另开一轮):
① classToken(展示层消毒,吃掉下划线)与 slug(基座读数消毒,吃掉连字符)是两套不同规则,
手写 .v-pin_to_pin 这类选择器永远匹配不上,件上显示「待定」——改用 vtok()/badgeCss()
从 tally 值算选择器,不再手写;
② requiredWhen 是按行不按列,缺口表补充内容从前用静态 class="req" 把每一行都标必填,
改成 <td data-class-of="status"> 按本行 status 值投影,只在 v-gap 那一档出红胶囊。
- 途中发现旧的 CSS 说明性注释里写着字面的「<script>」,会被 host.ts 的
/<\s*script\b/i 检测判定为「含脚本」在装包时被拒 —— 顺手改写措辞避开(纯文档性质,
不影响任何绑定或判断)。
- 升版本号 1.0.1 → 1.1.0(版面重做 + 两处呈现层修复是内容变更);pnpm activities:install
已验证:ACT-WORK-05 · 差异评估(part-substitution-assessment@1.1.0)装包成功。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
* ACT-WORK-05 差异评估的包内自测 —— **只准 import @sdk / @contracts / @sfeel**。
|
||||
* 案例活动没有 `run`,自测不跑回合;测的是清单本身与授权面对齐,结构照 E04 / I01。
|
||||
*
|
||||
* 五组:清单本身合规 / 台账声明 / 案例模型与清单的对齐 / 报告模版:编辑面与只读面各就各位 / 活动入口。
|
||||
* 六组:清单本身合规 / 台账声明 / 案例模型与清单的对齐 / 报告模版:编辑面与只读面各就各位 /
|
||||
* 徽标别名与值域相关的坑(2026-08-27 实测账,回归网)/ 活动入口。
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { activityManifestSchema } from '@contracts';
|
||||
@@ -237,6 +238,70 @@ describe('报告模版:编辑面与只读面各就各位', () => {
|
||||
const host = createMockHost({ contract: CONTRACT });
|
||||
await expect(host.base.interaction.pushHtml('<div><script>x()</script></div>')).rejects.toThrow(/无脚本执行权/);
|
||||
});
|
||||
|
||||
it('五张模版的外壳都是搬自目标件的 page/card,不再含 E04/I01 那套视觉底(.proto-root)的痕迹', () => {
|
||||
for (const view of VIEWS) {
|
||||
const html = viewHtml(view);
|
||||
expect(html, view).toContain('class="card-head"');
|
||||
expect(html, view).toContain('class="card-foot"');
|
||||
expect(html, view).not.toContain('proto-root');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* 2026-08-27 实测账,两条都真截图 + SQL 复核过:
|
||||
* ① **带下划线的枚举值一律显示成「待定」**——平台有两套互相打架的消毒规则:
|
||||
* `data-class-of` 投影出的 class 走 `view-render.ts` 的 `classToken`(只留 `[a-z0-9-]`,
|
||||
* 吃掉下划线,`pin_to_pin` → `v-pintopin`);基座读数名走 `engines.ts` 的 `slug`
|
||||
* (只留 `[a-z0-9_]`,吃掉连字符)。手写 `.v-pin_to_pin` 那种选择器永远匹配不上 ——
|
||||
* 这条测试把 `manifest.declarations.ledger` 里每一个「模版真的摆了徽标」的 tally 列过一遍,
|
||||
* 用与 `classToken` 一字不差的 `vtok` 算出选择器,断言对应的 CSS 规则确实存在。
|
||||
* ② **缺口表补充内容每一行都摆红「必填」,而闸只认 3 行**——`requiredWhen` 是按行不按列,
|
||||
* 静态 `class="req"` 把整列都标了必填。
|
||||
*/
|
||||
describe('徽标别名与值域相关的坑(2026-08-27 实测账,回归网)', () => {
|
||||
/** 必须与 `apps/api/src/modules/interaction/internal/domain/view-render.ts` 的 `classToken` 一字不差 */
|
||||
const vtok = (v: string): string => v.toLowerCase().replace(/[^a-z0-9-]/g, '').slice(0, 24);
|
||||
|
||||
/** 每张模版真的摆了徽标的那几列(带下划线的 tally 值最容易漏,漏了就是「待定」那次事故) */
|
||||
const BADGED: ReadonlyArray<{ view: string; domain: string; object: string; column: string }> = [
|
||||
{ view: 'p08-master-create', domain: 'parts', object: 'profile', column: 'lifecycle_status' },
|
||||
{ view: 'p10-document-create', domain: 'parts', object: 'document', column: 'document_type' },
|
||||
{ view: 'p10-document-create', domain: 'parts', object: 'document', column: 'document_status' },
|
||||
{ view: 'eval-input-list', domain: D, object: 'pn_check', column: 'exists_status' },
|
||||
{ view: 'eval-input-list', domain: D, object: 'pn_check', column: 'role' },
|
||||
{ view: 'eval-input-list', domain: D, object: 'input_gap', column: 'status' },
|
||||
{ view: 'difference-review', domain: 'substitution', object: 'relation', column: 'substitution_type' },
|
||||
{ view: 'difference-review', domain: 'substitution', object: 'relation', column: 'recommendation_level' },
|
||||
{ view: 'difference-review', domain: 'substitution', object: 'relation', column: 'risk_level' },
|
||||
{ view: 'difference-review', domain: 'substitution', object: 'difference_item', column: 'constraint_type' },
|
||||
{ view: 'difference-review', domain: 'substitution', object: 'difference_item', column: 'difference_status' },
|
||||
{ view: 'difference-review', domain: 'substitution', object: 'difference_item', column: 'risk_level' },
|
||||
{ view: 'landing-confirm', domain: 'substitution', object: 'relation', column: 'substitution_type' },
|
||||
{ view: 'landing-confirm', domain: 'substitution', object: 'relation', column: 'recommendation_level' },
|
||||
{ view: 'landing-confirm', domain: 'substitution', object: 'relation', column: 'analysis_status' },
|
||||
{ view: 'landing-confirm', domain: 'substitution', object: 'relation', column: 'risk_level' },
|
||||
{ view: 'landing-confirm', domain: 'demand', object: 'request', column: 'request_status' },
|
||||
];
|
||||
|
||||
it('每张模版摆了徽标的每个 tally 列,声明里的每个值都有对应的 v-<token> 规则(选择器是从值算出来的,不是手写的)', () => {
|
||||
for (const { view, domain, object, column } of BADGED) {
|
||||
const tally = byAddress(domain, object)?.fields.find((f) => f.name === column)?.tally ?? [];
|
||||
expect(tally.length, `${domain}.${object}.${column} 没有声明 tally`).toBeGreaterThan(0);
|
||||
const html = viewHtml(view);
|
||||
for (const value of tally) {
|
||||
expect(html, `${view}: ${domain}.${object}.${column}=${value} → 应有 .v-${vtok(value)}::after`).toContain(`.v-${vtok(value)}::after`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('缺口表补充内容按行投影必填,不是整列静态 req(requiredWhen 是按行的,不是按列的)', () => {
|
||||
const html = viewHtml('eval-input-list');
|
||||
expect(html).not.toContain('class="req" data-h="补充内容"');
|
||||
expect(html).toContain('data-class-of="status"');
|
||||
expect(html).toContain('.sup.v-gap');
|
||||
});
|
||||
});
|
||||
|
||||
describe('活动入口', () => {
|
||||
|
||||
Reference in New Issue
Block a user