阅读量:3
前言
本文是根据项目实际开发中一个需求开发的demo,仅用了elementUI,可当作独立组件使用,C V即用。 当然没考虑其他的扩展性和一些数据的校验,主要是提供一个处理思路,有需要的小伙伴可以直接复制; 本demo的思路是根据开始时间和结束时间动态生成工程时间表,再根据工程的计划开始和结束日期、 实际开始和结束日期再对应单元格生成进度条,最后根据完成进度百分比计算红色进度条。
一、demo成品图
表格使用的是elementUI,表头是动态的,根据开始日期的年月和结束时间的年月计算获取;
单元格第一行绿色进度条是计划工程进度,第二行绿色是实际功能进度条,红色是实际进度的百分比
二、代码
<template> <div class="app-container"> <el-table :data="tableData" style="width: 100%"> <el-table-column label="名称" prop="name" width="200"></el-table-column> <el-table-column align="center" v-for="(months, year) in dateList" :key="year" :label="`${year}年`"> <el-table-column v-for="month in months" align="center" width="100" :key="month" :label="`${month}月`"> <template slot-scope="scope"> <div class="process-box" v-if="scope.row.plan_process[year] && scope.row.plan_process[year].includes(month) || scope.row.actual_process[year] && scope.row.actual_process[year].includes(month)"> <div class="plan-process" v-if="scope.row.plan_process[year] && scope.row.plan_process[year].includes(month)"> <div class="item" v-if="scope.row.planSameYearMonth" :style="scope.row.planProcessStyle"></div> <div v-else> <div class="item start" v-if="scope.row.plan_start.Y == year && scope.row.plan_start.M == month" :style="scope.row.plan_start.itemStyle"></div> <div class="item end" v-if="scope.row.plan_end.Y == year && scope.row.plan_end.M == month" :style="scope.row.plan_end.itemStyle"></div> <div class="item" v-if="!(scope.row.plan_start.Y == year && scope.row.plan_start.M == month || scope.row.plan_end.Y == year && scope.row.plan_end.M == month)"></div> </div> </div> <div class="actual-process" v-if="scope.row.actual_process[year] && scope.row.actual_process[year].includes(month)"> <div class="item" v-if="scope.row.actualSameYearMonth" :style="scope.row.actualProcessStyle"> <div class="percent_item start" v-if="scope.row.percentSameYearMonth" :style="scope.row.percentProcessStyle"></div> </div> <div class="item-box" v-else> <div class="item start" v-if="scope.row.actual_start.Y == year && scope.row.actual_start.M == month" :style="scope.row.actual_start.itemStyle"> <div class="percent_item start" v-if="scope.row.percent_process[year] && scope.row.percent_process[year].includes(month) && scope.row.percent_start.Y == year && scope.row.percent_start.M == month" :style="scope.row.percent_start.itemStyle"></div> </div> <div class="item end" v-if="scope.row.actual_end.Y == year && scope.row.actual_end.M == month" :style="scope.row.actual_end.itemStyle"> <div class="percent_item end" v-if="scope.row.percent_process[year] && scope.row.percent_process[year].includes(month) && scope.row.percent_end.Y == year && scope.row.percent_end.M == month" :style="scope.row.percent_end.itemStyle"></div> </div> <div class="item" v-if="!(scope.row.actual_start.Y == year && scope.row.actual_start.M == month || scope.row.actual_end.Y == year && scope.row.actual_end.M == month)"> <div class="percent_item" v-if="scope.row.percent_process[year] && scope.row.percent_process[year].includes(month) && scope.row.percent_end.Y == year && scope.row.percent_end.M == month" :style="scope.row.percent_end.itemStyle"></div> <div class="percent_item" v-if="scope.row.percent_process[year] && scope.row.percent_process[year].includes(month) && !(scope.row.percent_end.Y == year && scope.row.percent_end.M == month)"></div> </div> </div> </div> </div> </template> </el-table-column> </el-table-column> </el-table> </div> </template> <script> export default { data() { return { tableData: [ { name: "单位A施工期间", plan_start_time: "2023-02-1", plan_end_time: "2023-2-28", actual_start_time: "2023-2-7", actual_end_time: "2023-6-22", percent: 85, }, { name: "单位B施工期间", plan_start_time: "2023-07-12", plan_end_time: "2024-01-12", actual_start_time: "2023-11-10", actual_end_time: "2024-01-10", percent: 76, } ], dateList: {}, } }, mounted(){ this.initTableData("2023-01-12", "2025-01-30") }, methods: { handleDate(date) { let monthHasDay = 30; let currentDate = new Date(date) let day = currentDate.getDate() let month = currentDate.getMonth() + 1; let year = currentDate.getFullYear(); if ([1, 3, 5, 7, 8, 10, 12].includes(month)) { monthHasDay = 31 } else { if (month === 2) { if ((year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0)) { monthHasDay = 29; } else { monthHasDay = 28; } } else { monthHasDay = 30; } } return {d: day, M: month, Y: year, monthHasDay: monthHasDay} }, getDataBetweenDates(startTime, endTime){ let start = this.handleDate(startTime); let end = this.handleDate(endTime); let data = {} data[start.Y] = []; data[end.Y] = []; let year = end.Y - start.Y if (year === 0) { for(let m = start.M; m <= end.M; m++) { data[start.Y].push(m) } } else if (year === 1) { for(let m = start.M; m <= 12; m++) { data[start.Y].push(m) } for(let n = 1; n <= end.M; n++) { data[end.Y].push(n) } } else { for(let m = start.M; m <= 12; m++) { data[start.Y].push(m) } for(let mid = 1; mid < year; mid++) { data[start.Y + mid] = [1,2,3,4,5,6,7,8,9,10,11,12]; } for(let n = 1; n <= end.M; n++) { data[end.Y].push(n) } } return data; }, getDaysBetweenDates(startTime, endTime) { let d1 = new Date(startTime); let d2 = new Date(endTime); let timeDiff = Math.abs(d2.getTime() - d1.getTime()); let days = Math.ceil(timeDiff / (1000 * 3600 * 24)); return days; }, handleDateStyle(startDate, endDate){ let start = this.handleDate(startDate) let end = this.handleDate(endDate); let sameYearMonth = false; let processStyle = null; if (end.Y === start.Y && end.M === start.M) { processStyle = { "left": ((start.d - 1) * 100 / start.monthHasDay) + "%", "right": ((start.monthHasDay - end.d) * 100 / start.monthHasDay) + "%", "border-radius": '4px' } if (end.d > start.monthHasDay) processStyle.right = 0 sameYearMonth = true } else { start.itemStyle = { "left": ((start.d + 1) * 100 / start.monthHasDay) + "%", "right": 0 } end.itemStyle = { "left": 0, "right": ((start.monthHasDay - (end.d + 1)) * 100 / start.monthHasDay) + "%" } } return { start: start, end: end, sameYearMonth: sameYearMonth, processStyle: processStyle } }, handlePercentDateStyle(actualStartTime, actualEndTime, percent){ let start = this.handleDate(actualStartTime) let end = this.handleDate(actualEndTime); let days = this.getDaysBetweenDates(actualStartTime, actualEndTime) let percentTime = new Date(actualStartTime).getTime() + days * percent * 24 * 36000 let percentProcess = this.getDataBetweenDates(actualStartTime, percentTime) let startBorderRadius = '4px 0 0 4px' let endBorderRadius = '0 4px 4px 0' let percentDate = this.handleDate(percentTime) let sameYearMonth = false; let processStyle = null; if (end.Y === start.Y) { if (end.M === start.M) { processStyle = { "left": 0, "right": ((end.d - (percentDate.d)) * 100 / end.d) + "%", "border-radius": '4px' } sameYearMonth = true } else { if(percentDate.M === start.M) { start.itemStyle = { "left": 0, "right": ((start.monthHasDay - percentDate.d) * 100 / (start.monthHasDay - start.d)) + "%", "border-radius": '4px' } percentDate.itemStyle = { "left": 0, "right": ((start.monthHasDay - percentDate.d) * 100 / start.monthHasDay) + "%", "border-radius": '4px' } } else if (percentDate.M > start.M && percentDate.M < end.M) { start.itemStyle = { "left": 0, "right": 0, "border-radius": startBorderRadius } percentDate.itemStyle = { "left": 0, "right": ((percentDate.monthHasDay - percentDate.d) * 100 / percentDate.monthHasDay) + "%", "border-radius": endBorderRadius } } else if (percentDate.M === end.M) { start.itemStyle = { "left": 0, "right": 0, "border-radius": startBorderRadius } percentDate.itemStyle = { "left": 0, "right": ((end.d - percentDate.d) * 100 / end.d) + "%", "border-radius": endBorderRadius } } } } else { if (percentDate.M === start.M) { start.itemStyle = { "left": 0, "right": ((start.monthHasDay - percentDate.d) * 100 / (start.monthHasDay - start.d)) + "%", "border-radius": '4px' } } else if (percentDate.M === end.M && percentDate.Y === end.Y) { start.itemStyle = { "left": 0, "right": 0, "border-radius": startBorderRadius } percentDate.itemStyle = { "left": 0, "right": ((end.d - percentDate.d) * 100 / end.d) + "%", "border-radius": endBorderRadius } } else { start.itemStyle = { "left": 0, "right": 0, "border-radius": startBorderRadius } percentDate.itemStyle = { "left": 0, "right": ((percentDate.monthHasDay - percentDate.d) * 100 / percentDate.monthHasDay) + "%", "border-radius": endBorderRadius } } } return { start: start, end: percentDate, sameYearMonth: sameYearMonth, processStyle: processStyle, percentProcess: percentProcess } }, initTableData(startTime, endTime){ this.dateList = this.getDataBetweenDates(startTime, endTime); this.tableData.map(item => { item.plan_process = this.getDataBetweenDates(item.plan_start_time, item.plan_end_time); item.actual_process = this.getDataBetweenDates(item.actual_start_time, item.actual_end_time); let dateStyle = this.handleDateStyle(item.plan_start_time,item.plan_end_time) ; item.planSameYearMonth = dateStyle.sameYearMonth; item.planProcessStyle = dateStyle.processStyle ? dateStyle.processStyle : ''; item.plan_start = dateStyle.start; item.plan_end = dateStyle.end; let actualDateStyle = this.handleDateStyle(item.actual_start_time,item.actual_end_time); item.actualSameYearMonth = actualDateStyle.sameYearMonth; item.actualProcessStyle = actualDateStyle.processStyle ? actualDateStyle.processStyle : ''; item.actual_start = actualDateStyle.start; item.actual_end = actualDateStyle.end; let percentDateStyle = this.handlePercentDateStyle(item.actual_start_time, item.actual_end_time, item.percent); item.percent_start = percentDateStyle.start; item.percent_end = percentDateStyle.end; item.percentProcessStyle = percentDateStyle.processStyle ? percentDateStyle.processStyle : ''; item.percentSameYearMonth = percentDateStyle.sameYearMonth; item.percent_process = percentDateStyle.percentProcess console.log(item) }) }, }, } </script> <style lang="scss" scoped> ::v-deep .el-table td.el-table__cell div{ padding: 0; } .process-box{ width: 100px; height: 40px; position: relative; .plan-process{ position: absolute; top: 0; left: 0; right: 0; height: 15px; } .actual-process{ position: absolute; top: 25px; left: 0; right: 0; height: 15px; } .percent_item{ position: absolute; height: 15px; left: 0; right: 0; background-color: red; } } .item { position: absolute; left: 0; right: 0; background: greenyellow; height: 15px; &.start{ border-radius: 4px 0 0 4px; } &.end{ border-radius: 0 4px 4px 0 ; } } </style>