三面拆分⑳:属性卡图纸化重排 —— 检验单判据、索引栏拣选、明细表映射(SubAgent 设计)
frontend-design 技能专项重排流程包详情下半区三卡(仅呈现,零逻辑改动): - 激活双判据卡 → **检验单**:判据一·图编排 / 判据二·活动装载两个检验格, 各带面头状态字;全清盖检验章(编排闭合 CHK·1 / 装载齐备 CHK·2,反向微旋), 两面全清出黄铜裁定带「两面都齐 —— vN 可运行」;违规行红左线方角压回 - 节点属性卡 → **索引栏 + 明细区**:节点/边两行分度带(行首 mono 竖签带计数, 22px 方角芯片,行间虚线),下方唯一滚动层;空态改居中图纸空页 - 节点态两列手排(语义成组):身份与命名+输入+输出口 | 类型与责任+链接活动实现; 小卡按内容自高(去 250 一刀切),卡头 mono 微签,字段改**底线填写式** (mono 大写微签标签、readonly 虚线底、聚焦转钢蓝) - 边态映射卡加 mono 列头(下游字段/←/来源/口名/路径·值), 与行共用 6 列 grid,起单载荷/常量的值输入跨列合并 —— BOM 式明细表 - 钩子类名(g-pick/g-ver/g-exfacet/g-props/g-maprow)全保留; 顺手清一句过时空态文案(「节点填岗位」→ 负责人运营时分配) 三态截图核对(空态检验章/A1 两列/E7 明细表)+ 五闸退出码全过。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -105,7 +105,16 @@ function EdgeProps({ edge, onPatch }: { edge: FlowEdge; onPatch: (f: (e: FlowEdg
|
||||
/>
|
||||
</Field>
|
||||
</Prop>
|
||||
<Prop ico="git-merge" title="映射 · 载荷装配" extra="下游字段 ← 来源;必填映射齐了这条边才闭合">
|
||||
<Prop ico="git-merge" title="映射 · 载荷装配" extra="必填映射齐了这条边才闭合">
|
||||
{/* 明细表列头:行与列头同一 6 列栅格;非「上游输出口」的来源在口/路径两列上合并 */}
|
||||
<div className="g-maphead" aria-hidden="true">
|
||||
<span>下游字段</span>
|
||||
<span className="a">←</span>
|
||||
<span>来源</span>
|
||||
<span>口名</span>
|
||||
<span>路径 · 值</span>
|
||||
<span />
|
||||
</div>
|
||||
{rows.map((r, i) => {
|
||||
const known = SOURCE_KINDS.some((k) => k.k === r.source.from);
|
||||
return (
|
||||
@@ -161,7 +170,7 @@ function EdgeProps({ edge, onPatch }: { edge: FlowEdge; onPatch: (f: (e: FlowEdg
|
||||
)}
|
||||
{r.source.from === 'initiation' && (
|
||||
<input
|
||||
className="g-input mono"
|
||||
className="g-input mono wide"
|
||||
value={String(r.source['path'] ?? '')}
|
||||
placeholder="起单字段路径"
|
||||
onChange={(e) => patchRow(i, (x) => ({ ...x, source: { ...x.source, path: e.target.value } }))}
|
||||
@@ -169,7 +178,7 @@ function EdgeProps({ edge, onPatch }: { edge: FlowEdge; onPatch: (f: (e: FlowEdg
|
||||
)}
|
||||
{r.source.from === 'constant' && (
|
||||
<input
|
||||
className="g-input mono"
|
||||
className="g-input mono wide"
|
||||
value={String(r.source['value'] ?? '')}
|
||||
placeholder="常量值"
|
||||
onChange={(e) => patchRow(i, (x) => ({ ...x, source: { ...x.source, value: e.target.value } }))}
|
||||
@@ -837,57 +846,80 @@ function FlowLab({
|
||||
*/}
|
||||
<aside className="g-vercard g-exboard">
|
||||
<div className="g-vercard-h">激活 · 双判据</div>
|
||||
<div className="g-vercard-list">
|
||||
{activeReport === undefined ? (
|
||||
{activeReport === undefined ? (
|
||||
<div className="g-vercard-list g-exsheet">
|
||||
<div className="g-exboard-empty">校验中…</div>
|
||||
) : (
|
||||
(() => {
|
||||
/* 能不能激活看两面:①图编排闭合完善 ②活动装载。unknown-activity 归装载,其余归编排。 */
|
||||
const loading = activeReport.violations.filter((v) => v.kind === 'unknown-activity');
|
||||
const drawing = activeReport.violations.filter((v) => v.kind !== 'unknown-activity');
|
||||
const row = (v: (typeof activeReport.violations)[number], i: number) => {
|
||||
const nid = v.nodeId ?? pkg.edges.find((e) => e.id === v.edgeId)?.to;
|
||||
return (
|
||||
<div
|
||||
key={`${v.kind}-${i}`}
|
||||
className="g-ver g-ex"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
if (nid !== undefined) { setSel(String(nid)); setSelEdge(null); }
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && nid !== undefined) { setSel(String(nid)); setSelEdge(null); }
|
||||
}}
|
||||
>
|
||||
{nid !== undefined && <b>{String(nid)}</b>}
|
||||
<span className="msg">{v.message}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
</div>
|
||||
) : (
|
||||
(() => {
|
||||
/* 能不能激活看两面:①图编排闭合完善 ②活动装载。unknown-activity 归装载,其余归编排。 */
|
||||
const loading = activeReport.violations.filter((v) => v.kind === 'unknown-activity');
|
||||
const drawing = activeReport.violations.filter((v) => v.kind !== 'unknown-activity');
|
||||
const row = (v: (typeof activeReport.violations)[number], i: number) => {
|
||||
const nid = v.nodeId ?? pkg.edges.find((e) => e.id === v.edgeId)?.to;
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
key={`${v.kind}-${i}`}
|
||||
className="g-ver g-ex"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
onClick={() => {
|
||||
if (nid !== undefined) { setSel(String(nid)); setSelEdge(null); }
|
||||
}}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && nid !== undefined) { setSel(String(nid)); setSelEdge(null); }
|
||||
}}
|
||||
>
|
||||
{nid !== undefined && <b>{String(nid)}</b>}
|
||||
<span className="msg">{v.message}</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
/* 检验单式:两格判据面均分卡高,面体列违规行;全清的面盖检验章,两面全清出裁定带 */
|
||||
return (
|
||||
<div className="g-vercard-list g-exsheet">
|
||||
<section className="g-exface">
|
||||
<div className={'g-exfacet' + (drawing.length === 0 ? ' ok' : '')}>
|
||||
<Ico name={drawing.length === 0 ? 'circle-check' : 'triangle-alert'} s={12} />
|
||||
图编排 {drawing.length === 0 ? '闭合' : `${drawing.length} 项待补`}
|
||||
判据一 · 图编排
|
||||
<span className="st">{drawing.length === 0 ? '闭合' : `${drawing.length} 项待补`}</span>
|
||||
</div>
|
||||
{drawing.map(row)}
|
||||
<div className="g-exface-b">
|
||||
{drawing.length === 0 ? (
|
||||
<div className="g-exstamp">
|
||||
编排闭合<small>CHK·1 — GRAPH CLOSED</small>
|
||||
</div>
|
||||
) : (
|
||||
drawing.map(row)
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
<section className="g-exface">
|
||||
<div className={'g-exfacet' + (loading.length === 0 ? ' ok' : '')}>
|
||||
<Ico name={loading.length === 0 ? 'circle-check' : 'triangle-alert'} s={12} />
|
||||
活动装载 {loading.length === 0 ? '齐' : `${loading.length} 个节点未链接实现`}
|
||||
判据二 · 活动装载
|
||||
<span className="st">{loading.length === 0 ? '齐' : `${loading.length} 个未链接实现`}</span>
|
||||
</div>
|
||||
{loading.map(row)}
|
||||
{activeReport.violations.length === 0 && (
|
||||
<div className="g-exboard-empty">
|
||||
两面都齐 —— v{pkg.version} 可运行。
|
||||
{pkg.version === activeVersion ? '这一版正在生效。' : '版本行的「激活」已亮,点它即可上线。'}
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
})()
|
||||
)}
|
||||
</div>
|
||||
<div className="g-exface-b">
|
||||
{loading.length === 0 ? (
|
||||
<div className="g-exstamp">
|
||||
装载齐备<small>CHK·2 — IMPL LINKED</small>
|
||||
</div>
|
||||
) : (
|
||||
loading.map(row)
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
{activeReport.violations.length === 0 && (
|
||||
<div className="g-exverdict">
|
||||
两面都齐 —— v{pkg.version} 可运行。
|
||||
{pkg.version === activeVersion ? '这一版正在生效。' : '版本行的「激活」已亮,点它即可上线。'}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})()
|
||||
)}
|
||||
<div className="g-vercard-f">点一条定位到属性去补;两面清零,激活才亮。</div>
|
||||
</aside>
|
||||
|
||||
@@ -895,7 +927,7 @@ function FlowLab({
|
||||
<div className="g-vercard g-nodecard">
|
||||
<div className="g-vercard-h">
|
||||
节点 · 属性
|
||||
<span className="n">{dirty ? '有改动待保存' : '点节点就地填写'}</span>
|
||||
<span className={'n' + (dirty ? ' dirty' : '')}>{dirty ? '有改动待保存' : '点节点就地填写'}</span>
|
||||
<button
|
||||
className="g-ver-new"
|
||||
disabled={!dirty || save.isPending}
|
||||
@@ -913,38 +945,57 @@ function FlowLab({
|
||||
{saveErr}
|
||||
</Note>
|
||||
)}
|
||||
<div className="g-picker">
|
||||
{nodes
|
||||
.filter((n) => n.kind !== 'start' && n.kind !== 'end' && n.kind !== 'end-bad')
|
||||
.map((n) => (
|
||||
<button
|
||||
key={n.id}
|
||||
className={`g-pick t-${kindTone(n)}` + (sel === n.id ? ' on' : '')}
|
||||
onClick={() => { setSel(sel === n.id ? null : n.id); setSelEdge(null); }}
|
||||
>
|
||||
<b className="g-mono">{n.id}</b>
|
||||
{n.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{/* 边拣选条:边号立牌(E1、E2…),条件住属性面不上按钮 —— 图只定顺序,契约在属性里填 */}
|
||||
<div className="g-picker">
|
||||
{(graph?.edges ?? []).map((e, i) => (
|
||||
<button
|
||||
key={`${e.from}→${e.to}-${i}`}
|
||||
className={'g-pick t-mute' + (selEdge === i ? ' on' : '')}
|
||||
onClick={() => { setSelEdge(selEdge === i ? null : i); setSel(null); }}
|
||||
>
|
||||
<b className="g-mono">{e.code ?? `边${i + 1}`}</b>
|
||||
{e.label ?? ''}
|
||||
</button>
|
||||
))}
|
||||
{/* 索引栏 = 图框分度带:节点行 + 边行,行首竖签;固定不滚,属性区在下面自己滚 */}
|
||||
<div className="g-pickrail">
|
||||
<div className="g-pickrow">
|
||||
<span className="g-picklbl">
|
||||
节点<b>{nodes.filter((n) => n.kind !== 'start' && n.kind !== 'end' && n.kind !== 'end-bad').length}</b>
|
||||
</span>
|
||||
<div className="g-picker">
|
||||
{nodes
|
||||
.filter((n) => n.kind !== 'start' && n.kind !== 'end' && n.kind !== 'end-bad')
|
||||
.map((n) => (
|
||||
<button
|
||||
key={n.id}
|
||||
className={`g-pick t-${kindTone(n)}` + (sel === n.id ? ' on' : '')}
|
||||
onClick={() => { setSel(sel === n.id ? null : n.id); setSelEdge(null); }}
|
||||
>
|
||||
<b className="g-mono">{n.id}</b>
|
||||
{n.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
{/* 边行:边号立牌(E1、E2…),条件住属性面不上按钮 —— 图只定顺序,契约在属性里填 */}
|
||||
<div className="g-pickrow">
|
||||
<span className="g-picklbl">
|
||||
边<b>{(graph?.edges ?? []).length}</b>
|
||||
</span>
|
||||
<div className="g-picker">
|
||||
{(graph?.edges ?? []).map((e, i) => (
|
||||
<button
|
||||
key={`${e.from}→${e.to}-${i}`}
|
||||
className={'g-pick t-mute' + (selEdge === i ? ' on' : '')}
|
||||
onClick={() => { setSelEdge(selEdge === i ? null : i); setSel(null); }}
|
||||
>
|
||||
<b className="g-mono">{e.code ?? `边${i + 1}`}</b>
|
||||
{e.label ?? ''}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* key 随选中对象换 —— 换一件看,明细区滚回顶,不带上一件的滚动位 */}
|
||||
<div className="g-propsheet" key={node !== null ? `n:${node.id}` : selEdge !== null ? `e:${selEdge}` : 'blank'}>
|
||||
{node === null && selEdge === null ? (
|
||||
<Note>
|
||||
点画布或上面的拣选条选一件 —— 节点填岗位与链接实现,边填传递契约(条件 + 映射)。
|
||||
审批裁决(verdict)与放行选口(outcome)由系统注入判据,不用你声明。
|
||||
</Note>
|
||||
<div className="g-sheetempty">
|
||||
<Ico name="target" s={20} />
|
||||
<b>点画布或拣选条,选一个节点或一条边</b>
|
||||
<p>
|
||||
节点填名称与链接实现;边填传递契约(条件 + 映射)。负责人运营时分配,不在这儿。
|
||||
审批裁决(verdict)与放行选口(outcome)由系统注入判据,不用你声明。
|
||||
</p>
|
||||
</div>
|
||||
) : node === null && selEdge !== null && graph !== null ? (
|
||||
<EdgeProps
|
||||
edge={graph.edges[selEdge]!}
|
||||
@@ -957,7 +1008,10 @@ function FlowLab({
|
||||
/>
|
||||
) : node === null ? null : (
|
||||
<>
|
||||
{/* 两列手排(不用自动砌体):左列 = 身份与数据口,右列 = 类型与装载 ——
|
||||
卡数随节点种类变(审批点无装载卡),手排保证两列始终语义成组、高度可控 */}
|
||||
<div className="g-props">
|
||||
<div className="g-propcol">
|
||||
<Prop ico="tag" title="身份与命名" extra={kindLabel(node)}>
|
||||
<Field k="节点 ID" hint="编排内唯一,边映射按它寻址">
|
||||
<input className="g-input mono" value={node.id} readOnly />
|
||||
@@ -984,6 +1038,27 @@ function FlowLab({
|
||||
)}
|
||||
</Prop>
|
||||
|
||||
{node.kind === 'activity' && (
|
||||
<Prop ico="git-merge" title="输入" extra="上游边映射的落点">
|
||||
<ListEdit
|
||||
items={node.in ?? []}
|
||||
placeholder="如:客户邮件"
|
||||
onChange={(v) => patch((n) => ({ ...n, in: v.length > 0 ? [...v] : undefined }))}
|
||||
/>
|
||||
</Prop>
|
||||
)}
|
||||
{node.kind === 'activity' && (
|
||||
<Prop ico="upload" title="输出口" extra="下游边映射的源">
|
||||
<ListEdit
|
||||
items={node.out ?? []}
|
||||
placeholder="如:受理记录"
|
||||
onChange={(v) => patch((n) => ({ ...n, out: v.length > 0 ? [...v] : undefined }))}
|
||||
/>
|
||||
</Prop>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="g-propcol">
|
||||
<Prop
|
||||
ico="user-check"
|
||||
title="类型与责任"
|
||||
@@ -1022,24 +1097,6 @@ function FlowLab({
|
||||
)}
|
||||
</Prop>
|
||||
|
||||
{node.kind === 'activity' && (
|
||||
<Prop ico="git-merge" title="输入" extra="上游边映射的落点">
|
||||
<ListEdit
|
||||
items={node.in ?? []}
|
||||
placeholder="如:客户邮件"
|
||||
onChange={(v) => patch((n) => ({ ...n, in: v.length > 0 ? [...v] : undefined }))}
|
||||
/>
|
||||
</Prop>
|
||||
)}
|
||||
{node.kind === 'activity' && (
|
||||
<Prop ico="upload" title="输出口" extra="下游边映射的源">
|
||||
<ListEdit
|
||||
items={node.out ?? []}
|
||||
placeholder="如:受理记录"
|
||||
onChange={(v) => patch((n) => ({ ...n, out: v.length > 0 ? [...v] : undefined }))}
|
||||
/>
|
||||
</Prop>
|
||||
)}
|
||||
{node.kind === 'activity' && node.flavor !== 'approval' && (
|
||||
<Prop ico="boxes" title="链接活动实现" extra="装载面:编排不含实现,链接决定可运行">
|
||||
<Field k="实现" hint="活动住库;链好了这节点才算装载(激活判据之二)">
|
||||
@@ -1083,9 +1140,11 @@ function FlowLab({
|
||||
)}
|
||||
</Prop>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user