feat: 添加复制节点

This commit is contained in:
zhanghao 2026-08-04 10:08:57 +08:00
parent 05f4f007a7
commit a3b3f7a1c3

View File

@ -193,7 +193,8 @@
v-if="property.componentType === 'number'"
v-model="property.input"
:min="0"
:controls="false"
:max="property.max || Infinity"
:step="property.step || 1"
:step-strictly="true"
placeholder="请输入"
clearable
@ -880,6 +881,7 @@ const nodeAction = ref("");
const singleNodeExecution = (e) => {
const { name, nodeType, nodeParams, action } = e.detail;
console.log('nodeParams', nodeParams)
nodeName.value = name;
visible.value = true;
nodeAction.value = action;
@ -932,11 +934,80 @@ const clearRun = () => {
totalRunningTime.value = 0;
}
const handleDeleteKey = (event) => {
const activeElement = document.activeElement;
if (["INPUT", "TEXTAREA"].includes(activeElement?.tagName) || activeElement?.isContentEditable) return;
const NODE_PASTE_OFFSET = 40;
const NON_COPYABLE_NODE_TYPES = new Set(["start", "end", "selectArea"]);
let copiedNodes = [];
let pasteCount = 0;
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "z") {
const isTextEditing = (event) => {
const element = event.target || document.activeElement;
return ["INPUT", "TEXTAREA", "SELECT"].includes(element?.tagName)
|| element?.isContentEditable;
};
const copySelectedNodes = (event) => {
const nodes = lf.getSelectElements().nodes.filter(
(node) => !NON_COPYABLE_NODE_TYPES.has(node.type),
);
if (!nodes.length) return;
event.preventDefault();
copiedNodes = JSON.parse(JSON.stringify(nodes));
pasteCount = 0;
};
const pasteCopiedNodes = (event) => {
if (!copiedNodes.length || flowStore.disableForm) return;
event.preventDefault();
pasteCount += 1;
const offset = NODE_PASTE_OFFSET * pasteCount;
const pastedNodes = copiedNodes.map((sourceNode) => {
const properties = JSON.parse(JSON.stringify(sourceNode.properties || {}));
const parentId = properties.parentId;
delete properties.parentId;
const newNode = lf.addNode({
type: sourceNode.type,
x: Number(sourceNode.x || 0) + offset,
y: Number(sourceNode.y || 0) + offset,
properties,
});
if (sourceNode.type === "branch" && Array.isArray(properties.anchor)) {
const oldElseId = `${sourceNode.id}_else`;
const newElseId = `${newNode.id}_else`;
const anchors = properties.anchor.map((anchor) => (
anchor.id === oldElseId ? { ...anchor, id: newElseId } : anchor
));
lf.setProperties(newNode.id, { ...properties, anchor: anchors });
}
if (parentId && lf.getNodeModelById(parentId)) {
lf.addToGroup(parentId, newNode.id, { x: newNode.x, y: newNode.y });
}
return newNode;
});
lf.clearSelectElements();
if (pastedNodes.length === 1) lf.selectElementById(pastedNodes[0].id);
};
const handleKeyboardShortcut = (event) => {
if (isTextEditing(event)) return;
const isModifierKey = event.ctrlKey || event.metaKey;
const key = event.key.toLowerCase();
if (isModifierKey && key === "c") {
if (!flowStore.disableForm) copySelectedNodes(event);
return;
}
if (isModifierKey && key === "v") {
pasteCopiedNodes(event);
return;
}
if (isModifierKey && key === "z") {
if (flowStore.disableForm) return;
event.preventDefault();
if (event.shiftKey) lf.redo();
@ -972,7 +1043,7 @@ onMounted(() => {
initFlow();
document.addEventListener("singleNodeExecution", singleNodeExecution);
registerBeforeUnload();
window.addEventListener("keydown", handleDeleteKey);
window.addEventListener("keydown", handleKeyboardShortcut);
onFlowEvent("openParamsDrawer", (node) => {
showParamsDrawer.value = true;
@ -1024,7 +1095,7 @@ onUnmounted(() => {
if (window.lf === lf) delete window.lf;
window.removeEventListener("beforeunload", handleBeforeUnload);
window.removeEventListener("keydown", handleDeleteKey);
window.removeEventListener("keydown", handleKeyboardShortcut);
document.removeEventListener("singleNodeExecution", singleNodeExecution);
});
</script>