vue使用html实现的一个项目进度图

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": []

}

]

相关推荐
你也向往长安城吗7 分钟前
推荐一个三维导航库:three-pathfinding-3d
javascript·算法
微小的xx13 分钟前
java + html 图片点击文字验证码
java·python·html
karrigan16 分钟前
async/await 的优雅外衣下:Generator 的核心原理与 JavaScript 执行引擎的精细管理
javascript
wycode24 分钟前
Vue2实践(3)之用component做一个动态表单(二)
前端·javascript·vue.js
意会1 小时前
微信闪照小程序实现
前端·css·微信小程序
wycode1 小时前
Vue2实践(2)之用component做一个动态表单(一)
前端·javascript·vue.js
第七种黄昏1 小时前
Vue3 中的 ref、模板引用和 defineExpose 详解
前端·javascript·vue.js
ankleless2 小时前
C语言(12)——进阶函数
前端·html
码码哈哈爱分享2 小时前
Tauri 框架介绍
css·rust·vue·html
我是哈哈hh2 小时前
【Node.js】ECMAScript标准 以及 npm安装
开发语言·前端·javascript·node.js