• vue2.X 中使用 echarts5.4.0实现项目进度甘特图


    vue2.X 中使用 echarts5.4.0实现项目进度甘特图

    效果图:
    在这里插入图片描述
    左侧都是名称上面时间,当中的内容是日志内容

    1. 组件: gantt.vue
    <template>
        <div id="main" style="width: 100%; height: 100%"></div>
      </template>
      <script>
      import * as echarts from "echarts";
      export default {
        name: "Gantt",
        props: {
          baseDate: {
            type: String,
            default: "",
          },
          ganttData: {
            type: Array,
            default: () => [],
          },
          roomData: {
            type: Array,
            default: () => [],
          },
        },
        data() {
          return {
              minHours: '08:00',
              maxHours: '24:00',
             // colors: ['#5c2223','#346c9c', '#525288', '#87723e', '#d1c2d3','#f07c82', '#835e1d', '#d99156', '#954416', '#ee8055', '#126e82', '#61649f', '#a7a8bd']
          };
        },
        created() {},
        mounted() {
         this.myEcharts();
        },
        watch: {
          ganttData(newVal) {
            this.myEcharts();
          },
        },
        methods: {
          getTimes() {
            let baseDate = `${this.baseDate}`;
            // 获取日志的最早和最晚时间,这样防止两边出现空白,比如00:00-09:00;
            this.$http.post(`/sys/task/times`, {taskDate: baseDate}).then(({ data: res }) => {
              if (res.code !== 0) {
                  return this.$message.error(res.msg)
              }
              this.minHours = res.data.minTime
              this.maxHours = res.data.maxTime
            }).catch(() => {})
          },
          myEcharts() {
            this.getTimes();
            // 基于准备好的dom,初始化echarts实例
            const container = document.getElementById("main");
            this.$echarts.init(container).dispose();
            var myChart = this.$echarts.init(container);
            // 用于随机颜色
            var colors= ['#8dddfa','#f98e72', '#f7b84f', '#a872f9', '#d6a9d1','#a7e56d', '#ff73c7', '#d6a9d1', '#b1e7fb', '#d3b3af', '#2859b1', '#1f6cb0']
            //let min = `${this.$moment().format("YYYY-MM-DD")} 00:00:00`;
            //let max1 = `${this.$moment().add(1, "day").format("YYYY-MM-DD")} 00:00:00`;
            // 指定图表的配置项和数据
            var option = {
              color: "#0A8BFF",
              backgroundColor: "#fff",
              title: {},
              tooltip: {
                // enterable: true,
                trigger: "item",
                show: true,
                // axisPointer: { // 坐标轴指示器,坐标轴触发有效
                //   type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
                // },
                // alwaysShowContent: true,
                hideDelay: 100,
                backgroundColor: "rgba(255,255,255,1)", // 背景颜色(此时为默认色)
                borderRadius: 5, // 边框圆角
                confine: true,
                textStyle: {
                  color: "#000"
                },
                // 悬浮的时候展示对应的数据
                formatter: function (params) {
                  for(var i = 0; i < params.data.value.length; i++) {
                    var content = (
                      params.data.value[i].content +
                      // "
    " +
    // (params.data.value[i].status === "1" ? '已完成' : '进行中') + "
    "
    + params.data.value[i].stime + " - " + params.data.value[i].etime ); // 作用:鼠标悬浮在内容,出现弹窗显示内容详情,这里限制了宽,以防止宽度过长 var tooltipHtml = '
    '+content+'
    '
    ; return tooltipHtml; //return content; } }, }, legend: { // left: '90px', top: "1%", itemWidth: 16, itemHeight: 16, show: true, // selectedMode: false, // 图例设为不可点击 textStyle: { color: "rgba(0, 0, 0, 0.45)", fontSize: 14, }, }, grid: { // 绘图网格 left: "2%", right: "3%", top: "10%", bottom: "10%", containLabel: true, }, xAxis: { type: "time", position: "top", // interval: 3600 * 1000, // 以一个小时递增 // 以一小时的时间递增 minInterval: 3600 * 1000 , maxInterval: 3600 * 1000 , min: `${this.baseDate} ` + this.minHours, max: `${this.baseDate} ` + this.maxHours, //max:`${this.baseDate} 24:00`, //max: `${this.baseDate} 19:00`, // 设置最大时间为18点 //min:`${this.baseDate} 00:00`, //将data里最小时间的整点时间设为min,否则min会以data里面的min为开始进行整点递增 //min: `${this.baseDate} 08:00`, // 将data里最小时间的整点时间设为min,否则min会以data里面的min为开始进行整点递增 axisLabel: { // 设置最后一个数据显示 showMaxLabel: true, formatter: function (value, index) { var data = new Date(value); var hours = data.getHours(); var minutes = data.getMinutes(); if ((index !== 0 && hours === 0) || index === 25){ return "23:59" } else { //return hours + ":00"; if (minutes === 0) { return hours + ":00"; } else { return hours + ":" + minutes; } } }, textStyle: { color: "rgba(0,0,0,0.65)", // 更改坐标轴文字颜色 fontSize: 14, // 更改坐标轴文字大小 }, }, axisLine: { lineStyle: { color: "#e5e5e5", }, onZero: false, }, splitLine: { show: true, lineStyle: { type: "dashed", }, }, }, // dataZoom: [ // // 给x轴设置滚动条 // { // type: 'slider', // show: true, // yAxisIndex: [0, 1], // left: '96%', // start: 1, // end: 100, // fiterMode: 'filter' // }, // { // type: 'inside', // fiterMode: 'filter', // yAxisIndex: [0, 1], // start: 1, // end: 100 // } // ], yAxis: { inverse: true, // 是否反转 type: "category", axisTick:{ show: true //不显示坐标轴刻度线 }, splitLine: { //网格线 "show": true }, axisLine: { show: true, lineStyle: { color: "#e5e5e5", }, }, data: this.roomData, axisLabel: { textStyle: { color: "rgba(0, 0, 0, 0.65)", // 刻度颜色 fontSize: 14, // 刻度大小 }, }, }, series: [ { type: "custom", clickable: false, renderItem: function (params, api) { // 开发者自定义的图形元素渲染逻辑,是通过书写 renderItem 函数实现的 var categoryIndex = api.value(0).index; // 这里使用 api.value(0) 取出当前 dataItem 中第一个维度的数值。 var start = api.coord([api.value(0).startTime, categoryIndex]); // 这里使用 api.coord(...) 将数值在当前坐标系中转换成为屏幕上的点的像素值。 var end = api.coord([api.value(0).endTime, categoryIndex]); var height = 26; return { type: "rect", // 表示这个图形元素是矩形。还可以是 'rect', 'circle', 'sector', 'polygon' 等等。 shape: echarts.graphic.clipRectByRect( { // 矩形的位置和大小。 x: start[0], y: start[1] - height / 2, width: end[0] - start[0], height: 27, }, { // 当前坐标系的包围盒。 x: params.coordSys.x, y: params.coordSys.y, width: params.coordSys.width, height: params.coordSys.height, } ), style: api.style(), }; }, label: { normal: { show: true, position: "insideBottom", formatter: function (params) { //return params.value[0].content; let value = params.value[0].content; if (!value) return ""; if (value.length > 6) { return value.slice(0, 6) + "..."; } return value; }, textStyle: { align: "center", fontSize: 14, fontWeight: "400", lineHeight: "20", }, }, }, encode: { x: 1, // data 中『维度1』对应到 X 轴 y: 0, // 把"维度0"映射到 Y 轴。 }, itemStyle: { normal: { color: function (params) { const randomIndex = Math.floor(Math.random() * colors.length); return colors[randomIndex]; }, }, }, // dataZoom: [ // { // show: true, // realtime: true, // start: 0, // end: 50 // } // ], data: this.ganttData, }, ], }; // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); window.onresize = function () { myChart.resize(); }; // myChart.getZr().on("mousemove", (param) => { // var pointInPixel = [param.offsetX, param.offsetY]; // if (myChart.containPixel("grid", pointInPixel)) { // // 若鼠标滑过区域位置在当前图表范围内 鼠标设置为小手 // // myChart.getZr().setBackgroundColor('red') // myChart.getZr().setCursorStyle("pointer"); // } else { // myChart.getZr().setCursorStyle("default"); // } // }); // 任意位置点击事件----注册双击 // myChart.getZr().on("click", (params) => { // if (!params.target) { // // 点击在了空白处,做些什么。 // const point = [params.offsetX, params.offsetY]; // if (myChart.containPixel("grid", point)) { // // 获取被点击的点在y轴上的索引 // const idxArr = myChart.convertFromPixel({ seriesIndex: 0 }, point); // const xValue = new Date(+idxArr[0]).getHours(); // const yValue = idxArr[1]; // const sendData = [xValue, yValue]; // this.$emit("getInfoCallback", sendData); // } // } // }); // // 图例点击事件-返回数据给父组件---单击事件 // myChart.on("click", (params) => { // this.$emit("getInfoCallback", params.data.value); // }); }, }, computed: {}, }; </script> <style scoped lang="less"> </style>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286
    • 287
    • 288
    • 289
    • 290
    • 291
    • 292
    • 293
    • 294
    • 295
    • 296
    • 297
    • 298
    • 299
    • 300
    • 301
    • 302
    • 303
    • 304
    • 305
    • 306
    • 307
    • 308
    • 309
    • 310
    • 311
    • 312
    • 313
    • 314
    • 315
    • 316
    • 317
    • 318
    • 319
    • 320
    • 321
    • 322
    • 323
    • 324
    • 325
    • 326
    • 327
    • 328
    • 329
    • 330
    1. js文件
    import Gantt from './src/gantt'
    
    Gantt.install = function (Vue) {
      Vue.component(Gantt.name, Gantt)
    }
    export default Gantt
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. main.js中组件引用组件
      在这里插入图片描述
      在这里插入图片描述
    2. log页面
    <template>
      <div class="appointment">
        <div class="a-gantt">
          <el-row style="padding: 12px 10px; background-color: #fff">
            <el-col :span="12" align="left" style="font-weight: 700">
              日志
            </el-col>
            <el-col :span="12" align="right">
              <el-date-picker
                v-model="baseDate"
                type="date"
                @change="handleSelect"
                placeholder="选择日期"
                value-format="yyyy-MM-dd"
              >
              </el-date-picker>
            </el-col>
          </el-row>
        </div>
        <div class="f-gantt">
          <Gantt
            :baseDate="baseDate"
            ref="gantt"
            :ganttData="ganttData"
            @getInfoCallback="getGanttInfo"
            :roomData="roomData"
          ></Gantt>
        </div>
        <!-- 新增编辑框 -->
        <!-- <el-dialog :title="formTitle" :visible.sync="dialogVisible" width="30%">
          <el-form :model="form" :label-width="formLabelWidth">
            <el-form-item label="会议室">
              <el-input
                disabled
                v-model="usernameData[form.index]"
                autocomplete="off"
              ></el-input>
            </el-form-item>
            <el-form-item label="内容">
              <el-input v-model="form.content" autocomplete="off"></el-input>
            </el-form-item>
            <el-form-item label="时间" :label-width="formLabelWidth">
              <el-date-picker
                v-model="form.startTime"
                type="datetime"
                placeholder="选择日期"
                value-format="yyyy-MM-dd HH:mm"
              >
              </el-date-picker>
              -
              <el-date-picker
                v-model="form.endTime"
                type="datetime"
                placeholder="选择日期"
                value-format="yyyy-MM-dd HH:mm"
              >
              </el-date-picker>
            </el-form-item>
            <el-form-item label="状态" :label-width="formLabelWidth">
              <el-select v-model="form.status" style="width: 100%">
                <el-option label="进行中" value="0"></el-option>
                <el-option label="已完成" value="1"></el-option>
              </el-select>
            </el-form-item>
          </el-form>
          <span slot="footer" class="dialog-footer">
            <el-button @click="dialogVisible = false">取 消</el-button>
            <el-button type="primary" @click="saveData">确 定</el-button>
          </span>
        </el-dialog> -->
      </div>
    </template>
    
    <script>
    
    
    export default { 
      data() {
        return {
          baseDate: `${new Date().getFullYear()}-${
            new Date().getMonth() + 1
          }-${new Date().getDate()}`,
          roomData: [],
          ganttData: [],
          dialogVisible: false,
          formLabelWidth: "120px",
          formTitle: "",
          form: {
            id: "",
            index: "",
            content: "",
            endTime: "",
            status: "",
            startTime: "",
          },
        };
      },
      created() {
        this.getDataList()
    
        // this.ganttData = [
        //   {
        //     value: [
        //       {
        //         index: 1,
        //         roomName: "会议室二",
        //         RoomId: "123",
        //         id: "333",
        //         startTime: '2023-09-05 08:28', //`${this.baseDate} 8:28`,
        //         endTime: '2023-09-05 09:28', // `${this.baseDate} 9:28`,
        //         status: "1",
        //         content: "睡觉",
        //       }
        //     ],
        //   },
        //   {
        //     value: [
        //     {
        //         index: 1,
        //         roomName: "会议室二",
        //         RoomId: "123",
        //         id: "333",
        //         startTime: '2023-09-05 12:28', //`${this.baseDate} 8:28`,
        //         endTime: '2023-09-05 15:28', // `${this.baseDate} 9:28`,
        //         status: "1",
        //         content: "工作",
        //       },
        //     ]
        //   },
        //   {
        //     value: [
        //       {
        //         index: 0,
        //         roomName: "会议室一",
        //         RoomId: "2234",
        //         id: "444",
        //         startTime: `2023-09-05 10:28`,
        //         endTime: `2023-09-05 12:28`,
        //         status: "0",
        //         content: "吃饭",
        //       },
        //     ],
        //   },
        //   {
        //     value: [
        //     {
        //         index: 0,
        //         roomName: "会议室一",
        //         RoomId: "123",
        //         id: "333",
        //         startTime: '2023-09-05 13:28', //`${this.baseDate} 8:28`,
        //         endTime: '2023-09-05 15:28', // `${this.baseDate} 9:28`,
        //         status: "1",
        //         content: "工作111",
        //       },
        //     ]
        //   }
        // ];
        //this.roomData = ['会议室一', '会议室二', '会议室三', '会议室四'];
      },
      mounted() {
        this.getDataList();
      },
      methods: {
        handleSelect() {
          this.getDataList()
          this.$refs.gantt.myEcharts();
        },
        getDataList() {
          this.$http.post(`/sys/task/ganteLog`, {taskDate: this.baseDate}).then(({ data: res }) => {
              if (res.code !== 0) {
                  return this.$message.error(res.msg)
              }
              this.roomData = res.data.usernameList
              this.ganttData = res.data.data
            }).catch(() => {})
        },
        getGanttInfo(data) {
          this.dialogVisible = true;
          // 根据data的长度判断是新增还是编辑
          // 新增
          if (data.length === 2) {
            this.formTitle = "新增";
            this.form = this.$options.data().form;
            this.$set(this.form, "index", data[1]);
          } else {
            this.formTitle = "修改";
            this.form = data[0];
          }
        },
        saveData() {
          if (this.formTitle === "修改") {
            this.ganttData = this.ganttData.filter((item) => {
              return item.value[0].id !== this.form.id;
            });
          } else {
            this.$set(this.form, "status", "0");
          }
          const obj = Object.assign({}, this.form);
          this.ganttData.push({ value: [obj] });
          this.$refs.gantt.myEcharts();
          this.dialogVisible = false;
        },
      },
    };
    </script>
    
    <style scoped>
    /* .a-gantt {
      position: absolute;
      top: 0;
      height: 60px;
      width: 100%;
      box-sizing: border-box;
      text-align: center;
    } */
    
    /* .appointment {
      position: relative;
      height: 100%;
      overflow-y: hidden;
      border: 1px solid #ddd;
      color: #0f1419;
      box-sizing: border-box;
    } */
    .f-gantt {
      position: absolute;
      bottom: 10px;
      top: 80px;
      width: 100%;
      /*height: 600px;*/
      box-sizing: border-box;
    }
    </style>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234

    注意数据结构
    在这里插入图片描述

  • 相关阅读:
    代码随想录 Day40 动态规划08 LeetCodeT198打家劫舍 T213打家劫舍II T337 打家劫舍III
    计算小于或等于n的非负整数区间包含的1的数量
    算法:最长递增子序列
    html、css、js和jQuery学习总结
    个人数据保全计划:(1) NAS开箱
    共享库soname机制
    C++ Core Guidelines 中文版 GSL
    机器学习笔记 - 深度学习中跳跃连接的直观解释
    Go开始:Go基本元素介绍
    SpringCloud学习笔记 - 链路监控 - SpringCloud Sleuth
  • 原文地址:https://blog.csdn.net/weixin_44021888/article/details/132736742