Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
151 changes: 150 additions & 1 deletion ui/src/workflow/common/shortcut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,50 @@ import { MsgSuccess, MsgError, MsgConfirm } from '@/utils/message'
import { WorkflowType } from '@/enums/application'
import { t } from '@/locales'
import { copyClick } from '@/utils/clipboard'
import { randomId } from '@/utils/common'
import { getMenuNodes, workflowModelDict } from './data'
let activeCanvasId: string | null = null
type Point = { x: number; y: number }
const lastMouse = {
x: 0,
y: 0,
hasValue: false,
}
let selected: any | null = null
const bindMousePosition = (lf: any) => {
const updateMouse = (e: MouseEvent) => {
lastMouse.x = e.clientX
lastMouse.y = e.clientY
lastMouse.hasValue = true
}

// 推荐直接监听容器,这样鼠标在节点上移动也能拿到
lf.container.addEventListener('mousemove', updateMouse)

return () => {
lf.container.removeEventListener('mousemove', updateMouse)
}
}
const bindCanvasActive = (lf: any) => {
const container = lf.container as HTMLElement
if (!container) return

// 让容器可聚焦
container.tabIndex = 0

const activate = () => {
activeCanvasId = lf.graphModel.flowId
container.focus()
}

container.addEventListener('mousedown', activate)
container.addEventListener('focus', activate)

return () => {
container.removeEventListener('mousedown', activate)
container.removeEventListener('focus', activate)
}
}
function translationNodeData(nodeData: any, distance: any) {
nodeData.x += distance
nodeData.y += distance
Expand Down Expand Up @@ -44,6 +85,8 @@ const TRANSLATION_DISTANCE = 40
let CHILDREN_TRANSLATION_DISTANCE = 40

export function initDefaultShortcut(lf: LogicFlow, graph: GraphModel) {
bindMousePosition(lf)
bindCanvasActive(lf)
const { keyboard } = lf
const {
options: { keyboard: keyboardOptions },
Expand Down Expand Up @@ -72,18 +115,124 @@ export function initDefaultShortcut(lf: LogicFlow, graph: GraphModel) {
copyClick(JSON.stringify(selected))
return false
}
// 3. 求节点包围盒
const getBounds = (nodes: any[]) => {
if (!nodes.length) {
return { minX: 0, maxX: 0, minY: 0, maxY: 0 }
}

let minX = nodes[0].x
let maxX = nodes[0].x
let minY = nodes[0].y
let maxY = nodes[0].y

for (const node of nodes) {
if (node.x < minX) minX = node.x
if (node.x > maxX) maxX = node.x
if (node.y < minY) minY = node.y
if (node.y > maxY) maxY = node.y
}

return { minX, maxX, minY, maxY }
}

// 4. 整体平移
const moveData = (data: any, dx: number, dy: number) => {
for (const node of data.nodes ?? []) {
node.x += dx
node.y += dy
}

for (const edge of data.edges ?? []) {
if (edge.startPoint) {
edge.startPoint.x += dx
edge.startPoint.y += dy
}
if (edge.endPoint) {
edge.endPoint.x += dx
edge.endPoint.y += dy
}
if (edge.text && typeof edge.text.x === 'number' && typeof edge.text.y === 'number') {
edge.text.x += dx
edge.text.y += dy
}
if (Array.isArray(edge.pointsList)) {
edge.pointsList = edge.pointsList.map((p: Point) => ({
...p,
x: p.x + dx,
y: p.y + dy,
}))
}
}
}
const resetData = (data: any) => {
const idMap = new Map<string, string>()

const getOrCreateId = (oldId: string) => {
let newId = idMap.get(oldId)
if (!newId) {
newId = randomId()
idMap.set(oldId, newId)
}
return newId
}

for (const node of data.nodes) {
node.id = getOrCreateId(node.id)
}

for (const edge of data.edges) {
const oldEdgeId = edge.id
const oldSourceNodeId = edge.sourceNodeId
const oldTargetNodeId = edge.targetNodeId

edge.id = getOrCreateId(oldEdgeId)
edge.sourceNodeId = getOrCreateId(oldSourceNodeId)
edge.targetNodeId = getOrCreateId(oldTargetNodeId)

if (typeof edge.sourceAnchorId === 'string') {
edge.sourceAnchorId = edge.sourceAnchorId.replace(oldSourceNodeId, edge.sourceNodeId)
}

if (typeof edge.targetAnchorId === 'string') {
edge.targetAnchorId = edge.targetAnchorId.replace(oldTargetNodeId, edge.targetNodeId)
}
}

return data
}

const paste_node = async (e: ClipboardEvent) => {
if (lf.graphModel.flowId !== activeCanvasId) {
return true
}
if (!keyboardOptions?.enabled) return true
if (graph.textEditElement) return true
const text = e.clipboardData?.getData('text/plain') || ''
const data = parseAndValidate(text)
selected = data
selected = resetData(data)
const workflowMode = lf.graphModel.get_provide(null, null).workflowMode
const menus = getMenuNodes(workflowMode)
const nodes = menus?.flatMap((m: any) => m.list).map((n) => n.type)

if (selected && (selected.nodes || selected.edges)) {
if (!lastMouse.hasValue) {
moveData(data, 40, 40)
} else {
// LogicFlow 文档里 getPointByClient 会把页面坐标转成画布坐标
const point = lf.graphModel.getPointByClient({
x: lastMouse.x,
y: lastMouse.y,
})
const mouseCanvasX = point.canvasOverlayPosition.x
const mouseCanvasY = point.canvasOverlayPosition.y

const { minX, maxX, minY, maxY } = getBounds(selected.nodes)
const centerX = (minX + maxX) / 2
const centerY = (minY + maxY) / 2
moveData(data, mouseCanvasX - centerX, mouseCanvasY - centerY)
}

selected.nodes = selected.nodes.filter(
(n: any) => nodes?.includes(n.type) || workflowModelDict[workflowMode](n),
)
Expand Down
Loading