|
@@ -1,7 +1,30 @@
|
|
|
<template>
|
|
|
- <g @mousedown="handleMousedown" :id="id">
|
|
|
+ <g
|
|
|
+ :id="id"
|
|
|
+ class="wrap"
|
|
|
+ @mousedown="handleMousedown"
|
|
|
+ @mouseenter="boxMouseEnter"
|
|
|
+ @mouseleave="boxMouseLeave"
|
|
|
+ >
|
|
|
<rect v-bind="outlineRectAttr" />
|
|
|
<rect v-bind="attr" />
|
|
|
+ <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" stroke="#5ba8ff" fill="#5ba8ff" />
|
|
|
+ </marker>
|
|
|
+ <line
|
|
|
+ v-for="i in linesAttr"
|
|
|
+ :key="i.id"
|
|
|
+ v-bind="i"
|
|
|
+ marker-end="url(#markerArrow)"
|
|
|
+ />
|
|
|
<circle
|
|
|
v-for="i in pointsAttr"
|
|
|
v-bind="i"
|
|
@@ -22,19 +45,18 @@ import {
|
|
|
defineEmits,
|
|
|
computed,
|
|
|
} from "vue";
|
|
|
-import { sticky } from "../utils/index";
|
|
|
+import { sticky, countPointByPos } from "../utils/index";
|
|
|
const props = defineProps([
|
|
|
"pos",
|
|
|
"size",
|
|
|
+ "lines",
|
|
|
+ "allItems",
|
|
|
"mousePos",
|
|
|
"startPoint",
|
|
|
"id",
|
|
|
"draggingId",
|
|
|
+ "isDragging",
|
|
|
]);
|
|
|
-const boxPos = ref(props.pos);
|
|
|
-const boxSize = ref(props.size);
|
|
|
-const innerPos = ref({ x: 0, y: 0 });
|
|
|
-const isDraging = computed(() => props.id === props.draggingId);
|
|
|
const emits = defineEmits([
|
|
|
"startDrag",
|
|
|
"startLine",
|
|
@@ -42,14 +64,19 @@ const emits = defineEmits([
|
|
|
"endLine",
|
|
|
"cancelEndLine",
|
|
|
]);
|
|
|
+const boxPos = ref(props.pos);
|
|
|
+const boxSize = ref(props.size);
|
|
|
+const innerPos = ref({ x: 0, y: 0 });
|
|
|
+const isFocus = computed(() => props.id === props.draggingId);
|
|
|
+const isHover = ref(false);
|
|
|
|
|
|
const attr = computed(() => {
|
|
|
return {
|
|
|
- x: boxPos.value.x,
|
|
|
- y: boxPos.value.y,
|
|
|
- width: boxSize.value.width,
|
|
|
- height: boxSize.value.height,
|
|
|
- fill: "rgba(255,255,255,1)",
|
|
|
+ x: props.pos.x,
|
|
|
+ y: props.pos.y,
|
|
|
+ width: props.size.width,
|
|
|
+ height: props.size.height,
|
|
|
+ fill: "rgba(255,255,255,0.6)",
|
|
|
stroke: "#666",
|
|
|
rx: "15",
|
|
|
};
|
|
@@ -58,51 +85,66 @@ const attr = computed(() => {
|
|
|
const outlineRectAttr = computed(() => {
|
|
|
const space = 8;
|
|
|
return {
|
|
|
- x: boxPos.value.x - space,
|
|
|
- y: boxPos.value.y - space,
|
|
|
- width: boxSize.value.width + space * 2,
|
|
|
- height: boxSize.value.height + space * 2,
|
|
|
+ x: props.pos.x - space,
|
|
|
+ y: props.pos.y - space,
|
|
|
+ width: props.size.width + space * 2,
|
|
|
+ height: props.size.height + space * 2,
|
|
|
fill: "rgba(255,255,255,0)",
|
|
|
"stroke-dasharray": "3 4",
|
|
|
- stroke: "#000",
|
|
|
+ stroke: isFocus.value ? "#000" : "rgba(0,0,0,0)",
|
|
|
rx: "18",
|
|
|
};
|
|
|
});
|
|
|
|
|
|
-const pointsAttr = computed(() => [
|
|
|
- {
|
|
|
- pos: `top`,
|
|
|
- cx: boxPos.value.x + boxSize.value.width / 2,
|
|
|
- cy: boxPos.value.y,
|
|
|
- r: 4,
|
|
|
- stroke: "#000",
|
|
|
- fill: "rgba(255,255,255,1)",
|
|
|
- },
|
|
|
- {
|
|
|
- pos: `right`,
|
|
|
- cx: boxPos.value.x + boxSize.value.width,
|
|
|
- cy: boxPos.value.y + boxSize.value.height / 2,
|
|
|
- r: 4,
|
|
|
- stroke: "#000",
|
|
|
- fill: "rgba(255,255,255,1)",
|
|
|
- },
|
|
|
- {
|
|
|
- pos: `bottom`,
|
|
|
- cx: boxPos.value.x + boxSize.value.width / 2,
|
|
|
- cy: boxPos.value.y + boxSize.value.height,
|
|
|
- r: 4,
|
|
|
- stroke: "#000",
|
|
|
- fill: "rgba(255,255,255,1)",
|
|
|
- },
|
|
|
- {
|
|
|
- pos: `left`,
|
|
|
- cx: boxPos.value.x,
|
|
|
- cy: boxPos.value.y + boxSize.value.height / 2,
|
|
|
- r: 4,
|
|
|
- stroke: "#000",
|
|
|
- fill: "rgba(255,255,255,1)",
|
|
|
- },
|
|
|
-]);
|
|
|
+const pointsAttr = computed(() => {
|
|
|
+ const visible = isHover.value || isFocus.value;
|
|
|
+ const defaults = {
|
|
|
+ r: 6,
|
|
|
+ stroke: visible ? "#000" : "rgba(0,0,0,0)",
|
|
|
+ fill: visible ? "rgba(255,255,255,1)" : "rgba(255,255,255,0)",
|
|
|
+ };
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ pos: "top",
|
|
|
+ ...countPointByPos("top", props.pos, props.size),
|
|
|
+ ...defaults,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ pos: "right",
|
|
|
+ ...countPointByPos("right", props.pos, props.size),
|
|
|
+ ...defaults,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ pos: "bottom",
|
|
|
+ ...countPointByPos("bottom", props.pos, props.size),
|
|
|
+ ...defaults,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ pos: "left",
|
|
|
+ ...countPointByPos("left", props.pos, props.size),
|
|
|
+ ...defaults,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+});
|
|
|
+
|
|
|
+const linesAttr = computed(() =>
|
|
|
+ props.lines.map((line) => {
|
|
|
+ const startPos = line.start.pos;
|
|
|
+ const { targetId, pos: endPos } = line.end;
|
|
|
+ const startPoint = countPointByPos(startPos, props.pos, props.size);
|
|
|
+ const endBox = props.allItems.find((i) => i.id === targetId);
|
|
|
+ const endPoint = countPointByPos(endPos, endBox.pos, endBox.size);
|
|
|
+ return {
|
|
|
+ id: `${startPos}-${targetId}-${endPos}`,
|
|
|
+ x1: startPoint.cx,
|
|
|
+ y1: startPoint.cy,
|
|
|
+ x2: endPoint.cx,
|
|
|
+ y2: endPoint.cy,
|
|
|
+ stroke: "#5ba8ff",
|
|
|
+ "stroke-width": "2",
|
|
|
+ };
|
|
|
+ })
|
|
|
+);
|
|
|
|
|
|
function handleMousedown(e) {
|
|
|
emits("startDrag", {
|
|
@@ -110,8 +152,8 @@ function handleMousedown(e) {
|
|
|
event: e,
|
|
|
});
|
|
|
innerPos.value = {
|
|
|
- x: e.offsetX - boxPos.value.x,
|
|
|
- y: e.offsetY - boxPos.value.y,
|
|
|
+ x: e.offsetX - props.pos.x,
|
|
|
+ y: e.offsetY - props.pos.y,
|
|
|
};
|
|
|
}
|
|
|
|
|
@@ -124,8 +166,7 @@ function startLine(e, point) {
|
|
|
}
|
|
|
|
|
|
function pointMouseEnter(p) {
|
|
|
- if (props.startPoint?.itemId !== props.id) {
|
|
|
- console.log(props.startPoint);
|
|
|
+ if (props.startPoint?.targetId !== props.id) {
|
|
|
emits("endLine", {
|
|
|
point: p,
|
|
|
id: props.id,
|
|
@@ -133,23 +174,33 @@ function pointMouseEnter(p) {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-function pointMouseLeave() {}
|
|
|
+function pointMouseLeave() {
|
|
|
+ emits("cancelEndLine");
|
|
|
+}
|
|
|
+
|
|
|
+function boxMouseEnter() {
|
|
|
+ emits("hover", props.id);
|
|
|
+ isHover.value = true;
|
|
|
+}
|
|
|
+
|
|
|
+function boxMouseLeave() {
|
|
|
+ isHover.value = false;
|
|
|
+}
|
|
|
|
|
|
function setPos(pos) {
|
|
|
- boxPos.value = {
|
|
|
- x: sticky(pos.x - innerPos.value.x),
|
|
|
- y: sticky(pos.y - innerPos.value.y),
|
|
|
- };
|
|
|
emits("updatePos", {
|
|
|
id: props.id,
|
|
|
- pos: boxPos.value,
|
|
|
+ pos: {
|
|
|
+ x: sticky(pos.x - innerPos.value.x),
|
|
|
+ y: sticky(pos.y - innerPos.value.y),
|
|
|
+ },
|
|
|
});
|
|
|
}
|
|
|
|
|
|
watch(
|
|
|
() => props.mousePos,
|
|
|
(pos) => {
|
|
|
- if (isDraging.value) {
|
|
|
+ if (props.isDragging && isFocus.value) {
|
|
|
setPos(pos);
|
|
|
}
|
|
|
}
|
|
@@ -157,7 +208,11 @@ watch(
|
|
|
</script>
|
|
|
|
|
|
<style lang="less" scoped>
|
|
|
-circle {
|
|
|
- cursor: pointer;
|
|
|
+.wrap {
|
|
|
+ &:hover {
|
|
|
+ rect {
|
|
|
+ fill: red;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|