如何在Vue项目中实现与表格搭配的拓扑图插件?

求助拓扑图插件?
使用的框架是vue3+ElemntPlus
可以与表格搭配使用的
或者有没有实现这种功能的思路?

在网上找了一些插件 如Vue-D3-Network等 但是都没办法和表格配合使用

阅读 382
avatarAI BotBETA

这里推荐两种实现方案,并提供联动思路:

  1. 组合使用现有库方案
    推荐使用 Vis.js + ElementPlus Table 联动实现
template>
  div class="container">
    el-table 
      :data="tableData" 
      @row-click="handleRowClick"
      style="width: 40%">
      <!-- 表格列定义 -->
    </el-table>

    div ref="network" style="width: 60%; height: 600px"></div>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { Network } from 'vis-network'
import 'vis-network/styles/vis-network.css'

// 统一数据源
const nodes = ref(new DataSet([
  { id: 1, label: '节点1' },
  { id: 2, label: '节点2' }
]))

const edges = ref(new DataSet([
  { from: 1, to: 2 }
]))

const tableData = ref([
  { id: 1, name: '节点1' },
  { id: 2, name: '节点2' }
])

// 网络图配置
const options = {
  nodes: {
    borderWidth: 2,
    shape: 'box'
  },
  interaction: {
    hover: true
  }
}

let network = null

onMounted(() => {
  network = new Network(
    network.value,
    { nodes: nodes.value, edges: edges.value },
    options
  )

  // 图表点击事件
  network.on("click", (params) => {
    if(params.nodes.length) {
      const nodeId = params.nodes[0]
      highlightTableRow(nodeId)
    }
  })
})

// 表格行点击处理
const handleRowClick = (row) => {
  network.selectNodes([row.id])
  network.focus(row.id, { animation: true })
}

// 高亮表格行
const highlightTableRow = (nodeId) => {
  // 通过操作DOM或数据驱动方式高亮对应行
}
</script>

<style>
.container {
  display: flex;
  gap: 20px;
}
</style>
  1. 实现思路
  2. 数据同步:维护统一的状态管理(Pinia)
  3. 交互联动

    • 表格hover/click时通过CSS类名高亮对应图形节点
    • 图形节点点击时更新表格当前选中行状态
    • 双向数据绑定同步选中状态
  4. 布局控制:使用flex布局实现左右/上下排版
  5. 性能优化:对大数据量使用虚拟滚动
  6. 推荐库选择
  7. Vis.js:成熟的网络拓扑库,支持复杂交互
  8. AntV G6:阿里图形引擎,更适合企业级应用
  9. D3.js:高度定制化但需要较多开发量

安装依赖:

npm install vis-network vis-data

建议优先采用Vis.js方案,其交互API完善且社区资源丰富。关键点在于通过统一数据源和事件总线实现组件间通信,同时注意保持可视化层与业务逻辑层的分离。

1 个回答

蚂蚁G6和X6,还有GoJS和mxGraph/diagrams.net

<template>
  <div class="container">
    <el-row :gutter="20">
      <el-col :span="10">
        <!-- 左侧表格 -->
        <el-table :data="tableData" @row-click="handleRowClick">
          <!-- 表格列定义 -->
        </el-table>
      </el-col>
      <el-col :span="14">
        <!-- 右侧拓扑图 -->
        <div ref="graphContainer" class="graph-container"></div>
      </el-col>
    </el-row>
  </div>
</template>
<script setup>
import { ref, onMounted, reactive } from 'vue'
import { Graph } from '@antv/g6'

const tableData = ref([
  {
    id: '1',
    name: '关于xxxx的工作',
    description: 'xxxx的任务'
  },
  //...
])

// 拓扑图数据
const graphData = reactive({
  nodes: [],
  edges: []
})

// 图实例
let graph = null
const graphContainer = ref(null)

// 初始化图
onMounted(() => {
  initGraph()
  // 初始化时加载数据
  loadGraphData()
})

// 初始化图实例
const initGraph = () => {
  const container = graphContainer.value
  const width = container.scrollWidth
  const height = container.scrollHeight || 500
  
  graph = new Graph({
    container,
    width,
    height,
    modes: {
      default: ['drag-canvas', 'zoom-canvas', 'drag-node']
    },
    layout: {
      type: 'dagre',
      rankdir: 'TB', // 从上到下布局
      nodesep: 50,
      ranksep: 70
    },
    defaultNode: {
      type: 'rect',
      size: [180, 60],
      style: {
        radius: 5,
        fill: '#C6E5FF',
        stroke: '#5B8FF9',
        lineWidth: 1
      },
      labelCfg: {
        style: {
          fill: '#000',
          fontSize: 12
        }
      }
    },
    defaultEdge: {
      type: 'polyline',
      style: {
        radius: 5,
        offset: 30,
        endArrow: true,
        lineWidth: 1,
        stroke: '#C2C8D5'
      }
    }
  })
}

const loadGraphData = () => {

  const data = {
    nodes: [
      { id: 'node1', label: '关于xxxx工作的执行\nxxxxxxxx的任务', style: { fill: '#C6E5FF' } },
      { id: 'node2', label: '关于xxxx工作的执行\nxxxxxxxx的任务', style: { fill: '#95DE64' } },
      { id: 'node3', label: '关于xxxx工作的执行\nxxxxxxxx的任务', style: { fill: '#5B8FF9' } }
    ],
    edges: [
      { source: 'node1', target: 'node2' },
      { source: 'node2', target: 'node3' }
    ]
  }
  
  graphData.nodes = data.nodes
  graphData.edges = data.edges
  
  graph.data(data)
  graph.render()
  graph.fitView()
}


const handleRowClick = (row) => {

  const nodeId = `node${row.id}` 
  graph.setItemState(nodeId, 'selected', true)
  
  graph.focusItem(nodeId)
}
</script>

<style scoped>
.container {
  width: 100%;
  height: 100%;
}
.graph-container {
  height: 600px;
  border: 1px solid #ddd;
  border-radius: 4px;
}
</style>
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏