基于vue3和element plus實現(xiàn)甘特圖
vue3 + element plus實現(xiàn)甘特圖
效果展示
實現(xiàn)思路
甘特圖,個人理解就是表格的一種展現(xiàn)形式。左側(cè)填充數(shù)據(jù),右側(cè)用圖例填充表示時間、工期、進度等信息。
技術(shù)選型
常用的ui框架有很多,以前用vue2的時候多搭配element ui,最近在看vue3的內(nèi)容,所以選擇了element plus。所以本文示例使用vue3+element plus實現(xiàn)甘特圖??催^原理之后改用其他技術(shù)方案也很簡單。
代碼實現(xiàn)
新建項目 vue3 + element plus
自己搜索吧,有很多,這里不是重點。
封裝組件
這里說下為什么要封裝成組件,因為項目里可能多處用到類似的功能,總不能每次都拷貝,然后修改。一次封裝,多次引用。
上代碼:我存放的路徑 /src/components/gantt/index.vue
<template> <div class="gantt"> <div class="legend"> <!-- 渲染圖例 --> <i class="plan"></i> <label>計劃</label> <i class="actuality"></i> <label>實際</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() } /** * 顯示計劃進度 */ 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() } /** * 顯示實際進度 */ 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: '事項', 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實現(xiàn)甘特圖的文章就介紹到這了,更多相關(guān)vue3 element plus甘特圖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue-cli中vue本地實現(xiàn)跨域調(diào)試接口
這篇文章主要介紹了vue-cli中vue本地實現(xiàn)跨域調(diào)試接口,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-01-01vue引用BootStrap以及引用bootStrap-vue.js問題
這篇文章主要介紹了vue引用BootStrap以及引用bootStrap-vue.js問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-10-10