推荐一个支持 vue2、vue3 甘特图 vxe-gantt 秒级渲染万级数据量,国内优秀的插件,高性能的企业级甘特图组件,支持常用的项目管理甘特图功能、自定义日期轴、右键菜单、可编辑、拖拽任务、依赖线、里程碑等等功能非常多。
接下来做个10万行任务以内的渲染量测试,不仅功能强大,渲染性能也是非常强的。

https://gantt.vxeui.com/

列表虚拟滚动

纵向虚拟滚动,设置 virtual-y-config={ enabled: true, gt: 0 } 和 height | max-height 来开启

extend_gantt_chart_gantt_scroll_vertical

<template>
  <div>
    <vxe-select v-model="rowSize" :options="dataOptions" @change="changeRowSizeEvent"></vxe-select>

    <vxe-gantt ref="ganttRef" v-bind="ganttOptions"></vxe-gantt>
  </div>
</template>

<script setup>
import { ref, reactive } from 'vue'
import { VxeUI } from 'vxe-pc-ui'

const ganttRef = ref()
const rowSize = ref(500)

const dataOptions = ref([
  { label: '加载 3 行', value: 3 },
  { label: '加载 20 行', value: 20 },
  { label: '加载 100 行', value: 100 },
  { label: '加载 500 行', value: 500 },
  { label: '加载 1000 行', value: 1000 },
  { label: '加载 5000 行', value: 5000 },
  { label: '加载 10000 行', value: 10000 },
  { label: '加载 50000 行', value: 50000 },
  { label: '加载 100000 行', value: 100000 }
])
const ganttOptions = reactive({
  border: true,
  loading: false,
  showOverflow: true,
  showHeaderOverflow: true,
  showFooterOverflow: true,
  height: 600,
  rowConfig: {
    keyField: 'id' // 行主键
  },
  taskBarConfig: {
    showProgress: true, // 是否显示进度条
    showContent: true, // 是否在任务条显示内容
    moveable: true, // 是否允许拖拽任务移动日期
    resizable: true, // 是否允许拖拽任务调整日期
    barStyle: {
      round: true, // 圆角
      bgColor: '#fca60b', // 任务条的背景颜色
      completedBgColor: '#65c16f' // 已完成部分任务条的背景颜色
    }
  },
  taskViewConfig: {
    tableStyle: {
      width: 480 // 表格宽度
    }
  },
  virtualYConfig: {
    gt: 0,
    enabled: true
  },
  columns: [
    { type: 'seq', width: 70 },
    { field: 'title', title: '任务名称' },
    { field: 'start', title: '开始时间', width: 100 },
    { field: 'end', title: '结束时间', width: 100 },
    { field: 'progress', title: '进度(%)', width: 80 }
  ],
  data: []
})

// 模拟后端数据
const loadList = (size = 200) => {
  ganttOptions.loading = true
  setTimeout(() => {
    const dataList = []
    for (let i = 0; i < size; i++) {
      dataList.push({
        id: 10000 + i,
        title: `任务${i + 1}`,
        start: i % 3 ? '2024-03-03' : i % 2 ? '2024-03-10' : '2024-03-22',
        end: i % 3 ? '2024-03-11' : i % 2 ? '2024-03-19' : '2024-04-04',
        progress: i % 2 ? 50 : 30
      })
    }
    const $gantt = ganttRef.value
    if ($gantt) {
      const startTime = Date.now()
      $gantt.loadData(dataList).then(() => {
        ganttOptions.loading = false
        VxeUI.modal.message({
          content: `加载时间 ${Date.now() - startTime} 毫秒`,
          status: 'success'
        })
      })
    } else {
      ganttOptions.loading = false
    }
  }, 150)
}

const changeRowSizeEvent = () => {
  loadList(rowSize.value)
}

loadList(rowSize.value)
</script>

子任务虚拟滚动

加载1万节点的子任务基本在1秒以内

extend_gantt_chart_gantt_scroll_tree

<template>
  <div>
    <vxe-select v-model="rowSize" :options="dataOptions" @change="changeRowSizeEvent"></vxe-select>

    <vxe-gantt ref="ganttRef" v-bind="ganttOptions"></vxe-gantt>
  </div>
</template>

<script setup>
import { ref, reactive, onMounted } from 'vue'
import { VxeUI } from 'vxe-pc-ui'

const ganttRef = ref()
const rowSize = ref(500)

const dataOptions = ref([
  { label: '加载 3 行', value: 3 },
  { label: '加载 20 行', value: 20 },
  { label: '加载 100 行', value: 100 },
  { label: '加载 500 行', value: 500 },
  { label: '加载 1000 行', value: 1000 },
  { label: '加载 5000 行', value: 5000 },
  { label: '加载 10000 行', value: 10000 },
  { label: '加载 50000 行', value: 50000 },
  { label: '加载 100000 行', value: 100000 }
])

const ganttOptions = reactive({
  border: true,
  showOverflow: true,
  showHeaderOverflow: true,
  showFooterOverflow: true,
  height: 600,
  treeConfig: {
    transform: true,
    rowField: 'id',
    parentField: 'parentId'
  },
  taskBarConfig: {
    showProgress: true, // 是否显示进度条
    showContent: true, // 是否在任务条显示内容
    moveable: true, // 是否允许拖拽任务移动日期
    resizable: true, // 是否允许拖拽任务调整日期
    barStyle: {
      round: true, // 圆角
      bgColor: '#fca60b', // 任务条的背景颜色
      completedBgColor: '#65c16f' // 已完成部分任务条的背景颜色
    }
  },
  taskViewConfig: {
    tableStyle: {
      width: 480 // 表格宽度
    }
  },
  virtualYConfig: {
    gt: 0,
    enabled: true
  },
  columns: [
    { type: 'seq', width: 70 },
    { field: 'title', title: '任务名称', treeNode: true },
    { field: 'start', title: '开始时间', width: 100 },
    { field: 'end', title: '结束时间', width: 100 }
  ],
  data: []
})

// 模拟后端数据
const loadTreeData = (nodeSize) => {
  ganttOptions.loading = true
  setTimeout(() => {
    const dataList = []
    let idCounter = 1000000
    const rootCount = Math.floor(nodeSize / 2)
    const roots = []
    for (let i = 0; i < rootCount; i++) {
      const rootNode = {
        id: idCounter++,
        parentId: null,
        title: `任务${i + 1}`,
        start: i % 3 ? '2024-03-03' : i % 2 ? '2024-03-10' : '2024-03-22',
        end: i % 3 ? '2024-03-11' : i % 2 ? '2024-03-19' : '2024-04-04',
        progress: i % 2 ? 50 : 30
      }
      roots.push(rootNode)
      dataList.push(rootNode)
    }
    let generatedCount = rootCount
    const secondLevelNodes = []
    const secondLevelCount = Math.min(Math.floor(Math.random() * (nodeSize - generatedCount)) + 1, nodeSize - generatedCount)
    for (let i = 0; i < secondLevelCount; i++) {
      const parent = roots[Math.floor(Math.random() * roots.length)]
      const node = {
        id: idCounter++,
        parentId: parent.id,
        title: `任务${i + 1}`,
        start: i % 3 ? '2024-03-05' : i % 2 ? '2024-03-8' : '2024-03-17',
        end: i % 3 ? '2024-03-09' : i % 2 ? '2024-03-18' : '2024-03-27',
        progress: i % 2 ? 50 : 30
      }
      secondLevelNodes.push(node)
      dataList.push(node)
      generatedCount++
    }
    if (generatedCount < nodeSize) {
      const thirdLevelCount = nodeSize - generatedCount
      for (let i = 0; i < thirdLevelCount; i++) {
        let parent
        if (secondLevelNodes.length > 0) {
          parent = secondLevelNodes[Math.floor(Math.random() * secondLevelNodes.length)]
        } else {
          parent = roots[Math.floor(Math.random() * roots.length)]
        }
        const node = {
          id: idCounter++,
          parentId: parent.id,
          title: `任务${i + 1}`,
          start: i % 3 ? '2024-04-03' : i % 2 ? '2024-04-08' : '2024-04-14',
          end: i % 3 ? '2024-04-07' : i % 2 ? '2024-04-15' : '2024-04-22',
          progress: i % 2 ? 50 : 30
        }
        dataList.push(node)
        generatedCount++
      }
    }
    const $gantt = ganttRef.value
    if ($gantt) {
      const startTime = Date.now()
      $gantt.loadData(dataList).then(() => {
        ganttOptions.loading = false
        VxeUI.modal.message({
          content: `加载时间 ${Date.now() - startTime} 毫秒`,
          status: 'success'
        })
      })
    } else {
      ganttOptions.loading = false
    }
  }, 150)
}

const changeRowSizeEvent = () => {
  loadTreeData(rowSize.value)
}

onMounted(() => {
  loadTreeData(rowSize.value)
})
</script>

自定义日期轴,多维度日期切换

在这里插入图片描述

里程碑

通过设置 task-bar-milestone-config 和 type=moveable 启用里程碑类型,当设置为里程碑类型时,只需要设置 start 开始日期就可以,无需设置 end 结束日期
在这里插入图片描述

<template>
  <div>
    <vxe-gantt v-bind="ganttOptions"></vxe-gantt>
  </div>
</template>

<script setup>
import { reactive } from 'vue'
import { VxeGanttTaskType } from 'vxe-gantt'

const ganttOptions = reactive({
  border: true,
  height: 500,
  taskBarConfig: {
    showProgress: true, // 是否显示进度条
    showContent: true, // 是否在任务条显示内容
    moveable: true, // 是否允许拖拽任务移动日期
    resizable: true, // 是否允许拖拽任务调整日期
    barStyle: {
      round: true, // 圆角
      bgColor: '#fca60b', // 任务条的背景颜色
      completedBgColor: '#65c16f' // 已完成部分任务条的背景颜色
    }
  },
  taskViewConfig: {
    tableStyle: {
      width: 280 // 表格宽度
    },
    gridding: {
      leftSpacing: 1, // 左侧间距多少列
      rightSpacing: 4 // 右侧间距多少列
    }
  },
  taskBarMilestoneConfig: {
    // 自定义里程碑图标状态颜色
    iconStatus ({ row }) {
      if (row.id === 10001) {
        return 'error'
      }
      if (row.id === 10007) {
        return 'success'
      }
      return 'warning'
    }
  },
  columns: [
    { type: 'seq', width: 70 },
    { field: 'title', title: '任务名称' }
  ],
  data: [
    { id: 10001, title: '项目启动会议', start: '2024-03-01', end: '', progress: 0, type: VxeGanttTaskType.Milestone },
    { id: 10002, title: '项目启动与计划', start: '2024-03-03', end: '2024-03-08', progress: 80, type: '' },
    { id: 10003, title: '需求评审完成', start: '2024-03-03', end: '', progress: 0, type: VxeGanttTaskType.Milestone },
    { id: 10004, title: '技术及方案设计', start: '2024-03-05', end: '2024-03-11', progress: 80, type: '' },
    { id: 10005, title: '功能开发', start: '2024-03-08', end: '2024-03-15', progress: 70, type: '' },
    { id: 10007, title: '测试环境发布', start: '2024-03-11', end: '', progress: 0, type: VxeGanttTaskType.Milestone },
    { id: 10008, title: '系统测试', start: '2024-03-14', end: '2024-03-19', progress: 80, type: '' },
    { id: 10009, title: '测试完成', start: '2024-03-19', end: '', progress: 0, type: VxeGanttTaskType.Milestone },
    { id: 10010, title: '正式发布上线', start: '2024-03-20', end: '', progress: 0, type: VxeGanttTaskType.Milestone }
  ]
})
</script>

右键菜单

右键菜单加载中,通过 visibleMethod 和 loading | disabled | visible 属性来控制菜单选项的异步禁用与显示

在这里插入图片描述

任务拖拽调整日期

通过设置 task-bar-config.moveable 启用拖拽任务条移动日期功能

请添加图片描述

<template>
  <div>
    <vxe-gantt v-bind="ganttOptions"></vxe-gantt>
  </div>
</template>

<script setup>
import { reactive } from 'vue'

const ganttOptions = reactive({
  border: true,
  height: 500,
  taskBarConfig: {
    showProgress: true, // 是否显示进度条
    showContent: true, // 是否在任务条显示内容
    moveable: true, // 是否允许拖拽任务移动日期
    barStyle: {
      round: true, // 圆角
      bgColor: '#fca60b', // 任务条的背景颜色
      completedBgColor: '#65c16f' // 已完成部分任务条的背景颜色
    }
  },
  taskViewConfig: {
    tableStyle: {
      width: 480 // 表格宽度
    }
  },
  columns: [
    { type: 'seq', width: 70 },
    { field: 'title', title: '任务名称' },
    { field: 'start', title: '开始时间', width: 100 },
    { field: 'end', title: '结束时间', width: 100 },
    { field: 'progress', title: '进度(%)', width: 80 }
  ],
  data: [
    { id: 10001, title: '任务1', start: '2024-03-01', end: '2024-03-04', progress: 3 },
    { id: 10002, title: '任务2', start: '2024-03-03', end: '2024-03-08', progress: 10 },
    { id: 10003, title: '任务3', start: '2024-03-03', end: '2024-03-11', progress: 90 },
    { id: 10004, title: '任务4', start: '2024-03-05', end: '2024-03-11', progress: 15 },
    { id: 10005, title: '任务5', start: '2024-03-08', end: '2024-03-15', progress: 100 },
    { id: 10006, title: '任务6', start: '2024-03-10', end: '2024-03-21', progress: 5 },
    { id: 10007, title: '任务7', start: '2024-03-15', end: '2024-03-24', progress: 70 },
    { id: 10008, title: '任务8', start: '2024-03-05', end: '2024-03-15', progress: 50 },
    { id: 10009, title: '任务9', start: '2024-03-19', end: '2024-03-20', progress: 5 },
    { id: 10010, title: '任务10', start: '2024-03-12', end: '2024-03-20', progress: 10 },
    { id: 10011, title: '任务11', start: '2024-03-01', end: '2024-03-08', progress: 90 },
    { id: 10012, title: '任务12', start: '2024-03-03', end: '2024-03-06', progress: 60 },
    { id: 10013, title: '任务13', start: '2024-03-02', end: '2024-03-05', progress: 50 },
    { id: 10014, title: '任务14', start: '2024-03-04', end: '2024-03-15', progress: 0 },
    { id: 10015, title: '任务15', start: '2024-03-01', end: '2024-03-05', progress: 30 }
  ]
})
</script>

依赖线

通过设置 links 定义连接线,from 对应源任务的行主键,tom 对应目标任务的行主键

类型说明
0 FinishToStart 结束后才开始,表示一个任务必须在另一个任务开始之前完成
1 StartToFinish 开始到结束,表示从某个过程的开始到结束的整个过程
2 StartToStart 开始后才开始,表示一个活动结束了,另一个活动才能开始,它们之间按先后顺序进行
3 FinishToFinish 完成到完成,表示一个任务必须在另一个任务完成之后才能完成

请添加图片描述

<template>
  <div>
    <vxe-gantt v-bind="ganttOptions"></vxe-gantt>
  </div>
</template>

<script setup>
import { reactive } from 'vue'
import { VxeGanttDependencyType } from 'vxe-gantt'

const ganttOptions = reactive({
  border: true,
  height: 600,
  rowConfig: {
    keyField: 'id' // 行主键
  },
  taskBarConfig: {
    showProgress: true, // 是否显示进度条
    showContent: true, // 是否在任务条显示内容
    moveable: true, // 是否允许拖拽任务移动日期
    resizable: true, // 是否允许拖拽任务调整日期
    barStyle: {
      round: true, // 圆角
      bgColor: '#fca60b', // 任务条的背景颜色
      completedBgColor: '#65c16f' // 已完成部分任务条的背景颜色
    }
  },
  taskViewConfig: {
    tableStyle: {
      width: 480 // 表格宽度
    }
  },
  links: [
    { from: 10001, to: 10002, type: VxeGanttDependencyType.FinishToFinish },
    { from: 10004, to: 10005, type: VxeGanttDependencyType.StartToStart },
    { from: 10005, to: 10006, type: VxeGanttDependencyType.FinishToStart },
    { from: 10013, to: 10012, type: VxeGanttDependencyType.StartToFinish }
  ],
  columns: [
    { type: 'seq', width: 70 },
    { field: 'title', title: '任务名称' },
    { field: 'start', title: '开始时间', width: 100 },
    { field: 'end', title: '结束时间', width: 100 },
    { field: 'progress', title: '进度(%)', width: 80 }
  ],
  data: [
    { id: 10001, title: '任务1', start: '2024-03-01', end: '2024-03-04', progress: 3 },
    { id: 10002, title: '任务2', start: '2024-03-03', end: '2024-03-08', progress: 10 },
    { id: 10003, title: '任务3', start: '2024-03-03', end: '2024-03-11', progress: 90 },
    { id: 10004, title: '任务4', start: '2024-03-05', end: '2024-03-11', progress: 15 },
    { id: 10005, title: '任务5', start: '2024-03-08', end: '2024-03-15', progress: 100 },
    { id: 10006, title: '任务6', start: '2024-03-10', end: '2024-03-21', progress: 5 },
    { id: 10007, title: '任务7', start: '2024-03-15', end: '2024-03-24', progress: 70 },
    { id: 10008, title: '任务8', start: '2024-03-05', end: '2024-03-15', progress: 50 },
    { id: 10009, title: '任务9', start: '2024-03-19', end: '2024-03-20', progress: 5 },
    { id: 10010, title: '任务10', start: '2024-03-12', end: '2024-03-20', progress: 10 },
    { id: 10011, title: '任务11', start: '2024-03-01', end: '2024-03-08', progress: 90 },
    { id: 10012, title: '任务12', start: '2024-03-03', end: '2024-03-06', progress: 60 },
    { id: 10013, title: '任务13', start: '2024-03-02', end: '2024-03-05', progress: 50 },
    { id: 10014, title: '任务14', start: '2024-03-04', end: '2024-03-15', progress: 0 },
    { id: 10015, title: '任务15', start: '2024-03-01', end: '2024-03-05', progress: 30 }
  ]
})
</script>

拖拽创建连接线

请添加图片描述

https://gitee.com/x-extends/vxe-gantt

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐