两处 UI 重做:节点属性 = 图签 + 画布同款节点芯片;邮件输入 = 台面上的一张纸信

**流程包 · 节点属性**:泛用表单换成**图签**(title block)——
  · 卡顶一枚从画布上「拎下来」的节点芯片:活动蓝 E8F1FB/4E7DAF、审批琥珀
    FBF0DC/B98432,左上角标同画布 —— 属性改的是哪一件,一眼对上,不用回图上找。
  · 字段收成图签行:76px 标签格(mono 竖签 + 格线)+ 值格,行间细实线 ——
    表格,不是表单;值格输入无框,格子本身就是边界。
  · 出边收成路由小表:带题一行(共用提示住右角:活动点「条件读本节点的输出字段」/
    审批点「条件读审批裁决 verdict」),每行 E号 → 去向 + 去向节点名,条件在值位;
    从前三条重复的长提示删成一条,空条件的语义进 placeholder(留空 = 默认兜底)。

**测试台 · 邮件输入**:表单换成**一张纸信压在暗台面上** —— 整台仪器里唯一的浅色面,
把「机器说 mono、信是外界」推到视觉结论:
  · 暖白纸面(FBFAF6)+ 纸边细线 + 投影浮起;信上全是无衬线,标签是小号暖灰。
  · 解剖照邮件写:信头三行(发件人 / 收件箱▾ / 主题加粗)→ 正文(纸上的一片空,
    不描边不描线 —— 写信不是填表,resize 手柄也摘了)→ 附件签条(虚线隔开,
    纸质小签 + 「加一个 BOM」虚框钮)。
  · 聚焦色在纸上转琥珀(纸是暖的,磷光绿留给机器侧)。
  实测:改坏主题 → 预检 0/2,改回 → 2/2 —— 灯对纸上的字照样实时。

旧 .tb-ml / .g-att 两组样式退役。五闸绿。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
2026-07-29 18:11:44 +08:00
co-authored by Claude Opus 5
parent 6cff6ab34c
commit f8f208e945
3 changed files with 217 additions and 141 deletions
@@ -419,59 +419,65 @@ function LetterForm({
}): ReactNode {
const set = <K extends keyof MailDraft>(k: K, v: MailDraft[K]): void => onChange({ ...draft, [k]: v });
return (
<div className="tb-ml">
<label className="tb-ml-r">
<em>发件人</em>
<input className="tb-ml-i" value={draft.from} onChange={(e) => set('from', e.target.value)} />
</label>
<label className="tb-ml-r">
<em>收件箱</em>
{/* 投给哪个信箱决定用哪套抽键规则 —— 选,不手打:地址错一位,预检整屏熄灯还不知道为什么 */}
<select className="tb-ml-i sel" value={channel?.address ?? ''} onChange={(e) => set('to', e.target.value)}>
{channels.map((ch) => (
<option key={ch.id} value={ch.address}>
{ch.address} —— {ch.name}
</option>
))}
</select>
</label>
<label className="tb-ml-r">
<em>主题</em>
<input className="tb-ml-i" value={draft.subject} onChange={(e) => set('subject', e.target.value)} />
</label>
<label className="tb-ml-r tall">
<em>正文</em>
<textarea className="tb-ml-i" rows={4} value={draft.body} onChange={(e) => set('body', e.target.value)} />
</label>
<div className="tb-ml-r tall">
<em>附件</em>
<div className="tb-ml-at">
{draft.attachments.map((a, i) => (
<span key={a.name} className="g-att">
<Ico name="paperclip" s={11} />
{a.name}
<i>{(a.size / 1024).toFixed(0)} KB</i>
<button
aria-label={`移除 ${a.name}`}
onClick={() => set('attachments', draft.attachments.filter((_, j) => j !== i))}
>
<Ico name="x" s={10} />
</button>
</span>
))}
<button
className="g-att-add"
onClick={() =>
set('attachments', [
...draft.attachments,
{ name: `BOM-${draft.attachments.length + 1}.xlsx`, contentType: XLSX, size: 20_480 },
])
}
>
<Ico name="plus" s={11} />
加一个 BOM
</button>
</div>
/*
* 一张**纸信**压在暗台面上 —— 整台仪器里唯一的浅色面。
* 「机器说 mono、信是外界」推到视觉结论:外界伸进仪器的那件东西,就长得像张纸。
* 解剖照邮件写:信头三行(发件人 / 收件箱 / 主题)→ 正文 → 附件签条。
* 收件箱是**选**的不是打的:投给哪个信箱决定用哪套抽键规则,地址错一位,
* 左边预检整屏熄灯还不知道为什么。
*/
<div className="tb-paper">
<div className="tb-pp-h">
<label className="tb-pp-r">
<em>发件人</em>
<input className="tb-pp-i" value={draft.from} onChange={(e) => set('from', e.target.value)} />
</label>
<label className="tb-pp-r">
<em>收件箱</em>
<select className="tb-pp-i sel" value={channel?.address ?? ''} onChange={(e) => set('to', e.target.value)}>
{channels.map((ch) => (
<option key={ch.id} value={ch.address}>
{ch.address} —— {ch.name}
</option>
))}
</select>
</label>
<label className="tb-pp-r">
<em>主题</em>
<input className="tb-pp-i strong" value={draft.subject} onChange={(e) => set('subject', e.target.value)} />
</label>
</div>
<textarea
className="tb-pp-body"
rows={5}
value={draft.body}
placeholder="正文…"
aria-label="正文"
onChange={(e) => set('body', e.target.value)}
/>
<div className="tb-pp-att">
{draft.attachments.map((a, i) => (
<span key={a.name} className="tb-att">
<Ico name="paperclip" s={11} />
{a.name}
<i>{(a.size / 1024).toFixed(0)} KB</i>
<button aria-label={`移除 ${a.name}`} onClick={() => set('attachments', draft.attachments.filter((_, j) => j !== i))}>
<Ico name="x" s={10} />
</button>
</span>
))}
<button
className="tb-att-add"
onClick={() =>
set('attachments', [
...draft.attachments,
{ name: `BOM-${draft.attachments.length + 1}.xlsx`, contentType: XLSX, size: 20_480 },
])
}
>
<Ico name="plus" s={11} />
加一个 BOM
</button>
</div>
</div>
);