基于vue3和element plus實(shí)現(xiàn)甘特圖
vue3 + element plus實(shí)現(xiàn)甘特圖
效果展示

實(shí)現(xiàn)思路
甘特圖,個(gè)人理解就是表格的一種展現(xiàn)形式。左側(cè)填充數(shù)據(jù),右側(cè)用圖例填充表示時(shí)間、工期、進(jìn)度等信息。
技術(shù)選型
常用的ui框架有很多,以前用vue2的時(shí)候多搭配element ui,最近在看vue3的內(nèi)容,所以選擇了element plus。所以本文示例使用vue3+element plus實(shí)現(xiàn)甘特圖??催^原理之后改用其他技術(shù)方案也很簡單。
代碼實(shí)現(xiàn)
新建項(xiàng)目 vue3 + element plus
自己搜索吧,有很多,這里不是重點(diǎn)。
封裝組件
這里說下為什么要封裝成組件,因?yàn)轫?xiàng)目里可能多處用到類似的功能,總不能每次都拷貝,然后修改。一次封裝,多次引用。
上代碼:我存放的路徑 /src/components/gantt/index.vue
<template>
<div class="gantt">
<div class="legend">
<!-- 渲染圖例 -->
<i class="plan"></i>
<label>計(jì)劃</label>
<i class="actuality"></i>
<label>實(shí)際</label>
</div>
<el-table :data="data">
<!-- 渲染表格 -->
<el-table-column
v-for="(column, index) in columnsConfig"
:key="index"
v-bind="column"
></el-table-column>
<el-table-column
v-for="monthItem in monthData"
:key="monthItem.month"
align="center"
min-width="80"
:prop="monthItem.month"
:label="monthItem.month"
>
<template #header>
<span>{{ monthItem.month.substring(5) + '月' }}</span>
</template>
<el-table-column
v-for="day in monthItem.dayArray"
:key="day"
align="center"
:width="50"
:prop="day"
>
<template #header>
<span>{{ day.substring(8) }}</span>
</template>
<template #default="scope">
<i class="plan" v-if="showPlan(scope)"></i>
<i class="empty" v-else></i>
<i class="actuality" v-if="showActuality(scope)"></i>
<i class="empty" v-else></i>
</template>
</el-table-column>
</el-table-column>
</el-table>
</div>
</template>
<script setup>
import { ref } from 'vue';
const props = defineProps({
data: {
type: Array,
default: []
},
columnsConfig: {
type: Array,
default: []
},
ganttConfig: {
type: Object,
default: {
planBeginColumn: 'planBegin',
planEndColumn: 'planEnd',
actualityBeginColumn: 'actualityBegin',
actualityEndColumn: 'actualityEnd'
}
}
})
const monthData = ref({})
const init = () => {
let minDate = undefined
let maxDate = undefined
props.data.forEach((row, index) => {
let current = new Date(row[props.ganttConfig.planBeginColumn])
if (minDate) {
minDate = minDate.getTime() < current.getTime() ? minDate : current
} else {
minDate = current
}
current = new Date(row[props.ganttConfig.planEndColumn])
if (maxDate) {
maxDate = maxDate.getTime() > current.getTime() ? maxDate : current
} else {
maxDate = current
}
current = props.ganttConfig.actualityBeginColumn || row[props.ganttConfig.actualityBeginColumn] ? new Date(row[props.ganttConfig.actualityBeginColumn]) : undefined
if (current) {
minDate = minDate.getTime() < current.getTime() ? minDate : current
}
current = props.ganttConfig.actualityEndColumn || row[props.ganttConfig.actualityEndColumn] ? new Date(row[props.ganttConfig.actualityEndColumn]) : undefined
if (current) {
maxDate = maxDate.getTime() > current.getTime() ? maxDate : current
}
})
// 甘特圖前后各放寬2天
minDate = new Date(minDate.getTime() - 2 * 24 * 60 * 60 * 1000)
maxDate = new Date(maxDate.getTime() + 2 * 24 * 60 * 60 * 1000)
let current = new Date(format(minDate))
while(!isAfter(current, maxDate)) {
const month = formatYearMonth(current)
const day = format(current)
if (monthData.value[month]) {
monthData.value[month].dayArray.push(day)
} else {
monthData.value[month] = {
month: month,
dayArray: [day]
}
}
// 加一天
current = after(current)
}
}
/**
* 格式化 YYYY-MM-DD
*/
const format = (date) => {
const day = String(date.getDate()).padStart(2, '0')
return formatYearMonth(date) + '-' + day
}
/**
* 格式化 YYYY-MM
*/
const formatYearMonth = (date) => {
const year = date.getFullYear()
const month = String(date.getMonth() + 1).padStart(2, '0')
return year + '-' + month
}
/**
* 加一天
*/
const after = (date) => {
return new Date(date.getTime() + 24 * 60 * 60 * 1000)
}
/**
* date1是否大于等于date2
*/
const isAfter = (date1, date2) => {
return date1.getTime() >= date2.getTime()
}
/**
* 顯示計(jì)劃進(jìn)度
*/
const showPlan = ({row, column}) => {
const currentDay = new Date(column.property)
const begin = new Date(row[props.ganttConfig.planBeginColumn])
const end = new Date(row[props.ganttConfig.planEndColumn])
return currentDay.getTime() >= begin.getTime() && currentDay.getTime() <= end.getTime()
}
/**
* 顯示實(shí)際進(jìn)度
*/
const showActuality = ({row, column}) => {
const currentDay = new Date(column.property)
const begin = props.ganttConfig.actualityBeginColumn || row[props.ganttConfig.actualityBeginColumn] ? new Date(row[props.ganttConfig.actualityBeginColumn]) : undefined
const end = props.ganttConfig.actualityEndColumn || row[props.ganttConfig.actualityEndColumn] ? new Date(row[props.ganttConfig.actualityEndColumn]) : undefined
return begin && end && currentDay.getTime() >= begin.getTime() && currentDay.getTime() <= end.getTime()
}
init()
</script>
<style scoped>
.plan {
display: flex;
width: calc(100% + 24px);
height: 16px;
background-color: limegreen;
margin: 0 -12px;
}
.actuality {
display: flex;
width: calc(100% + 24px);
height: 16px;
background-color: yellow;
margin: 0 -12px;
}
.empty {
display: flex;
width: calc(100% + 24px);
height: 16px;
margin: 0 -12px;
}
.legend {
display: flex;
line-height: 40px;
flex-direction: row;
justify-content: right;
align-items: center;
padding: 0 20px;
* {
margin: 0 5px;
}
i {
width: 32px;
height: 16px;
}
}
</style>
引用組件
app.vue中引用上面的組件
<script setup>
import Gantt from '@/components/gantt/index.vue'
const data = ref([
{
title: '第一階段',
planBegin: '2022-01-01',
planEnd: '2022-01-09',
actualityBegin: '2022-01-02',
actualityEnd: '2022-01-10'
},
{
title: '第二階段',
planBegin: '2022-01-09',
planEnd: '2022-01-15',
actualityBegin: '2022-01-09',
actualityEnd: '2022-01-18'
}
])
const columnsConfig = ref([
{
label: '事項(xiàng)',
prop: 'title',
fixed: 'left',
align: 'center',
'min-width': 120
},
{
label: '開始',
prop: 'planBegin',
fixed: 'left',
align: 'center',
'min-width': 120
},
{
label: '結(jié)束',
prop: 'planEnd',
fixed: 'left',
align: 'center',
'min-width': 120
}
])
</script>
<template>
<div>
<Gantt
:data="data"
:columnsConfig="columnsConfig"
:ganttConfig ="{
planBeginColumn: 'planBegin',
planEndColumn: 'planEnd',
actualityBeginColumn: 'actualityBegin',
actualityEndColumn: 'actualityEnd'
}"
/>
</div>
</template>
<style scoped>
</style>
到此這篇關(guān)于基于vue3和element plus實(shí)現(xiàn)甘特圖的文章就介紹到這了,更多相關(guān)vue3 element plus甘特圖內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
使用Bootrap和Vue實(shí)現(xiàn)仿百度搜索功能
這篇文章主要介紹了使用Bootrap和Vue實(shí)現(xiàn)仿百度搜索功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2017-10-10
vue-cli中vue本地實(shí)現(xiàn)跨域調(diào)試接口
這篇文章主要介紹了vue-cli中vue本地實(shí)現(xiàn)跨域調(diào)試接口,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-01-01
Vue3中ref和reactive的使用場(chǎng)景詳解
這篇文章主要介紹了Vue3中ref和reactive的使用場(chǎng)景,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-04-04
vue引用BootStrap以及引用bootStrap-vue.js問題
這篇文章主要介紹了vue引用BootStrap以及引用bootStrap-vue.js問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-10-10
Vue Autocomplete 自動(dòng)完成功能簡單示例
這篇文章主要介紹了Vue Autocomplete 自動(dòng)完成功能,結(jié)合簡單示例形式分析了Vue使用el-autocomplete組件實(shí)現(xiàn)自動(dòng)完成功能相關(guān)操作技巧,需要的朋友可以參考下2019-05-05
vue實(shí)現(xiàn)折疊展開收縮動(dòng)畫效果
這篇文章主要介紹了vue實(shí)現(xiàn)折疊展開收縮動(dòng)畫,通過scrollHeight實(shí)現(xiàn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),感興趣的朋友一起看看吧2023-11-11

