版本栏动作再分工:启停回到版本行上,新建·编辑·改名住顶条,编辑态行钮换删除

· 顶条只放**建改类**(作用于整条流/工作区):新建 · 编辑 · 改名。
    「编辑」从图卡题行迁来 —— 编辑态升成**工作区级受控状态**
    (FlowFigureBlock 的 editing 提升为受控 props):版本行的按钮语义都跟着它变,
    这不是图卡的私事。编辑中顶条示「编辑中」,收尾走画布里的 保存绘图/取消。
  · 停用/激活是**某一版**的运行时开关 —— 回到每条版本行的行尾
    (矮一号的小钮,行不发胖;行本身照旧点击切版,小钮拦冒泡)。
  · **编辑态下行钮换成删除**:正在改定义的人,手边该是版本管理,不是启停开关;
    生效版照旧禁删(先停用)。

实测:顶条 新建·编辑·改名;平时行钮 停用/激活;点编辑 → 画布进手绘、行钮变删除
(生效版灰);取消 → 行钮变回停用。五闸绿。

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 19:29:11 +08:00
co-authored by Claude Opus 5
parent af2d00a107
commit 89ea6e609c
3 changed files with 81 additions and 50 deletions
@@ -361,6 +361,8 @@ function FlowLab({
const [ask, setAsk] = useState<{ spec: AskSpec; danger?: boolean } | undefined>(undefined);
/** 改名就地开一个输入框(名字改的是整条流,不是某一版) */
const [renaming, setRenaming] = useState(false);
/** 手绘编辑态 —— 工作区级:动作条与版本行的按钮语义都跟着它变 */
const [editingGraph, setEditingGraph] = useState(false);
const canvasRef = useRef<HTMLDivElement>(null);
const nodes = graph?.nodes ?? [];
@@ -667,37 +669,26 @@ function FlowLab({
*/}
<aside className="g-vercard g-verslim">
<div className="g-vercard-h">版本</div>
{/* 动作条:四枚同款同排 —— 新建 · 激活/停用 · 改名 · 删除,作用于正看的那一版 */}
{/*
* 动作条只放**建改类**:新建 · 编辑 · 改名 —— 它们作用于整条流/工作区。
* 停用/激活是**某一版**的运行时开关,落在版本行上(编辑态换成删除:
* 正在改定义的人,手边该是版本管理,不是启停开关)。
*/}
<div className="g-veracts">
<button className="g-ver-act" onClick={askClone} disabled={cloneVersion.isPending} title="以当前版为底克隆一版">
<button className="g-ver-act" onClick={askClone} disabled={cloneVersion.isPending || editingGraph} title="以当前版为底克隆一版">
<Ico name="plus" s={10} /> 新建
</button>
{pkg.version === activeVersion ? (
<button className="g-ver-act" onClick={() => askRetire(pkg)}>
停用
</button>
) : (
<button
className="g-ver-act t-go"
/* 不闭合点不了:结构闭合 + 活动装载两面清零才亮 */
disabled={closedOf(pkg.version) !== true}
title={closedOf(pkg.version) === true ? `激活 v${pkg.version}` : '还不闭合 —— 属性面会列出差哪几条'}
onClick={() => askActivate(pkg)}
>
激活
</button>
)}
<button
className={'g-ver-act' + (editingGraph ? ' t-go' : '')}
disabled={editingGraph}
title={editingGraph ? '正在绘图 —— 用画布里的 保存绘图 / 取消 收尾' : '手工绘图:活动点/审批点/起止,拖拽连线'}
onClick={() => setEditingGraph(true)}
>
<Ico name="pen-line" s={10} /> {editingGraph ? '编辑中' : '编辑'}
</button>
<button className="g-ver-act" onClick={() => setRenaming(true)} title="改这条流的名字(所有版本同名)">
改名
</button>
<button
className="g-ver-act t-bad"
disabled={pkg.version === activeVersion || dropVersion.isPending}
title={pkg.version === activeVersion ? '生效版删不掉 —— 先停用' : `删掉 v${pkg.version}`}
onClick={() => askDrop(pkg)}
>
删除
</button>
</div>
{renaming && (
<div className="g-verrename">
@@ -719,17 +710,62 @@ function FlowLab({
const isActive = v.version === activeVersion;
const isShown = v.version === pkg.version;
return (
<button
/* 行本身可点(切到这一版看);行尾一枚小钮 —— 平时是启停,编辑态是删除 */
<div
key={v.version}
className={'g-verrow' + (isShown ? ' on' : '') + (isActive ? ' live' : '')}
role="button"
tabIndex={0}
onClick={() => onPick(v.version)}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
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>
{editingGraph ? (
<button
className="g-ver-act t-bad"
disabled={isActive || dropVersion.isPending}
title={isActive ? '生效版删不掉 —— 先停用' : `删掉 v${v.version}`}
onClick={(e) => {
e.stopPropagation();
askDrop(v);
}}
>
删除
</button>
) : isActive ? (
<button
className="g-ver-act"
onClick={(e) => {
e.stopPropagation();
askRetire(v);
}}
>
停用
</button>
) : (
<button
className="g-ver-act t-go"
/* 不闭合点不了:结构闭合 + 活动装载两面清零才亮 */
disabled={closedOf(v.version) !== true}
title={closedOf(v.version) === true ? `激活 v${v.version}` : '还不闭合 —— 属性面会列出差哪几条'}
onClick={(e) => {
e.stopPropagation();
askActivate(v);
}}
>
激活
</button>
)}
</div>
);
})}
</div>
@@ -750,7 +786,8 @@ function FlowLab({
* 图上真正生效可编辑的只有**名字** —— 不传 positions,双击就不出岗位一栏。
* 岗位、判据这些闭环要素在**节点属性**里决定;分配到人是运营的事,这里不碰。
*/
canEdit
editing={editingGraph}
onEditingChange={setEditingGraph}
onNodeClick={(nid) => setSel(nid)}
/* 边不是可编辑对象 —— 点一条线,选中**发起它的那个节点**(分支归它管) */
onEdgeClick={(edge) => setSel(edge.from)}