Archived
版本栏三改:显示完整 · 动作统一住顶部 · 「实现」明确为链接自活动库
· 栏宽 132 → 196–224:一版一行排满「v4 生效 10 节点 · 14 边」,不再挤成三行。
· 激活/停用 · 改名 · 删除收成**顶部动作条**(右端注明作用对象 v几,
换版看,条上的钮跟着换语义);行里只剩事实,不在行内展开小钮。
改名输入就地在动作条下方展开,不再吊在卡底。
· 「实现」行改口:标签「实现 · 链接自活动库」,空选项写
「从活动库链接一个活动 —— 未链接,激活会被拦」—— 节点不含实现,
链接才算装载,这层语义直接写在控件上。
五闸绿。
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m
This commit is contained in:
@@ -521,7 +521,7 @@
|
|||||||
属性区从右侧窄栏挪到图下整行(g-wide),声明式契约细节再往下。 */
|
属性区从右侧窄栏挪到图下整行(g-wide),声明式契约细节再往下。 */
|
||||||
/* 三栏(2026-07-30):左**窄**版本条 · 中流程图 · 右节点属性。
|
/* 三栏(2026-07-30):左**窄**版本条 · 中流程图 · 右节点属性。
|
||||||
右栏用包壳绝对定位 —— 行高由中栏的图定,属性长了在卡内滚,不把图拉长。 */
|
右栏用包壳绝对定位 —— 行高由中栏的图定,属性长了在卡内滚,不把图拉长。 */
|
||||||
.g-flowlab { display: grid; grid-template-columns: 132px minmax(0, 2.5fr) minmax(320px, 1.25fr); gap: 12px; align-items: stretch; }
|
.g-flowlab { display: grid; grid-template-columns: minmax(196px, 224px) minmax(0, 2.5fr) minmax(320px, 1.25fr); gap: 12px; align-items: stretch; }
|
||||||
.g-nodewrap { position: relative; min-height: 0; }
|
.g-nodewrap { position: relative; min-height: 0; }
|
||||||
.g-nodewrap > .g-nodecard { position: absolute; inset: 0; height: auto; }
|
.g-nodewrap > .g-nodecard { position: absolute; inset: 0; height: auto; }
|
||||||
/* 版本卡:与右侧的图**等高**(grid stretch),版本多了在卡内滚,不把页面撑长。
|
/* 版本卡:与右侧的图**等高**(grid stretch),版本多了在卡内滚,不把页面撑长。
|
||||||
@@ -545,24 +545,27 @@
|
|||||||
.g-ver-new:disabled { opacity: .5; cursor: default; box-shadow: none; }
|
.g-ver-new:disabled { opacity: .5; cursor: default; box-shadow: none; }
|
||||||
.g-vercard-list { flex: 1; min-height: 0; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
|
.g-vercard-list { flex: 1; min-height: 0; overflow-y: auto; padding: 12px 14px; display: flex; flex-direction: column; gap: 8px; }
|
||||||
|
|
||||||
/* ── 窄版本条:一版一行(v几 + 状态 + 计数),动作只在**看着的那一版**上展开 ── */
|
/* ── 版本条:**动作统一住顶部**(作用于正看的这一版),行里只放事实、单行排满 ── */
|
||||||
.g-verslim .g-vercard-h { padding: 30px 10px 16px; gap: 4px; }
|
.g-verslim .g-vercard-h { padding: 30px 14px 16px; gap: 4px; }
|
||||||
.g-verslim .g-vercard-list { padding: 10px 8px; gap: 6px; }
|
.g-verslim .g-vercard-list { padding: 10px 12px; gap: 6px; }
|
||||||
.g-verrow { display: flex; flex-direction: column; border: 1px solid var(--sh-line); border-left: 3px solid var(--sh-steel); background: #fff; }
|
/* 动作条:一排小钮 + 右端注明作用对象(v几)—— 换版看,条上的钮跟着换语义 */
|
||||||
|
.g-veracts { flex: none; display: flex; align-items: center; gap: 5px; padding: 9px 14px; border-bottom: 1px solid var(--sh-line); background: #f9fafc; }
|
||||||
|
.g-veracts .g-ver-act { padding: 4px 10px; letter-spacing: .06em; }
|
||||||
|
.g-veracts .who { margin-left: auto; font: 600 9.5px/1 var(--sh-mono); letter-spacing: .1em; color: var(--sh-muted); }
|
||||||
|
.g-verrow { display: flex; align-items: baseline; gap: 8px; width: 100%; padding: 9px 11px; border: 1px solid var(--sh-line); border-left: 3px solid var(--sh-steel); background: #fff; font-family: var(--sh-mono); font-size: 10px; color: #3a4452; text-align: left; cursor: pointer; transition: border-color var(--t-fast); }
|
||||||
|
.g-verrow:hover { border-color: #aeb9c6; }
|
||||||
.g-verrow.on { border-color: #9fb0c0; background: #f2f5f8; }
|
.g-verrow.on { border-color: #9fb0c0; background: #f2f5f8; }
|
||||||
.g-verrow.live { border-left-color: var(--sh-brass); background: var(--sh-brass-wash); }
|
.g-verrow.live { border-left-color: var(--sh-brass); background: var(--sh-brass-wash); }
|
||||||
.g-verrow.live.on { border-color: #cbb98d; }
|
.g-verrow.live.on { border-color: #cbb98d; }
|
||||||
.g-verrow-h { display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 6px; padding: 7px 8px; border: 0; background: transparent; font-family: var(--sh-mono); font-size: 10px; color: #3a4452; text-align: left; cursor: pointer; }
|
.g-verrow:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }
|
||||||
.g-verrow-h > b { font-size: 12.5px; font-weight: 700; color: var(--sh-ink); }
|
.g-verrow > b { font-size: 13px; font-weight: 700; color: var(--sh-ink); }
|
||||||
.g-verrow-h .st { font-size: 9px; letter-spacing: .1em; color: var(--sh-muted); }
|
.g-verrow .st { font-size: 9px; letter-spacing: .12em; color: var(--sh-muted); }
|
||||||
.g-verrow-h .st.live { color: var(--sh-brass); font-weight: 700; }
|
.g-verrow .st.live { color: var(--sh-brass); font-weight: 700; }
|
||||||
.g-verrow-h .cnt { flex-basis: 100%; font-size: 9px; color: var(--sh-muted); }
|
.g-verrow .cnt { margin-left: auto; font-size: 9.5px; color: var(--sh-muted); white-space: nowrap; }
|
||||||
.g-verrow-a { display: flex; flex-wrap: wrap; gap: 4px; padding: 0 8px 8px; }
|
|
||||||
.g-verrow-a .g-ver-act { flex: 1 1 auto; text-align: center; padding: 3px 6px; letter-spacing: .06em; }
|
|
||||||
.g-ver-act.t-bad { color: #b0413e; border-color: #e0bcba; }
|
.g-ver-act.t-bad { color: #b0413e; border-color: #e0bcba; }
|
||||||
.g-ver-act:disabled { opacity: .45; cursor: default; }
|
.g-ver-act:disabled { opacity: .45; cursor: default; }
|
||||||
/* 改名条:名字改的是整条流,所以落在卡底、不挂在某一行上 */
|
/* 改名条:紧贴动作条下方(点「改名」就地展开) */
|
||||||
.g-verrename { flex: none; display: flex; flex-direction: column; gap: 5px; padding: 9px 10px; border-top: 1px solid var(--sh-line); background: #f9fafc; }
|
.g-verrename { flex: none; display: flex; flex-direction: column; gap: 5px; padding: 9px 14px; border-bottom: 1px solid var(--sh-line); background: #f9fafc; }
|
||||||
.g-verrename .g-input { height: 26px; font-size: 12px; }
|
.g-verrename .g-input { height: 26px; font-size: 12px; }
|
||||||
.g-verrename .t { font-family: var(--sh-mono); font-size: 8.5px; line-height: 1.7; color: var(--sh-muted); }
|
.g-verrename .t { font-family: var(--sh-mono); font-size: 8.5px; line-height: 1.7; color: var(--sh-muted); }
|
||||||
.g-ver { display: flex; align-items: center; gap: 9px; padding: 9px 11px; border: 1px solid var(--sh-line); border-left: 3px solid var(--sh-steel); background: #fff; cursor: pointer; font-family: var(--sh-mono); font-size: 11px; color: #3a4452; }
|
.g-ver { display: flex; align-items: center; gap: 9px; padding: 9px 11px; border: 1px solid var(--sh-line); border-left: 3px solid var(--sh-steel); background: #fff; cursor: pointer; font-family: var(--sh-mono); font-size: 11px; color: #3a4452; }
|
||||||
|
|||||||
@@ -661,41 +661,30 @@ function FlowLab({
|
|||||||
{/* 编排工作区:左 1/3 版本列表 + 右 2/3 画布 —— 版本收在流程内部,不平铺台面 */}
|
{/* 编排工作区:左 1/3 版本列表 + 右 2/3 画布 —— 版本收在流程内部,不平铺台面 */}
|
||||||
<Sec ico="route" title="编排工作区">
|
<Sec ico="route" title="编排工作区">
|
||||||
<div className="g-flowlab">
|
<div className="g-flowlab">
|
||||||
{/* 左栏窄:一版一行只放「v几 + 状态」;动作(激活/停用/改名/删除)只在选中行展开 */}
|
{/*
|
||||||
|
* 左栏:版本。**动作统一住顶部**(作用于正看的这一版),行里只放事实
|
||||||
|
* (v几 · 状态 · 点边计数)—— 完整显示,不在行内再展开一排小钮。
|
||||||
|
*/}
|
||||||
<aside className="g-vercard g-verslim">
|
<aside className="g-vercard g-verslim">
|
||||||
<div className="g-vercard-h">
|
<div className="g-vercard-h">
|
||||||
版本
|
版本
|
||||||
<button className="g-ver-new" onClick={askClone} disabled={cloneVersion.isPending} title="以当前版为底克隆一版">
|
<button className="g-ver-new" onClick={askClone} disabled={cloneVersion.isPending} title="以当前版为底克隆一版">
|
||||||
<Ico name="plus" s={11} />
|
<Ico name="plus" s={11} /> 新建
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div className="g-vercard-list">
|
{/* 动作条:激活/停用 · 改名 · 删除 —— 全部作用于下面列表里选中的那一版 */}
|
||||||
{versions.map((v) => {
|
<div className="g-veracts">
|
||||||
const isActive = v.version === activeVersion;
|
{pkg.version === activeVersion ? (
|
||||||
const isShown = v.version === pkg.version;
|
<button className="g-ver-act" onClick={() => askRetire(pkg)}>
|
||||||
return (
|
|
||||||
<div key={v.version} className={'g-verrow' + (isShown ? ' on' : '') + (isActive ? ' live' : '')}>
|
|
||||||
<button className="g-verrow-h" onClick={() => onPick(v.version)}>
|
|
||||||
<b>v{v.version}</b>
|
|
||||||
<span className={'st' + (isActive ? ' live' : '')}>{isActive ? '生效' : '停用'}</span>
|
|
||||||
<span className="cnt">
|
|
||||||
{v.nodes.length}点·{v.edges.length}边
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
{isShown && (
|
|
||||||
/* 动作只在**看着的那一版**上出现:窄栏放不下每行一排钮,也不该让人误点别版 */
|
|
||||||
<div className="g-verrow-a">
|
|
||||||
{isActive ? (
|
|
||||||
<button className="g-ver-act" onClick={() => askRetire(v)}>
|
|
||||||
停用
|
停用
|
||||||
</button>
|
</button>
|
||||||
) : (
|
) : (
|
||||||
<button
|
<button
|
||||||
className="g-ver-act t-go"
|
className="g-ver-act t-go"
|
||||||
/* 不闭合点不了:结构闭合 + 活动装载两面清零才亮 */
|
/* 不闭合点不了:结构闭合 + 活动装载两面清零才亮 */
|
||||||
disabled={closedOf(v.version) !== true}
|
disabled={closedOf(pkg.version) !== true}
|
||||||
title={closedOf(v.version) === true ? `激活 v${v.version}` : '还不闭合 —— 属性面会列出差哪几条'}
|
title={closedOf(pkg.version) === true ? `激活 v${pkg.version}` : '还不闭合 —— 属性面会列出差哪几条'}
|
||||||
onClick={() => askActivate(v)}
|
onClick={() => askActivate(pkg)}
|
||||||
>
|
>
|
||||||
激活
|
激活
|
||||||
</button>
|
</button>
|
||||||
@@ -705,17 +694,13 @@ function FlowLab({
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="g-ver-act t-bad"
|
className="g-ver-act t-bad"
|
||||||
disabled={isActive || dropVersion.isPending}
|
disabled={pkg.version === activeVersion || dropVersion.isPending}
|
||||||
title={isActive ? '生效版删不掉 —— 先停用' : `删掉 v${v.version}`}
|
title={pkg.version === activeVersion ? '生效版删不掉 —— 先停用' : `删掉 v${pkg.version}`}
|
||||||
onClick={() => askDrop(v)}
|
onClick={() => askDrop(pkg)}
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
</button>
|
</button>
|
||||||
</div>
|
<span className="who">v{pkg.version}</span>
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
{renaming && (
|
{renaming && (
|
||||||
<div className="g-verrename">
|
<div className="g-verrename">
|
||||||
@@ -732,6 +717,25 @@ function FlowLab({
|
|||||||
<span className="t">回车改名 · Esc 取消 —— 名字是这条流的身份,各版本同名</span>
|
<span className="t">回车改名 · Esc 取消 —— 名字是这条流的身份,各版本同名</span>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
<div className="g-vercard-list">
|
||||||
|
{versions.map((v) => {
|
||||||
|
const isActive = v.version === activeVersion;
|
||||||
|
const isShown = v.version === pkg.version;
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
key={v.version}
|
||||||
|
className={'g-verrow' + (isShown ? ' on' : '') + (isActive ? ' live' : '')}
|
||||||
|
onClick={() => onPick(v.version)}
|
||||||
|
>
|
||||||
|
<b>v{v.version}</b>
|
||||||
|
<span className={'st' + (isActive ? ' live' : '')}>{isActive ? '生效' : '停用'}</span>
|
||||||
|
<span className="cnt">
|
||||||
|
{v.nodes.length} 节点 · {v.edges.length} 边
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
|
|
||||||
<div className="g-canvas" ref={canvasRef}>
|
<div className="g-canvas" ref={canvasRef}>
|
||||||
@@ -897,14 +901,15 @@ function FlowLab({
|
|||||||
)}
|
)}
|
||||||
{node.kind === 'activity' && node.flavor !== 'approval' && (
|
{node.kind === 'activity' && node.flavor !== 'approval' && (
|
||||||
<div className="g-tbrow">
|
<div className="g-tbrow">
|
||||||
<em>实现</em>
|
{/* 实现 = **从活动库链接一个活动**:节点不含实现,链接才算装载(激活判据之二) */}
|
||||||
|
<em>实现 · 链接自活动库</em>
|
||||||
<span className="v col">
|
<span className="v col">
|
||||||
<select
|
<select
|
||||||
className="g-tbin sel"
|
className="g-tbin sel"
|
||||||
value={refAct?.activityId ?? ''}
|
value={refAct?.activityId ?? ''}
|
||||||
onChange={(e) => patch((n) => ({ ...n, act: e.target.value === '' ? undefined : e.target.value }))}
|
onChange={(e) => patch((n) => ({ ...n, act: e.target.value === '' ? undefined : e.target.value }))}
|
||||||
>
|
>
|
||||||
<option value="">未链接 —— 激活会被「活动装载」拦下</option>
|
<option value="">从活动库链接一个活动 —— 未链接,激活会被拦</option>
|
||||||
{activities.map((a) => (
|
{activities.map((a) => (
|
||||||
<option key={a.activityId} value={a.activityId}>
|
<option key={a.activityId} value={a.activityId}>
|
||||||
{a.name} · {a.activityId}
|
{a.name} · {a.activityId}
|
||||||
|
|||||||
Reference in New Issue
Block a user