vue 实现类似甘特图大屏效果

最近在做大屏展示,其中一个需求是展示生产过程中投料情况,效果类似甘特图。
思路:1.先得到整个过程的开始时间startTime和结束时间endTime。计算出整个过长经历的时长。
2.计算横向坐标的开始时间start和结束时间end,坐标的开始时间为生产开始时间-百分之十的生产时长,即start = startTime - 0.1h;坐标的结束时间为生产结束时间+百分之十的生产时长,即end = endTime + 0.1h.
3.确定横纵坐标值,先计算横计算坐标间隔,我固定了横坐标的打点数,直接用横坐标结束时间-坐标开始时间除以点数+1,即 (end-start) / (number +1)。得到间隔之后就可以计算出横坐标值。纵坐标值直接使用物料名称
4.显示每个料的投放情况显示出来。显示是用的div,每种物料的投料时长就是div的长度。
注意:计算宽度的时候需要依据自己的实际情况进行调整
效果
vue 实现类似甘特图大屏效果_第1张图片

代码

<template>
    <div class="Gantt">
      <div class="content" ref="scrollbar">
        
        <div
          class="info"
          style="margin-top: 2px; overflow: scroll; overflow-x: hidden;height: calc(100vh * 220 / 1080);"
        >
          <div id="gui-content" class="gui-content">
            <div
              class="gui-list clear room-gui-list"
              v-for="task in taskList"
              :key="task.materialId"
            >
              <div id="name" class="fasten ellipsis" :title="task.name">
                {{ task.name }}
              div>
              <div class="gui-tab">
                <li
                  v-for="(o, i)  in config.xAxis"
                  :key="i" 
                  :style="{
                    width: 100 / (number + 1) + '%',
                    cursor: 'pointer',
                  }"
                >li>
              div>
              <template v-if="task.materialId">
                <div
                    v-for="(item, index) in task.materialData"
                    :key="index"
                    class="meet-item-one"
                    v-bind:class="[
                    !item.status ? 'meet-color-having' : 'meet-color-finished',
                    ]"
        
                    :style="{
                    left: getLeftTime(item.startTime) + '%',
                    width: getWidth(item) + '%',
                    backgroundColor: item.color,
                    // opacity:0.8
                    }"
                    v-show="getWidth(item) != 0"
                    @click="edit(item)"
                    
                >
                    <el-popover
                        placement="top-start"
                        trigger="hover"
                        :style="{fontSize:'10px',padding:'0px'}"
                         >
                        <p>{{'开始:'+item.startTime}}p>
                        <p>{{'结束:'+item.endTime}}p>
                        <div slot="reference" class="meet-item-one-content">
                          <p class="ellipsis" >{{ item.content }}p>
                        div>
                        
                    el-popover>
                div>
               
              template>
            div>
          div>
        div>
        
        <div class="time-bar clear">
          <div class="gui-table clear">
            <li
              :style="{
                width: 100 / (number + 1) +'%',
              }"
              v-for="(item, index) in config.xAxis"
              :key="index"
            >
              <div class="gui-cle">div>
              <div class="gui-lit">div>
              <div class="gui-title"><span class="">{{item.xAxis}}span>div>
            li>
          div>
        div>
      div>
    div>
  template>
  
  <script>
  import {Popover,Scrollbar} from 'element-ui'
  export default {
    name: "GanttDemo",
    data() {
      return {
        date: "",
        number:6,
        xInterval:0.1,
        config: {xAxis:[]},
        taskList: [
          {
            materialId: "1",
            name: "物料一",
            startTime: "2022-11-16 08:00:00",
            endTime: "2022-11-16 20:00:00",
            materialData: [
              {
                materialId: "1",
                id: "14444",
                status: 0,
                startTime: '2022-11-16 08:00:00',
                endTime: '2022-11-16 12:00:00',
                color:"#32c5e9",
                content:"800",
              },
              {
                materialId: "1",
                id: "15555",
                status: 0,
                startTime: '2022-11-16 18:30:10',
                endTime: '2022-11-16 20:00:00',
                color:"#32c5e9",
                content:"600",
              },
            ],
          },
          {
            materialId: "2",
            name: "物料二",
            startTime: "2022-11-16 20:00:00",
            endTime: "2022-11-16 23:30:00",
            materialData:[{
                materialId: "2",
                id: "223865",
                status: 0,
                startTime: '2022-11-16 20:00:00',
                endTime: '2022-11-16 23:30:00',
                color:"#eb865e",
                content:"800",
              },
            ]
          },{
            materialId: "3",
            name: "物料三",
            startTime: "2022-11-16 15:00:00",
            endTime: "2022-11-16 16:30:00",
            materialData:[{
                materialId: "3",
                id: "31",
                status: 0,
                startTime: '2022-11-16 15:00:00',
                endTime: '2022-11-16 16:30:00',
                color:"#eb865e",
                content:"500",
              },
            ]
          },{
            materialId: "4",
            name: "物料四",
            startTime: "2022-11-16 12:00:00",
            endTime: "2022-11-16 16:30:00",
            materialData:[{
                materialId: "4",
                id: "41",
                status: 0,
                startTime: '2022-11-16 12:00:00',
                endTime: '2022-11-16 16:30:00',
                color:"#eb865e",
                content:"450",
              },
            ]
          },{
            materialId: "5",
            name: "物料五",
            startTime: "2022-11-16 11:00:00",
            endTime: "2022-11-16 15:30:00",
            materialData:[{
                materialId: "5",
                id: "51",
                status: 0,
                startTime: '2022-11-16 11:00:00',
                endTime: '2022-11-16 15:30:00',
                color:"#eb865e",
                content:"300",
              },
            ]
          },{
            materialId: "6",
            name: "物料六",
            startTime: "2022-11-16 11:00:00",
            endTime: "2022-11-16 12:30:00",
            materialData:[{
                materialId: "6",
                id: "61",
                status: 0,
                startTime: '2022-11-16 11:00:00',
                endTime: '2022-11-16 12:30:00',
                color:"#eb865e",
                content:"200",
              },
            ]
          },{
            materialId: "7",
            name: "物料七",
            startTime: "2022-11-16 09:00:00",
            endTime: "2022-11-16 10:30:00",
            materialData:[{
                materialId: "7",
                id: "71",
                status: 0,
                startTime: '2022-11-16 09:00:00',
                endTime: '2022-11-16 10:30:00',
                color:"#eb865e",
                content:"300",
              },
            ]
          },
        ]
      };
    },
    components:{
      elPopover: Popover,
      elScrollbar:Scrollbar,
    },
    props:{
    },
    watch:{

    },

    mounted(){
      this.getConfig();
    },
    methods: {
     
      getLeftTime(cTime) {
        const dTime = new Date(cTime).getTime();
        const leftTime = new Date(this.config.startTime).getTime();
        const time = (dTime - leftTime) / (1000 * 60 * 60); // 小时数
        const leftPercent = 0.88 * (time * 100) / (this.xInterval * (this.number+1)) ;
        return leftPercent + 12;
      },
      getWidth(item) {
        const _left1 = this.getLeftTime(item.startTime);
        const _left2 = this.getLeftTime(item.endTime);
        return _left2 - _left1;
      },
      edit(item) {
        console.log(item);
      },
      getConfig(){
        
        if(this.taskList.length>0){
          let startTime = this.taskList[0].startTime;
          let endTime = this.taskList[0].endTime;
          console.log(endTime)
          for(let index in this.taskList){
              let task = this.taskList[index];
              let start1 = startTime?new Date(startTime):new Date();
              let start2 = new Date(task.startTime);
              if(start1 > start2 && null != task.startTime){
                  startTime = task.startTime;
              }
              let end1 = new Date(endTime);
              let end2 = new Date(task.endTime?task.endTime:start2);
              if(end1 < end2){
                  endTime = task.endTime;
              }
          }
          let hour =((new Date(endTime)).getTime() - (new Date(startTime)).getTime());

          this.config.start = startTime;
          this.config.end = endTime;
          this.config.startTime = this.getDateTime(new Date((new Date(startTime)).getTime()-0.1*hour));
          this.config.endTime = this.getDateTime(new Date((new Date(endTime)).getTime()+0.1*hour));
          hour = ((new Date(this.config.endTime)).getTime() - (new Date(this.config.startTime)).getTime());
          this.config.hour = hour;
          if(hour){
            this.xInterval = (hour / (this.number+1)) / (1000*60*60);
          }
          this.config.xAxis = []
          let hh = (new Date(this.config.startTime)).getHours() < 10 ? '0' + (new Date(this.config.startTime)).getHours() : (new Date(this.config.startTime)).getHours();
          let mm = (new Date(this.config.startTime)).getMinutes() < 10 ? '0' + (new Date(this.config.startTime)).getMinutes() : (new Date(this.config.startTime)).getMinutes();
          let xOne = hh +":" +mm;
          this.config.xAxis.push({xAxis:xOne});
          for(let i  = 0;i < this.number;i++){
            console.log();
              let node = (new Date(this.config.startTime)).getTime()+(this.xInterval * 60 *60 * 1000 * ( i + 1 ));
              let nodeHh = (new Date(node)).getHours() < 10 ? '0' + (new Date(node)).getHours() : (new Date(node)).getHours();
              let nodeMm = (new Date(node)).getMinutes() < 10 ? '0' + (new Date(node)).getMinutes() : (new Date(node)).getMinutes();
              let nodeX = nodeHh + ":" + nodeMm;
              this.config.xAxis.push({xAxis:nodeX});
          }
        }
      },
      getDateTime(time){
        let year = time.getFullYear();  //获取年 2021
        let month = time.getMonth() + 1;  // 获取月  5
        let day = time.getDate();    // 获取天  11
  
        let h = time.getHours() < 10 ? '0' + time.getHours() : time.getHours();   // 获取小时  18
        let m = time.getMinutes() < 10 ? '0' + time.getMinutes() : time.getMinutes();  // 获取分钟  42
        let s = time.getSeconds() < 10 ? '0' + time.getSeconds() : time.getSeconds();    // 获取秒  51
        let dataTime = year + '-' + month + '-' + day + ' ' + h + ':' + m + ':' + s;
        return dataTime;
      },
      
    }
   
  };
  script>
  
  <style lang="scss" scoped >
  .Gantt {
    padding: calc(100vw * 20 / 1920 );;
    border-radius: 3px;
    /* border: 1px solid red; */
    height: 100%;
    box-sizing: border-box;
    display: flex;
    flex-direction: column-reverse;
    position: relative;
  }
  .content {
    padding-top: calc(100vw * 20 / 1920 );

  }
  
  .content /deep/ .el-scrollbar{
    height: calc(100vh * 220 / 1080);
    .el-scrollbar__wrap {
      overflow-x: hidden;
    }
  }
  .content /deep/ .el-scrollbar__view {
    height: 100%;
    // display: flex;
    // flex-direction: column-reverse;
  }
  .Gantt .gui-table,
  .gui-tab {
    font-size: calc(100vw * 12 / 1920 );
    color: #333;
    margin: 0;
    width: 88%;
    float: right;
    white-space: nowrap;
    height: calc(100vh * 48 / 1080 );
  }
  .Gantt .gui-table li {
    position: relative;
    border-top: solid 1px #1ba5fa;
    z-index: 666;
    word-wrap: break-word;
  }
  .Gantt .gui-table li {
    cursor: default;
  }
  
  .Gantt .gui-table li{
    float: left;
    text-align: left;
    width: 8.9%;
    height: calc(100vh * 48 / 1080 );
    line-height: calc(100vh * 48 / 1080 );
    /* cursor: pointer; */
    white-space: pre-wrap;
  }
  .Gantt .gui-table .gui-title{
    line-height: calc(100vh * 24 / 1080 );
    position: relative;
    width: 100%;
  }
  .Gantt .gui-table .gui-title span{
    line-height: calc(100vh * 24 / 1080 );
    color: rgba(255,255,255,.7);
    position: absolute;
    left: -50%;
    text-align: center;
    width: 100%;
  }
  .gui-tab li {
    float: left;
    text-align: left;
    width: 8.9%;
    height: calc(100vh * 28 / 1080 );
    line-height: calc(100vh * 28 / 1080 );
    /* cursor: pointer; */
    white-space: pre-wrap;

  }
  .Gantt .gui-cle {
    position: absolute;
    left: -5px;
    top: -5px;
    width: 6px;
    height: 6px;
    background: #fff;
    border-radius: 50%;
    border: solid 2px #1ba5fa;
    z-index: 666;
  }
  .Gantt .gui-lit {
    position: absolute;
    left: 0;
    top: -3px;
    width: 3px;
    height: 3px;
    background: #fff;
    border-radius: 50%;
    border: solid 1px #1ba5fa;
    margin-left: 50%;
    z-index: 666;
  }
  .clear {
    *zoom: 1;
  }
  .clear:after {
    content: ".";
    display: block;
    clear: both;
    visibility: hidden;
    line-height: 0;
    height: 0;
    font-size: 0;
  }
  .Gantt .fasten {
    width: 12%;
    float: left;
    height: calc(100vh * 28 / 1080 );
    text-align: center;
    line-height: calc(100vh * 28 / 1080 );
    font-size: calc(100vw * 12 / 1920 );
    color: rgba(255, 255, 255, .7);
    border-right: solid 1px #1ba5fa;
    /* word-break: break-word;
    white-space: pre-line; */
  }
  .Gantt .gui-table{
    font-size: calc(100vw * 12 / 1920 );
    color: #333;
    margin: 0;
    width: 88%;
    float: right;
    white-space: nowrap;
    height: calc(100vh * 48 / 1080 );
  }
  .gui-tab {
    font-size: calc(100vw * 12 / 1920 );
    color: #333;
    margin: 0;
    width: 88%;
    float: right;
    white-space: nowrap;
    height: calc(100vh * 28 / 1080 );
  }
  /* .Gantt .gui-list:first-child {
    background: #fff;
  } */
  .Gantt .gui-list {
    position: relative;
    display: flex;
  }
  

  .Gantt .meet-color-finished {
    background: #4dc394;
  }
  
  .Gantt .meet-color-having {
    background: #eb865e;
  }
  .Gantt .meet-item-one {
    color: #000;
    text-align: center;
    position: absolute;
    height: calc(100vh * 26 / 1080 );
    left: 12%;
    top: 1px;
    color: #fafafa;
    font: 14px/60px microsoft yahei;
    /* padding: 0 5px; */
    box-sizing: border-box;
    -moz-box-sizing: border-box;
    -webkit-box-sizing: border-box;
    overflow: hidden;
    cursor: pointer;
    width: 20%;
    line-height: calc(100vh * 26 / 1080 );
  }
  
  li {
    list-style: none;
  }
  .Gantt .meet-item-one-content{
    height: 100%;
  }
  .ellipsis {
    overflow: hidden;
    margin-top: 0;
    white-space: nowrap;
    text-overflow: ellipsis;
    line-height: calc(100vh * 28 / 1080 );
  }
  .info,.gui-content{
    display: flex;
    flex-direction: column-reverse;
  }

  .info::-webkit-scrollbar {
  width : 5px;  
  height: 1px;
  }
  .info::-webkit-scrollbar-thumb {
    box-shadow: 0px 1px 3px rgba(144,147,153,.3) inset; /*滚动条的内阴影*/
    border-radius: 10px; /*滚动条的圆角*/
    background-color: rgba(144,147,153,.3); /*滚动条的背景颜色*/
  }
  .info::-webkit-scrollbar-track {
    box-shadow: 0px 1px 3px #071e4a inset; /*滚动条的背景区域的内阴影*/
    border-radius: 10px; /*滚动条的背景区域的圆角*/
    background-color: #071e4a; /*滚动条的背景颜色*/
  }
  /* .gui-yAxis{
    position: absolute;
    height: 100%;
    border: solid 1px #1ba5fa;
  } */
  style>
  
  

你可能感兴趣的:(VUE,vue.js,甘特图)