2023-03-15

排程甘特图 v1.0

image.png
<template>
    <div id="gante">
      <el-button @click="refresh" style="margin-bottom:5px">刷新(测试)</el-button>
      <table border="1" cellpadding="8px" class="gante-table" v-loading="loading">
        <tr>
          <th style="width:200px"></th>
          <th style="width:100px" colspan="2" v-for="item in headerData" :key="item.key">{{ item.value }}</th>
        </tr>
        <tr v-for="(item, index) in rows" :key="index" :class="{ firstline: index == 0}">
          <template v-if="index == 0">
            <td class="cell_td">设备</td>
            <td class="cell_td" colspan="2" v-for="item in headerData" :key="item.id"></td>
          </template>
  
          <template v-else>
            <td>资源 {{index}}</td>
            <template v-for="(r, index) in item.row">
              <td :key="index" :style="{ position: index % 2 == 0 ? 'relative' : 'static' }">
  
                <div :class="{'contant_block_hover': colorMap.get(r.status).length}" @mouseover="mouseover(item.id, r.iid, r.status == 'Danger')" @mouseleave="mouseleave(item.id, r.iid,  r.status == 'Danger')" :style="{backgroundColor: colorMap.get(r.status)}" class="center contant_block" v-if="index % 2 == 0">
                  {{colorMap.get(r.status) ? (r.status=="Danger"? '' : '100%') : '0%'}}
                  <div class="contant_hover" v-show="r.showContant"  @mouseleave.stop @mouseover.stop >
                    设备:QG#01切割机 <br>
                    故障起始时间:2023-2-27 8:00 <br>
                    故障修复时间:2023-2-27 12:00 <br>
                    影响任务单个数:10 <br>
                  </div>
  
                </div>
  
              </td>
            </template>
          </template>
        </tr>
      </table>
    </div>
  
  </template>
  
  <script>
  export default {
    name: 'gantt',
    data() {
      return {
        rows: [],
        bodyData: [],
        headerData: [],
        loading: true,
        colorMap: new Map()
      };
    },
    props: {
    },
    created() {
      this.colorMap.set('Success', '#67C23A')
      this.colorMap.set('Warning', '#E6A23C')
      this.colorMap.set('Danger', '#F56C6C')
      this.colorMap.set('Info', '#909399')
      this.colorMap.set('none', '')
  
      this.createData()
  
    },
    beforeDestroy() {
  
    },
    watch: {
  
    },
    methods: {
      createData() {
        const colorMap = ['Success', 'Warning', 'Danger', 'Info', 'none']
        this.loading = true
        new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve(); 
          }, 1000)
        }).then(() => {
          this.headerData = []
          this.rows = [];
  
          for (let index = 0; index < 6; index++) {
            const obj = { row: [] };
            obj.id = this.uuid()
  
            for (let index = 0; index < 22; index++) {
              const num = Math.floor(Math.random()*Math.floor(5));
  
              const _obj = {}
              _obj.value = 'contant' + '-' + index
              _obj.iid = this.uuid()
              _obj.showContant = false;
              _obj.status = colorMap[num]
              obj.row.push(_obj)
            }
  
            this.rows.push(obj);
          }
  
          for (let index = 0; index < 11; index++) {
            const obj = {};
            obj.value = this.getDateFun(index)
            obj.key = this.uuid()
            this.headerData.push(obj)
          }
        }).finally(() => {
          this.loading = false
        })
      },
      uuid() {
          return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
              var r = Math.random() * 16 | 0,
                  v = c == 'x' ? r : (r & 0x3 | 0x8);
              return v.toString(16);
          });
      },
      refresh() {
        this.createData();
      },
      mouseover(id, iid, key) {
        if(!key) return
        const index = this.rows.findIndex(item => item.id == id);
        if(index != -1) {
          const i = this.rows[index].row.findIndex(item => item.iid == iid)
          if(i != -1) {
            this.rows[index].row[i].showContant = true
          }
        }
      },
      mouseleave(id, iid, key) {
        if(!key) return
        console.log('mouseleave')
        const index = this.rows.findIndex(item => item.id == id);
        if(index != -1) {
          const i = this.rows[index].row.findIndex(item => item.iid == iid)
          if(i != -1) {
            this.rows[index].row[i].showContant = false
          }
        }
      },
      getDateFun(index) {
        const day = new Date();
        day.setTime(day.getTime() + index * 24*60*60*1000);
        return day.getFullYear()+"-" + (day.getMonth()+1) + "-" + day.getDate();
      },
      bgcFun(status) {
        console.log('???????????????????????????????')
        return "background-color: #67C23A"
      }
    }
  }
  </script>
  <style lang="scss" scoped>
    #gante {
      margin-top: 10px;
      padding: 10px;
      padding-bottom: 30px;
      overflow-x: auto;
      background-color: #fff;
      .gante-table {
        border: none;
        width: 1300px;
      }
    }
    .center {
      display: flex;
      justify-content: center;
      align-items: center;
    }
    .contant_block {
      cursor: pointer; 
      width:90px; 
      height:30px; 
      border-radius: 3px;
      position: absolute; 
      top: 3px; 
      left: 5px;
    }
    .contant_block_hover:hover {
      // transform: scale(1.02);
      border: 2px solid #cccccc;
    }
    .firstline {
      line-height: 6px;
    }
    .cell_td {
      width: 150px;
    }
    .contant_hover {
      z-index:9999; 
      position:absolute; 
      left:100px; 
      width:220px; 
      height:100px; 
      padding: 5px;
      background-color:#ffffff;
      border: 1px solid #cccccc;
    }
  </style>

排程甘特图 v2.0

image.png
<template>
  <div id="gante">
    <el-button @click="refresh" style="margin-bottom:5px">刷新(测试)</el-button>
    <table border="1" cellpadding="8px" class="gante-table" v-loading="loading">
      <tr style="text-align:center;">
        <th style="width:200px"></th>
        <th style="width:200px" colspan="2" v-for="item in headerData" :key="item.key">{{ item.value }}</th>
      </tr>
      <tr v-for="(item, index) in rows" :key="index" :class="{ firstline: index == 0}">
        <template v-if="index == 0">
          <td>设备</td>
          <td colspan="2" v-for="item in headerData" :key="item.id">
              <div style="display:flex;justify-content:space-between;">
                  <span class="time_item">02</span>
                  <span class="time_item">04</span>
                  <span class="time_item">06</span>
                  <span class="time_item">08</span>
                  <span class="time_item">10</span>
                  <span class="time_item">12</span>
                  <span class="time_item">14</span>
                  <span class="time_item">16</span>
                  <span class="time_item">18</span>
                  <span class="time_item">20</span>
                  <span class="time_item">22</span>
              </div>
          </td>
        </template>

        <template v-else>
          <td>资源 {{index}}</td>
          <template v-for="(r, index) in item.row">
            <td :key="index" :style="{ position: index % 2 == 0 ? 'relative' : 'static' }">

              <template v-if="index != item.row.length - 2 && index % 2 == 0" >
                <div :class="{'contant_block_hover': colorMap.get(r.am.status).length}" @mouseover="mouseover(item.id, r.am.iid, r.am.status == 'Danger','am')" @mouseleave="mouseleave(item.id, r.am.iid,  r.am.status == 'Danger', 'am')" :style="{border: '1px ' + 'solid ' + colorMap.get(r.am.status)}" class="center contant_block">
                  <div :style="{backgroundColor: colorMap.get(r.am.status),height: r.am.percentage * 100 + '%', width: '100%' }">
                  </div>
                  <div style="position: absolute;left:50%;transform: translateX(-50%);">
                    {{r.am.percentage * 100 }}%
                  </div>
                  <div class="contant_hover" v-show="r.am.showContant"  @mouseleave.stop @mouseover.stop >
                    设备:QG#01切割机 <br>
                    故障起始时间:2023-2-27 8:00 <br>
                    故障修复时间:2023-2-27 12:00 <br>
                    影响任务单个数:10 <br>
                  </div>

                </div>

                <div style="left:196px" :class="{'contant_block_hover': colorMap.get(r.pm.status).length}" @mouseover="mouseover(item.id, r.pm.iid, r.pm.status == 'Danger','pm')" @mouseleave="mouseleave(item.id, r.pm.iid,  r.pm.status == 'Danger','pm')" :style="{border: '1px ' + 'solid ' + colorMap.get(r.pm.status), width: '117px'}" class="center contant_block">
                  <div :style="{backgroundColor: colorMap.get(r.pm.status),height: r.pm.percentage * 100 + '%', width: '100%' }">
                  </div>
                  <div style="position: absolute;left:50%;transform: translateX(-50%);">
                    {{r.pm.percentage * 100 }}%
                  </div>
                  <div class="contant_hover" v-show="r.pm.showContant"  @mouseleave.stop @mouseover.stop >
                    设备:QG#01切割机 <br>
                    故障起始时间:2023-2-27 8:00 <br>
                    故障修复时间:2023-2-27 12:00 <br>
                    影响任务单个数:10 <br>
                  </div>
                </div>
              </template>

            </td>
          </template>
        </template>
      </tr>
    </table>
  </div>

</template>

<script>
export default {
  name: 'gantt',
  data() {
    return {
      rows: [],
      bodyData: [],
      headerData: [],
      loading: true,
      colorMap: new Map()
    };
  },
  props: {
  },
  created() {
    this.colorMap.set('Success', '#67C23A')
    this.colorMap.set('Warning', '#E6A23C')
    this.colorMap.set('Danger', '#F56C6C')
    this.colorMap.set('Info', '#909399')
    this.colorMap.set('none', '')

    this.createData()

  },
  methods: {
    createData() {
      this.loading = true
      new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve(); 
        }, 1000)
      }).then(() => {
        this.headerData = []
        this.rows = [];

        for (let index = 0; index < 6; index++) {
          this.rows.push(this.createBlockData());
        }

        for (let index = 0; index < 6; index++) {
          const obj = {};
          obj.value = this.getDateFun(index)
          obj.key = this.uuid()
          this.headerData.push(obj)
        }
      }).finally(() => {
        this.loading = false
      })
    },

    uuid() {
        return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
            var r = Math.random() * 16 | 0,
                v = c == 'x' ? r : (r & 0x3 | 0x8);
            return v.toString(16);
        });
    },

    createBlockData() {
      const obj = { row: [] };
      obj.id = this.uuid()
      obj.row = this.createBlockDataItem()
      return obj
    },

    createBlockDataItem() {
      const colorMap = ['Success', 'Warning', 'Danger', 'Info', 'none']
      const row = []
      for (let index = 0; index < 12; index++) {
        const _obj = {am:{},pm:{}}

        _obj.am.value = 'contant' + '-' + index
        _obj.am.iid = this.uuid()
        _obj.am.showContant = false;
        _obj.am.status = colorMap[Math.floor(Math.random()*Math.floor(5))]
        if(_obj.am.status !== 'none') {
          _obj.am.percentage = Math.random().toFixed(1) 
        }else {
          _obj.am.percentage = 0
        }

        _obj.pm.value = 'contant' + '-' + index
        _obj.pm.iid = this.uuid()
        _obj.pm.showContant = false;
        _obj.pm.status = colorMap[Math.floor(Math.random()*Math.floor(5))]
        if(_obj.pm.status !== 'none') {
          _obj.pm.percentage = Math.random().toFixed(1) 
        }else {
          _obj.pm.percentage = 0
        }


        row.push(_obj)
      }
      return row
    },

    refresh() {
      this.createData();
    },

    mouseover(id, iid, key, mark) {
      console.log('mouseover')
      if(!key) return
      const index = this.rows.findIndex(item => item.id == id);
      if(index != -1) {
        const i = this.rows[index].row.findIndex(item => item[mark].iid == iid)
        if(i != -1) {
          this.rows[index].row[i][mark].showContant = true
        }
      }
    },

    mouseleave(id, iid, key, mark) {
      if(!key) return
      console.log('mouseleave')
      const index = this.rows.findIndex(item => item.id == id);
      if(index != -1) {
        const i = this.rows[index].row.findIndex(item => item[mark].iid == iid)
        if(i != -1) {
          this.rows[index].row[i][mark].showContant = false
        }
      }
    },

    getDateFun(index) {
      const day = new Date();
      day.setTime(day.getTime() + index * 24*60*60*1000);
      return day.getFullYear()+"-" + (day.getMonth()+1) + "-" + day.getDate();
    },
  }
}
</script>
<style lang="scss" scoped>
  #gante {
    margin-top: 10px;
    padding: 10px;
    padding-bottom: 30px;
    overflow-x: auto;
    background-color: #fff;
    .gante-table {
      border: none;
      width: 1600px;
      // overflow: hidden;
    }
  }
  .center {
    display: flex;
    // justify-content: center;
    align-items: end;
  }
  .contant_block {
    z-index: 9;
    cursor: pointer; 
    width:116px; 
    height:30px; 
    border-radius: 3px;
    position: absolute; 
    top: 3px; 
    left: 80px;
  }
  .contant_block_hover:hover {
    z-index: 99999;
    border: 2px solid #cccccc;
  }
  .firstline {
    line-height: 6px;
  }
  .contant_hover {
    z-index:9999; 
    position: absolute; 
    left:120px; 
    width:220px; 
    height:100px; 
    padding: 5px;
    background-color:#ffffff;
    border: 1px solid #cccccc;
  }
  .time_item {
    flex:1;
    text-align:center
  }
</style>
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容

  • 我们每个人都从单纯、热情、完整无缺,渐渐变得复杂、怯懦、支离破碎。遇到一些人,爱上一些人,忘记一些人。被伤害的同时...
    周公子爱坤坤阅读 104评论 0 1
  • 烦恼本无根,不捡自然无;忧愁本无源,不究自轻松。世间之事,一念而已,心中若有事事重,心中若无事事轻,淡定之人不负赘...
    考研学姐阅读 259评论 0 29
  • 烦恼本无根,不捡自然无;忧愁本无源,不究自轻松。世间之事,一念而已,心中若有事事重,心中若无事事轻,淡定之人不负赘...
    考研学姐阅读 257评论 0 30
  • 治国必先治党,党兴才能国强 习近平总书记在党的二十大报告...
    ayi_0ece阅读 66评论 0 0
  • 今天上课写作业时,我走到小晔的身边,看到他书写的字太小,每个字都是团在一起的,不会一笔一划的写。于是我拿了一本书写...
    gll123456阅读 62评论 0 0