推薦一個支持 vue2、vue3 甘特圖 vxe-gantt 秒級渲染萬級數據量,高性能甘特圖組件,支持常用的項目管理甘特圖功能、自定義日期軸、右鍵菜單、可編輯、拖拽任務、依賴線、里程碑等等功能非常多。 接下來做個10萬行任務以內的渲染量測試,不僅功能強大,渲染性能也是非常強的。
https://gantt.vxeui.com/
列表虛擬滾動
縱向虛擬滾動,設置 virtual-y-config={ enabled: true, gt: 0 } 和 height | max-height 來開啓
<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>
子任務虛擬滾動
<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>
https://gitee.com/x-extends/vxe-gantt