vue使用html实现的一个项目进度图
比如一个项目,可能对应多个不同的时间阶段,需要单独显示。如下图。那么echart和一些其他进度组件,实现起来就比较麻烦,甚至echart无法实现这么好的效果。于是就自己写了一个。
需求是:一个项目需要显示多个阶段,并且每个阶段都要一样的渐变色,并且按照日期显示在准确的比例位置
效果图:
对比echart实现的效果
这个echart的原理是用坐标,将月份视为x坐标,那么比如mar有数据,jun有数据,那么就会出现这么一段,但是他无法实现精准的显示,比如我一个时间是3月5日,他不能按比例显示在正确的位置
再对比另一个echart效果
这个echart的原理是先显示带颜色的部分,然后在用一个和echart背景色相同的,长度为从0到这个带颜色部分的起点位置的bar盖住带颜色的bar就形成了这个效果,那么可见。渐变色达不到效果,圆角达不到效果,而且要实现多个,这个数据结构会很复杂。
综上
还是决定自己用html实现一个,设计一个算法,显示再正确的比例位置
再看一下效果图

html
bash
<div class="chart-box">
<div class="chart-top">
<div class="chart-top-item" v-for="(item,index) in headerData" :style="{width:100/headerData.length+'%'}" :key="index">
<span class="chart-top-item-year">{{ item.year||'' }}</span>
<span class="chart-top-item-year2">{{ item.lable }}</span>
</div>
</div>
<div class="chart-bot">
<div class="chart-line" :style="{minWidth:headerData.length*80+'px',width:'100%'}" v-for="(item,index) in productData" :key="index">
<el-tooltip effect="dark" placement="top" v-for="(ite,idx) in item.items" :key="idx">
<template #content>{{ite.name||'--'}}<br/>开始日期:{{ite.startDateStr}}<br/>结束日期:{{ite.endDateStr}}</template>
<div class="chart-line-item" :style="{left:ite.st+(100/headerData.length/2)+'%',width:(ite.et-ite.st)?(ite.et-ite.st)+'%':'',minWidth:'20px'}"></div>
</el-tooltip>
<div class="chart-line-split" v-for="(it,ix) in headerData" :style="{left:(ix*(100/headerData.length)+100/headerData.length/2)+'%'}" :key="ix"></div>
</div>
</div>
</div>
scss
bash
.chart-box{
width: 100%;
height: calc(100% - 0px);
// border:1px solid #333;
overflow-x: scroll;
/* 设置滚动条的大小 */
&::-webkit-scrollbar {
width: 3px;
height: 5px;
}
/* 设置滚动条可拖动滑块的样式 */
&::-webkit-scrollbar-thumb {
border-radius: 5px;
background: rgba(226, 232, 240, 1);
}
/* 设置外层轨道的样式 */
&::-webkit-scrollbar-track {
border-radius: 0;
background: #fff;
}
box-sizing: content-box;
display: flex;
flex-direction: column;
// padding: 5px 0 0;
.chart-top{
width: 100%;
display: flex;
.chart-top-item{
min-width: 80px !important;
height: 50px;
display: flex;
align-items: center;
justify-content: center;
color: rgb(76,73,72);
position: relative;
// border-top: 1px solid rgb(76,73,72);
font-weight: bolder;
&::before{
content: '';
display: block;
width: 8px;
height: 8px;
border-radius: 50%;
background-color: rgb(76,73,72);
position: absolute;
top: 52%;
left: 50%;
transform: translate(-52%,-50%);
}
&:after{
content: '';
display: block;
width: 100%;
height: 0px;
border-top: 2px solid rgb(76,73,72);
position: absolute;
top: 52%;
transform: translateY(-52%);
left: 0;
}
.chart-top-item-year{
display: block;
position: absolute;
top: 10%;
left: 50%;
transform: translateX(-50%);
}
.chart-top-item-year2{
position: absolute;
top: 55%;
left: 50%;
transform: translateX(-55%);
}
}
}
.chart-bot{
width: 100%;
display: flex;
flex-direction: column;
flex: 1;
.chart-line{
width: 100%;
position: relative;
display: flex;
align-items: center;
height: 40px;
.chart-line-split{
width: 0.1px;
height: 100%;
// border-right: 0.1px solid #ccc;
background: #ccc;
position: absolute;
top: 0;
transform: translateX(-50%);
z-index: 1;
}
.chart-line-item{
border-radius: 10px;
height: 20px;
position: absolute;
top: 50%;
transform: translateY(-50%);
background: linear-gradient(to right, rgba(251,147,139,1), rgba(179,13,0,1));
cursor: pointer;
z-index: 2;
}
}
}
}
js

数据由表头和内容构成,如图:
1、表头是需要过滤的,比如23-26年之间都没有数据,那么就这之间的数据就全部不显示,只显示有数据的部分。
2、但是为了避免头部和尾部显示不全,又在已有表头数据的基础上,分别增加了一个节点。
3、为了便于区分,给每个这一年第一次出现的月份增加了一个年份的标记
数据
headerData.value = [
{
"lable": "Sep",
"value": "2023-09",
"year": 2023,
"color": "rgba(255,154,146,1)",
"isfirst": true
},
{
"lable": "Oct",
"value": "2023-10"
},
{
"lable": "Jan",
"value": "2024-01",
"year": 2024,
"color": "rgba(255,103,92,1)",
"isfirst": true
},
{
"lable": "Aug",
"value": "2024-08"
},
{
"lable": "Sep",
"value": "2024-09"
},
{
"lable": "Feb",
"value": "2025-02",
"year": 2025,
"color": "rgba(255,103,92,1)",
"isfirst": true
},
{
"lable": "Mar",
"value": "2025-03"
},
{
"lable": "Apr",
"value": "2025-04"
},
{
"lable": "Jun",
"value": "2025-06"
},
{
"lable": "Jul",
"value": "2025-07"
},
{
"lable": "Sep",
"value": "2025-09"
},
{
"lable": "Jan",
"value": "2026-01",
"year": 2026,
"color": "rgba(255,103,92,1)",
"isfirst": true
},
{
"lable": "Feb",
"value": "2026-02"
},
{
"lable": "Mar",
"value": "2026-03"
}
]
productData.value=[{
"isShowFolder": true,
"activityFolders": null,
"id": 643109350596677,
"category": "test",
"name": "activity1",
"description": "activity1activity1",
"organizer": "test",
"enabled": true,
"items": [
{
"name": "item1",
"startDate": "2024-01-01 00:00:00",
"startDateStr": "2024-01-01",
"endDate": "2024-01-03 00:00:00",
"endDateStr": "2024-01-03",
"st": 14.516129032258066,
"et": 14.976958525345623
},
{
"name": "items3",
"startDate": "2025-02-12 00:00:00",
"startDateStr": "2025-02-12",
"endDate": "2025-02-13 00:00:00",
"endDateStr": "2025-02-13",
"st": 38.47926267281106,
"et": 38.70967741935484
},
{
"name": "item4",
"startDate": "2024-08-02 00:00:00",
"startDateStr": "2024-08-02",
"endDate": "2024-09-30 00:00:00",
"endDateStr": "2024-09-30",
"st": 21.88940092165899,
"et": 35.483870967741936
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 646198382841925,
"category": "test",
"name": "activity2",
"description": "activity2",
"organizer": "activity2",
"enabled": true,
"items": [
{
"name": "子事项",
"startDate": "2025-06-13 00:00:00",
"startDateStr": "2025-06-13",
"endDate": "2025-07-23 00:00:00",
"endDateStr": "2025-07-23",
"st": 60.13824884792627,
"et": 69.58525345622121
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 646273017249861,
"category": "test",
"name": "activity3",
"description": "activity3",
"organizer": "activity3",
"enabled": true,
"items": [
{
"name": "子事项1",
"startDate": "2025-02-01 00:00:00",
"startDateStr": "2025-02-01",
"endDate": "2025-02-28 00:00:00",
"endDateStr": "2025-02-28",
"st": 35.944700460829495,
"et": 42.16589861751152
},
{
"name": "子事项2",
"startDate": "2025-03-01 00:00:00",
"startDateStr": "2025-03-01",
"endDate": "2025-03-31 00:00:00",
"endDateStr": "2025-03-31",
"st": 43.08755760368664,
"et": 50
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 645519724691525,
"category": "qqqqq",
"name": "qqqqq",
"description": "qqqqq",
"organizer": "qqqqq",
"enabled": true,
"items": [
{
"name": "123",
"startDate": "2023-10-03 00:00:00",
"startDateStr": "2023-10-03",
"endDate": "2025-09-25 00:00:00",
"endDateStr": "2025-09-25",
"st": 7.8341013824884795,
"et": 77.1889400921659
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 646201372651589,
"category": "qqqqq",
"name": "activity2",
"description": "activity2",
"organizer": "activity2",
"enabled": true,
"items": [
{
"name": "子事项1",
"startDate": "2025-04-17 00:00:00",
"startDateStr": "2025-04-17",
"endDate": "2025-04-30 00:00:00",
"endDateStr": "2025-04-30",
"st": 53.91705069124424,
"et": 56.91244239631337
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 645964431528005,
"category": "广泛大概",
"name": "广泛大概",
"description": "广泛大概",
"organizer": "广泛大概",
"enabled": true,
"items": [
{
"name": "子圣西昂",
"startDate": "2026-01-15 00:00:00",
"startDateStr": "2026-01-15",
"endDate": "2026-02-16 00:00:00",
"endDateStr": "2026-02-16",
"st": 82.02764976958525,
"et": 89.40092165898618
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 646201454104645,
"category": "所发生的d",
"name": "的法国队发给",
"description": "d风格豆腐干",
"organizer": "古典风格d",
"enabled": true,
"items": [
{
"name": "",
"startDate": "2025-02-19 00:00:00",
"startDateStr": "2025-02-19",
"endDate": "2025-03-21 00:00:00",
"endDateStr": "2025-03-21",
"st": 40.09216589861751,
"et": 47.69585253456221
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 646274932224069,
"category": "测试测试",
"name": "测试测试",
"description": "测试测试",
"organizer": "测试测试",
"enabled": true,
"items": []
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 649139617321029,
"category": "Test2月27",
"name": "Test",
"description": "Test",
"organizer": "Test",
"enabled": true,
"items": [
{
"name": "Test",
"startDate": "2025-02-01 00:00:00",
"startDateStr": "2025-02-01",
"endDate": "2025-03-31 00:00:00",
"endDateStr": "2025-03-31",
"st": 35.944700460829495,
"et": 50
},
{
"name": "Test2",
"startDate": "2025-02-02 00:00:00",
"startDateStr": "2025-02-02",
"endDate": "2025-04-01 00:00:00",
"endDateStr": "2025-04-01",
"st": 36.175115207373274,
"et": 50.23041474654378
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 649410891395141,
"category": "Test2.28",
"name": "test",
"description": "test",
"organizer": "test",
"enabled": true,
"items": [
{
"name": "test",
"startDate": "2025-02-28 00:00:00",
"startDateStr": "2025-02-28",
"endDate": "2025-03-01 00:00:00",
"endDateStr": "2025-03-01",
"st": 42.16589861751152,
"et": 43.08755760368664
},
{
"name": "test2",
"startDate": "2025-03-01 00:00:00",
"startDateStr": "2025-03-01",
"endDate": "2025-03-02 00:00:00",
"endDateStr": "2025-03-02",
"st": 43.08755760368664,
"et": 43.31797235023042
},
{
"name": "",
"startDate": "2025-02-19 00:00:00",
"startDateStr": "2025-02-19",
"endDate": "2025-02-20 00:00:00",
"endDateStr": "2025-02-20",
"st": 40.09216589861751,
"et": 40.322580645161295
}
]
},
{
"isShowFolder": true,
"activityFolders": null,
"id": 649451403628613,
"category": "A1",
"name": "A2",
"description": "D1",
"organizer": "O1",
"enabled": true,
"items": []
}
]