docs(w05): 入库换版面的目标件 —— target-style.ts 头注与机械抽取的 diff 核对都指着它
CI / ci (push) Failing after 10s

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LKMZDwMbyKzjw6Hembwvet
This commit is contained in:
xcs
2026-08-30 21:27:10 +08:00
co-authored by Claude Opus 5
parent f0789a0f13
commit 1373eb4980
@@ -0,0 +1,444 @@
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>数据工单 · PN 主数据创建(类型驱动 Demo) · SS02 ACT-WORK-05 差异评估</title>
<style>
: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;
--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}
.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)}
.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 .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)}
.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)}
.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 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)}
.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.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 .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}
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)}
.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}}
</style>
</head>
<body>
<div class="page">
<div class="card">
<header class="card-head">
<div class="badge">数据</div>
<div class="tit">
<div class="kick">SS02 · ACT-WORK-05 差异评估 · 数据工单 · 类型驱动 Demo</div>
<h1>PN 主数据创建</h1>
<div class="sub">收件人:<span data-slot="meta.task.owner">FAE · 李工</span> · 关联任务:<span data-slot="meta.task.id">TASK-BT02-20260828-003</span></div>
</div>
<div class="side">
<span class="tag">数据工单</span>
<span class="step" data-slot="meta.phase">步骤 2/5 · 读取并标准化资料(读 P-08 查无触发)</span>
<span class="deadline">最迟交回:<b data-slot="card.deadline">1 个工作日</b></span>
</div>
</header>
<div class="card-bd">
<p class="zone"><b>【触发原因】</b>—— 系统查 P-08 芯片主数据无该 PN 记录(完全没有,非字段缺失)</p>
<div class="note">
<p><b>流程</b>:填 PN 名称 → 选 PN 类型(下拉首行「+新增类型」)→ 按类型自动加载该类型的核心参数名 → 逐项填值。找不到类型就现场新建:填类型名称 + 它的核心参数名,保存后参数名自动加载成输入框。</p>
<p><span class="need">AI 预填情况</span>:未提供 datasheet → 本单全手填(合法路径)。<b>纪律</b>:确认即正式入库(无草稿、无审核),确认人 = 数据责任人并留痕;只创建主数据/资料类记录。</p>
</div>
<div class="jc-editgate" id="editgate">
<span class="jc-editgate-t"><b>必填未完成</b>:PN 名称、PN 类型、全部核心参数值为必填;点「编辑」进入编辑态</span>
<button class="jc-egbtn eg-open" type="button">编辑</button>
<button class="jc-egbtn eg-cancel" type="button">取消</button>
<button class="jc-egbtn eg-save" type="button">保存</button>
<span class="gateprog"><span class="track"><span class="fill" id="gp-fill"></span></span><span class="pct" id="gp-pct">0 / 3 项</span></span>
</div>
<!-- 渲染态 -->
<div class="viewarea">
<div class="idcard">
<div class="cell">
<div class="lab">PN 名称</div>
<div class="val empty" id="v-pn">(未填写)</div>
</div>
<div class="cell">
<div class="lab">PN 类型</div>
<div class="val empty" id="v-type">(未选择)</div>
</div>
</div>
<p class="zone"><b>核心参数</b>(由 PN 类型决定参数名)</p>
<div class="tbl-wrap">
<table>
<thead><tr><th style="width:220px">参数名</th><th>参数值</th></tr></thead>
<tbody id="v-params" data-rows="in.PN核心参数">
<tr class="empty-row"><td colspan="2">未选择 PN 类型,暂无参数</td></tr>
</tbody>
</table>
</div>
</div>
<!-- 编辑态 -->
<div class="editarea">
<div class="steps">
<div class="stepbox" id="sb1">
<span class="sdot">1</span>
<div class="scard">
<div class="sh">PN 名称<span class="req-note">必填 · 内部料号</span></div>
<div class="sb">
<div class="fld"><span class="fk req">PN 名称</span><div class="fv"><input id="e-pn" data-field="PN名称" placeholder="如 SGM6132-5.0"></div></div>
</div>
</div>
</div>
<div class="stepbox" id="sb2">
<span class="sdot">2</span>
<div class="scard">
<div class="sh">PN 类型<span class="req-note">必填 · 类型决定第 3 步加载哪些核心参数</span></div>
<div class="sb">
<div class="fld"><span class="fk req">PN 类型</span><div class="fv">
<select id="e-type" data-field="PN类型">
<option value="__new__">+ 新增类型</option>
<option>DCDC 降压稳压器</option>
<option>LDO 线性稳压器</option>
<option>运算放大器</option>
</select>
<div class="hint">类型库里没有的类型,选首行「+ 新增类型」现场定义。</div>
</div></div>
<div class="newtype" id="newtype">
<h4>新增 PN 类型</h4>
<div class="fld"><span class="fk req">类型名称</span><div class="fv"><input id="nt-name" data-field="新类型名称" placeholder="如 负载开关"></div></div>
<div class="fld" style="margin-top:10px"><span class="fk req">核心参数名</span><div class="fv">
<div id="nt-params"></div>
<div class="nt-actions">
<button type="button" class="btn" id="nt-addrow">+ 加一行参数名</button>
<button type="button" class="btn primary" id="nt-save">保存类型并加载参数</button>
</div>
<div class="hint">保存后:类型进入下拉库(本单生效),下方自动加载这些参数名的输入框。</div>
</div></div>
</div>
</div>
</div>
</div>
<div class="stepbox" id="sb3">
<span class="sdot">3</span>
<div class="scard">
<div class="sh">核心参数值<span class="req-note" id="pv-note">先选类型</span></div>
<div class="sb">
<div class="pv-empty" id="pv-empty">已在第 2 步选择类型后,这里按类型列出核心参数输入框</div>
<div class="pvgrid" id="pv-grid"></div>
</div>
</div>
</div>
</div>
</div>
<div class="flow"><b>审批 / 提交:</b>本工单只承载内容;「确认」请在对话区的【数据卡】中进行。<b>确认后去向:</b>PN 主数据正式入库 P-08(无草稿、无审核),活动从断点续跑;本单不复用。<b>留痕:</b>确认人 / 本单编号 / 所属活动随记录入库。</div>
</div>
<footer class="card-foot">
<span>SS02 · ACT-WORK-05 差异评估 · 数据工单 · PN 主数据创建(类型驱动 Demo)</span>
<span style="font-family:var(--mono)">工单类型:数据 · <span data-slot="card.id">WO-ACT-WORK-05-D02</span></span>
</footer>
</div>
</div>
<script>
(function(){
// ===== 类型库:类型名 → 核心参数名列表 =====
var PN_TYPES = {
"DCDC 降压稳压器": ["输入电压范围","输出电压","输出电流","开关频率","静态电流","工作温度","使能阈值","反馈网络"],
"LDO 线性稳压器": ["输入电压范围","输出电压","输出电流","压差电压","静态电流","工作温度"],
"运算放大器": ["供电电压范围","增益带宽积","压摆率","输入失调电压","静态电流","工作温度"]
};
var BUILTIN = {"DCDC 降压稳压器":1,"LDO 线性稳压器":1,"运算放大器":1};
var body = document.body;
var gate = document.getElementById('editgate');
var ePn = document.getElementById('e-pn');
var eType = document.getElementById('e-type');
var newtype = document.getElementById('newtype');
var ntName = document.getElementById('nt-name');
var ntParams = document.getElementById('nt-params');
var pvGrid = document.getElementById('pv-grid');
var pvEmpty = document.getElementById('pv-empty');
var pvNote = document.getElementById('pv-note');
var gpFill = document.getElementById('gp-fill');
var gpPct = document.getElementById('gp-pct');
var values = {}; // 参数名 → 值(编辑态暂存,切类型时保留同名值)
var snap = null; // 取消用快照
// ===== 必填完成度 =====
function progress(){
var total = 2, done = 0; // PN 名称 + PN 类型
if(ePn.value.trim()) done++;
var names = (eType.value && eType.value !== '__new__') ? PN_TYPES[eType.value] : null;
if(names){ done++; total += names.length; names.forEach(function(pn){ if(values[pn] && values[pn].trim()) done++; }); }
var pct = total ? Math.round(done/total*100) : 0;
gpFill.style.width = pct + '%';
gpPct.textContent = done + ' / ' + total + ' 项';
document.getElementById('sb1').classList.toggle('done', !!ePn.value.trim());
document.getElementById('sb2').classList.toggle('done', !!names);
document.getElementById('sb3').classList.toggle('done', !!names && names.every(function(pn){ return values[pn] && values[pn].trim(); }));
}
// ===== 编辑态:参数值区渲染 =====
function clearParams(msg, note){
pvGrid.innerHTML = '';
pvEmpty.textContent = msg;
pvEmpty.style.display = 'block';
pvNote.textContent = note || '待类型确定';
}
function renderParams(typeName){
pvGrid.innerHTML = '';
var names = PN_TYPES[typeName];
if(!names){ clearParams('先选类型','先选类型'); progress(); return; }
pvEmpty.style.display='none';
pvNote.textContent = typeName + ' · ' + names.length + ' 项核心参数';
names.forEach(function(pn){
var div = document.createElement('div');
div.className = 'pv';
div.innerHTML = '<div class="pn2"></div><input data-field="参数值" placeholder="填写 ' + pn + '">';
div.querySelector('.pn2').textContent = pn;
var inp = div.querySelector('input');
inp.value = values[pn] || '';
inp.addEventListener('input', function(){ values[pn] = inp.value; progress(); });
pvGrid.appendChild(div);
});
progress();
}
// 类型下拉:选中即加载参数;选「+新增类型」展开新建面板并清空参数区(等保存后按新类型重渲染)
eType.addEventListener('change', function(){
if(eType.value === '__new__'){
newtype.classList.add('show'); ntName.focus();
clearParams('正在新增类型——保存后这里按新类型的参数名加载输入框');
progress();
return;
}
newtype.classList.remove('show');
renderParams(eType.value);
});
ePn.addEventListener('input', progress);
// 新增类型:参数名行动态增删
function addNtRow(v){
var row = document.createElement('div');
row.className = 'prow';
row.innerHTML = '<input data-field="新类型参数名" placeholder="参数名,如 导通电阻"><button type="button" class="del" title="删除">×</button>';
row.querySelector('input').value = v || '';
row.querySelector('.del').addEventListener('click', function(){ row.remove(); });
ntParams.appendChild(row);
}
document.getElementById('nt-addrow').addEventListener('click', function(){ addNtRow(''); });
// 保存类型:入库(本单生效)→ 进下拉并选中 → 加载参数名
document.getElementById('nt-save').addEventListener('click', function(){
var name = ntName.value.trim();
var names = Array.prototype.map.call(ntParams.querySelectorAll('input'), function(i){ return i.value.trim(); }).filter(Boolean);
if(!name){ ntName.focus(); return; }
if(names.length === 0){ addNtRow(''); return; }
PN_TYPES[name] = names;
var opt = document.createElement('option');
opt.textContent = name; opt.value = name;
eType.insertBefore(opt, eType.children[1]); // 首行固定为「+新增类型」,新类型插到其后
eType.value = name;
newtype.classList.remove('show');
renderParams(name);
});
// ===== 渲染态回写 =====
function paint(){
var vPn = document.getElementById('v-pn');
var vType = document.getElementById('v-type');
vPn.textContent = ePn.value.trim() || '(未填写)';
vPn.classList.toggle('empty', !ePn.value.trim());
vType.innerHTML = '';
if(eType.value && eType.value !== '__new__'){
var pill = document.createElement('span'); pill.className = 'tpill'; pill.textContent = eType.value;
vType.appendChild(pill);
if(!BUILTIN[eType.value]){ var s=document.createElement('span'); s.className='st new'; s.textContent='本单新建类型'; vType.appendChild(s); }
vType.classList.remove('empty');
} else { vType.textContent='(未选择)'; vType.classList.add('empty'); }
var tb = document.getElementById('v-params');
tb.innerHTML = '';
var names = PN_TYPES[eType.value] || [];
if(names.length === 0){
tb.innerHTML = '<tr class="empty-row"><td colspan="2">未选择 PN 类型,暂无参数</td></tr>';
} else {
names.forEach(function(pn){
var tr = document.createElement('tr');
tr.setAttribute('data-field','参数名');
var val = values[pn] && values[pn].trim() ? values[pn] : '(未填写)';
tr.innerHTML = '<td><b></b></td><td class="mono"></td>';
tr.children[0].querySelector('b').textContent = pn;
tr.children[1].textContent = val;
if(val === '(未填写)'){ tr.children[1].style.color = 'var(--ink-4)'; tr.children[1].classList.remove('mono'); }
tb.appendChild(tr);
});
}
}
// ===== 编辑闸 =====
gate.querySelector('.eg-open').addEventListener('click', function(){
snap = { pn: ePn.value, type: eType.value, values: JSON.parse(JSON.stringify(values)) };
body.classList.add('editing'); gate.classList.add('on');
if(eType.value && eType.value !== '__new__'){ renderParams(eType.value); }
else { clearParams('先选类型','先选类型'); }
progress();
});
gate.querySelector('.eg-cancel').addEventListener('click', function(){
if(snap){ ePn.value = snap.pn; eType.value = snap.type; values = snap.values; }
newtype.classList.remove('show');
body.classList.remove('editing'); gate.classList.remove('on');
progress();
});
gate.querySelector('.eg-save').addEventListener('click', function(){
paint();
body.classList.remove('editing'); gate.classList.remove('on');
});
// 初始:新增类型面板预置 3 行参数名输入
addNtRow(''); addNtRow(''); addNtRow('');
progress();
})();
</script>
</body>
</html>