diff --git a/ui/src/workflow/common/shortcut.ts b/ui/src/workflow/common/shortcut.ts new file mode 100644 index 000000000..24477467f --- /dev/null +++ b/ui/src/workflow/common/shortcut.ts @@ -0,0 +1,119 @@ +import type LogicFlow from '@logicflow/core'; +import {type GraphModel} from '@logicflow/core'; +import { ElMessageBox, ElMessage } from 'element-plus' +let selected:any|null = null; + +function translationNodeData(nodeData:any, distance:any) { + nodeData.x += distance; + nodeData.y += distance; + if (nodeData.text) { + nodeData.text.x += distance; + nodeData.text.y += distance; + } + return nodeData; +} + +function translationEdgeData(edgeData:any, distance:any) { + if (edgeData.startPoint) { + edgeData.startPoint.x += distance; + edgeData.startPoint.y += distance; + } + if (edgeData.endPoint) { + edgeData.endPoint.x += distance; + edgeData.endPoint.y += distance; + } + if (edgeData.pointsList && edgeData.pointsList.length > 0) { + edgeData.pointsList.forEach((point:any) => { + point.x += distance; + point.y += distance; + }); + } + if (edgeData.text) { + edgeData.text.x += distance; + edgeData.text.y += distance; + } + return edgeData; +} + +const TRANSLATION_DISTANCE = 40; +let CHILDREN_TRANSLATION_DISTANCE = 40; + +export function initDefaultShortcut(lf: LogicFlow, graph: GraphModel) { + const { keyboard } = lf; + const { options: { keyboard: keyboardOptions } } = keyboard; + const copy_node=() => { + CHILDREN_TRANSLATION_DISTANCE = TRANSLATION_DISTANCE; + if (!keyboardOptions?.enabled) return true; + if (graph.textEditElement) return true; + const { guards } = lf.options; + const elements = graph.getSelectElements(false); + const enabledClone = guards && guards.beforeClone ? guards.beforeClone(elements) : true; + if (!enabledClone || (elements.nodes.length === 0 && elements.edges.length === 0)) { + selected = null; + return true; + } + selected = elements; + selected.nodes.forEach((node:any) => translationNodeData(node, TRANSLATION_DISTANCE)); + selected.edges.forEach((edge:any) => translationEdgeData(edge, TRANSLATION_DISTANCE)); + ElMessage.success({ + message: '已复制节点', + type: 'success', + showClose: true, + duration: 1500 + }) + return false; + } + const paste_node=() => { + if (!keyboardOptions?.enabled) return true; + if (graph.textEditElement) return true; + if (selected && (selected.nodes || selected.edges)) { + lf.clearSelectElements(); + const addElements = lf.addElements(selected, CHILDREN_TRANSLATION_DISTANCE); + if (!addElements) return true; + addElements.nodes.forEach(node => lf.selectElementById(node.id, true)); + addElements.edges.forEach(edge => lf.selectElementById(edge.id, true)); + selected.nodes.forEach((node:any) => translationNodeData(node, TRANSLATION_DISTANCE)); + selected.edges.forEach((edge:any) => translationEdgeData(edge, TRANSLATION_DISTANCE)); + CHILDREN_TRANSLATION_DISTANCE = CHILDREN_TRANSLATION_DISTANCE + TRANSLATION_DISTANCE; + } + return false; + } + const delete_node=()=>{ + const defaultOptions: Object = { + showCancelButton: true, + confirmButtonText: '确定', + cancelButtonText: '取消' + } + ElMessageBox.confirm('确定删除改节点?', defaultOptions).then(() => { + if (!keyboardOptions?.enabled) return true; + if (graph.textEditElement) return true; + const elements = graph.getSelectElements(true); + lf.clearSelectElements(); + elements.edges.forEach((edge:any) => lf.deleteEdge(edge.id)); + elements.nodes.forEach((node:any) => lf.deleteNode(node.id)); + }) + return false + } + graph.eventCenter.on('copy_node', copy_node) + graph.eventCenter.on('paste_node', copy_node) + // 复制 + keyboard.on(['cmd + c', 'ctrl + c'], copy_node); + // 粘贴 + keyboard.on(['cmd + v', 'ctrl + v'],paste_node ); + // undo + keyboard.on(['cmd + z', 'ctrl + z'], () => { + if (!keyboardOptions?.enabled) return true; + if (graph.textEditElement) return true; + lf.undo(); + return false; + }); + // redo + keyboard.on(['cmd + y', 'ctrl + y'], () => { + if (!keyboardOptions?.enabled) return true; + if (graph.textEditElement) return true; + lf.redo(); + return false; + }); + // delete + keyboard.on(['backspace'], delete_node); +} \ No newline at end of file diff --git a/ui/src/workflow/index.vue b/ui/src/workflow/index.vue index 49c661e29..654c38b2c 100644 --- a/ui/src/workflow/index.vue +++ b/ui/src/workflow/index.vue @@ -12,8 +12,40 @@ import { baseNodes } from '@/workflow/common/data' import '@logicflow/extension/lib/style/index.css' import '@logicflow/core/dist/style/index.css' import { ElMessageBox, ElMessage } from 'element-plus' +import {initDefaultShortcut} from '@/workflow/common/shortcut' const nodes: any = import.meta.glob('./nodes/**/index.ts', { eager: true }) +function translationNodeData(nodeData, distance) { + nodeData.x += distance; + nodeData.y += distance; + if (nodeData.text) { + nodeData.text.x += distance; + nodeData.text.y += distance; + } + return nodeData; +} + +function translationEdgeData(edgeData, distance) { + if (edgeData.startPoint) { + edgeData.startPoint.x += distance; + edgeData.startPoint.y += distance; + } + if (edgeData.endPoint) { + edgeData.endPoint.x += distance; + edgeData.endPoint.y += distance; + } + if (edgeData.pointsList && edgeData.pointsList.length > 0) { + edgeData.pointsList.forEach((point) => { + point.x += distance; + point.y += distance; + }); + } + if (edgeData.text) { + edgeData.text.x += distance; + edgeData.text.y += distance; + } + return edgeData; +} defineOptions({ name: 'WorkFlow' }) type ShapeItem = { @@ -490,7 +522,8 @@ const graphData = { } const lf = ref() - +const TRANSLATION_DISTANCE = 40; +let CHILDREN_TRANSLATION_DISTANCE = 40; onMounted(() => { const container: any = document.querySelector('#container') if (container) { @@ -509,40 +542,7 @@ onMounted(() => { }, keyboard: { enabled: true, - shortcuts: [ - { - keys: ['backspace'], - callback: (edge: any) => { - const defaultOptions: Object = { - showCancelButton: true, - confirmButtonText: '确定', - cancelButtonText: '取消' - } - const elements = lf.value.getSelectElements(true) - ElMessageBox.confirm('确定删除改节点?', defaultOptions).then((ok) => { - const elements = lf.value.getSelectElements(true) - lf.value.clearSelectElements() - elements.edges.forEach((edge: any) => { - lf.value.deleteEdge(edge.id) - }) - elements.nodes.forEach((node: any) => { - lf.value.deleteNode(node.id) - }) - }) - } - }, - { - keys: ['cmd + c', 'ctrl + c'], - callback: (edge: any) => { - ElMessage.success({ - message: '已复制节点', - type: 'success', - showClose: true, - duration: 1500 - }) - } - } - ] + }, isSilentMode: false, container: container @@ -553,11 +553,12 @@ onMounted(() => { strokeWidth: 1 } }) - + initDefaultShortcut(lf.value,lf.value.graphModel) lf.value.batchRegister([...Object.keys(nodes).map((key) => nodes[key].default), AppEdge]) lf.value.setDefaultEdgeType('app-edge') lf.value.render(graphData) + lf.value.graphModel.eventCenter.on('delete_edge', (id_list: Array) => { id_list.forEach((id: string) => { lf.value.deleteEdge(id)