2026-01-13 15:11:39 +08:00
|
|
|
|
<!-- AntV G6 Graph Component -->
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { ref, onMounted } from 'vue';
|
|
|
|
|
|
import { ElMessage } from "element-plus";
|
|
|
|
|
|
import {
|
|
|
|
|
|
Clipboard,
|
|
|
|
|
|
Graph,
|
|
|
|
|
|
History,
|
|
|
|
|
|
Keyboard,
|
|
|
|
|
|
Selection,
|
|
|
|
|
|
Shape,
|
|
|
|
|
|
Snapline,
|
|
|
|
|
|
Stencil,
|
|
|
|
|
|
Transform
|
|
|
|
|
|
} from '@antv/x6'
|
|
|
|
|
|
// @ts-ignore
|
|
|
|
|
|
import insertCss from 'insert-css'
|
|
|
|
|
|
import { updateProjects} from "@/api/business/project";
|
|
|
|
|
|
|
2026-01-13 17:39:09 +08:00
|
|
|
|
import img1 from '@/assets/x6/1.png'
|
|
|
|
|
|
import img2 from '@/assets/x6/2.png'
|
|
|
|
|
|
import img3 from '@/assets/x6/3.png'
|
|
|
|
|
|
import img4 from '@/assets/x6/4.png'
|
|
|
|
|
|
import img5 from '@/assets/x6/5.png'
|
|
|
|
|
|
import img6 from '@/assets/x6/6.png'
|
|
|
|
|
|
import img7 from '@/assets/x6/7.png'
|
|
|
|
|
|
|
|
|
|
|
|
import line1 from '@/assets/x6/line1.png'
|
|
|
|
|
|
import line2 from '@/assets/x6/line2.png'
|
|
|
|
|
|
import line3 from '@/assets/x6/line3.png'
|
|
|
|
|
|
import line4 from '@/assets/x6/line4.png'
|
|
|
|
|
|
import line5 from '@/assets/x6/line5.png'
|
|
|
|
|
|
import line6 from '@/assets/x6/line6.png'
|
2026-01-13 15:11:39 +08:00
|
|
|
|
const emit = defineEmits([ 'closeAntvx6']);
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
projectInfo: {
|
|
|
|
|
|
required: false,
|
|
|
|
|
|
type: Object,
|
|
|
|
|
|
default: {}
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
const projectInfo:any = ref(props.projectInfo)
|
|
|
|
|
|
const isDisplay = ref(true) // 是否显示
|
|
|
|
|
|
// 为了协助代码演示
|
|
|
|
|
|
let graph: Graph
|
|
|
|
|
|
let currentArrowStyle = 'single' // 默认单箭头
|
|
|
|
|
|
let currentLineStyle = 'solid' // 默认实线
|
|
|
|
|
|
|
|
|
|
|
|
// 设置箭头样式
|
|
|
|
|
|
// function setArrowStyle(style: 'single' | 'double' | 'none', lineStyle: 'solid' | 'dashed' = 'solid') {
|
|
|
|
|
|
// currentArrowStyle = style
|
|
|
|
|
|
// currentLineStyle = lineStyle
|
|
|
|
|
|
// // 重新配置连接线
|
|
|
|
|
|
// graph.options.connecting.createEdge = () => {
|
|
|
|
|
|
// const edge = new Shape.Edge({
|
|
|
|
|
|
// attrs: {
|
|
|
|
|
|
// line: {
|
|
|
|
|
|
// stroke: '#A2B1C3',
|
|
|
|
|
|
// strokeWidth: 2,
|
|
|
|
|
|
// strokeDasharray: lineStyle === 'dashed' ? 5 : 0,
|
|
|
|
|
|
// targetMarker: style === 'none' ? null : {
|
|
|
|
|
|
// name: 'block',
|
|
|
|
|
|
// width: 12,
|
|
|
|
|
|
// height: 8,
|
|
|
|
|
|
// },
|
|
|
|
|
|
// sourceMarker: style === 'double' ? {
|
|
|
|
|
|
// name: 'block',
|
|
|
|
|
|
// width: 12,
|
|
|
|
|
|
// height: 8,
|
|
|
|
|
|
// } : null,
|
|
|
|
|
|
// },
|
|
|
|
|
|
// },
|
|
|
|
|
|
// zIndex: 0,
|
|
|
|
|
|
// })
|
|
|
|
|
|
// return edge
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
preWork()
|
2026-01-13 17:39:09 +08:00
|
|
|
|
if(projectInfo.value.topology != null && projectInfo.value.topology != ''){
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
// #region 初始化画布
|
|
|
|
|
|
graph = new Graph({
|
|
|
|
|
|
container: document.getElementById('graph-container') as HTMLElement,
|
|
|
|
|
|
grid: true,
|
|
|
|
|
|
mousewheel: {
|
|
|
|
|
|
enabled: true,
|
|
|
|
|
|
zoomAtMousePosition: true,
|
|
|
|
|
|
modifiers: 'ctrl',
|
|
|
|
|
|
minScale: 0.5,
|
|
|
|
|
|
maxScale: 3,
|
|
|
|
|
|
},
|
|
|
|
|
|
connecting: {
|
|
|
|
|
|
router: 'manhattan',
|
|
|
|
|
|
connector: {
|
|
|
|
|
|
name: 'rounded',
|
|
|
|
|
|
args: {
|
|
|
|
|
|
radius: 8,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
anchor: 'center',
|
|
|
|
|
|
connectionPoint: 'anchor',
|
|
|
|
|
|
allowBlank: false,
|
|
|
|
|
|
snap: {
|
|
|
|
|
|
radius: 20,
|
|
|
|
|
|
},
|
|
|
|
|
|
createEdge() {
|
|
|
|
|
|
return new Shape.Edge({
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
line: {
|
|
|
|
|
|
stroke: '#A2B1C3',
|
|
|
|
|
|
strokeWidth: 2,
|
|
|
|
|
|
strokeDasharray: 0,
|
|
|
|
|
|
targetMarker: {
|
|
|
|
|
|
name: 'block',
|
|
|
|
|
|
width: 12,
|
|
|
|
|
|
height: 8,
|
|
|
|
|
|
},
|
|
|
|
|
|
sourceMarker: null,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
zIndex: 0,
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
validateConnection({ targetMagnet }) {
|
|
|
|
|
|
return !!targetMagnet
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
highlighting: {
|
|
|
|
|
|
magnetAdsorbed: {
|
|
|
|
|
|
name: 'stroke',
|
|
|
|
|
|
args: {
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
fill: '#5F95FF',
|
|
|
|
|
|
stroke: '#5F95FF',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
|
|
|
|
|
// #endregion
|
|
|
|
|
|
|
|
|
|
|
|
// #region 使用插件
|
|
|
|
|
|
graph
|
2026-01-13 17:39:09 +08:00
|
|
|
|
.use(
|
2026-01-13 15:11:39 +08:00
|
|
|
|
new Transform({
|
|
|
|
|
|
resizing: false, // 通过拖拽边缘调整节点大小
|
|
|
|
|
|
rotating: true, // 允许旋转节点
|
|
|
|
|
|
// scaling 不是 Transform 插件的有效配置项,已移除
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
|
|
|
|
|
.use(
|
|
|
|
|
|
new Selection({
|
|
|
|
|
|
rubberband: true,
|
|
|
|
|
|
showNodeSelectionBox: true,
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
|
|
|
|
|
.use(new Snapline())
|
|
|
|
|
|
.use(new Keyboard())
|
|
|
|
|
|
.use(new Clipboard())
|
|
|
|
|
|
.use(new History())
|
|
|
|
|
|
// #endregion
|
|
|
|
|
|
|
|
|
|
|
|
// #region 初始化 stencil
|
|
|
|
|
|
const stencil = new Stencil({
|
|
|
|
|
|
title: '',
|
|
|
|
|
|
target: graph,
|
|
|
|
|
|
stencilGraphWidth: 300,
|
|
|
|
|
|
stencilGraphHeight: 180,
|
|
|
|
|
|
stencilGraphOptions: { panning: false },
|
|
|
|
|
|
collapsable: false,
|
|
|
|
|
|
groups: [
|
|
|
|
|
|
// {
|
|
|
|
|
|
// title: '基础流程图',
|
|
|
|
|
|
// name: 'group1',
|
|
|
|
|
|
// },
|
|
|
|
|
|
{
|
|
|
|
|
|
collapsable: false,
|
|
|
|
|
|
title: '设备',
|
|
|
|
|
|
name: 'group2',
|
|
|
|
|
|
graphHeight: 550,
|
|
|
|
|
|
layoutOptions: {
|
|
|
|
|
|
rowHeight: 120,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
collapsable: false,
|
|
|
|
|
|
title: '管线',
|
|
|
|
|
|
name: 'group3',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
graphHeight: 400,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
layoutOptions: {
|
2026-01-13 17:39:09 +08:00
|
|
|
|
rowHeight: 120,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
layoutOptions: {
|
|
|
|
|
|
columns: 2,
|
|
|
|
|
|
columnWidth: 140,
|
|
|
|
|
|
rowHeight: 100,
|
|
|
|
|
|
},
|
|
|
|
|
|
})
|
2026-01-13 17:39:09 +08:00
|
|
|
|
graph.on('node:contextmenu', ({ e, node }) => {
|
|
|
|
|
|
selectedNode.value = node
|
|
|
|
|
|
e.preventDefault()
|
|
|
|
|
|
// 显示自定义上下文菜单,包含箭头样式选项
|
|
|
|
|
|
const pos = node.position?.() || { x: 0, y: 0 }
|
|
|
|
|
|
showContextMenu(pos.x, pos.y)
|
|
|
|
|
|
})
|
|
|
|
|
|
function showContextMenu(x: number, y: number) {
|
|
|
|
|
|
left.value = x + 260
|
|
|
|
|
|
top.value = y - 50
|
2026-01-13 15:11:39 +08:00
|
|
|
|
|
2026-01-13 17:39:09 +08:00
|
|
|
|
isMenuShow.value = true
|
|
|
|
|
|
// 创建并显示上下文菜单
|
|
|
|
|
|
// 包含箭头样式选项
|
|
|
|
|
|
}
|
2026-01-13 15:11:39 +08:00
|
|
|
|
// 监听Stencil内部的Dnd事件
|
|
|
|
|
|
stencil.on('dnd:start', (args:any) => {
|
|
|
|
|
|
console.log('Stencil节点开始拖拽', args)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
stencil.on('dnd:drag', (args:any) => {
|
|
|
|
|
|
console.log('Stencil节点拖拽中', args)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
stencil.on('dnd:end', (args:any) => {
|
|
|
|
|
|
console.log('Stencil节点结束拖拽', args)
|
|
|
|
|
|
})
|
|
|
|
|
|
document
|
|
|
|
|
|
.getElementById('stencil')
|
|
|
|
|
|
?.appendChild(stencil.container as HTMLElement)
|
|
|
|
|
|
|
|
|
|
|
|
// 监听Graph中节点创建事件
|
2026-01-13 17:39:09 +08:00
|
|
|
|
graph.on('node:added', (args) => {
|
|
|
|
|
|
console.log('节点已添加到画布', args)
|
|
|
|
|
|
const { node } = args
|
|
|
|
|
|
|
|
|
|
|
|
// 检查是否是管线节点
|
|
|
|
|
|
if (node.data && node.data.lineStyle) {
|
|
|
|
|
|
// 应用管线样式到连接线
|
|
|
|
|
|
const lineStyle = node.data.lineStyle
|
|
|
|
|
|
graph.options.connecting.createEdge = () => {
|
|
|
|
|
|
return new Shape.Edge({
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
line: {
|
|
|
|
|
|
stroke: '#A2B1C3',
|
|
|
|
|
|
strokeWidth: 2,
|
|
|
|
|
|
strokeDasharray: lineStyle.strokeDasharray,
|
|
|
|
|
|
targetMarker: lineStyle.targetMarker,
|
|
|
|
|
|
sourceMarker: lineStyle.sourceMarker
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
zIndex: 0
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
// 移除管线节点,因为它只是一个样式选择器
|
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
graph.removeNode(node)
|
|
|
|
|
|
}, 100)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 设置固定大小
|
|
|
|
|
|
node.size(160, 160)
|
|
|
|
|
|
// 去掉背景节点
|
|
|
|
|
|
node.attr('body/fill', 'none')
|
|
|
|
|
|
// 图片居中
|
|
|
|
|
|
node.attr('image/refX', 0)
|
|
|
|
|
|
node.attr('image/refY', 0)
|
|
|
|
|
|
node.attr('image/width', 160) // 修改宽度
|
|
|
|
|
|
node.attr('image/height', 160) // 修改高度
|
|
|
|
|
|
// 删除节点上的文字
|
|
|
|
|
|
node.attr('text/text', '')
|
|
|
|
|
|
node.attr('label/text', '')
|
2026-01-13 15:11:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-13 17:39:09 +08:00
|
|
|
|
})
|
2026-01-13 15:11:39 +08:00
|
|
|
|
// #endregion
|
|
|
|
|
|
|
|
|
|
|
|
// #region 快捷键与事件
|
|
|
|
|
|
graph.bindKey(['meta+c', 'ctrl+c'], () => {
|
|
|
|
|
|
const cells = graph.getSelectedCells()
|
|
|
|
|
|
if (cells.length) {
|
|
|
|
|
|
graph.copy(cells)
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
})
|
|
|
|
|
|
graph.bindKey(['meta+x', 'ctrl+x'], () => {
|
|
|
|
|
|
const cells = graph.getSelectedCells()
|
|
|
|
|
|
if (cells.length) {
|
|
|
|
|
|
graph.cut(cells)
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
})
|
|
|
|
|
|
graph.bindKey(['meta+v', 'ctrl+v'], () => {
|
|
|
|
|
|
if (!graph.isClipboardEmpty()) {
|
|
|
|
|
|
const cells = graph.paste({ offset: 32 })
|
|
|
|
|
|
graph.cleanSelection()
|
|
|
|
|
|
graph.select(cells)
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// undo redo
|
|
|
|
|
|
graph.bindKey(['meta+z', 'ctrl+z'], () => {
|
|
|
|
|
|
if (graph.canUndo()) {
|
|
|
|
|
|
graph.undo()
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
})
|
|
|
|
|
|
graph.bindKey(['meta+shift+z', 'ctrl+shift+z'], () => {
|
|
|
|
|
|
if (graph.canRedo()) {
|
|
|
|
|
|
graph.redo()
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// select all
|
|
|
|
|
|
graph.bindKey(['meta+a', 'ctrl+a'], () => {
|
|
|
|
|
|
const nodes = graph.getNodes()
|
|
|
|
|
|
if (nodes) {
|
|
|
|
|
|
graph.select(nodes)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// delete
|
|
|
|
|
|
graph.bindKey('backspace', () => {
|
|
|
|
|
|
const cells = graph.getSelectedCells()
|
|
|
|
|
|
if (cells.length) {
|
|
|
|
|
|
graph.removeCells(cells)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// zoom
|
|
|
|
|
|
graph.bindKey(['ctrl+1', 'meta+1'], () => {
|
|
|
|
|
|
const zoom = graph.zoom()
|
|
|
|
|
|
if (zoom < 1.5) {
|
|
|
|
|
|
graph.zoom(0.1)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
graph.bindKey(['ctrl+2', 'meta+2'], () => {
|
|
|
|
|
|
const zoom = graph.zoom()
|
|
|
|
|
|
if (zoom > 0.5) {
|
|
|
|
|
|
graph.zoom(-0.1)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 控制连接桩显示/隐藏
|
|
|
|
|
|
const showPorts = (ports: NodeListOf<SVGElement>, show: boolean) => {
|
|
|
|
|
|
for (let i = 0, len = ports.length; i < len; i += 1) {
|
|
|
|
|
|
ports[i].style.visibility = show ? 'visible' : 'hidden'
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
graph.on('node:mouseenter', () => {
|
|
|
|
|
|
const container = document.getElementById('graph-container') as HTMLElement
|
|
|
|
|
|
const ports = container.querySelectorAll(
|
|
|
|
|
|
'.x6-port-body',
|
|
|
|
|
|
) as NodeListOf<SVGElement>
|
|
|
|
|
|
showPorts(ports, true)
|
|
|
|
|
|
})
|
|
|
|
|
|
graph.on('node:mouseleave', () => {
|
|
|
|
|
|
const container = document.getElementById('graph-container') as HTMLElement
|
|
|
|
|
|
const ports = container.querySelectorAll(
|
|
|
|
|
|
'.x6-port-body',
|
|
|
|
|
|
) as NodeListOf<SVGElement>
|
|
|
|
|
|
showPorts(ports, false)
|
|
|
|
|
|
})
|
|
|
|
|
|
// #endregion
|
|
|
|
|
|
|
|
|
|
|
|
// #region 初始化图形
|
|
|
|
|
|
const ports = {
|
|
|
|
|
|
groups: {
|
|
|
|
|
|
top: {
|
|
|
|
|
|
position: 'top',
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
circle: {
|
|
|
|
|
|
r: 4,
|
|
|
|
|
|
magnet: true,
|
|
|
|
|
|
stroke: '#5F95FF',
|
|
|
|
|
|
strokeWidth: 1,
|
|
|
|
|
|
fill: '#fff',
|
|
|
|
|
|
style: {
|
|
|
|
|
|
visibility: 'hidden',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
right: {
|
|
|
|
|
|
position: 'right',
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
circle: {
|
|
|
|
|
|
r: 4,
|
|
|
|
|
|
magnet: true,
|
|
|
|
|
|
stroke: '#5F95FF',
|
|
|
|
|
|
strokeWidth: 1,
|
|
|
|
|
|
fill: '#fff',
|
|
|
|
|
|
style: {
|
|
|
|
|
|
visibility: 'hidden',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
bottom: {
|
|
|
|
|
|
position: 'bottom',
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
circle: {
|
|
|
|
|
|
r: 4,
|
|
|
|
|
|
magnet: true,
|
|
|
|
|
|
stroke: '#5F95FF',
|
|
|
|
|
|
strokeWidth: 1,
|
|
|
|
|
|
fill: '#fff',
|
|
|
|
|
|
style: {
|
|
|
|
|
|
visibility: 'hidden',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
left: {
|
|
|
|
|
|
position: 'left',
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
circle: {
|
|
|
|
|
|
r: 4,
|
|
|
|
|
|
magnet: true,
|
|
|
|
|
|
stroke: '#5F95FF',
|
|
|
|
|
|
strokeWidth: 1,
|
|
|
|
|
|
fill: '#fff',
|
|
|
|
|
|
style: {
|
|
|
|
|
|
visibility: 'hidden',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
items: [
|
|
|
|
|
|
{
|
|
|
|
|
|
group: 'top',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
group: 'right',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
group: 'bottom',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
group: 'left',
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
}
|
|
|
|
|
|
Graph.registerNode(
|
|
|
|
|
|
'custom-image',
|
|
|
|
|
|
{
|
|
|
|
|
|
inherit: 'rect',
|
|
|
|
|
|
width: 130,
|
|
|
|
|
|
height: 100,
|
|
|
|
|
|
markup: [
|
|
|
|
|
|
{
|
|
|
|
|
|
tagName: 'rect',
|
|
|
|
|
|
selector: 'body',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
tagName: 'image',
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
tagName: 'text',
|
|
|
|
|
|
selector: 'label',
|
|
|
|
|
|
},
|
|
|
|
|
|
],
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
body: {
|
|
|
|
|
|
stroke: 'transparent',
|
|
|
|
|
|
fill: '#f8f8f8',
|
|
|
|
|
|
},
|
|
|
|
|
|
image: {
|
|
|
|
|
|
width: 62,
|
|
|
|
|
|
height:84,
|
|
|
|
|
|
refX: 35,
|
|
|
|
|
|
refY: 0,
|
|
|
|
|
|
},
|
|
|
|
|
|
label: {
|
|
|
|
|
|
refX: 45,
|
|
|
|
|
|
refY: 80,
|
|
|
|
|
|
textAnchor: 'start',
|
|
|
|
|
|
textVerticalAnchor: 'top',
|
|
|
|
|
|
fontSize: 14,
|
|
|
|
|
|
fill: '#000',
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
ports: { ...ports },
|
|
|
|
|
|
},
|
|
|
|
|
|
true,
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
// stencil.load([r1, r2, r3, r4, r5, r6], 'group1')
|
|
|
|
|
|
|
2026-01-13 17:39:09 +08:00
|
|
|
|
const imageShapes = [
|
2026-01-13 15:11:39 +08:00
|
|
|
|
{
|
|
|
|
|
|
label: '圆柱槽',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image: img1,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '扁平槽',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image:img2,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '环形槽',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image:img3,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '管束槽',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image:img4,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '萃取柱',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image:img5,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '流化床',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image:img6,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},{
|
|
|
|
|
|
label: '锥底环形槽',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image:img7,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
]
|
|
|
|
|
|
const imageNodes = imageShapes.map((item) =>
|
|
|
|
|
|
graph.createNode({
|
|
|
|
|
|
shape: 'custom-image',
|
|
|
|
|
|
label: item.label,
|
|
|
|
|
|
attrs: {
|
|
|
|
|
|
image: {
|
|
|
|
|
|
'xlink:href': item.image,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}),
|
|
|
|
|
|
)
|
2026-01-13 17:39:09 +08:00
|
|
|
|
console.log(imageNodes)
|
2026-01-13 15:11:39 +08:00
|
|
|
|
stencil.load(imageNodes, 'group2')
|
|
|
|
|
|
|
|
|
|
|
|
// 创建管线节点
|
|
|
|
|
|
const lineShapes = [
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '实线',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image: line1,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
targetMarker: null,
|
|
|
|
|
|
sourceMarker: null,
|
|
|
|
|
|
strokeDasharray: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '单箭头实线',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image: line2,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
targetMarker: {
|
|
|
|
|
|
name: 'block',
|
|
|
|
|
|
width: 12,
|
|
|
|
|
|
height: 8
|
|
|
|
|
|
},
|
|
|
|
|
|
sourceMarker: null,
|
|
|
|
|
|
strokeDasharray: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '双箭头实线',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image: line3,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
targetMarker: {
|
|
|
|
|
|
name: 'block',
|
|
|
|
|
|
width: 12,
|
|
|
|
|
|
height: 8
|
|
|
|
|
|
},
|
|
|
|
|
|
sourceMarker: {
|
|
|
|
|
|
name: 'block',
|
|
|
|
|
|
width: 12,
|
|
|
|
|
|
height: 8
|
|
|
|
|
|
},
|
|
|
|
|
|
strokeDasharray: 0
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '虚线',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image: line4,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
targetMarker: null,
|
|
|
|
|
|
sourceMarker: null,
|
|
|
|
|
|
strokeDasharray: 5
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '单箭头虚线',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image: line5,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
targetMarker: {
|
|
|
|
|
|
name: 'block',
|
|
|
|
|
|
width: 12,
|
|
|
|
|
|
height: 8
|
|
|
|
|
|
},
|
|
|
|
|
|
sourceMarker: null,
|
|
|
|
|
|
strokeDasharray: 5
|
|
|
|
|
|
},
|
|
|
|
|
|
{
|
|
|
|
|
|
label: '双箭头虚线',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
image: line6,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
targetMarker: {
|
|
|
|
|
|
name: 'block',
|
|
|
|
|
|
width: 12,
|
|
|
|
|
|
height: 8
|
|
|
|
|
|
},
|
|
|
|
|
|
sourceMarker: {
|
|
|
|
|
|
name: 'block',
|
|
|
|
|
|
width: 12,
|
|
|
|
|
|
height: 8
|
|
|
|
|
|
},
|
|
|
|
|
|
strokeDasharray: 5
|
|
|
|
|
|
}
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
const lineNodes = lineShapes.map((item) => {
|
|
|
|
|
|
// 创建一个节点来显示管线样式
|
|
|
|
|
|
const node = graph.createNode({
|
|
|
|
|
|
shape: 'custom-image',
|
|
|
|
|
|
label: item.label,
|
2026-01-13 17:39:09 +08:00
|
|
|
|
width: 130,
|
|
|
|
|
|
height: 100,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
attrs: {
|
|
|
|
|
|
image: {
|
|
|
|
|
|
'xlink:href': item.image,
|
2026-01-13 17:39:09 +08:00
|
|
|
|
width: 31,
|
|
|
|
|
|
height: 31,
|
|
|
|
|
|
refX: 50,
|
|
|
|
|
|
refY: 20
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
body: {
|
|
|
|
|
|
stroke: 'transparent',
|
2026-01-13 17:39:09 +08:00
|
|
|
|
fill: '#f8f8f8'
|
2026-01-13 15:11:39 +08:00
|
|
|
|
},
|
|
|
|
|
|
label: {
|
|
|
|
|
|
text: item.label,
|
|
|
|
|
|
refX: 60,
|
2026-01-13 17:39:09 +08:00
|
|
|
|
refY: 65,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
textAnchor: 'middle',
|
|
|
|
|
|
fontSize: 12,
|
|
|
|
|
|
fill: '#666'
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
data: {
|
|
|
|
|
|
lineStyle: {
|
|
|
|
|
|
strokeDasharray: item.strokeDasharray,
|
|
|
|
|
|
targetMarker: item.targetMarker,
|
|
|
|
|
|
sourceMarker: item.sourceMarker
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
return node
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
stencil.load(lineNodes, 'group3')
|
2026-01-13 17:39:09 +08:00
|
|
|
|
if (!graph || !projectInfo.value || !projectInfo.value.topology) return;
|
|
|
|
|
|
graph.clearCells();
|
|
|
|
|
|
const topology:any = JSON.parse(projectInfo.value.topology)
|
|
|
|
|
|
if(!topology.designData)return
|
|
|
|
|
|
graph.fromJSON(topology.designData);
|
2026-01-13 15:11:39 +08:00
|
|
|
|
// #endregion
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
function preWork() {
|
|
|
|
|
|
// 这里协助演示的代码,在实际项目中根据实际情况进行调整
|
|
|
|
|
|
const container = document.getElementById('container') as HTMLElement
|
|
|
|
|
|
const stencilContainer = document.createElement('div')
|
|
|
|
|
|
stencilContainer.id = 'stencil'
|
|
|
|
|
|
const graphContainer = document.createElement('div')
|
|
|
|
|
|
graphContainer.id = 'graph-container'
|
|
|
|
|
|
container.appendChild(stencilContainer)
|
|
|
|
|
|
container.appendChild(graphContainer)
|
|
|
|
|
|
|
|
|
|
|
|
insertCss(`
|
|
|
|
|
|
#container {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
border: 1px solid #dfe3e8;
|
|
|
|
|
|
width: 100vw;
|
|
|
|
|
|
height: calc(100vh - 60px);
|
|
|
|
|
|
}
|
|
|
|
|
|
#stencil {
|
|
|
|
|
|
width: 300px;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
border-right: 1px solid #dfe3e8;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-stencil-content{
|
|
|
|
|
|
top:0px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-stencil-title{
|
|
|
|
|
|
display: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
#graph-container {
|
|
|
|
|
|
width: calc(100% - 180px);
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-stencil {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-stencil-title {
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-stencil-group-title {
|
|
|
|
|
|
background-color: #fff !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-transform {
|
|
|
|
|
|
margin: -1px 0 0 -1px;
|
|
|
|
|
|
padding: 0px;
|
|
|
|
|
|
border: 1px solid #239edd;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-transform > div {
|
|
|
|
|
|
border: 1px solid #239edd;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-transform > div:hover {
|
|
|
|
|
|
background-color: #3dafe4;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-transform-active-handle {
|
|
|
|
|
|
background-color: #3dafe4;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-transform-resize {
|
|
|
|
|
|
border-radius: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-selection-inner {
|
|
|
|
|
|
border: 1px solid #239edd;
|
|
|
|
|
|
}
|
|
|
|
|
|
.x6-widget-selection-box {
|
|
|
|
|
|
opacity: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
`)
|
|
|
|
|
|
}
|
|
|
|
|
|
const left = ref(0)
|
|
|
|
|
|
const top = ref(0)
|
|
|
|
|
|
const isMenuShow = ref(false)
|
|
|
|
|
|
const selectedNode:any = ref(null)
|
|
|
|
|
|
function deleteNode() { // 删除节点
|
|
|
|
|
|
graph.removeNode(selectedNode.value)
|
|
|
|
|
|
isMenuShow.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
function copyNode() { // 复制节点
|
|
|
|
|
|
if (selectedNode.value) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取当前节点的位置
|
|
|
|
|
|
const position = selectedNode.value.position()
|
|
|
|
|
|
// 创建新节点,使用节点的原始数据
|
|
|
|
|
|
const newNode = selectedNode.value.clone()
|
|
|
|
|
|
// 设置新位置(偏移50px)
|
|
|
|
|
|
newNode.position(position.x + 50, position.y + 50)
|
|
|
|
|
|
// 添加到画布
|
|
|
|
|
|
graph.addNode(newNode)
|
|
|
|
|
|
isMenuShow.value = false
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('节点复制失败:', error)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function closeAntvx6() {
|
|
|
|
|
|
emit('closeAntvx6')
|
|
|
|
|
|
}
|
|
|
|
|
|
function saveDesign() { // 保存设计
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 获取画布内容并转换为JSON
|
2026-01-13 17:39:09 +08:00
|
|
|
|
const designData:any = graph.toJSON()
|
|
|
|
|
|
let cells = []
|
|
|
|
|
|
if(designData !=null && designData.cells.length>0){
|
|
|
|
|
|
cells = designData.cells
|
|
|
|
|
|
}
|
2026-01-13 15:11:39 +08:00
|
|
|
|
const topology = {
|
|
|
|
|
|
projectId: projectInfo.value.projectId,
|
|
|
|
|
|
name: projectInfo.value.name,
|
|
|
|
|
|
devices:[],
|
|
|
|
|
|
pipelines:[],
|
|
|
|
|
|
systemboundaries:[],
|
|
|
|
|
|
globalDisplay:{
|
|
|
|
|
|
device:{
|
|
|
|
|
|
showProperties:[]
|
|
|
|
|
|
},
|
|
|
|
|
|
material:{
|
|
|
|
|
|
showProperties:[]
|
|
|
|
|
|
},
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
designData:cells
|
2026-01-13 15:11:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 添加保存信息
|
|
|
|
|
|
const saveData = {
|
2026-01-13 17:39:09 +08:00
|
|
|
|
projectId: projectInfo.value.projectId,
|
2026-01-13 15:11:39 +08:00
|
|
|
|
topology: JSON.stringify(topology)
|
|
|
|
|
|
}
|
|
|
|
|
|
updateProjects(saveData).then((res:any) => {
|
2026-01-13 17:39:09 +08:00
|
|
|
|
if(res === true){
|
|
|
|
|
|
ElMessage({
|
|
|
|
|
|
type: "success",
|
|
|
|
|
|
message: "保存成功",
|
|
|
|
|
|
});
|
2026-01-13 15:11:39 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
// 返回保存的数据
|
|
|
|
|
|
return saveData
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('保存设计失败:', error)
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
function revokeClick(){
|
|
|
|
|
|
graph.undo()
|
|
|
|
|
|
}
|
|
|
|
|
|
function removeClick(){
|
|
|
|
|
|
graph.clearCells();
|
|
|
|
|
|
}
|
|
|
|
|
|
function bigClick(){
|
|
|
|
|
|
graph.zoom(0.1)
|
|
|
|
|
|
}
|
|
|
|
|
|
function smallClick(){
|
|
|
|
|
|
graph.zoom(-0.1)
|
|
|
|
|
|
}
|
2026-01-13 15:11:39 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="app-layout" @click="isMenuShow = false">
|
|
|
|
|
|
<!-- <div class="toolbar">
|
|
|
|
|
|
<div class="style-group">
|
|
|
|
|
|
<h4>实线</h4>
|
|
|
|
|
|
<button @click="setArrowStyle('single', 'solid')">单箭头</button>
|
|
|
|
|
|
<button @click="setArrowStyle('double', 'solid')">双箭头</button>
|
|
|
|
|
|
<button @click="setArrowStyle('none', 'solid')">无箭头</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="style-group">
|
|
|
|
|
|
<h4>虚线</h4>
|
|
|
|
|
|
<button @click="setArrowStyle('single', 'dashed')">单箭头虚线</button>
|
|
|
|
|
|
<button @click="setArrowStyle('double', 'dashed')">双箭头虚线</button>
|
|
|
|
|
|
<button @click="setArrowStyle('none', 'dashed')">无箭头虚线</button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div> -->
|
|
|
|
|
|
<div class="antvx6-header">
|
|
|
|
|
|
<div class="header-left-box">
|
|
|
|
|
|
<div class="return-icon-box" @click="closeAntvx6">
|
|
|
|
|
|
<img src="@/assets/x6/return.png" alt="图标" title="返回工作台" style="cursor: pointer;">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="project-name">{{ projectInfo.name }}</div>
|
|
|
|
|
|
<div class="return-icon-box">
|
|
|
|
|
|
<img src="@/assets/x6/add.png" alt="图标" title="新增模拟分析" style="cursor: pointer;">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="return-icon-box">
|
|
|
|
|
|
<img src="@/assets/x6/history.png" alt="图标" title="历史模拟分析" style="cursor: pointer;">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="header-content-box">
|
2026-01-13 17:39:09 +08:00
|
|
|
|
<div class="operation-icon-box" @click="revokeClick">
|
2026-01-13 15:11:39 +08:00
|
|
|
|
<img src="@/assets/x6/revoke.png">
|
|
|
|
|
|
<div class="operation-icon-text">撤销</div>
|
|
|
|
|
|
</div>
|
2026-01-13 17:39:09 +08:00
|
|
|
|
<div class="operation-icon-box" @click="removeClick">
|
2026-01-13 15:11:39 +08:00
|
|
|
|
<img src="@/assets/x6/redo.png">
|
|
|
|
|
|
<div class="operation-icon-text">重做</div>
|
|
|
|
|
|
</div>
|
2026-01-13 17:39:09 +08:00
|
|
|
|
<div class="operation-icon-box" @click="bigClick">
|
2026-01-13 15:11:39 +08:00
|
|
|
|
<img src="@/assets/x6/magnify.png">
|
|
|
|
|
|
<div class="operation-icon-text">放大</div>
|
|
|
|
|
|
</div>
|
2026-01-13 17:39:09 +08:00
|
|
|
|
<div class="operation-icon-box" @click="smallClick">
|
2026-01-13 15:11:39 +08:00
|
|
|
|
<img src="@/assets/x6/reduce.png">
|
|
|
|
|
|
<div class="operation-icon-text">缩小</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="operation-icon-box" @click="isDisplay = !isDisplay">
|
|
|
|
|
|
<img v-if="isDisplay" src="@/assets/x6/display.png">
|
|
|
|
|
|
<img v-else src="@/assets/x6/hide.png">
|
|
|
|
|
|
<div class="operation-icon-text">显示</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="operation-icon-box" @click="saveDesign">
|
|
|
|
|
|
<img src="@/assets/x6/save.png">
|
|
|
|
|
|
<div class="operation-icon-text">保存</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="header-left-box"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div id="container" style="position: relative;">
|
|
|
|
|
|
<div class="context-menu" v-if="isMenuShow"
|
|
|
|
|
|
:style="{left: left +'px', top: top+'px'}">
|
|
|
|
|
|
<img src="@/assets/x6/info.png" alt="图标" title="设备信息" style="cursor: pointer;">
|
|
|
|
|
|
<img src="@/assets/x6/material.png" alt="图标" title="物料信息" style="cursor: pointer;">
|
|
|
|
|
|
<img src="@/assets/x6/change.png" alt="图标" title="变动设置" style="cursor: pointer;">
|
|
|
|
|
|
<img src="@/assets/x6/copy.png" alt="图标" title="复制" style="cursor: pointer;" @click="copyNode">
|
|
|
|
|
|
<img src="@/assets/x6/del.png" alt="图标" title="删除" style="cursor: pointer;"
|
|
|
|
|
|
@click="deleteNode">
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
* {
|
|
|
|
|
|
margin: 0;
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
.app-layout {
|
|
|
|
|
|
position: fixed;
|
2026-01-13 17:39:09 +08:00
|
|
|
|
z-index: 10;
|
2026-01-13 15:11:39 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
width: 100vw;
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
font-family: 'Segoe UI', sans-serif;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar {
|
|
|
|
|
|
padding: 10px;
|
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
|
border-bottom: 1px solid #e0e0e0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
gap: 20px;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.style-group {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
margin-right: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.style-group h4 {
|
|
|
|
|
|
margin: 0 0 5px 0;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar button {
|
|
|
|
|
|
padding: 8px 16px;
|
|
|
|
|
|
border: 1px solid #d9d9d9;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
transition: all 0.3s;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar button:hover {
|
|
|
|
|
|
border-color: #1890ff;
|
|
|
|
|
|
color: #1890ff;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.toolbar button:active {
|
|
|
|
|
|
background-color: #f0f0f0;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
/* 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);
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
.sidebar-section {
|
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
.sidebar-section h3 {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
padding-bottom: 8px;
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
.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;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
.device-item:hover {
|
|
|
|
|
|
background-color: #f0f5ff;
|
|
|
|
|
|
border-color: #1890ff;
|
|
|
|
|
|
transform: translateY(-1px);
|
|
|
|
|
|
box-shadow: 0 2px 4px rgba(24, 144, 255, 0.2);
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
.device-item:active {
|
|
|
|
|
|
cursor: grabbing;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
.device-icon {
|
|
|
|
|
|
font-size: 24px;
|
|
|
|
|
|
margin-right: 12px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
width: 32px;
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
.device-name {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
/* Graph Container Styles */
|
|
|
|
|
|
.graph-container {
|
|
|
|
|
|
flex: 1;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
background-color: #fafafa;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
2026-01-13 15:11:39 +08:00
|
|
|
|
/* Drag and drop visual feedback */
|
|
|
|
|
|
.graph-container.drag-over {
|
|
|
|
|
|
background-color: #e6f7ff;
|
|
|
|
|
|
border: 2px dashed #1890ff;
|
|
|
|
|
|
}
|
2026-01-13 17:39:09 +08:00
|
|
|
|
|
|
|
|
|
|
.context-menu {
|
2026-01-13 15:11:39 +08:00
|
|
|
|
position: absolute;
|
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
border: 1px solid #dfe3e8;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
width: 240px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-around;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
<style>
|
2026-01-13 17:39:09 +08:00
|
|
|
|
.x6-widget-stencil-content {
|
|
|
|
|
|
top: 0px !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.antvx6-header {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 60px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
padding: 10px;
|
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
|
border-bottom: 1px solid #e0e0e0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header-left-box {
|
|
|
|
|
|
width: 400px;
|
|
|
|
|
|
height: 60px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.header-content-box {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.return-icon-box {
|
|
|
|
|
|
width: 30px;
|
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.return-icon-box:hover {
|
|
|
|
|
|
background-color: #eeeeee;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.antvx6-header .project-name {
|
|
|
|
|
|
font-family: 'Arial Negreta', 'Arial Normal', 'Arial';
|
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
color: #333333;
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.antvx6-header .operation-icon-box {
|
|
|
|
|
|
width: 46px;
|
|
|
|
|
|
height: 46px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-content: center;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
margin-right: 20px;
|
|
|
|
|
|
font-family: '微软雅黑';
|
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
|
font-style: normal;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #4B4B4B;
|
|
|
|
|
|
padding-top: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.antvx6-header .operation-icon-box:hover {
|
|
|
|
|
|
background-color: #eeeeee;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.operation-icon-text {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
padding-top: 5px;
|
|
|
|
|
|
}
|
2026-01-13 15:11:39 +08:00
|
|
|
|
</style>
|