Archived
版本栏动作再分工:启停回到版本行上,新建·编辑·改名住顶条,编辑态行钮换删除
· 顶条只放**建改类**(作用于整条流/工作区):新建 · 编辑 · 改名。
「编辑」从图卡题行迁来 —— 编辑态升成**工作区级受控状态**
(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:
@@ -553,7 +553,9 @@
|
|||||||
.g-veracts { flex: none; display: flex; align-items: center; gap: 6px; padding: 9px 14px; border-bottom: 1px solid var(--sh-line); background: #f9fafc; }
|
.g-veracts { flex: none; display: flex; align-items: center; gap: 6px; padding: 9px 14px; border-bottom: 1px solid var(--sh-line); background: #f9fafc; }
|
||||||
.g-veracts { flex-wrap: nowrap; }
|
.g-veracts { flex-wrap: nowrap; }
|
||||||
.g-veracts .who { margin-left: auto; font: 600 9.5px/1 var(--sh-mono); letter-spacing: .1em; color: var(--sh-muted); }
|
.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 { display: flex; align-items: center; gap: 8px; width: 100%; padding: 7px 8px 7px 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 .g-ver-act { height: 22px; padding: 0 8px; font-size: 10.5px; }
|
||||||
.g-verrow:hover { border-color: #aeb9c6; }
|
.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); }
|
||||||
@@ -563,6 +565,7 @@
|
|||||||
.g-verrow .st { font-size: 9px; letter-spacing: .12em; color: var(--sh-muted); }
|
.g-verrow .st { font-size: 9px; letter-spacing: .12em; color: var(--sh-muted); }
|
||||||
.g-verrow .st.live { color: var(--sh-brass); font-weight: 700; }
|
.g-verrow .st.live { color: var(--sh-brass); font-weight: 700; }
|
||||||
.g-verrow .cnt { margin-left: auto; font-size: 9.5px; color: var(--sh-muted); white-space: nowrap; }
|
.g-verrow .cnt { margin-left: auto; font-size: 9.5px; color: var(--sh-muted); white-space: nowrap; }
|
||||||
|
.g-verrow .cnt + .g-ver-act { margin-left: 8px; }
|
||||||
/* 改名条:紧贴动作条下方(点「改名」就地展开) */
|
/* 改名条:紧贴动作条下方(点「改名」就地展开) */
|
||||||
.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 { 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; }
|
||||||
|
|||||||
@@ -361,6 +361,8 @@ function FlowLab({
|
|||||||
const [ask, setAsk] = useState<{ spec: AskSpec; danger?: boolean } | undefined>(undefined);
|
const [ask, setAsk] = useState<{ spec: AskSpec; danger?: boolean } | undefined>(undefined);
|
||||||
/** 改名就地开一个输入框(名字改的是整条流,不是某一版) */
|
/** 改名就地开一个输入框(名字改的是整条流,不是某一版) */
|
||||||
const [renaming, setRenaming] = useState(false);
|
const [renaming, setRenaming] = useState(false);
|
||||||
|
/** 手绘编辑态 —— 工作区级:动作条与版本行的按钮语义都跟着它变 */
|
||||||
|
const [editingGraph, setEditingGraph] = useState(false);
|
||||||
const canvasRef = useRef<HTMLDivElement>(null);
|
const canvasRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const nodes = graph?.nodes ?? [];
|
const nodes = graph?.nodes ?? [];
|
||||||
@@ -667,37 +669,26 @@ function FlowLab({
|
|||||||
*/}
|
*/}
|
||||||
<aside className="g-vercard g-verslim">
|
<aside className="g-vercard g-verslim">
|
||||||
<div className="g-vercard-h">版本</div>
|
<div className="g-vercard-h">版本</div>
|
||||||
{/* 动作条:四枚同款同排 —— 新建 · 激活/停用 · 改名 · 删除,作用于正看的那一版 */}
|
{/*
|
||||||
|
* 动作条只放**建改类**:新建 · 编辑 · 改名 —— 它们作用于整条流/工作区。
|
||||||
|
* 停用/激活是**某一版**的运行时开关,落在版本行上(编辑态换成删除:
|
||||||
|
* 正在改定义的人,手边该是版本管理,不是启停开关)。
|
||||||
|
*/}
|
||||||
<div className="g-veracts">
|
<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} /> 新建
|
<Ico name="plus" s={10} /> 新建
|
||||||
</button>
|
</button>
|
||||||
{pkg.version === activeVersion ? (
|
|
||||||
<button className="g-ver-act" onClick={() => askRetire(pkg)}>
|
|
||||||
停用
|
|
||||||
</button>
|
|
||||||
) : (
|
|
||||||
<button
|
<button
|
||||||
className="g-ver-act t-go"
|
className={'g-ver-act' + (editingGraph ? ' t-go' : '')}
|
||||||
/* 不闭合点不了:结构闭合 + 活动装载两面清零才亮 */
|
disabled={editingGraph}
|
||||||
disabled={closedOf(pkg.version) !== true}
|
title={editingGraph ? '正在绘图 —— 用画布里的 保存绘图 / 取消 收尾' : '手工绘图:活动点/审批点/起止,拖拽连线'}
|
||||||
title={closedOf(pkg.version) === true ? `激活 v${pkg.version}` : '还不闭合 —— 属性面会列出差哪几条'}
|
onClick={() => setEditingGraph(true)}
|
||||||
onClick={() => askActivate(pkg)}
|
|
||||||
>
|
>
|
||||||
激活
|
<Ico name="pen-line" s={10} /> {editingGraph ? '编辑中' : '编辑'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
|
||||||
<button className="g-ver-act" onClick={() => setRenaming(true)} title="改这条流的名字(所有版本同名)">
|
<button className="g-ver-act" onClick={() => setRenaming(true)} title="改这条流的名字(所有版本同名)">
|
||||||
改名
|
改名
|
||||||
</button>
|
</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>
|
</div>
|
||||||
{renaming && (
|
{renaming && (
|
||||||
<div className="g-verrename">
|
<div className="g-verrename">
|
||||||
@@ -719,17 +710,62 @@ function FlowLab({
|
|||||||
const isActive = v.version === activeVersion;
|
const isActive = v.version === activeVersion;
|
||||||
const isShown = v.version === pkg.version;
|
const isShown = v.version === pkg.version;
|
||||||
return (
|
return (
|
||||||
<button
|
/* 行本身可点(切到这一版看);行尾一枚小钮 —— 平时是启停,编辑态是删除 */
|
||||||
|
<div
|
||||||
key={v.version}
|
key={v.version}
|
||||||
className={'g-verrow' + (isShown ? ' on' : '') + (isActive ? ' live' : '')}
|
className={'g-verrow' + (isShown ? ' on' : '') + (isActive ? ' live' : '')}
|
||||||
|
role="button"
|
||||||
|
tabIndex={0}
|
||||||
onClick={() => onPick(v.version)}
|
onClick={() => onPick(v.version)}
|
||||||
|
onKeyDown={(e) => {
|
||||||
|
if (e.key === 'Enter' || e.key === ' ') {
|
||||||
|
e.preventDefault();
|
||||||
|
onPick(v.version);
|
||||||
|
}
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<b>v{v.version}</b>
|
<b>v{v.version}</b>
|
||||||
<span className={'st' + (isActive ? ' live' : '')}>{isActive ? '生效' : '停用'}</span>
|
<span className={'st' + (isActive ? ' live' : '')}>{isActive ? '生效' : '停用'}</span>
|
||||||
<span className="cnt">
|
<span className="cnt">
|
||||||
{v.nodes.length} 节点 · {v.edges.length} 边
|
{v.nodes.length} 节点 · {v.edges.length} 边
|
||||||
</span>
|
</span>
|
||||||
|
{editingGraph ? (
|
||||||
|
<button
|
||||||
|
className="g-ver-act t-bad"
|
||||||
|
disabled={isActive || dropVersion.isPending}
|
||||||
|
title={isActive ? '生效版删不掉 —— 先停用' : `删掉 v${v.version}`}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
askDrop(v);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
删除
|
||||||
</button>
|
</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>
|
</div>
|
||||||
@@ -750,7 +786,8 @@ function FlowLab({
|
|||||||
* 图上真正生效可编辑的只有**名字** —— 不传 positions,双击就不出岗位一栏。
|
* 图上真正生效可编辑的只有**名字** —— 不传 positions,双击就不出岗位一栏。
|
||||||
* 岗位、判据这些闭环要素在**节点属性**里决定;分配到人是运营的事,这里不碰。
|
* 岗位、判据这些闭环要素在**节点属性**里决定;分配到人是运营的事,这里不碰。
|
||||||
*/
|
*/
|
||||||
canEdit
|
editing={editingGraph}
|
||||||
|
onEditingChange={setEditingGraph}
|
||||||
onNodeClick={(nid) => setSel(nid)}
|
onNodeClick={(nid) => setSel(nid)}
|
||||||
/* 边不是可编辑对象 —— 点一条线,选中**发起它的那个节点**(分支归它管) */
|
/* 边不是可编辑对象 —— 点一条线,选中**发起它的那个节点**(分支归它管) */
|
||||||
onEdgeClick={(edge) => setSel(edge.from)}
|
onEdgeClick={(edge) => setSel(edge.from)}
|
||||||
|
|||||||
@@ -8,9 +8,7 @@
|
|||||||
*
|
*
|
||||||
* `FlowFigure` 保留为只读便捷壳(任务页的流程图用它)。
|
* `FlowFigure` 保留为只读便捷壳(任务页的流程图用它)。
|
||||||
*/
|
*/
|
||||||
import { useState } from 'react';
|
|
||||||
import type { ProcessPackage } from '@contracts';
|
import type { ProcessPackage } from '@contracts';
|
||||||
import { Ico } from '@ui';
|
|
||||||
import { FlowDiagram } from './FlowDiagram.tsx';
|
import { FlowDiagram } from './FlowDiagram.tsx';
|
||||||
import { FlowEditor } from './FlowEditor.tsx';
|
import { FlowEditor } from './FlowEditor.tsx';
|
||||||
import type { FlowEdge, FlowGraph } from './flow-layout.ts';
|
import type { FlowEdge, FlowGraph } from './flow-layout.ts';
|
||||||
@@ -23,7 +21,8 @@ export function FlowFigureBlock({
|
|||||||
oneLiner,
|
oneLiner,
|
||||||
graph,
|
graph,
|
||||||
drilledIds,
|
drilledIds,
|
||||||
canEdit,
|
editing,
|
||||||
|
onEditingChange,
|
||||||
onNodeClick,
|
onNodeClick,
|
||||||
onEdgeClick,
|
onEdgeClick,
|
||||||
onEditNodeText,
|
onEditNodeText,
|
||||||
@@ -34,7 +33,12 @@ export function FlowFigureBlock({
|
|||||||
graph: FlowGraph | null;
|
graph: FlowGraph | null;
|
||||||
/** 已建立下钻内容的节点 id(删除这些节点时编辑器会二次确认) */
|
/** 已建立下钻内容的节点 id(删除这些节点时编辑器会二次确认) */
|
||||||
drilledIds: string[];
|
drilledIds: string[];
|
||||||
canEdit: boolean;
|
/**
|
||||||
|
* 编辑态**受控**(2026-07-30):「编辑」钮住版本卡顶部的动作条,不在图题上 ——
|
||||||
|
* 编辑与否是整个工作区的状态(版本行的按钮语义都跟着变),不是图卡的私事。
|
||||||
|
*/
|
||||||
|
editing: boolean;
|
||||||
|
onEditingChange: (editing: boolean) => void;
|
||||||
onNodeClick: (nodeId: string) => void;
|
onNodeClick: (nodeId: string) => void;
|
||||||
/** 点击连线选中边(属性卡随之切到该边);不传则边不可点 */
|
/** 点击连线选中边(属性卡随之切到该边);不传则边不可点 */
|
||||||
onEdgeClick?: ((edge: FlowEdge) => void) | undefined;
|
onEdgeClick?: ((edge: FlowEdge) => void) | undefined;
|
||||||
@@ -43,22 +47,9 @@ export function FlowFigureBlock({
|
|||||||
/** 手工绘图:整图保存(idMap = 本次编辑会话 原id→现id,宿主用它重键下钻内容) */
|
/** 手工绘图:整图保存(idMap = 本次编辑会话 原id→现id,宿主用它重键下钻内容) */
|
||||||
onSaveFlowGraph?: ((graph: FlowGraph, idMap: Record<string, string>) => Promise<void>) | undefined;
|
onSaveFlowGraph?: ((graph: FlowGraph, idMap: Record<string, string>) => Promise<void>) | undefined;
|
||||||
}) {
|
}) {
|
||||||
const [editing, setEditing] = useState(false);
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="view-lbl">
|
<div className="view-lbl">{editing ? '流程图 · 手工绘图' : '流程图 · 顺序'}</div>
|
||||||
流程图 · 顺序
|
|
||||||
{canEdit && onSaveFlowGraph !== undefined && !editing && (
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
className="g-ver-act"
|
|
||||||
title="手工绘图(珊格正交:活动点/审批点/起止,拖拽连线);画布内工具栏有 取消/保存绘图"
|
|
||||||
onClick={() => setEditing(true)}
|
|
||||||
>
|
|
||||||
<Ico name="pen-line" s={11} /> 编辑
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
{editing && onSaveFlowGraph !== undefined ? (
|
{editing && onSaveFlowGraph !== undefined ? (
|
||||||
<div className="hero">
|
<div className="hero">
|
||||||
<div className="cap">
|
<div className="cap">
|
||||||
@@ -70,9 +61,9 @@ export function FlowFigureBlock({
|
|||||||
drilledIds={drilledIds}
|
drilledIds={drilledIds}
|
||||||
onSave={async (next, idMap) => {
|
onSave={async (next, idMap) => {
|
||||||
await onSaveFlowGraph(next, idMap);
|
await onSaveFlowGraph(next, idMap);
|
||||||
setEditing(false);
|
onEditingChange(false);
|
||||||
}}
|
}}
|
||||||
onCancel={() => setEditing(false)}
|
onCancel={() => onEditingChange(false)}
|
||||||
/>
|
/>
|
||||||
<Legend />
|
<Legend />
|
||||||
</div>
|
</div>
|
||||||
@@ -88,7 +79,7 @@ export function FlowFigureBlock({
|
|||||||
variant="business"
|
variant="business"
|
||||||
onNodeClick={onNodeClick}
|
onNodeClick={onNodeClick}
|
||||||
onEdgeClick={onEdgeClick}
|
onEdgeClick={onEdgeClick}
|
||||||
onEditNode={canEdit ? onEditNodeText : undefined}
|
onEditNode={onEditNodeText}
|
||||||
ariaLabel={`${title}业务流程图`}
|
ariaLabel={`${title}业务流程图`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user