添加编辑器
parent
f762f71144
commit
055f3847c2
@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: 100vh">
|
||||
<div class="top"></div>
|
||||
<div class="left">
|
||||
<el-card
|
||||
class="moduleCard"
|
||||
shadow="never"
|
||||
:draggable="true"
|
||||
@dragstart="onDragStart($event, 'line', {})"
|
||||
:style="{ display: 'inline-block', margin: '0 4px 4px 0' }"
|
||||
:body-style="{ padding: '4px 0' }"
|
||||
>
|
||||
<template #header>
|
||||
<div style="position: absolute; top: 50%; transform: translateY(-50%); width: 64px; height: 1px; background-color: #0002"></div>
|
||||
</template>
|
||||
<div class="moduleText">线</div>
|
||||
</el-card>
|
||||
<el-card
|
||||
class="moduleCard"
|
||||
shadow="never"
|
||||
:draggable="true"
|
||||
@dragstart="onDragStart($event, 'circuitComponent', { type: 1 })"
|
||||
:style="{ display: 'inline-block', margin: '0 4px 4px 0' }"
|
||||
:body-style="{ padding: '4px 0' }"
|
||||
>
|
||||
<template #header>
|
||||
<el-image style="width: 100%; height: 100%" :src="img1" fit="contain" />
|
||||
</template>
|
||||
<div class="moduleText">元件1</div>
|
||||
</el-card>
|
||||
<el-card
|
||||
class="moduleCard"
|
||||
shadow="never"
|
||||
:draggable="true"
|
||||
@dragstart="onDragStart($event, 'circuitComponent', { type: 2 })"
|
||||
:style="{ display: 'inline-block', margin: '0 4px 4px 0' }"
|
||||
:body-style="{ padding: '4px 0' }"
|
||||
>
|
||||
<template #header>
|
||||
<el-image style="width: 100%; height: 100%" :src="img2" fit="contain" />
|
||||
</template>
|
||||
<div class="moduleText">元件2</div>
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="right" @drop="onDrop">
|
||||
<VueFlow
|
||||
class="flow"
|
||||
id="flowA"
|
||||
:min-zoom="0.01"
|
||||
ref="flowRef"
|
||||
:snapToGrid="isSnapToGrid"
|
||||
:panOnDrag="false"
|
||||
:zoomOnScroll="false"
|
||||
:autoPanSpeed="0"
|
||||
:snapGrid="[10, 10]"
|
||||
v-model:nodes="nodes"
|
||||
v-model:edges="edges"
|
||||
fit-view-on-init
|
||||
default-marker-color="#409EFF"
|
||||
@node-click="logEvent('click', $event)"
|
||||
@pane-click="logEvent('paneClick', $event)"
|
||||
@node-drag-start="logEvent('nodeDrag', $event)"
|
||||
@node-contextMenu="logEvent('contextmenu', $event)"
|
||||
@dragover="onDragOver"
|
||||
>
|
||||
<Background :size="1" :gap="20" pattern-color="#BDBDBD" style="background-color: #fff" />
|
||||
|
||||
<template #node-circuitComponent="props">
|
||||
<CircuitComponentNode v-bind="props" />
|
||||
</template>
|
||||
<template #node-line="props">
|
||||
<LineNode v-bind="props" />
|
||||
</template>
|
||||
</VueFlow>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import img1 from '@/assets/electronicComponents/1.png';
|
||||
import img2 from '@/assets/electronicComponents/2.png';
|
||||
import CircuitComponentNode from './nodes/circuitComponent.vue';
|
||||
import LineNode from './nodes/line.vue';
|
||||
import { MarkerType, useVueFlow, VueFlow } from '@vue-flow/core';
|
||||
import { Background } from '@vue-flow/background';
|
||||
import tool from './tool.js';
|
||||
|
||||
const nodes = ref([]);
|
||||
const edges = ref([]);
|
||||
const isSnapToGrid = ref(true);
|
||||
|
||||
const { onDragStart, onDrop, onDragOver } = tool();
|
||||
const { addEdges, updateNode, removeNodes, addNodes } = useVueFlow({ id: 'flowA' });
|
||||
|
||||
const logEvent = async (eventname, event) => {
|
||||
switch (eventname) {
|
||||
case 'paneClick':
|
||||
break;
|
||||
case 'click':
|
||||
break;
|
||||
case 'nodeDrag':
|
||||
break;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.top{
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
}
|
||||
.left {
|
||||
display: inline-block;
|
||||
width: 200px;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
|
||||
.moduleCard {
|
||||
width: calc(50% - 4px);
|
||||
display: inline-block;
|
||||
height: 150px;
|
||||
|
||||
:deep(.el-card__header) {
|
||||
width: 100%;
|
||||
height: 100px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
:deep(.el-card__body) {
|
||||
padding: 0 20px 0 20px !important;
|
||||
|
||||
.moduleText {
|
||||
line-height: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
display: inline-block;
|
||||
width: 960px;
|
||||
height: 540px;
|
||||
position: absolute;
|
||||
left: 200px;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,73 @@
|
||||
<template>
|
||||
<div :style="{ width: props.dimensions.width * props.ratioWidth + 'px', height: props.dimensions.height * props.ratioHeight + 'px' }">
|
||||
<NodeResizer color="#000" v-if="!props.isView && !props.isHideHandle && props.selected" />
|
||||
|
||||
<div
|
||||
class="custom-node"
|
||||
:style="{
|
||||
width: props.dimensions.width * props.ratioWidth + 'px',
|
||||
height: props.dimensions.height * props.ratioHeight + 'px',
|
||||
pointerEvents: props.isView ? 'auto' : 'none'
|
||||
}"
|
||||
>
|
||||
<el-image style="width: 100%; height: 100%" :src="props.data.customData.type === 1 ? img1:img2" fit="contain" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import img1 from '@/assets/electronicComponents/1.png';
|
||||
import img2 from '@/assets/electronicComponents/2.png';
|
||||
import { defineEmits, defineProps, ref } from 'vue';
|
||||
import { NodeResizer } from '@vue-flow/node-resizer';
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
|
||||
const props = defineProps({
|
||||
ratioWidth: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
},
|
||||
ratioHeight: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
},
|
||||
isView: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
inputData: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isHideHandle: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
selected: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
dimensions: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
onMounted(() => {
|
||||
console.log(props);
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.custom-node {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,72 @@
|
||||
<template>
|
||||
<div :style="{ width: props.dimensions.width * props.ratioWidth + 'px', height: props.dimensions.height * props.ratioHeight + 'px' }">
|
||||
<NodeResizer color="#000" v-if="!props.isView && !props.isHideHandle && props.selected" />
|
||||
|
||||
<div
|
||||
class="custom-node"
|
||||
:style="{
|
||||
width: props.dimensions.width * props.ratioWidth + 'px',
|
||||
height: props.dimensions.height * props.ratioHeight + 'px',
|
||||
pointerEvents: props.isView ? 'auto' : 'none'
|
||||
}"
|
||||
>
|
||||
<div style="position: absolute;top: 50%;left: 0;transform: translateY(-50%);width: 100%;height: 1px;background-color: #0002"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import img1 from '@/assets/electronicComponents/1.png';
|
||||
import img2 from '@/assets/electronicComponents/2.png';
|
||||
import { defineEmits, defineProps, ref } from 'vue';
|
||||
import { NodeResizer } from '@vue-flow/node-resizer';
|
||||
import { Handle, Position } from '@vue-flow/core';
|
||||
|
||||
const props = defineProps({
|
||||
ratioWidth: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
},
|
||||
ratioHeight: {
|
||||
type: Number,
|
||||
required: false,
|
||||
default: 1
|
||||
},
|
||||
isView: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
inputData: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
isHideHandle: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
selected: {
|
||||
type: Boolean,
|
||||
required: false
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
dimensions: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
});
|
||||
onMounted(() => {
|
||||
console.log(props);
|
||||
});
|
||||
</script>
|
||||
<style scoped>
|
||||
.custom-node {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
@ -0,0 +1,87 @@
|
||||
import { useVueFlow } from '@vue-flow/core';
|
||||
import { ref, watch } from 'vue';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
const getId = (type) => {
|
||||
return `${type}_${uuidv4().replaceAll('-', '_')}`;
|
||||
};
|
||||
|
||||
const getOption = (e) => {
|
||||
if (e === 'circuitComponent') {
|
||||
return {
|
||||
title: ''
|
||||
};
|
||||
}
|
||||
};
|
||||
const getNodeSize = (e) => {
|
||||
if (e === 'circuitComponent') {
|
||||
return { width: 50, height: 50 };
|
||||
} else if (e === 'line') {
|
||||
return { width: 100, height: 20 };
|
||||
}
|
||||
};
|
||||
const nameEnum = {};
|
||||
const tool = () => {
|
||||
const nodeType = ref('');
|
||||
const customData = ref({ type: 1 });
|
||||
const { addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow({ id: 'flowA' });
|
||||
const onDragStart = (event, type, data) => {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.setData('application/vueflow', type);
|
||||
event.dataTransfer.effectAllowed = 'move';
|
||||
}
|
||||
|
||||
nodeType.value = type;
|
||||
customData.value = data || { type: 1 };
|
||||
document.addEventListener('drop', onDragEnd);
|
||||
};
|
||||
const onDragEnd = () => {
|
||||
nodeType.value = null;
|
||||
document.removeEventListener('drop', onDragEnd);
|
||||
};
|
||||
const onDrop = (event) => {
|
||||
const dimensions = getNodeSize(nodeType.value);
|
||||
|
||||
const position = screenToFlowCoordinate({
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
});
|
||||
|
||||
const nodeId = getId(nodeType.value);
|
||||
const newNode = {
|
||||
id: nodeId,
|
||||
name: nameEnum[nodeType.value],
|
||||
draggable: true,
|
||||
type: nodeType.value,
|
||||
dimensions,
|
||||
position,
|
||||
data: { options: getOption(nodeType.value), outputData: {}, customData: customData.value }
|
||||
};
|
||||
|
||||
const { off } = onNodesInitialized(() => {
|
||||
updateNode(nodeId, (node) => ({
|
||||
position: { x: node.position.x - node.dimensions.width / 2, y: node.position.y - node.dimensions.height / 2 }
|
||||
}));
|
||||
|
||||
off();
|
||||
});
|
||||
|
||||
addNodes(newNode);
|
||||
};
|
||||
const onDragOver = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (nodeType.value) {
|
||||
if (event.dataTransfer) {
|
||||
event.dataTransfer.dropEffect = 'move';
|
||||
}
|
||||
}
|
||||
};
|
||||
return {
|
||||
onDragStart,
|
||||
onDragEnd,
|
||||
onDrop,
|
||||
onDragOver
|
||||
};
|
||||
};
|
||||
export default tool;
|
||||
Loading…
Reference in New Issue