Archived
fix(w05): 终审两条 Critical + 地基缺陷的另一半 + 文档与欠账收口
**C1 新建芯片类型会改写既有字典行(共享主数据被静默污染)**
链条逐行复核成立:件上种子行漏挂 data-col → barred 与 isSeq 双保护同时失效
(isSeq 那条是因为 Task 7b 把 type_id 从 integer 改成了 text)→ 落进「全表一致」,
而 H35 的自投影恰好保证这张表只有一行 → 人新建的类型被认成既有 type_id=1 →
确认时随范围放行把既有字典行改名并落进正式表,屏上只说「改了 1 行」。
三条各自成立的裁定叠出来的静默污染。
修法:种子行改成 disabled 的真实 <input data-col=type_id>(desk.ts 零改动),
格子里摆「由系统生成」——不能一声不吭地拿掉,那样人会以为这张档少了一格。
测试不止断言结果:另加两条用**真实 resolveRowKey** 重放病灶/修法两种 cells 形状,
直接证明因果机制。平台根治(全表一致应排除 auto 列)记 debt。
**C2 两个启动参数都选填 → 留空则整单永久无解**
gate_reason 说「在下面把两格补上」,而块① 没有加一行、也不在 gateLedgers ——
一句指向空气的指路。改为必填;第一档改口为唯一真无解的那条路径(两格填了同一个号)。
**I1 地基缺陷只修了一半:recipe.let 同样按对象键序求值**
与 fields 是一模一样的假设,同样住 jsonb 列、同样被 PG 重排;今天不炸只因 W05 那五条
互不引用,而 ADR-014 是给后续所有活动用的。新增 orderLets/letReferences
(把 {in:X} 视为依赖 —— 语义与 fields 不同,故单独实现),auto-runner 与静态审同步接入。
**I2 审核卡按「重做」照样收步推进** —— 坐实为真实平台缺陷(「卡答过了就收步」快道不看裁决),
并证明活动侧加选项救不了、chosenGuidance 那条也救不了(那是喂模型的提示词,
而这条快道存在的目的就是跳过模型)。按裁定只留复现测试,平台未改。
**I4** 同一堵墙撞第二次即发兜底执行工单交人 + 立监听(头一次软化留痕),
不再空转到 exhausted 而人无处可改。
debt +4(C1 根治 / modules→app / thead-after-tbody / data-by 落回外层),
并补写此前被两处引用却从不存在的「refersTo 传递闭包」条目。
文档:ADR-014:201「按声明顺序求值」改对(照它写配方的人会重犯本轮的地基缺陷)、
陈旧注释四处、spec 四处分叉逐条判断处置、端到端测试记录标为历史、
CLAUDE.md 纠正「新建类型端到端通了」那句(它没通过真人路)并更新版本与文档表。
包版本 2.1.9(已装,SQL 直查库内 version/required/gate_reason/视图 HTML 四项核实)。
pnpm gates 退出码 0(2476 通过 / 34 跳过);E04 92/92、I01 55/55 无回归。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
@@ -335,7 +335,12 @@ export const ASSESSMENT_STEPS = [
|
||||
eval_ref: { in: 'evalRef' },
|
||||
internal_pn: { col: 'pn' },
|
||||
role: { col: 'role' },
|
||||
/* 先取依据,再按依据判 —— 字段按声明顺序求值,算出来的进同一行的 S-FEEL 作用域 */
|
||||
/*
|
||||
* 先取依据,再按依据判 —— `exists_status` 的 `when` 引用 `found_pn_ref`,
|
||||
* `orderFields` 按这条依赖排出安全求值序(不是「声明顺序恰好写对了」,
|
||||
* 2026-08-30 治 jsonb 键序重排那次改的:见 `auto-order.ts` 头注),
|
||||
* 算出来的进同一行的 S-FEEL 作用域。
|
||||
*/
|
||||
found_pn_ref: { lookup: 'parts.profile', by: { internal_pn: { col: 'pn' } }, take: 'internal_pn', else: '' },
|
||||
exists_status: { when: 'found_pn_ref != ""', then: 'found', else: 'absent' },
|
||||
chip_type_id: { lookup: 'parts.profile', by: { internal_pn: { col: 'pn' } }, take: 'chip_type_id', else: '' },
|
||||
@@ -397,13 +402,18 @@ export const ASSESSMENT_STEPS = [
|
||||
* 修法与 ② 完全同构:按当下选中的 `chip_type_id` 把那个类型的核心参数字段清单
|
||||
* 整套原样抄回自己身上(值没变,幂等,不产生多余写入)——**新建的类型也走这条路**:
|
||||
* 人在件上新增的字段行本就已经在 `mine:parts.type_param` 里,这一笔重新投一遍
|
||||
* 原样命中、照样幂等跳过,不会覆盖人刚填的内容。`prune:true` 清掉的只是「先前选了
|
||||
* 某个类型、后来改选了另一个」时那些不再相关的陈旧行。
|
||||
* 原样命中、照样幂等跳过,不会覆盖人刚填的内容。
|
||||
*
|
||||
* **同样不带 `prune`**(与 ② 同一处修正,同一个理由:字段清单也在 ⑥ 的
|
||||
* `gateLedgers` 上,人交回数据工单那一刻就会跟着落地;已落地的行 `prune` 撤不掉,
|
||||
* 带上 `prune:true` 会在「先前定过某个类型的字段清单、后来改选了另一个类型」这条
|
||||
* 路上原样复现 H35 e2e 案例 2 那个死锁)。历史选型的陈旧字段行留在草稿层不算错,
|
||||
* 与 ② 的字典行是同一条道理。
|
||||
*/
|
||||
{
|
||||
kind: 'project' as const,
|
||||
into: 'parts.type_param',
|
||||
/* 同上一笔同一个理由,同一次修正 —— 见 ② 那笔头上的大注释,不重复贴 */
|
||||
/* 同上一笔同一个理由,同一次修正(含"不带 prune") —— 见 ② 那笔头上的大注释,不重复贴 */
|
||||
from: {
|
||||
each: 'mine:sub_eval.part',
|
||||
then: { each: 'parts.type_param', where: { type_id: { outer: 'chip_type_id' } } },
|
||||
@@ -480,6 +490,20 @@ export const ASSESSMENT_STEPS = [
|
||||
/*
|
||||
* ⑥ 案卷一行:`gate_reason` 是**摆在件上给人看**的那句话(条件判断另用基座读数,见 ⑦)。
|
||||
*
|
||||
* **第一档改口(2026-08-30 修 Critical 2)**:两个启动参数现在是**必填**
|
||||
* (`contract.input.required`),`missingRequired` 挡在起单那一刻 —— 空字符串永远
|
||||
* 到不了这一步。`sub_eval_part_rows < 2` 因此只剩**一条**能触发它的真实路径:
|
||||
* `originalInternalPn === candidateInternalPn`(人把两颗料填成了同一个号)——
|
||||
* ① 那一笔 `project` 用它们铸 `sub_eval.part` 的主键 `internal_pn`,两行撞成同一个键,
|
||||
* 第二笔 `draftWrite` 覆盖第一笔,表里只剩一行。旧文案「原料号与候选料号还没填全 ——
|
||||
* 在下面把两格补上」在这条残余路径上是**两处都错**:①两格根本不是空的(必填墙已经
|
||||
* 拦住空值),说「没填全」是失实;②块① 的行源是 `mine:sub_eval.part`、人不加行
|
||||
* (没有「+加一行」按钮),即便真缺一行也补不出来 —— 那句「补上」指向一条不存在的路
|
||||
* (墙要配路,这句连墙都不是墙)。改口直说实情与唯一出路:两颗料号是这件事的身份,
|
||||
* 填成同一个号没法比出差异,只能终止重开——这是**真正没有其他出路**的场景(与
|
||||
* 「留空」不同,「重复」没有任何一步能在本单内部把两个身份重新分开),不算失职地
|
||||
* 指错方向。
|
||||
*
|
||||
* **排在 ④⑤ 之后 —— 这是承重的次序,不是随手写的**(2026-08-29 评审 Important 修正):
|
||||
* `gate_reason` 的 when 链最深一层要读 `parts_profile_missing + parts_parameter_missing`,
|
||||
* 而这两个数是 ④⑤ 刚铺出来的骨架撑出来的基座读数。这一笔若排在 ④⑤ **之前**
|
||||
@@ -502,7 +526,7 @@ export const ASSESSMENT_STEPS = [
|
||||
chip_type_id: { lookup: 'mine:sub_eval.part', by: { role: 'original' }, take: 'chip_type_id', else: '' },
|
||||
gate_reason: {
|
||||
when: 'sub_eval_part_rows < 2',
|
||||
then: '原料号与候选料号还没填全 —— 在下面把两格补上。',
|
||||
then: '原料号与候选料号填成了同一个 —— 没法比出差异,请终止本单,换两个不同的料号重开。',
|
||||
else: {
|
||||
when: 'sub_eval_part_exists_status_absent > 0',
|
||||
then: '有料号库里查不到 —— 在下面把它的主数据建起来。',
|
||||
@@ -650,8 +674,10 @@ export const ASSESSMENT_STEPS = [
|
||||
},
|
||||
/*
|
||||
* ②→④ **`RELATION_PROJECTION` 写两遍,中间夹着差异明细** —— 不是笔误
|
||||
* (2026-08-30,真跑 e2e 第一次撞见的循环依赖,`docs/ACT-WORK-05-端到端测试记录.md`
|
||||
* 追加记录):`substitution.difference_item.substitution_relation_id` 声明了
|
||||
* (2026-08-30,真跑 e2e 第一次撞见的循环依赖;**不是**记进
|
||||
* `docs/ACT-WORK-05-端到端测试记录.md`——那份文档整篇是四步重写[2026-08-29]之前
|
||||
* 的旧九步设计,已在文档开头标为历史、没有再追加过):
|
||||
* `substitution.difference_item.substitution_relation_id` 声明了
|
||||
* `refersTo: 'substitution.relation.substitution_relation_id'`(要求引用的那一行
|
||||
* 已经真实存在),而 `substitution.relation` 的 `recommendation_level`/`risk_level`/
|
||||
* `difference_summary`/`recommendation_reason` 这几列**要读差异明细的
|
||||
|
||||
@@ -9,12 +9,12 @@
|
||||
* 「本来就该是确定的」那几步(编号抄别单、判在库判错、数据工单派给在库的料…),
|
||||
* 真正需要判断的只有一件事:**这一条参数,候选跟原厂比算不算事**。
|
||||
*
|
||||
* 这份清单是 Task 2 的产物,只动 `declarations.ledger` 与 `contract` 两段,以及 `assets`
|
||||
* 里已经不自洽的那几处(spec §8 的退役清单前移到这一步,见「计划裁定 G3」)。**案例模型
|
||||
* (`case-model.ts`)还是旧的九步、旧的 `guide` 文本** —— 下一个任务(Task 3)才整体重写成
|
||||
* spec §4 的四步(`S1_inputs` / `S2_assess` / `S3_review` / `T_release`)+ 四道哨兵 + 零 `guide`
|
||||
* (行为改由 `PlanStep.auto` 声明式配方接管)。旧剧本引用本次删掉的台账地址而导致的包内自测红,
|
||||
* 已按台账逐条 `it.skip` 并注明「Task 3 会补回」,不删测试(§12.5 铁律)。
|
||||
* **2026-08-30 更新**:这份注释原写于重写 Task 2(只动 `declarations.ledger` 与
|
||||
* `contract` 两段那一刻),彼时 `case-model.ts` 还是旧九步——**Task 3 已把它整体重写成
|
||||
* spec §4 的四步**(`S1_inputs` / `S2_assess` / `S3_review` / `T_release`)+ 四道哨兵 +
|
||||
* 零 `guide`(行为改由 `PlanStep.auto` 声明式配方接管),清单与案例模型此刻同版一致,
|
||||
* 不再有「一边新一边旧」的过渡态。`assets` 里当时已不自洽的那几处(spec §8 的退役清单,
|
||||
* 见「计划裁定 G3」)同批处理完毕。
|
||||
*
|
||||
* ## 九张台账声明(spec §3)
|
||||
* 两张新字典(`parts.chip_type` / `parts.type_param`,Task 1 已物化 + 播种)承接「约束类型按类型
|
||||
@@ -54,7 +54,7 @@ export const manifest: ActivityManifest = {
|
||||
activityId: ACTIVITY_ID,
|
||||
name: 'ACT-WORK-05 · 差异评估',
|
||||
/* 重写不是改 —— 同版重装是 no-op(静默丢失,踩过) */
|
||||
packageVersion: '2.1.8',
|
||||
packageVersion: '2.1.9',
|
||||
category: '需求分析与方案匹配',
|
||||
sdkVersion: '0.1.0-m5',
|
||||
checksum: 'dev-checksum-part-substitution-assessment',
|
||||
@@ -66,21 +66,41 @@ export const manifest: ActivityManifest = {
|
||||
version: '1.0.0',
|
||||
input: {
|
||||
/*
|
||||
* 两格,都选填(spec §4.1):库里有就选,没有就自己写一个新的 —— 启动后由第一步的
|
||||
* 配方核对存在性、缺了就派数据工单补齐。运营端启动表单渲染这两格为
|
||||
* 「选择 + 新增」下拉(`x-control: combo`),候选从 `parts.profile.internal_pn`
|
||||
* 现读(`x-options`);`x-order` 定表单次序(清单存在 jsonb 里,书写顺序到不了运营端)。
|
||||
* requestDescription / candidateInternalPns(数组)/ datasheets 三键已随重写撤下 ——
|
||||
* 这一版没有「需求主记录」「N 候选并行」「预填 datasheet」这几件事。
|
||||
* **两格,都必填**(2026-08-30 修 Critical 2,推翻 spec §4.1 原来的「都选填」)——
|
||||
* 一次评估恰是「一对一候选」,这两颗料号是这件事的**身份**,不是可以事后补的细节:
|
||||
* 库里有就选,没有就自己写一个新的(合法的新料号字符串,不是留空)。
|
||||
* 「启动后由第一步的配方核对存在性、缺了就派数据工单补齐」这句话说的是料号的**主数据**
|
||||
* (品牌/类型/参数),从来不是料号**本身**——旧措辞把两件事含糊成了一件事。
|
||||
*
|
||||
* 都选填时留空的下场(评审逐行核过的链条):S1 配方 `project into sub_eval.part` 拿
|
||||
* `originalInternalPn`/`candidateInternalPn` 铸两行的主键 `internal_pn`,两个空字符串
|
||||
* 是**同一个主键**——第二笔 `draftWrite` 覆盖第一笔,`sub_eval.part` 收成**一行**不是
|
||||
* 两行;`gate_reason` 的 `sub_eval_part_rows < 2` 当场为真,提示「在下面把两格补上」——
|
||||
* 而块① 的行源是 `mine:sub_eval.part`(人不加行,没有「+加一行」按钮),那两格根本
|
||||
* 不存在。`doneWhen` 永不成立,提交也按不下去,**唯一出路是终止重开**,且五条样例与
|
||||
* e2e 五案例都没填过空值,两条验证线都照不到这条死路。
|
||||
*
|
||||
* 改必填**不是关上「留空」这扇门就完事**——平台早有配套机制接住它(2026-07-30 立的
|
||||
* 「受理归一」:缺必填不拒单,任务落成 `awaiting-human`,一张补全卡问负责人要那几个
|
||||
* 字段,答完合并进 `data` 才转 `running`;`apps/api/src/modules/task/internal/usecase/
|
||||
* task-service.ts` 的 `missingRequired`/`askCompletion`,`__tests__/intake.test.ts`
|
||||
* 已经把这条路测得很实)。必填不是墙,是把「两颗料号是什么」这件唯一要人先说清楚的
|
||||
* 事挪到起单那一刻问清楚,不留给配方去猜一个猜不出来的空字符串。
|
||||
*
|
||||
* 运营端启动表单渲染这两格为「选择 + 新增」下拉(`x-control: combo`),候选从
|
||||
* `parts.profile.internal_pn` 现读(`x-options`);`x-order` 定表单次序(清单存在
|
||||
* jsonb 里,书写顺序到不了运营端)。requestDescription / candidateInternalPns(数组)/
|
||||
* datasheets 三键已随重写撤下 —— 这一版没有「需求主记录」「N 候选并行」「预填
|
||||
* datasheet」这几件事。
|
||||
*/
|
||||
data: {
|
||||
type: 'object',
|
||||
required: [],
|
||||
required: ['originalInternalPn', 'candidateInternalPn'],
|
||||
properties: {
|
||||
originalInternalPn: {
|
||||
type: 'string',
|
||||
title: '原内部料号',
|
||||
description: '差异评估基准料号;库里有就选,没有就自己写一个新的,启动后由第一步派数据工单补齐。',
|
||||
description: '差异评估基准料号(必填);库里有就选,没有就自己写一个新的 —— 它的主数据(品牌/类型/参数)缺了才由第一步派数据工单补齐。',
|
||||
examples: ['LM2596S-5.0'],
|
||||
'x-control': 'combo',
|
||||
'x-options': { ledger: 'parts.profile', value: 'internal_pn', label: 'internal_pn' },
|
||||
@@ -89,7 +109,7 @@ export const manifest: ActivityManifest = {
|
||||
candidateInternalPn: {
|
||||
type: 'string',
|
||||
title: '候选内部料号',
|
||||
description: '被评估的候选料号;库里有就选,没有就自己写一个新的,启动后由第一步派数据工单补齐。',
|
||||
description: '被评估的候选料号(必填);库里有就选,没有就自己写一个新的 —— 它的主数据(品牌/类型/参数)缺了才由第一步派数据工单补齐。',
|
||||
examples: ['XL2596S-5.0'],
|
||||
'x-control': 'combo',
|
||||
'x-options': { ledger: 'parts.profile', value: 'internal_pn', label: 'internal_pn' },
|
||||
@@ -180,8 +200,11 @@ export const manifest: ActivityManifest = {
|
||||
predicate: 'validation_record_rows = 1 and validation_record_missing = 0',
|
||||
},
|
||||
/*
|
||||
* **两条 L4 补了谓词**(2026-08-30,真跑 e2e 撞见的结构性发现,记入
|
||||
* `docs/ACT-WORK-05-端到端测试记录.md`)——「裁量项带谓词时谓词说了算」这条纪律
|
||||
* **两条 L4 补了谓词**(2026-08-30,真跑 e2e 撞见的结构性发现 —— **不是**记进
|
||||
* `docs/ACT-WORK-05-端到端测试记录.md`:那份文档整篇是 2026-08-27 及更早的旧
|
||||
* 九步设计,四步重写[2026-08-29]之后没有再往里追加过任何一条,已在文档开头标为
|
||||
* 历史;这条发现连同其余 2026-08-30 的都只活在这份源码注释与 `docs/debt.md` 里)。
|
||||
* 「裁量项带谓词时谓词说了算」这条纪律
|
||||
* (2026-08-02 立,`engines.ts` 的 `criterionCheck`:`settledByData`)本仓已有先例,
|
||||
* 从前 W05 这两条 L4 是裸的(无 `predicate`),而 T_release 全程 `auto` 驱动、
|
||||
* `submit` 是五个声明式动词之一(`packages/contracts/src/auto-recipe.ts`),
|
||||
@@ -505,7 +528,7 @@ export const manifest: ActivityManifest = {
|
||||
/*
|
||||
* DMN 判定表已退役:D01(逐参数四态)改由 S2 唯一一次 analyze 判定;D02(推荐等级)的规则
|
||||
* 搬进配方的 when 链(spec §3.5)—— 规则只许有一处真源,再留一份 DMN 就是同一件事两处
|
||||
* 各存一份。Task 3 会把 `steps` 换成四步、`guide` 全部清空(auto 配方接管行为)。
|
||||
* 各存一份。`steps` 已是四步、`guide` 全部清空(auto 配方接管行为,Task 3 完成)。
|
||||
*/
|
||||
businessModel: { cmmnXml: CMMN_XML, narrative: NARRATIVE, dmn: { drd: [], tables: [] }, steps: ASSESSMENT_STEPS, phases: ASSESSMENT_PHASES },
|
||||
/* 三条技能已随重写退役(全程无 agent,没有临场编排可言);skills.ts 已在 Task 10 删除 */
|
||||
|
||||
@@ -330,9 +330,32 @@ const PART_TABLE = `<div class="jc-tbl-wrap"><table id="tbl1">
|
||||
/**
|
||||
* 块②a 新建芯片类型 —— `parts.chip_type`(Task 7b,2026-08-29 补齐)。类型下拉(块①)里
|
||||
* 没有合适的,人在这里现造一个:类型名称(必填)+ 说明(建议)。`type_id` 是自增主键
|
||||
* (`auto:true`,声明改 `text` 的理由见 manifest 那条大注释)—— **不进表单**,自增列
|
||||
* 不问人(与联系人编码 `contact_code` 同一条纪律,2026-08-02 立);只读态摆 `ro()`,
|
||||
* 连孪生输入框都不给。
|
||||
* (`auto:true`,声明改 `text` 的理由见 manifest 那条大注释)—— **人不填它**,自增列
|
||||
* 不问人(与联系人编码 `contact_code` 同一条纪律,2026-08-02 立);已有行只读态摆 `ro()`。
|
||||
*
|
||||
* **种子行里 `type_id` 必须挂一个真实的(禁用、恒空)`data-col`,不能干脆不给 ——
|
||||
* 这是 2026-08-30 终审 Critical 1 的病灶与修法**(链条评审逐行核实过,详见
|
||||
* `docs/debt.md`「auto:true 又是整数型的主键」条目的姊妹坑):第一版图省事,把这一格
|
||||
* 写成纯文本 `<span>系统生成</span>`,不带 `data-col` —— 保存时 `collect()` 只认
|
||||
* `input[data-col]/select[data-col]/textarea[data-col]`,`type_id` 于是**压根不出现在
|
||||
* 回传的 `cells` 里**。而 `desk.ts` 的 `resolveRowKey` 用 `k in cells`(不是「值是否非空」)
|
||||
* 判断「这一列人碰过没有」——漏挂 `data-col` 在它眼里等同「这张表压根没有这一列」,
|
||||
* 专门为「auto 列件上摆着却空着」设计的 `barred` 保护(`(input.autos??[]).filter((k)=>k in
|
||||
* cells && ...)`)因此从未触发,退回给「全表一致」那条兜底路径。而 Task 7b 把
|
||||
* `type_id` 从 `integer` 改成 `text` 之后,「序号型主键不走全表一致」(2026-08-23 那条
|
||||
* 保护,认的是 `isSeq`)也顺带失效 —— 两条保护同时缺位,「全表一致」直接接管:
|
||||
* `parts.chip_type` 此刻**恰好一行**(Ruling H35 的自投影②保证了这一点),
|
||||
* `seen.size===1` 当场把人新建的类型判成「就是那唯一一行」,一次保存就把种子字典行
|
||||
* (如 `type_id=1` 的「DC-DC 降压稳压器」)**静默改名**——`mintable`/`autoKeys()`(真正
|
||||
* 为它设计的续号器)永远轮不到。
|
||||
* **修法**:给这一格换成一个真的 `<input data-col="type_id" disabled>`(值恒空,
|
||||
* 只显示「由系统生成」占位)——`'type_id' in cells` 从此为真、值为空,`barred` 正确
|
||||
* 命中,`resolveRowKey` 交回 `undefined`,`mintable` 才轮得到、续出一个不撞任何既有行
|
||||
* 的新号。**平台侧 `resolveRowKey` 本身一行没改**——这是活动侧绕开,`isSeq`/「全表
|
||||
* 一致」两条平台保护的根治仍记在 `docs/debt.md`(下一个建「单一主键又是 auto 列」的
|
||||
* 活动仍会踩,除非那两条也一并治了)。回归钉在 `__tests__/activity.test.ts`
|
||||
* (报告层的 markup)与 `apps/api/src/app/__tests__/view-table-edit.test.ts`
|
||||
* (真实 `resolveRowKey` 的因果机制,两种 `cells` 形状各自的结果)。
|
||||
*
|
||||
* **零行照样加得出第一行**(这是这一块存在的全部理由):`mine:parts.chip_type` 每单
|
||||
* 开局是零行,`ViewContainer.addRow` 靠克隆 `tr:last-of-type` 添新行,零行时找不到可
|
||||
@@ -381,7 +404,7 @@ const CHIP_TYPE_TABLE = `<div class="jc-tbl-wrap"><table data-edit="parts.chip_t
|
||||
</tr></tbody>
|
||||
<tbody><tr class="jc-row-static">
|
||||
<td class="rn"><span class="jc-rn-num"></span></td>
|
||||
<td><span class="jc-vcell ro">系统生成</span></td>
|
||||
<td><input class="jc-cell mono" data-col="type_id" placeholder="由系统生成" disabled></td>
|
||||
<td><input class="jc-cell" data-col="type_name" placeholder="新类型名称"></td>
|
||||
<td><input class="jc-cell" data-col="description" placeholder="这个类型是什么(供选型参考)"></td>
|
||||
</tr></tbody>
|
||||
|
||||
Reference in New Issue
Block a user