fix(w05): 都齐了红条整条收起 —— 开关是案卷真数据 gate_state,不看那句话空不空

2026-08-31 用户:「没有必填字段不需要警告了,还差什么这个红色区域就不要显示」。
病根在一层机制之下:配方把 gate_reason 清成空串时,**空值按列归型不落笔**
(jsonb 合并保旧值)—— 那句话一旦写下就永远清不掉,红条亮过就灭不了。
治法:案卷加开关列 gate_state(tally open/clear,两个值都非空、两个方向都真写),
与发卡条件共用同一句判定(GATE_OPEN_WHEN 一处真源);红条挂 data-class-of=gate_state,
v-clear 整条收起(判断留在样式表);人补齐最后一格保存,quiet 拍照跑 project、
重推件,红条当场消失。空文本 :has(:empty) 兜底保留(首拍还没写过的形)。
107 包内(开关双向 + 一处真源两锚,else 侧变异红过)+ 定向 1109 测;
2.13.0 装库核过(开关/CSS/else=clear)。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-31 18:05:53 +08:00
co-authored by Claude Fable 5
parent 9412dd7aa8
commit 228739eaca
6 changed files with 42 additions and 5 deletions
@@ -801,6 +801,12 @@ describe('件① 评估输入补全', () => {
* 类似还差 1 行必填没填这种提示」—— 逐块 gapbar 撤下,点名并进顶条(同一批基座读数,
* 数值 0 的那行由样式表自己收起来)。
*/
it('都齐了红条整条收起:开关是案卷真数据 gate_state(v-clear),不看那句话空不空', () => {
expect(html).toContain('class="jc-gate-bar" data-class-of="gate_state"');
expect(html).toContain('.jc-gate-bar.v-clear{display:none}');
expect(colOf('sub_eval.case', 'gate_state')?.tally).toEqual(['open', 'clear']);
});
it('缺项点名全并进顶条「还差什么」,正文不再有逐块红带', () => {
expect(html).toContain('class="jc-gate-sub" data-class-of="parts_profile_missing"');
expect(html).toContain('data-slot="parts_profile_missing_keys"');
@@ -251,6 +251,16 @@ describe('S1 配方', () => {
expect((card?.kind === 'card' ? card.guide ?? [] : []).length).toBeGreaterThanOrEqual(3);
});
it('红条开关 gate_state 与发卡条件同一句判定(一处真源)—— gate_reason 清空不落笔,开关列两个方向都真写', () => {
const kase = acts[3];
const fields = kase?.kind === 'project' ? kase.fields : undefined;
const gs = fields?.['gate_state'] as { when?: string; then?: string; else?: string } | undefined;
const card = acts[4];
expect(gs?.then).toBe('open');
expect(gs?.else).toBe('clear');
expect(card?.kind === 'card' && card.when).toBe(gs?.when);
});
it('发卡条件与 doneWhen 是同一件事的正反面', () => {
const when = acts[4]?.kind === 'card' ? (acts[4].when ?? '') : '';
for (const reading of [
@@ -330,6 +330,14 @@ const VALIDATION_PROJECTION = {
* 两处不给 `doneWhen`:`S3_review`(人拍板且可循环)、`T_release`(签下去)——
* 判据不许摆在还欠一张卡的那一步眼前(会诱使它去凑判据,2026-08-28 用户实测逼出的立墙)。
*/
/**
* 「输入还差东西」的判定 —— 发卡条件与案卷 `gate_state` 共用同一句(一处真源):
* 条件只用基座读数(gate_reason 为空串时那一列压根不写、未绑定名参与比较会翻车)。
*/
const GATE_OPEN_WHEN =
'sub_eval_part_exists_status_found < 2 or sub_eval_part_missing > 0 or ' +
'sub_eval_part_distinct_chip_type_id > 1 or parts_profile_missing > 0 or parts_parameter_missing > 0';
export const ASSESSMENT_STEPS = [
{
item: 'S1_inputs' as const,
@@ -488,6 +496,11 @@ export const ASSESSMENT_STEPS = [
fields: {
eval_ref: { in: 'evalRef' },
chip_type_id: { lookup: 'mine:sub_eval.part', by: { role: 'original' }, take: 'chip_type_id', else: '' },
/*
* 红条的开关(2026-08-31):gate_reason 清空时按列归型不落笔(旧话留在草稿里),
* 这面开关两个值都非空、两个方向都真写 —— 件上按它收红条,与发卡条件同一句判定。
*/
gate_state: { when: GATE_OPEN_WHEN, then: 'open', else: 'clear' },
gate_reason: {
when: 'sub_eval_part_rows < 2',
then: '原料号与候选料号填成了同一个 —— 没法比出差异,请终止本单,换两个不同的料号重开。',
@@ -518,9 +531,7 @@ export const ASSESSMENT_STEPS = [
*/
{
kind: 'card' as const,
when:
'sub_eval_part_exists_status_found < 2 or sub_eval_part_missing > 0 or ' +
'sub_eval_part_distinct_chip_type_id > 1 or parts_profile_missing > 0 or parts_parameter_missing > 0',
when: GATE_OPEN_WHEN,
as: 'execution' as const,
variant: 'data' as const,
title: '数据工单 · 差异评估的输入还差几格',
@@ -54,7 +54,7 @@ export const manifest: ActivityManifest = {
activityId: ACTIVITY_ID,
name: 'ACT-WORK-05 · 差异评估',
/* 重写不是改 —— 同版重装是 no-op(静默丢失,踩过) */
packageVersion: '2.12.1',
packageVersion: '2.13.0',
category: '需求分析与方案匹配',
sdkVersion: '0.1.0-m5',
checksum: 'dev-checksum-part-substitution-assessment',
@@ -431,6 +431,13 @@ export const manifest: ActivityManifest = {
/* 与 `parts.chip_type.type_id` 同一条身份链路,同一个理由改成 text —— 见该字段的大注释 */
col({ name: 'chip_type_id', type: 'text', label: '本单锁定的芯片类型' }),
col({ name: 'gate_reason', type: 'text', label: '入参未就绪原因(件上摆的就是它)' }),
/*
* **红条的开关是真数据,不是那句话空不空**(2026-08-31 用户:「没有必填要警告了,
* 红色区域就不要显示」):gate_reason 清成空串时按列归型**不落笔**(空值=不写这一列,
* jsonb 合并保旧值)—— 那句话一旦写下就清不掉,红条永远亮着。开关列两个值都非空,
* 两个方向都写得动;件上 data-class-of 投影 v-clear 收红条,判断留在样式表。
*/
col({ name: 'gate_state', type: 'text', label: '入参闸门状态', tally: ['open', 'clear'] }),
col({ name: 'summary', type: 'text', label: '结论摘要' }),
],
},
@@ -98,7 +98,7 @@ const optionSelect = (col: string, address: string, valueCol: string, labelCol:
* `classToken` 在它上面永远算不出 v-token;显隐靠 `.jc-gate-text` 的 `data-blank` +
* CSS `:has(:empty)`(规则在 SHELL_CSS,包内测试钉着)。
*/
const gateBar = (): string => `<div class="jc-gate-bar">
const gateBar = (): string => `<div class="jc-gate-bar" data-class-of="gate_state">
<span><b>还差什么</b> —— <span class="jc-gate-text" data-slot="gate_reason" data-blank></span></span>
<span class="jc-gate-sub" data-class-of="parts_profile_missing">主数据还差 <b data-slot="parts_profile_missing"></b> 行必填 —— <span data-slot="parts_profile_missing_keys"></span></span>
<span class="jc-gate-sub" data-class-of="parts_parameter_missing">参数值还差 <b data-slot="parts_parameter_missing"></b> 行必填 —— <span data-slot="parts_parameter_missing_keys"></span></span>
@@ -149,6 +149,9 @@ i{font-style:normal}
.jc-gate-bar{display:flex;flex-direction:column;gap:5px;margin:0 0 16px;padding:11px 14px;border-radius:10px;background:var(--danger-soft);border:1px solid #f5c0ba;color:var(--danger);font-size:12.5px;line-height:1.7}
.jc-gate-bar b{font-weight:800}
.jc-gate-bar:has(.jc-gate-text:empty){display:none}
/* 都齐了就整条收起(2026-08-31 用户定)—— 开关是案卷真数据 gate_state(v-clear),
不看那句话空不空:gate_reason 清空时按列归型不落笔,旧话会一直留在草稿里 */
.jc-gate-bar.v-clear{display:none}
/* 逐块的缺项点名并进这一条(2026-08-31 用户:「页面上所有的问题都合并到最上面的还差什么」)——
与提交闸同源的基座读数,数值为 0 的那一行自己消失,只在待处理态出现 */
.jc-gate-sub{display:none;font-size:12px;color:#a03d34;padding-left:2px}