From af789f546c1145a44e9dc580ab21ac232f279648 Mon Sep 17 00:00:00 2001 From: 18628386721 <18628386721@163.com> Date: Tue, 28 Jul 2026 02:49:50 +0800 Subject: [PATCH] =?UTF-8?q?UI=E2=91=A3:=E5=85=83=E7=B4=A0=E7=9B=98?= =?UTF-8?q?=E7=82=B9=E5=87=BB=E8=90=BD=E5=AD=90=E7=9C=9F=E8=83=BD=E8=90=BD?= =?UTF-8?q?=20=E2=80=94=E2=80=94=20=E5=8E=9F=E5=9E=8B=E9=87=8C=E8=BF=99?= =?UTF-8?q?=E6=9D=A1=E8=B7=AF=E6=98=AF=E5=9D=8F=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 事件流实测:click → drag.init → create.start → create.move → **create.rejected**。 create.move 要有 hover 目标才算 canExecute,而 click 到达时鼠标早已抬起, 拖拽在同一个调用栈里就被拒 —— 工具提示写着「点击后点画布落子」,一颗子也落不下。 点击这条路改为直接 modeling.createShape 到空位(撞车往下让、一列满换列); 拖拽那条路不动。实测:落子 32→33、追加自动连线 33→34、撤销回退干净。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01KeDBx2PHLavbURXB8LJJ7m --- .../src/shared/gov/flow/flow-editor-core.ts | 78 ++++++++++++++++--- 1 file changed, 67 insertions(+), 11 deletions(-) diff --git a/apps/web/src/shared/gov/flow/flow-editor-core.ts b/apps/web/src/shared/gov/flow/flow-editor-core.ts index 7f75b7b..45b0493 100644 --- a/apps/web/src/shared/gov/flow/flow-editor-core.ts +++ b/apps/web/src/shared/gov/flow/flow-editor-core.ts @@ -441,36 +441,92 @@ export const PALETTE_SPECS: Array<{ key: string; label: string; className: strin ]; class FlowPalette { - static $inject = ["palette", "create", "elementFactory", "elementRegistry", "flowEditorConfig"]; + static $inject = ["palette", "create", "elementFactory", "elementRegistry", "flowEditorConfig", "modeling", "canvas"]; private _create: Any; private _elementFactory: Any; private _elementRegistry: Any; private _config: Any; + private _modeling: Any; + private _canvas: Any; - constructor(palette: Any, create: Any, elementFactory: Any, elementRegistry: Any, flowEditorConfig: Any) { + constructor( + palette: Any, + create: Any, + elementFactory: Any, + elementRegistry: Any, + flowEditorConfig: Any, + modeling: Any, + canvas: Any, + ) { this._create = create; this._elementFactory = elementFactory; this._elementRegistry = elementRegistry; this._config = flowEditorConfig; + this._modeling = modeling; + this._canvas = canvas; palette.registerProvider(this); } + /** 造一枚待落的新节点(id 现取,避开已用与保留号) */ + private _mint(spec: NewNodeSpec): Any { + const bo = specNode(spec); + bo.id = mintNodeId(spec.kind, usedIds(this._elementRegistry), this._config.reserved); + const size = MANUAL_SIZES[spec.kind]; + return this._elementFactory.createShape({ width: size.w, height: size.h, businessObject: bo }); + } + + /** + * 点击落子的落点:视口中心起,撞车就往下让一格,直到空位。 + * + * 为什么不复用拖拽那条路:`create.start` 靠鼠标**移动**取 hover 目标才算 `canExecute`, + * 而 click 事件到达时鼠标早已抬起 —— 拖拽在同一个调用栈里就以 `create.rejected` 收场, + * 一颗子也落不下(实测)。所以点击这条路直接建,不走拖拽。 + */ + private _freeSpot(shape: Any): { x: number; y: number } { + const viewbox = this._canvas.viewbox(); + let x = snap(viewbox.x + viewbox.width / 2); + let y = snap(viewbox.y + Math.min(viewbox.height / 2, 200)); + const hits = (px: number, py: number) => + this._elementRegistry + .getAll() + .some( + (el: Any) => + !el.waypoints && + el.parent && + !el.labelTarget && + Math.abs(el.x + el.width / 2 - px) < (el.width + shape.width) / 2 + 20 && + Math.abs(el.y + el.height / 2 - py) < (el.height + shape.height) / 2 + 20, + ); + let guard = 0; + while (hits(x, y) && guard < 40) { + y = snap(y + 70); + guard += 1; + // 一列排满就换到右边一列重来,别无限往下掉出视野 + if (guard % 8 === 0) { + x = snap(x + 160); + y = snap(viewbox.y + Math.min(viewbox.height / 2, 200)); + } + } + return { x, y }; + } + getPaletteEntries(): Record { const entries: Record = {}; for (const item of PALETTE_SPECS) { - const action = (event: Any) => { - const bo = specNode(item.spec); - bo.id = mintNodeId(item.spec.kind, usedIds(this._elementRegistry), this._config.reserved); - const size = MANUAL_SIZES[item.spec.kind]; - const shape = this._elementFactory.createShape({ width: size.w, height: size.h, businessObject: bo }); - this._create.start(event, shape); - }; entries[item.key] = { group: "flow", className: item.className, - title: `${item.label} — 拖到画布或点击后点画布落子`, - action: { dragstart: action, click: action }, + title: `${item.label} — 拖到画布,或直接点击(落在空位上,再拖到想要的地方)`, + action: { + // 拖:鼠标一路带着走,落点由手定 + dragstart: (event: Any) => this._create.start(event, this._mint(item.spec)), + // 点:当场落在空位上 —— 见 `_freeSpot` 注释,点击走不通拖拽那条路 + click: () => { + const shape = this._mint(item.spec); + this._modeling.createShape(shape, this._freeSpot(shape), this._canvas.getRootElement()); + }, + }, }; } return entries;