实现效果:
项目背景:由于我们是vue2升级vue3项目,之前老项目Vue2使用的是gantt-elastic ,但是引入到Vue3版本中报错
Uncaught SyntaxError: The requested module ‘/node_modules/.vite/deps/vue.js?v=80f42b69’ does not provide an export named ‘default’
感觉是Vue3的导出方式不兼容这种vue2的默认导出引用方式导致的,又在网上扒了几个甘特图几乎都是只有图表部分,没有表格内容,遂自己实现。
实现思路:获取表格中所有开始时间与结束时间,筛选出最小与最大的时间节点进行计算得出总天数用来遍历生成多少个dom节点,然后根据dayjs().isBetween判断当前节点是否在所选范围之内,如果在就展示进度条,如果大于1天就把表格中的名称信息展示在进度条后一天。
具体代码:
<a-table
:columns="data.ganntColumns"
:data-source="data.dataSource"
size="small"
:pagination="false"
bordered
ref="tableRef"
defaultExpandAllRows
:scroll="{ x: 2000 }"
>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'dom'">
<div class="block" v-if="dayjs(column.text).isBetween(dayjs(record.start).add(-1, 'days'), dayjs(record.endVal))"> </div>
<div v-if="dayjs(column.text).isSame(dayjs(record.endVal))"> {{ record.label }} </div>
</template>
</template>
</a-table>
这里需要把开始时间与结束时间换成你自己的真实key节点
setColunmPush 方法只需要在获取完表格数据后调用一次,再把获取到的数组数据传入进来就好了,其他的都不用改动
import { onMounted, reactive, inject, watch, ref, nextTick } from 'vue';
import dayjs from 'dayjs';
import isBetween from 'dayjs/plugin/isBetween';
dayjs.extend(isBetween);
//类型注解
interface DataProps {
dataSource: Array<object>;
ganntColumns: object[];
}
const data: DataProps = reactive({
dataSource: [],
ganntColumns: [
{
title: '序号',
dataIndex: 'planCode',
key: 'planCode',
resizable: true,
fixed: 'left',
width: 100,
},
{
title: '计划名称',
dataIndex: 'label',
key: 'label',
resizable: true,
fixed: 'left',
width: 100,
},
{
title: '阶段',
dataIndex: 'stageText',
key: 'stageText',
resizable: true,
fixed: 'left',
width: 80,
},
{
title: '项目节点',
dataIndex: 'projectMilestoneText',
key: 'projectMilestoneText',
resizable: true,
fixed: 'left',
width: 100,
},
{
title: '里程碑计划',
dataIndex: 'isMilestoneText',
key: 'isMilestoneText',
resizable: true,
fixed: 'left',
width: 120,
},
{
title: '计划开始时间',
dataIndex: 'start',
key: 'start',
resizable: true,
fixed: 'left',
width: 125,
},
{
title: '计划完成时间',
dataIndex: 'endVal',
key: 'endVal',
resizable: true,
fixed: 'left',
width: 125,
},
{
title: '前置任务',
dataIndex: 'dependentOnText',
key: 'dependentOnText',
resizable: true,
fixed: 'left',
width: 100,
},
{
title: '负责人',
dataIndex: 'userText',
key: 'userText',
resizable: true,
fixed: 'left',
width: 80,
},
{
title: '完成度',
dataIndex: 'planSchedule',
key: 'planSchedule',
resizable: true,
fixed: 'left',
width: 80,
customRender: ({ text }) => {
return text + '%';
},
},
],
});
const getDateMax = (dataArr) => {
let max = dataArr[0];
for (let i = 1; i < dataArr.length; i++) {
if (Date.parse(dataArr[i]) > Date.parse(max)) {
max = dataArr[i];
}
}
return max;
};
// 求最小值
const getDateMin = (dataArr) => {
let min = dataArr[0];
for (let i = 1; i < dataArr.length; i++) {
if (Date.parse(dataArr[i]) < Date.parse(min)) {
min = dataArr[i];
}
}
return min;
};
// newData为你的表格数据
const setColunmPush = (newData) => {
if (newData && newData.length) {
// start改成你自己的真实数据中的开始时间字段
const startAr = newData.map((item) => item.start);
// endVal改成你自己的真实数据中的结束时间字段
const endAr = newData.map((item) => item.endVal);
const minTime = getDateMin(startAr);
const maxTime = getDateMax(endAr);
const intervalDay = Math.abs(dayjs(minTime).diff(maxTime, 'day')) + 3;
const months = ['', '一', '二', '三', '四', '五', '六', '七', '八', '九', '十', '十一', '十二'];
const days = ['日', '一', '二', '三', '四', '五', '六'];
const now = dayjs(minTime);
const month: number = dayjs(minTime).add(-1, 'day').format('MM') * 1;
const arr = [
{
title: months[month] + '月 ' + dayjs(minTime).add(-1, 'day').format('YYYY'),
value: month + dayjs(minTime).add(-1, 'day').format('YYYY'),
children: [],
},
];
for (var i = -1; i < intervalDay; i++) {
const values = arr.map((item) => item.value);
const newMonth: any = dayjs(minTime).add(i, 'day').format('MM') * 1;
if (!values.includes(newMonth)) {
arr.push({
title: months[newMonth] + '月 ' + now.add(i, 'day').format('YYYY'),
value: newMonth + now.add(i, 'day').format('YYYY'),
children: [],
});
}
const index = arr.findIndex((item) => item.value == newMonth + now.add(i, 'day').format('YYYY'));
arr[index].children.push({
title: now.add(i, 'day').format('DD') + ' 周' + days[now.add(i, 'day').day()],
dataIndex: i < 0 ? 'root' : i >= 0 ? 'dom' : '',
key: now.add(i, 'day').format('MM'),
width: 90,
text: now.add(i, 'day').format('YYYY-MM-DD'),
});
}
data.ganntColumns = [...data.ganntColumns, ...arr.filter((item) => item.children.length)];
}
};
最后把dom节点的样式引入进来就ok了
<style lang="less" scoped>
.block {
padding: 0 !important;
left: -8px;
top: 0;
right: 0;
bottom: 0;
margin: auto;
position: absolute;
cursor: pointer;
width: calc(100% + 1px);
height: 15px;
margin-left: 8px;
background-color: rgb(30, 188, 97);
background-image: -webkit-gradient(
linear,
0 0,
100% 100%,
color-stop(0.25, rgb(33, 201, 103)),
color-stop(0.25, transparent),
color-stop(0.5, transparent),
color-stop(0.5, rgb(33, 201, 103)),
color-stop(0.75, rgb(33, 201, 103)),
color-stop(0.75, transparent),
to(transparent)
);
background-image: -moz-linear-gradient(
-45deg,
rgb(33, 201, 103) 25%,
transparent 25%,
transparent 50%,
rgb(33, 201, 103) 50%,
rgb(33, 201, 103) 75%,
transparent 75%,
transparent
);
background-image: -o-linear-gradient(
-45deg,
rgb(33, 201, 103) 25%,
transparent 25%,
transparent 50%,
rgb(33, 201, 103) 50%,
rgb(33, 201, 103) 75%,
transparent 75%,
transparent
);
background-image: linear-gradient(
-45deg,
rgb(33, 201, 103) 25%,
transparent 25%,
transparent 50%,
rgb(33, 201, 103) 50%,
rgb(33, 201, 103) 75%,
transparent 75%,
transparent
);
}
</style>