414 lines
10 KiB
Vue
414 lines
10 KiB
Vue
|
|
<!-- AntV G6 Graph Component -->
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, onMounted } from 'vue';
|
|||
|
|
import { Graph,NodeEvent } from '@antv/g6';
|
|||
|
|
|
|||
|
|
const container = ref<HTMLElement | null>(null);
|
|||
|
|
let graph: Graph | null = null;
|
|||
|
|
// Device types data
|
|||
|
|
const deviceTypes = [
|
|||
|
|
{ id: 'device1', name: '圆柱槽', icon: '🟢',
|
|||
|
|
img: 'http://localhost:3000/dev-api/avatar/1.png'
|
|||
|
|
},
|
|||
|
|
{ id: 'device2', name: '扁平槽', icon: '🔵',
|
|||
|
|
img: 'http://localhost:3000/dev-api/avatar/2.png'
|
|||
|
|
},
|
|||
|
|
{ id: 'device3', name: '环形槽', icon: '🟢',
|
|||
|
|
img: 'http://localhost:3000/dev-api/avatar/3.png'
|
|||
|
|
},
|
|||
|
|
{ id: 'device4', name: '管束槽', icon: '🟢',
|
|||
|
|
img: 'http://localhost:3000/dev-api/avatar/4.png'
|
|||
|
|
},
|
|||
|
|
{ id: 'device5', name: '萃取柱', icon: '🟦',
|
|||
|
|
img: 'http://localhost:3000/dev-api/avatar/5.png'
|
|||
|
|
},
|
|||
|
|
{ id: 'device6', name: '流化床', icon: '🟦',
|
|||
|
|
img: 'http://localhost:3000/dev-api/avatar/6.png'
|
|||
|
|
},
|
|||
|
|
{ id: 'device7', name: '锥底环形槽', icon: '🟦',
|
|||
|
|
img: 'http://localhost:3000/dev-api/avatar/7.png'
|
|||
|
|
},
|
|||
|
|
];
|
|||
|
|
const data = {
|
|||
|
|
nodes: [
|
|||
|
|
],
|
|||
|
|
edges: [
|
|||
|
|
],
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Drag and drop functions
|
|||
|
|
const handleDragStart = (event: DragEvent, device: any) => {
|
|||
|
|
if (event.dataTransfer) {
|
|||
|
|
event.dataTransfer.setData('application/json', JSON.stringify(device));
|
|||
|
|
event.dataTransfer.effectAllowed = 'copy';
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleDragOver = (event: DragEvent) => {
|
|||
|
|
event.preventDefault();
|
|||
|
|
if (event.dataTransfer) {
|
|||
|
|
event.dataTransfer.dropEffect = 'copy';
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleDrop = (event: DragEvent) => {
|
|||
|
|
event.preventDefault();
|
|||
|
|
if (!graph || !event.dataTransfer || !container.value) return;
|
|||
|
|
|
|||
|
|
let newNode = null;
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
const deviceData = JSON.parse(event.dataTransfer.getData('application/json'));
|
|||
|
|
let imgUrl = deviceData.img;
|
|||
|
|
|
|||
|
|
// Calculate coordinates relative to graph container
|
|||
|
|
const rect = container.value.getBoundingClientRect();
|
|||
|
|
const x = event.clientX - rect.left;
|
|||
|
|
const y = event.clientY - rect.top;
|
|||
|
|
// const point = graph.getPointByClient(event.clientX, event.clientY);
|
|||
|
|
// const x = point.x;
|
|||
|
|
// const y = point.y;
|
|||
|
|
const nodeId = 'node1-' + Date.now();
|
|||
|
|
graph.addNodeData([
|
|||
|
|
{
|
|||
|
|
type: 'image',
|
|||
|
|
id: nodeId,
|
|||
|
|
img: imgUrl, // 图片地址
|
|||
|
|
data: { label: '自定义图片1', status: 'online' },
|
|||
|
|
style: {
|
|||
|
|
x: x,
|
|||
|
|
y: y,
|
|||
|
|
size: 60,
|
|||
|
|
src: imgUrl, // 图片地址
|
|||
|
|
labelText: '111111111111',
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
]);
|
|||
|
|
graph.draw();
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('Error handling drop:', error);
|
|||
|
|
console.error('Graph instance:', graph);
|
|||
|
|
if (newNode) {
|
|||
|
|
console.error('New node:', JSON.stringify(newNode, null, 2));
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
const dragelement = ref('');
|
|||
|
|
function setDragElement(type: string) {
|
|||
|
|
dragelement.value = type;
|
|||
|
|
}
|
|||
|
|
const dragLineData = ref([{
|
|||
|
|
stroke: '#333',
|
|||
|
|
lineWidth: 2,
|
|||
|
|
endArrow: false,
|
|||
|
|
endArrowType: '',
|
|||
|
|
endArrowSize: 10,
|
|||
|
|
},{
|
|||
|
|
stroke: '#333',
|
|||
|
|
lineWidth: 2,
|
|||
|
|
endArrow: true,
|
|||
|
|
endArrowType: 'vee',
|
|||
|
|
endArrowSize: 10,
|
|||
|
|
},{
|
|||
|
|
startArrow: true,
|
|||
|
|
startArrowType: 'vee',
|
|||
|
|
startArrowSize: 10,
|
|||
|
|
endArrow: true,
|
|||
|
|
endArrowType: 'vee',
|
|||
|
|
endArrowSize: 10,
|
|||
|
|
stroke: '#333',
|
|||
|
|
lineWidth: 2
|
|||
|
|
},{
|
|||
|
|
stroke: '#333',
|
|||
|
|
lineWidth: 2,
|
|||
|
|
lineDash: [5, 5],
|
|||
|
|
},{
|
|||
|
|
stroke: '#333',
|
|||
|
|
lineWidth: 2,
|
|||
|
|
lineDash: [5, 5],
|
|||
|
|
endArrow: true,
|
|||
|
|
endArrowType: 'vee',
|
|||
|
|
endArrowSize: 10,
|
|||
|
|
},{
|
|||
|
|
startArrow: true,
|
|||
|
|
startArrowType: 'vee',
|
|||
|
|
startArrowSize: 10,
|
|||
|
|
lineDash: [5, 5],
|
|||
|
|
endArrow: true,
|
|||
|
|
endArrowType: 'vee',
|
|||
|
|
endArrowSize: 10,
|
|||
|
|
stroke: '#333',
|
|||
|
|
lineWidth: 2
|
|||
|
|
}]);
|
|||
|
|
const dragLine:any = ref({
|
|||
|
|
stroke: '#333',
|
|||
|
|
lineWidth: 2,
|
|||
|
|
lineDash: [5, 5],
|
|||
|
|
});
|
|||
|
|
const dragLineType = ref('实线');
|
|||
|
|
function setDragLineStyle(index: any, type: string) {
|
|||
|
|
dragLine.value = dragLineData.value[index];
|
|||
|
|
dragLineType.value = type;
|
|||
|
|
// graph.updateBehavior({
|
|||
|
|
// key: 'create-edge',
|
|||
|
|
// style: (edgeData:any) => {
|
|||
|
|
// debugger
|
|||
|
|
// return {
|
|||
|
|
// ...dragLine.value
|
|||
|
|
// };
|
|||
|
|
|
|||
|
|
// }
|
|||
|
|
// });
|
|||
|
|
}
|
|||
|
|
onMounted(() => {
|
|||
|
|
if (container.value) {
|
|||
|
|
// Create G6 graph instance with data in constructor - G6 v4 compatible
|
|||
|
|
graph = new Graph({
|
|||
|
|
container: container.value,
|
|||
|
|
width: container.value.offsetWidth,
|
|||
|
|
height: container.value.offsetHeight,
|
|||
|
|
node: {
|
|||
|
|
type: 'image',
|
|||
|
|
style: {
|
|||
|
|
size: 80,
|
|||
|
|
labelText: (d) => '',
|
|||
|
|
src: (d: any) => d.img
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
edge: {
|
|||
|
|
type: 'line',
|
|||
|
|
style: (datum) => {
|
|||
|
|
// 正确的动态样式配置方式
|
|||
|
|
return dragLine.value
|
|||
|
|
},
|
|||
|
|
// style: {
|
|||
|
|
// lineDash: function (datum) {
|
|||
|
|
// if (dragLineType.value === '虚线' || dragLineType.value === '单箭头虚线' || dragLineType.value === '双箭头虚线') {
|
|||
|
|
// return [5, 5];
|
|||
|
|
// }
|
|||
|
|
// return [];
|
|||
|
|
// },
|
|||
|
|
// }
|
|||
|
|
},
|
|||
|
|
behaviors: [
|
|||
|
|
// 'drag-canvas', // 允许拖拽画布
|
|||
|
|
// 'zoom-canvas', // 允许缩放画布
|
|||
|
|
{
|
|||
|
|
type: 'drag-element',
|
|||
|
|
enable: (event:any) => {
|
|||
|
|
// 只有当拖拽目标是节点且其id不为'fixedNode'时才允许拖拽
|
|||
|
|
return dragelement.value === 'drag-element' ;
|
|||
|
|
},
|
|||
|
|
}, // 允许拖拽节点
|
|||
|
|
{
|
|||
|
|
type: 'create-edge',
|
|||
|
|
trigger: 'drag',
|
|||
|
|
enable: (event:any) => {
|
|||
|
|
// 动态判断是否允许创建边
|
|||
|
|
// 例如:只有按住 Shift 键时才允许连线
|
|||
|
|
return dragelement.value === '' ;
|
|||
|
|
},
|
|||
|
|
// style: dragLine.value,
|
|||
|
|
style: (edgeData:any) => {
|
|||
|
|
debugger
|
|||
|
|
// 动态设置虚线还是实线
|
|||
|
|
const baseStyle = dragLine.value;
|
|||
|
|
if (edgeData.data && edgeData.data.isDashed) {
|
|||
|
|
return {
|
|||
|
|
...baseStyle,
|
|||
|
|
lineDash: [5, 5], // 虚线
|
|||
|
|
};
|
|||
|
|
} else {
|
|||
|
|
return {
|
|||
|
|
...baseStyle,
|
|||
|
|
lineDash: [], // 实线
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
},
|
|||
|
|
],
|
|||
|
|
|
|||
|
|
data: data // Pass data directly in constructor
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
graph.on(NodeEvent.CLICK, (evt) => {
|
|||
|
|
debugger
|
|||
|
|
const { target } = evt;
|
|||
|
|
const nodeId = target.id;
|
|||
|
|
|
|||
|
|
// 在控制台输出点击的节点信息
|
|||
|
|
console.log(`节点 ${nodeId} 被点击了`);
|
|||
|
|
|
|||
|
|
// 获取节点数据
|
|||
|
|
const nodeData = graph.getNodeData(nodeId);
|
|||
|
|
console.log('节点数据:', nodeData);
|
|||
|
|
|
|||
|
|
// 设置节点选中状态
|
|||
|
|
graph.setElementState(nodeId, 'selected', true);
|
|||
|
|
});
|
|||
|
|
// Just render, no need for separate data() call
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<template>
|
|||
|
|
<div class="app-layout">
|
|||
|
|
<!-- Left Sidebar -->
|
|||
|
|
<div class="sidebar">
|
|||
|
|
<div class="sidebar-section">
|
|||
|
|
<h3>设备</h3>
|
|||
|
|
<div
|
|||
|
|
v-for="device in deviceTypes"
|
|||
|
|
:key="device.id"
|
|||
|
|
class="device-item"
|
|||
|
|
draggable="true"
|
|||
|
|
@dragstart="handleDragStart($event, device)"
|
|||
|
|
>
|
|||
|
|
<div class="device-icon">
|
|||
|
|
<img :src="device.img" alt="设备图标" style="width: 40px; height: 40px;">
|
|||
|
|
</div>
|
|||
|
|
<div class="device-name">{{ device.name }}</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<div class="sidebar-section">
|
|||
|
|
<h3>链接</h3>
|
|||
|
|
|
|||
|
|
<div class="device-item" @click="setDragElement('drag-element')">
|
|||
|
|
<div class="device-icon">↔️</div>
|
|||
|
|
<div class="device-name">移动</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="device-item" @click="setDragElement('')">
|
|||
|
|
<div class="device-icon">↔️</div>
|
|||
|
|
<div class="device-name">不移动</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="device-item" @click="setDragLineStyle(0,'实线')">
|
|||
|
|
<div class="device-icon">↔️</div>
|
|||
|
|
<div class="device-name">实线</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="device-item" @click="setDragLineStyle(1,'单箭头实线')">
|
|||
|
|
<div class="device-icon">↔️</div>
|
|||
|
|
<div class="device-name">单箭头实线</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="device-item" @click="setDragLineStyle(2,'双箭头实线')">
|
|||
|
|
<div class="device-icon">⇄</div>
|
|||
|
|
<div class="device-name">双箭头实线</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="device-item" @click="setDragLineStyle(3,'虚线')">
|
|||
|
|
<div class="device-icon">↔️</div>
|
|||
|
|
<div class="device-name">虚线</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="device-item" @click="setDragLineStyle(4,'单箭头虚线')">
|
|||
|
|
<div class="device-icon">↔️</div>
|
|||
|
|
<div class="device-name">单箭头虚线</div>
|
|||
|
|
</div>
|
|||
|
|
<div class="device-item" @click="setDragLineStyle(5,'双箭头虚线')">
|
|||
|
|
<div class="device-icon">⇄</div>
|
|||
|
|
<div class="device-name">双箭头虚线</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<!-- Graph Container -->
|
|||
|
|
<div
|
|||
|
|
class="graph-container"
|
|||
|
|
ref="container"
|
|||
|
|
@dragover="handleDragOver"
|
|||
|
|
@drop="handleDrop"
|
|||
|
|
></div>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<style>
|
|||
|
|
* {
|
|||
|
|
margin: 0;
|
|||
|
|
padding: 0;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.app-layout {
|
|||
|
|
display: flex;
|
|||
|
|
width: 100vw;
|
|||
|
|
height: 100vh;
|
|||
|
|
font-family: 'Segoe UI', sans-serif;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Sidebar Styles */
|
|||
|
|
.sidebar {
|
|||
|
|
width: 200px;
|
|||
|
|
background-color: #ffffff;
|
|||
|
|
border-right: 1px solid #e0e0e0;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
padding: 16px;
|
|||
|
|
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.sidebar-section {
|
|||
|
|
margin-bottom: 24px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.sidebar-section h3 {
|
|||
|
|
font-size: 14px;
|
|||
|
|
font-weight: 600;
|
|||
|
|
color: #333;
|
|||
|
|
margin-bottom: 12px;
|
|||
|
|
padding-bottom: 8px;
|
|||
|
|
border-bottom: 1px solid #f0f0f0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.device-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
padding: 10px;
|
|||
|
|
margin-bottom: 8px;
|
|||
|
|
background-color: #fafafa;
|
|||
|
|
border: 1px solid #e0e0e0;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
cursor: grab;
|
|||
|
|
transition: all 0.2s ease;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.device-item:hover {
|
|||
|
|
background-color: #f0f5ff;
|
|||
|
|
border-color: #1890ff;
|
|||
|
|
transform: translateY(-1px);
|
|||
|
|
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.device-item:active {
|
|||
|
|
cursor: grabbing;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.device-icon {
|
|||
|
|
font-size: 24px;
|
|||
|
|
margin-right: 12px;
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
width: 32px;
|
|||
|
|
height: 32px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.device-name {
|
|||
|
|
font-size: 13px;
|
|||
|
|
color: #333;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Graph Container Styles */
|
|||
|
|
.graph-container {
|
|||
|
|
flex: 1;
|
|||
|
|
position: relative;
|
|||
|
|
background-color: #fafafa;
|
|||
|
|
overflow: hidden;
|
|||
|
|
width: 100%;
|
|||
|
|
height: 100%;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Drag and drop visual feedback */
|
|||
|
|
.graph-container.drag-over {
|
|||
|
|
background-color: #e6f7ff;
|
|||
|
|
border: 2px dashed #1890ff;
|
|||
|
|
}
|
|||
|
|
</style>
|