|
@@ -2,7 +2,7 @@
|
|
|
<!-- eslint-disable vue/valid-v-for -->
|
|
|
<template>
|
|
|
<div ref="wrapRef" class="svg-wrap" @mousemove="handleMouseMove">
|
|
|
- <Bar />
|
|
|
+ <Bar @create-node="createNode" />
|
|
|
<svg
|
|
|
version="1.1"
|
|
|
baseProfile="full"
|
|
@@ -12,37 +12,21 @@
|
|
|
:width="wrapSize.width"
|
|
|
:height="wrapSize.height"
|
|
|
>
|
|
|
- <marker
|
|
|
- id="markerArrow"
|
|
|
- markerUnits="strokeWidth"
|
|
|
- markerWidth="5"
|
|
|
- markerHeight="4"
|
|
|
- refX="6"
|
|
|
- refY="2"
|
|
|
- orient="auto"
|
|
|
- >
|
|
|
- <path d="M 0 0 L 5 2 L 0 4 z" class="trangle" />
|
|
|
- </marker>
|
|
|
- <line
|
|
|
- v-for="i in linesAttr"
|
|
|
- :key="i.id"
|
|
|
- v-bind="i"
|
|
|
- class="line"
|
|
|
- marker-end="url(#markerArrow)"
|
|
|
- />
|
|
|
- <SvgRect
|
|
|
- v-for="i in children"
|
|
|
- :key="i.id"
|
|
|
- v-bind="i"
|
|
|
- :isDragging="isDragging"
|
|
|
- :curNodeId="curNodeId"
|
|
|
- :startPoint="lineStartPoint"
|
|
|
- @click="nodeClick"
|
|
|
- @startLine="startLine"
|
|
|
- @endLine="endLine"
|
|
|
- @cancelEndLine="cancelEndLine"
|
|
|
- @startDrag="startDrag"
|
|
|
- />
|
|
|
+ <SvgLines :lines="lines" :nodes="nodes" />
|
|
|
+ <template v-for="i in nodes" :key="i.id">
|
|
|
+ <component
|
|
|
+ :is="getComponentType(i.type)"
|
|
|
+ v-bind="i"
|
|
|
+ :isDragging="isDragging"
|
|
|
+ :curNodeId="curNodeId"
|
|
|
+ :startPoint="lineStartPoint"
|
|
|
+ @click="nodeClick"
|
|
|
+ @startLine="startLine"
|
|
|
+ @endLine="endLine"
|
|
|
+ @cancelEndLine="cancelEndLine"
|
|
|
+ @startDrag="startDrag"
|
|
|
+ ></component>
|
|
|
+ </template>
|
|
|
<line v-if="lineStartPoint" v-bind="dashLineAttr" class="dash-line" />
|
|
|
</svg>
|
|
|
</div>
|
|
@@ -51,6 +35,9 @@
|
|
|
<script setup>
|
|
|
import { reactive, ref, onBeforeUnmount, onMounted, computed } from "vue";
|
|
|
import SvgRect from "./components/svgRect.vue";
|
|
|
+import SvgCircle from "./components/svgCircle.vue";
|
|
|
+import SvgRhombic from "./components/svgRhombic.vue";
|
|
|
+import SvgLines from "./components/svgLines.vue";
|
|
|
import Bar from "./components/bar.vue";
|
|
|
import {
|
|
|
findFromArray,
|
|
@@ -69,31 +56,43 @@ const curNodeConfig = ref({});
|
|
|
const isDragging = ref(false);
|
|
|
const lineStartPoint = ref(null);
|
|
|
const lineEndPoint = ref(null);
|
|
|
-const lines = reactive([]);
|
|
|
-const children = ref([
|
|
|
+const lines = reactive([
|
|
|
+ { start: { targetId: 1, pos: "right" }, end: { targetId: 2, pos: "left" } },
|
|
|
+ { start: { targetId: 2, pos: "right" }, end: { targetId: 3, pos: "left" } },
|
|
|
+]);
|
|
|
+const nodes = ref([
|
|
|
{
|
|
|
id: 1,
|
|
|
- pos: { x: 160, y: 110 },
|
|
|
+ pos: { x: 200, y: 200 },
|
|
|
+ type: "rect",
|
|
|
size: { width: 100, height: 100 },
|
|
|
},
|
|
|
{
|
|
|
id: 2,
|
|
|
- pos: { x: 170, y: 120 },
|
|
|
- size: { width: 100, height: 100 },
|
|
|
+ type: "rhombic",
|
|
|
+ pos: { x: 350, y: 210 },
|
|
|
+ size: { width: 120, height: 80 },
|
|
|
},
|
|
|
{
|
|
|
id: 3,
|
|
|
- pos: { x: 180, y: 130 },
|
|
|
- size: { width: 100, height: 100 },
|
|
|
+ pos: { x: 550, y: 220 },
|
|
|
+ type: "circle",
|
|
|
+ size: { width: 60, height: 60 },
|
|
|
},
|
|
|
]);
|
|
|
|
|
|
const curNode = computed(() => {
|
|
|
return curNodeId.value !== 0
|
|
|
- ? children.value.find((i) => i.id === curNodeId.value)
|
|
|
+ ? nodes.value.find((i) => i.id === curNodeId.value)
|
|
|
: null;
|
|
|
});
|
|
|
|
|
|
+function getComponentType(type) {
|
|
|
+ return (
|
|
|
+ { circle: SvgCircle, rect: SvgRect, rhombic: SvgRhombic }[type] ?? SvgRect
|
|
|
+ );
|
|
|
+}
|
|
|
+
|
|
|
const dashLineAttr = computed(() => {
|
|
|
return lineStartPoint.value
|
|
|
? {
|
|
@@ -105,24 +104,6 @@ const dashLineAttr = computed(() => {
|
|
|
: {};
|
|
|
});
|
|
|
|
|
|
-const linesAttr = computed(() =>
|
|
|
- lines.map((line) => {
|
|
|
- const { targetId: startTargetId, pos: startPos } = line.start;
|
|
|
- const { targetId: endTargetId, pos: endPos } = line.end;
|
|
|
- const startNode = children.value.find((i) => i.id === startTargetId);
|
|
|
- const endNode = children.value.find((i) => i.id === endTargetId);
|
|
|
- const endPoint = countPointByPos(endPos, endNode.pos, endNode.size);
|
|
|
- const startPoint = countPointByPos(startPos, startNode.pos, startNode.size);
|
|
|
- return {
|
|
|
- id: `${startTargetId}-${startPos}-${endTargetId}-${endPos}`,
|
|
|
- x1: startPoint.cx,
|
|
|
- y1: startPoint.cy,
|
|
|
- x2: endPoint.cx,
|
|
|
- y2: endPoint.cy,
|
|
|
- };
|
|
|
- })
|
|
|
-);
|
|
|
-
|
|
|
function startDrag({ id, event, innerPos }) {
|
|
|
isDragging.value = true;
|
|
|
curNodeId.value = id;
|
|
@@ -133,9 +114,62 @@ function startDrag({ id, event, innerPos }) {
|
|
|
lineEndPoint.value = null;
|
|
|
// 移动当前的节点最后渲染
|
|
|
curMousePos.value = getPosFromEvent(event, wrapRef.value);
|
|
|
- const { index, result } = findFromArray(children.value, (i) => i.id === id);
|
|
|
- children.value.splice(index, 1);
|
|
|
- children.value.push(result);
|
|
|
+ const { index, result } = findFromArray(nodes.value, (i) => i.id === id);
|
|
|
+ nodes.value.splice(index, 1);
|
|
|
+ nodes.value.push(result);
|
|
|
+}
|
|
|
+
|
|
|
+function createNode(e, config) {
|
|
|
+ switch (config.type) {
|
|
|
+ case "rect":
|
|
|
+ config.width = 100;
|
|
|
+ config.height = 100;
|
|
|
+ break;
|
|
|
+ case "circle":
|
|
|
+ config.width = 60;
|
|
|
+ config.height = 60;
|
|
|
+ break;
|
|
|
+ case "rhombic":
|
|
|
+ config.width = 120;
|
|
|
+ config.height = 80;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ const newNode = {
|
|
|
+ id: nodes.value.length + 1,
|
|
|
+ type: config.type,
|
|
|
+ pos: {
|
|
|
+ x: curMousePos.value.x - config.width / 2,
|
|
|
+ y: curMousePos.value.y - config.height / 2,
|
|
|
+ },
|
|
|
+ size: { width: config.width, height: config.height },
|
|
|
+ };
|
|
|
+ nodes.value.push(newNode);
|
|
|
+ isDragging.value = true;
|
|
|
+ curNodeId.value = newNode.id;
|
|
|
+ curNodeConfig.value = {
|
|
|
+ innerPos: {
|
|
|
+ x: config.width / 2,
|
|
|
+ y: config.height / 2,
|
|
|
+ },
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+function createCircle() {
|
|
|
+ const newCircle = {
|
|
|
+ id: nodes.value.length + 1,
|
|
|
+ type: "circle",
|
|
|
+ pos: { x: curMousePos.value.x - 50, y: curMousePos.value.y - 50 },
|
|
|
+ size: { width: 50, height: 50 },
|
|
|
+ };
|
|
|
+ nodes.value.push(newCircle);
|
|
|
+ isDragging.value = true;
|
|
|
+ curNodeId.value = newCircle.id;
|
|
|
+ curNodeConfig.value = {
|
|
|
+ innerPos: {
|
|
|
+ x: 50,
|
|
|
+ y: 50,
|
|
|
+ },
|
|
|
+ };
|
|
|
}
|
|
|
|
|
|
function startLine({ id, point }) {
|
|
@@ -164,7 +198,6 @@ function cancelEndLine() {
|
|
|
|
|
|
function handleMouseMove(e) {
|
|
|
curMousePos.value = getPosFromEvent(e, wrapRef.value);
|
|
|
- console.log(curMousePos.value);
|
|
|
if (curNode.value && isDragging.value) {
|
|
|
curNode.value.pos = {
|
|
|
x: sticky(curMousePos.value.x - curNodeConfig.value.innerPos.x),
|
|
@@ -226,6 +259,7 @@ onBeforeUnmount(() => {
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
.svg-wrap {
|
|
|
+ height: 100%;
|
|
|
min-height: 400px;
|
|
|
background-color: blue;
|
|
|
background-image: linear-gradient(
|
|
@@ -237,6 +271,9 @@ onBeforeUnmount(() => {
|
|
|
linear-gradient(90deg, #ccc 0px, #ccc 2px, #fff 2px, #fff 15px);
|
|
|
background-size: 15px 15px, 15px 15px;
|
|
|
position: relative;
|
|
|
+ * {
|
|
|
+ user-select: none;
|
|
|
+ }
|
|
|
svg {
|
|
|
display: block;
|
|
|
}
|
|
@@ -246,15 +283,5 @@ onBeforeUnmount(() => {
|
|
|
stroke-dasharray: 3 4;
|
|
|
stroke-width: 2;
|
|
|
}
|
|
|
-
|
|
|
- .line {
|
|
|
- stroke: #5ba8ff;
|
|
|
- stroke-width: 2;
|
|
|
- }
|
|
|
-
|
|
|
- .trangle {
|
|
|
- stroke: #5ba8ff;
|
|
|
- fill: #5ba8ff;
|
|
|
- }
|
|
|
}
|
|
|
</style>
|