123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- <template>
- <div class="container">
- <div id="graph_container" ref="graphRef"></div>
- </div>
- </template>
- <script setup>
- import { ref, onMounted } from 'vue'
- import { Graph } from '@antv/x6'
- import { register } from '@antv/x6-vue-shape'
- import ProgressNode from './common/ProgressNode.vue'
- import ProgressChildrenNode from './common/ProgressChildrenNode.vue'
- import imgIcon from '../../../../assets/img/节点连线箭头.png'
- // **注册 Vue 组件节点**
- register({
- shape: 'custom-vue-node',
- width: 140,
- height: 60,
- component: ProgressNode,
- getComponent(node) {
- return {
- component: ProgressNode,
- props: {
- label: node.getData().label,
- state: node.getData().state
- }
- }
- }
- })
- register({
- shape: 'custom-vue-children-node',
- width: 140,
- height: 60,
- component: ProgressChildrenNode,
- getComponent(node) {
- return {
- component: ProgressChildrenNode,
- props: {
- label: node.getData().label,
- state: node.getData().state
- }
- }
- }
- })
- // **画布**
- const graphRef = ref(null)
- let graph = null
- // **数据**
- const NodeData = {
- dagNode: [
- {
- level: 1,
- fId: 1,
- nodeId: 1,
- nodeName: '发现报告',
- state: '已完成',
- x: 10,
- y: 80
- },
- {
- level: 2,
- nodeId: '1-1',
- nodeName: '信息接报',
- state: '已完成',
- x: 240,
- y: 0
- },
- {
- level: 2,
- nodeId: '1-2',
- nodeName: '信息核实',
- state: '已完成',
- x: 240,
- y: 55
- },
- {
- level: 2,
- nodeId: '1-3',
- nodeName: '快速评估',
- state: '已完成',
- x: 240,
- y: 110
- },
- {
- level: 2,
- nodeId: '1-4',
- nodeName: '信息上报',
- state: '已完成',
- x: 240,
- y: 165
- },
- {
- level: 1,
- fId: 2,
- nodeId: 2,
- nodeName: '个案调查处置',
- state: '进行中',
- x: 10,
- y: 295
- },
- {
- level: 2,
- twoFId: '2-1',
- nodeId: '2-1',
- nodeName: '李梦康',
- state: '已完成',
- x: 240,
- y: 240
- },
- {
- level: 2,
- twoFId: '2-2',
- nodeId: '2-2',
- nodeName: '毛超',
- state: '已完成',
- x: 240,
- y: 295
- },
- {
- level: 2,
- twoFId: '2-3',
- nodeId: '2-3',
- nodeName: '贾子敏',
- state: '进行中',
- x: 240,
- y: 350
- },
- {
- level: 3,
- nodeId: '2-2-1',
- nodeName: '病例管理',
- state: '已完成',
- x: 420,
- y: 210
- },
- {
- level: 3,
- nodeId: '2-2-2',
- nodeName: '流行病学调查',
- state: '已完成',
- x: 420,
- y: 265
- },
- {
- level: 3,
- nodeId: '2-2-3',
- nodeName: '实验室检测',
- state: '已完成',
- x: 420,
- y: 320
- },
- {
- level: 3,
- nodeId: '2-2-4',
- nodeName: '风险评估',
- state: '已完成',
- x: 420,
- y: 375
- },
- {
- level: 1,
- fId: 3,
- nodeId: 3,
- nodeName: '风险人员和环境排查管控',
- state: '进行中',
- x: 10,
- y: 480
- },
- {
- level: 2,
- twoFId: '3-1',
- nodeId: '3-1',
- nodeName: '段伟',
- state: '已完成',
- x: 240,
- y: 420
- },
- {
- level: 2,
- twoFId: '3-2',
- nodeId: '3-2',
- nodeName: '陆成奇',
- state: '已完成',
- x: 240,
- y: 475
- },
- {
- level: 2,
- twoFId: '3-3',
- nodeId: '3-3',
- nodeName: '曾强',
- state: '进行中',
- x: 240,
- y: 530
- },
- {
- level: 3,
- twoFId: '3-3-1',
- nodeId: '3-3-1',
- nodeName: '病例管理',
- state: '已完成',
- x: 420,
- y:450
- },
- {
- level: 3,
- twoFId: '3-3-2',
- nodeId: '3-3-2',
- nodeName: '流行病学调查',
- state: '已完成',
- x: 420,
- y:505
- },
- {
- level: 3,
- twoFId: '3-3-2',
- nodeId: '3-3-3',
- nodeName: '实验室检测',
- state: '进行中',
- x: 420,
- y:560
- },
- {
- level: 3,
- twoFId: '3-3-3',
- nodeId: '3-3-4',
- nodeName: '风险人员管控',
- state: '进行中',
- x: 420,
- y:615
- },
- {
- level: 3,
- twoFId: '3-3-4',
- nodeId: '3-3-5',
- nodeName: '风险场所管控',
- state: '进行中',
- x: 420,
- y:670
- },
- { level: 1, fId: 4, nodeId: 4, nodeName: '区域风险排查管控', state: '未开始', x: 10, y: 620 },
- { level: 1, fId: 5, nodeId: 5, nodeName: '结案', x: 10, y: 720 }
- ],
- dagLine: [
- { from: 1, to: 2 },
- { from: 2, to: 3 },
- { from: 3, to: 4 },
- { from: 4, to: 5 },
- { from: 1, to: '1-1' },
- { from: 1, to: '1-2' },
- { from: 1, to: '1-3' },
- { from: 1, to: '1-4' },
- { from: 2, to: '2-1' },
- { from: 2, to: '2-2' },
- { from: 2, to: '2-3' },
- { from: '2-2', to: '2-2-1' },
- { from: '2-2', to: '2-2-2' },
- { from: '2-2', to: '2-2-3' },
- { from: '2-2', to: '2-2-4' },
- { from: 3, to: '3-1' },
- { from: 3, to: '3-2' },
- { from: 3, to: '3-3' },
- { from: '3-3', to: '3-3-1' },
- { from: '3-3', to: '3-3-2' },
- { from: '3-3', to: '3-3-3' },
- { from: '3-3', to: '3-3-4' },
- { from: '3-3', to: '3-3-5' },
- ]
- }
- // **自动合并多条连线**
- const mergeConnections = edges => {
- const targetMap = {}
- // 统计指向同一 target 的 source
- edges.forEach(edge => {
- if (!targetMap[edge.to]) {
- targetMap[edge.to] = []
- }
- targetMap[edge.to].push(edge.from)
- })
- const newEdges = []
- Object.keys(targetMap).forEach(target => {
- const sources = targetMap[target]
- if (sources.length > 1) {
- // 1. 创建“合并节点”
- const mergeNodeId = `merge_${target}`
- newEdges.push(...sources.map(src => ({ from: src, to: mergeNodeId })))
- newEdges.push({ from: mergeNodeId, to: target })
- } else {
- newEdges.push({ from: sources[0], to: target })
- }
- })
- return newEdges
- }
- // **计算竖排三列布局**
- // const computeLayout = () => {
- // const levels = {}
- // const columnSpacing = 160 // 列间距
- // const rowSpacing = 72 // 行间距
- // // **按 level 存储节点**
- // NodeData.dagNode.forEach(node => {
- // if (!levels[node.level]) {
- // levels[node.level] = []
- // }
- // levels[node.level].push(node)
- // })
- // const nodePositions = {}
- // let x = 50 // 第一列的 x 轴位置
- // Object.keys(levels).forEach(level => {
- // let y = 50 // 第一行 y 轴初始位置
- // const nodes = levels[level]
- // nodes.forEach(node => {
- // nodePositions[node.nodeId] = { x, y }
- // y += rowSpacing // **每个节点往下排列**
- // })
- // x += columnSpacing // **每列向右移动**
- // })
- // return nodePositions
- // }
- // **初始化 X6 画布**
- onMounted(() => {
- graph = new Graph({
- container: graphRef.value,
- width: 580,
- height: 801,
- connecting: {
- router: 'orth' // 自动优化连线
- // connector: 'smooth', // 平滑曲线
- // anchor: 'center',
- // connectionPoint: 'anchor',
- // allowBlank: false,
- // snap: true
- }
- })
- // const nodePositions = computeLayout()
- // **添加节点**
- NodeData.dagNode.forEach(node => {
- graph.addNode({
- id: node.nodeId,
- shape: node.level === 1 ? 'custom-vue-node' : 'custom-vue-children-node',
- x: node.x,
- y: node.y,
- data: {
- label: node.nodeName,
- state: node.state
- }
- })
- })
- // **处理合并连线**
- const mergedEdges = mergeConnections(NodeData.dagLine)
- // **添加合并后的连线**
- mergedEdges.forEach(edge => {
- graph.addEdge({
- source: String(edge.from),
- target: String(edge.to),
- attrs: {
- line: {
- stroke: '#5B8FF9',
- strokeDasharray: '5 5',
- strokeWidth: 2,
- targetMarker: {
- tagName: 'image',
- 'xlink:href': imgIcon,
- width: 24,
- height: 24,
- y: -12,
- x:-10,
- transform: 'rotate(0)' // 旋转箭头图片
- }
- }
- },
- router: { name: 'manhattan' }, // 自动贴合边缘
- connector: { name: 'rounded' } // 让线条更圆滑
- })
- })
- })
- </script>
- <style scoped>
- .container {
- width: 100%;
- height: 100%;
- }
- #graph_container {
- width: 100%;
- height: 100%;
- }
- </style>
|