首页 > 其他分享 >gantt

gantt

时间:2023-08-26 22:33:09浏览次数:30  
标签:end item gantt new start year const

$("#gantttable").on('mouseover mousemove','.progress',function(e,b,c){
    console.log(e);
    let parentleft=e.target.style.transform.replace(/\D/g,'');
    let left=Number(parentleft)+e.offsetX+40;  
    let top=e.target.offsetTop+e.offsetY+40;  
    $(e.target).prev().css({
        "display":"block",
        "position":"absolute",
        "background":"red",
        "left":left+'px',
        "top":top+'px',
        "z-index":'999',
    })
    })

    $("#gantttable").on('mouseout','.progress',function(e,b,c){
       
        $(e.target).prev().css({
            "display":"none",
           
        })
 })

<template>
  <div class="box" ref="box">
    <div class="placeholder" v-show="!ganttTitleDate.length">暂无数据</div>
    <div class="top" v-show="ganttTitleDate.length">
      <div v-for="item in ganttTitleDate" :key="item.id">{{ item.name }}</div>
    </div>
    <div class="main" v-show="ganttTitleDate.length">
      <div class="row" v-for="(item, index) in calcList" :key="item.id">
        <div class="posx" :style="{ width: width + 'px' }">
          <!-- 蓝色 -->

          <div v-show="false">{{ `${item.planTimeScope}` }}</div>
          <div
            class="progress"
            slot="reference"
            :style="handlecalc(item, ['planStartTime', 'planEndTime'])"
            @mouseenter="handlerMouseenter($event, '1popover' + index)"
          ></div>

          <div v-show="false">
            <div class="vc-toolbox" v-if="item.status === '2'">
              <p style="text-align: center">{{ item.actTimeScope || "" }}</p>

              <div v-if="item.weekbackTime">
                <p style="text-align: center">最新周反馈:</p>
                <p>周时间段: {{ item.weekbackTime }}</p>
                <p>周反馈内容:</p>
                <p>{{ item.weekbackContent }}</p>
              </div>

              <p style="text-align: center" v-else>最新周反馈:无</p>
            </div>
            <div v-else>{{ `${item.actTimeScope}` }}</div>
          </div>

          <div
            class="progress"
            slot="reference"
            :class="getStyleclass(item.status, index, 'class')"
            :style="handlecalc(item, ['actStartTime', 'actEndTime'])"
            @mouseenter="handlerMouseenter($event, '2popover' + index)"
          ></div>
        </div>

        <!-- <div class='fixed1'>111</div>
        <div class='fixed2'>22</div>-->
        <div v-for="item in ganttTitleDate" :key="item.id" class="cell"></div>
      </div>
    </div>
    <div class="point_line" v-if="showpointline" :style="{ left: showpointlineleft + 'px' }"></div>
  </div>
</template>

<script>
import dayjs from "dayjs"; // 导入日期js

const uuidv4 = () =>
  "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
    const r = (Math.random() * 16) | 0;
    const v = c == "x" ? r : (r & 0x3) | 0x8;
    return v.toString(16);
  });
export default {
  props: {
    type: {
      default: "月",
      type: String,
    },
    isfullscreen: {
      default: false,
      type: Boolean,
    },
    listshow: {
      default: () => [],
      type: Array,
    },
  },
  data() {
    return {
      startTime: "2023-01-01",
      endTime: "2026-01-01",
      width: "",
      ganttTitleDate: [],
      list: [],
      showpointline: false,
      showpointlineleft: 0,
    };
  },
  computed: {
    //显示的数据 listshow
    calcList() {
      if (Array.isArray(this.listshow) && Array.isArray(this.list)) {
        return this.list.filter((item, index) => this.listshow.includes(index));
      }
      return [];
    },
    // 单元格宽度
    cellWidth() {
      if (this.width && this.ganttTitleDate.length) {
        return parseInt(this.width / this.ganttTitleDate.length);
      }
      return 0;
    },
  },
  directives: {
    tooltip(el, binding) {
      const config = binding.value || {};
      const classname = `${config.type || ""}_tootip`;
      // 鼠标移入时,将浮沉元素插入到body中
      el.onmouseenter = function (e) {
        // 创建浮层元素并设置样式
        const vcTooltipDom = document.createElement("div");
        vcTooltipDom.style.cssText = `
                  position:absolute;
                  font-size:14px;
                  z-index:1000
            `;
        // 设置id方便寻找
        vcTooltipDom.setAttribute("id", "vc-tooltip");
        vcTooltipDom.setAttribute("class", classname);
        // 将浮层插入到body中
        document.body.appendChild(vcTooltipDom);

        // 浮层中的文字 通过属性值传递动态的显示文案
        document.getElementById("vc-tooltip").innerHTML = config.text;
      };
      // 鼠标移动时,动态修改浮沉的位置属性
      el.onmousemove = function (e) {
        const vcTooltipDom = document.getElementById("vc-tooltip");
        vcTooltipDom.style.top = `${e.clientY + 15}px`;
        vcTooltipDom.style.left = `${e.clientX + 15}px`;
      };
      // 鼠标移出时将浮层元素销毁
      el.onmouseleave = function () {
        // 找到浮层元素并移出
        const vcTooltipDom = document.getElementById("vc-tooltip");
        vcTooltipDom && document.body.removeChild(vcTooltipDom);
      };
    },
  },
  watch: {
    type(val) {
      this.typechange();
    },
  },
  mounted() {
    this.init(0);
    // 监听 window.onresize
    // window.addEventListener("resize", this.resize, false);
  },
  beforeDestroy() {
    window.removeEventListener("resize", this.resize);
  },
  methods: {
    typechange() {
      this.ganttTitleDate = this.generateHeader();
      this.handlelist();
      this.resize();
      this.showpointline = false;
      document.getElementById("gantttable").scrollLeft = 0;
    },
    resize() {
      this.width = 0;
      this.$nextTick(() => {
        this.width = this.$refs.box.scrollWidth;
      });
    },
    ganttfix(e) {
      const index = e.number;
      const fixed = this.list[index].planStartTimepos;

      const width = fixed * this.cellWidth;
      this.$refs.box.scrollLeft = width;
    },
    getStyleclass(status, index, type) {
      let result = "info";
      switch (status) {
        case "0":
          result = "info";
          break;
        case "1":
          result = "primary";
          break;
        case "2":
          result = "danger";
          break;
        case "3":
          result = "info";
          break;
        case "4":
          result = "success";
          break;
        case "5":
          result = "warning";
          break;

        default:
          result = "emptyState";
          break;
      }
      if (index === 0 || index === this.list.length - 1) {
        return "primary";
      }
      if (type === "class") {
        return {
          [result]: true,
        };
      }
      return result;
    },
    handlerMouseenter(event, ref) {
      // 鼠标进入触发元素
      const popover = this.$refs[ref][0];

      const timer = setTimeout(() => {
        const { clientX } = event;
        const bodyWidth = document.body.clientWidth;
        // 找到气泡元素
        const { popperElm } = popover;
        /**
         * 鼠标位置+气泡弹框宽度是否小于body的宽度
         * 1. 是,设置鼠标位置为气泡弹框横向位移
         * 2. 否,设置body宽度-气泡弹框宽度为气泡弹框横向位移
         */
        const disX =
          clientX + popperElm.offsetWidth < bodyWidth
            ? clientX
            : bodyWidth - popperElm.offsetWidth;

        popover.popperElm.style.left = `${disX}px`;

        clearTimeout(timer);
      }, 5);
    },
    init(top) {
      this.width = 0;

      this.ganttTitleDate = this.generateHeader();
      this.$nextTick(() => {
        this.width = this.$refs.box.scrollWidth;
      });
      //处理列表 得到所有单元格列+百分比
      this.handlelist();
      // this.$nextTick(() => {
      //   this.$refs.box.scrollTop = top;
      //   this.$refs.box.scrollLeft = 0;
      // });
    },
    // 生成表头
    generateHeader() {
      if (this.startTime === "" && this.endTime === "") {
        return [];
      }
      // 分解开始和结束日期
      const start_date_spilt = dayjs(this.startTime)
        .format("YYYY-M-D")
        .split("-");
      const end_date_spilt = dayjs(this.endTime).format("YYYY-M-D").split("-");
      const start_year = Number(start_date_spilt[0]);
      const start_mouth = Number(start_date_spilt[1]);
      const end_year = Number(end_date_spilt[0]);
      const end_mouth = Number(end_date_spilt[1]);
      let database = [];
      if (this.type === "月") {
        database = this.yearAndMouthTitleDate(
          start_year,
          start_mouth,
          end_year,
          end_mouth
        );
        database = this.databasechange(database);
      } else if (this.type === "年") {
        database = this.yearTitleDate(start_year, end_year);
      } else if (this.type === "季度") {
        database = this.jdTitleDate(
          start_year,
          start_mouth,
          end_year,
          end_mouth
        );
      }

      console.log(database);
      this.$nextTick(() => {
        this.width = this.$refs.box.scrollWidth; //总宽度
      });
      return database;
    },
    //跳转到今天 等
    toTimePos(toTime) {
      //位置+ 容器一半宽度
      if (this.ganttTitleDate.length) {
        if (
          new Date(toTime).valueOf() < new Date(this.startTime).valueOf() ||
          new Date(toTime).valueOf() > new Date(this.endTime).valueOf()
        ) {
          return this.$message.warning("今天不在当前范围");
        }
        let left = document.getElementById("gantttable").offsetWidth / 2;
        const downindex = this.ganttTitleDate.findIndex((item) => {
          return (
            new Date(item.endTime).valueOf() >= new Date(toTime).valueOf() &&
            new Date(item.startTime).valueOf() <= new Date(toTime).valueOf()
          );
        });
        let all =
          new Date(this.ganttTitleDate[downindex].endTime).valueOf() -
          new Date(this.ganttTitleDate[downindex].startTime).valueOf();
        let thisitem =
          new Date(toTime).valueOf() -
          new Date(this.ganttTitleDate[downindex].startTime).valueOf();
        const downratio = (thisitem / all).toFixed(4);
        let width = this.cellWidth * (downindex + Number(downratio));
        this.showpointline = true;
        this.showpointlineleft = width;
        document.getElementById("gantttable").scrollLeft = width - left;
      }
    },
    // 处理list  获取区间width 和偏移量
    handlelist() {
      this.list = this.list.map((item) => this.handlelistitemcalc(item));
    },
    handlelistitemcalcnext(item = {}, fieldmap = ["", ""]) {
      let uppos = []; // 值1为 单元格索引   值2为在单元格的百分比
      let downpos = [];
      const startTime = item[fieldmap[0]];
      const endTime = item[fieldmap[1]];
      const celllenindex = this.ganttTitleDate.length - 1;

      // 开始时间不在区间
      if (new Date(startTime).valueOf() < new Date(this.startTime).valueOf()) {
        uppos = [0, 0];
      } else if (
        new Date(startTime).valueOf() > new Date(this.endTime).valueOf()
      ) {
        uppos = [celllenindex, 100];
      } else {
        if (this.type === "月") {
          const upindex = this.ganttTitleDate.findIndex(
            (item) => item.full_date === dayjs(startTime).format("YYYY-M")
          );
          const upgetMonthDay = this.getMonthDay(
            dayjs(startTime).format("YYYY-M")
          );
          const upday = dayjs(startTime).format("D");
          let upratio = "";
          if (upday == 1) {
            upratio = 0;
          } else if (upday == upgetMonthDay) {
            upratio = 1;
          } else {
            upratio = parseFloat(upday / upgetMonthDay).toFixed(3);
          }

          uppos = [upindex, upratio];
        } else if (this.type === "年") {
          const upindex = this.ganttTitleDate.findIndex(
            (item) => item.full_date === dayjs(startTime).format("YYYY")
          );
          let all =
            new Date(this.ganttTitleDate[upindex].endTime).valueOf() -
            new Date(this.ganttTitleDate[upindex].startTime).valueOf();
          let thisitem =
            new Date(startTime).valueOf() -
            new Date(this.ganttTitleDate[upindex].startTime).valueOf();
          const upratio = (thisitem / all).toFixed(5);
          uppos = [upindex, upratio];
        } else if (this.type === "季度") {
          const upindex = this.ganttTitleDate.findIndex((item) => {
            return (
              new Date(item.endTime).valueOf() >=
                new Date(startTime).valueOf() &&
              new Date(item.startTime).valueOf() <=
                new Date(startTime).valueOf()
            );
          });

          let all =
            new Date(this.ganttTitleDate[upindex].endTime).valueOf() -
            new Date(this.ganttTitleDate[upindex].startTime).valueOf();
          let thisitem =
            new Date(startTime).valueOf() -
            new Date(this.ganttTitleDate[upindex].startTime).valueOf();
          const upratio = (thisitem / all).toFixed(4);
          uppos = [upindex, upratio];
        }
      }
      // 结束时间不在区间
      if (new Date(endTime).valueOf() < new Date(this.startTime).valueOf()) {
        downpos = [0, 0];
      } else if (
        new Date(endTime).valueOf() > new Date(this.endTime).valueOf()
      ) {
        downpos = [celllenindex, 100];
      } else {
        if (this.type === "月") {
          const downindex = this.ganttTitleDate.findIndex(
            (item) => item.full_date === dayjs(endTime).format("YYYY-M")
          );
          const downgetMonthDay = this.getMonthDay(
            dayjs(endTime).format("YYYY-M")
          );
          const downday = dayjs(endTime).format("D");
          let downratio = "";
          if (downday == 1) {
            downratio = 0;
          } else if (downday == downgetMonthDay) {
            downratio = 1;
          } else {
            downratio = parseFloat(downday / downgetMonthDay).toFixed(3);
          }

          downpos = [downindex, downratio];
        } else if (this.type === "年") {
          const downindex = this.ganttTitleDate.findIndex(
            (item) => item.full_date === dayjs(endTime).format("YYYY")
          );
          let all =
            new Date(this.ganttTitleDate[downindex].endTime).valueOf() -
            new Date(this.ganttTitleDate[downindex].startTime).valueOf();
          let thisitem =
            new Date(endTime).valueOf() -
            new Date(this.ganttTitleDate[downindex].startTime).valueOf();
          const downratio = (thisitem / all).toFixed(5);

          downpos = [downindex, downratio];
        } else if (this.type === "季度") {
          const downindex = this.ganttTitleDate.findIndex((item) => {
            return (
              new Date(item.endTime).valueOf() >= new Date(endTime).valueOf() &&
              new Date(item.startTime).valueOf() <= new Date(endTime).valueOf()
            );
          });
          let all =
            new Date(this.ganttTitleDate[downindex].endTime).valueOf() -
            new Date(this.ganttTitleDate[downindex].startTime).valueOf();
          let thisitem =
            new Date(endTime).valueOf() -
            new Date(this.ganttTitleDate[downindex].startTime).valueOf();
          const downratio = (thisitem / all).toFixed(4);
          //--
          downpos = [downindex, downratio];
        }
      }

      return {
        [`${fieldmap[0]}pos`]: uppos[0] + Number(uppos[1]),
        [`${fieldmap[1]}pos`]: downpos[0] + Number(downpos[1]),
      };
    },
    handlelistitemcalc(item) {
      const { actStartTimepos, actEndTimepos } = this.handlelistitemcalcnext(
        item,
        ["actStartTime", "actEndTime"]
      );

      const { planStartTimepos, planEndTimepos } = this.handlelistitemcalcnext(
        item,
        ["planStartTime", "planEndTime"]
      );
      return {
        ...item,
        // 计划
        planStartTimepos,
        planEndTimepos,
        // 实际
        actStartTimepos,
        actEndTimepos,
      };
    },
    // 计算 开头 结尾所在的单元格 //计算 单元格内的具体百分比 //已经是否超出当前区间
    handlecalc(item, fieldmap = ["", ""]) {
      if (
        item[fieldmap[0]] &&
        item[fieldmap[1]] &&
        new Date(item[fieldmap[0]]).valueOf() <=
          new Date(item[fieldmap[1]]).valueOf()
      ) {
        const start = item[`${fieldmap[0]}pos`];
        const end = item[`${fieldmap[1]}pos`];
        const startpx = start * this.cellWidth;
        let progresswidth = end * this.cellWidth - startpx;

        if (
          new Date(item[fieldmap[0]]).valueOf() ===
            new Date(item[fieldmap[1]]).valueOf() &&
          new Date(item[fieldmap[0]]).valueOf() >=
            new Date(this.startTime).valueOf() &&
          new Date(item[fieldmap[0]]).valueOf() <=
            new Date(this.endTime).valueOf()
        ) {
          progresswidth = "3";
        }
        return {
          width: `${progresswidth}px`,
          transform: `translateX(${startpx}px)`,
        };
      }
      return {
        width: 0,
        display: "none",
      };
    },
    // 获取一个月有多少天
    getMonthDay(time) {
      const date = new Date(time);
      date.setMonth(date.getMonth() + 1); // 先设置为下个月
      date.setDate(0); // 再置0,变成当前月最后一天
      return date.getDate();
    },
    databasechange(data) {
      const array = [];
      if (Array.isArray(data) && data.length) {
        data.forEach((item) => {
          if (Array.isArray(item.children) && data.length) {
            item.children.forEach((item2) => {
              let monthdays = new Date(
                item2.full_date.split("-")[0],
                item2.full_date.split("-")[1],
                0
              ).getDate();
              array.push({
                ...item2,
                startTime: item2.full_date + "-1",
                endTime: item2.full_date + "-" + monthdays,
                name: item.name + item2.name,
              });
            });
          }
        });
      }
      return array;
    },
    /**
     * 是否闰年函数
     * year: Number 当前年份
     */
    isLeap(year) {
      return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0);
    },
    /**
     * 生成月份函数
     * year: Number 当前年份
     * start_num: Number 开始月分
     * end_num:Number 结束月份
     * isLeap: Boolean 是否闰年
     * insert_days: Boolean 是否需要插入 日
     * week: 是否以周的间隔
     */
    generationMonths(
      year,
      start_num = 1,
      end_num = 13,
      isLeap = false,
      insert_days = true,
      week = false
    ) {
      const months = [];
      if (insert_days) {
        // 无需 日 的模式
        for (let i = start_num; i < end_num; i++) {
          // 需要 日 的模式
          const days = this.generationDays(year, i, isLeap, week);
          months.push({
            name: `${i}月`,
            date: i,
            full_date: `${year}-${i}`,
            children: days,
            id: uuidv4(),
          });
        }
        return months;
      }
      for (let i = start_num; i < end_num; i++) {
        // 需要 日 的模式
        months.push({
          name: `${i}月`,
          date: i,
          full_date: `${year}-${i}`,
          id: uuidv4(),
        });
      }
      return months;
    },
    yearTitleDate(start_year, end_year) {
      if (end_year && start_year && end_year >= start_year) {
        let res = [];
        while (start_year <= end_year) {
          res.push({
            name: start_year + "年",
            startTime: start_year + "-1-1", //区间开始时间
            endTime: start_year + "-12-31", //结束开始时间
            full_date: `${start_year}`,
            id: uuidv4(),
          });
          start_year++;
        }

        return res;
      }
      return [];
    },
    jdTitleDate(start_year, start_mouth, end_year, end_mouth) {
      if (end_year && start_year && end_year >= start_year) {
        let timespace = [
          { name: "一", start: "01-01", end: "03-31" },
          { name: "二", start: "04-01", end: "06-30" },
          { name: "三", start: "07-01", end: "09-30" },
          { name: "四", start: "10-01", end: "12-31" },
        ];
        function getQuarterByMonth(month) {
          if (month >= 1 && month <= 3) {
            return 0;
          } else if (month >= 4 && month <= 6) {
            return 1;
          } else if (month >= 7 && month <= 9) {
            return 2;
          } else {
            return 3;
          }
        }
        let jd1 = getQuarterByMonth(start_mouth);
        let jd2 = getQuarterByMonth(end_mouth);
        console.log(jd1, jd2);
        let res = [];
        while (Number(start_year + "." + jd1) <= Number(end_year + "." + jd2)) {
          res.push({
            name: start_year + "年第" + timespace[jd1].name + "季度",
            startTime: start_year + "-" + timespace[jd1].start, //区间开始时间
            endTime: start_year + "-" + timespace[jd1].end, //结束开始时间
            id: uuidv4(),
          });
          jd1++;
          if (jd1 > 3) {
            jd1 = 0;
            start_year++;
          }
          console.log(111);
        }
        return res;
      } else {
        return [];
      }
    },
    yearAndMouthTitleDate(start_year, start_mouth, end_year, end_mouth) {
      // 日期数据盒子
      const dates = [
        {
          name: `${start_year}年`,
          date: start_year,
          id: uuidv4(),
          children: [],
        },
      ];
      // 处理年份
      const year_diff = end_year - start_year;
      // 年间隔小于一年
      if (year_diff === 0) {
        const isLeap = this.isLeap(start_year); // 是否闰年
        const mouths = this.generationMonths(
          start_year,
          start_mouth,
          end_mouth + 1,
          isLeap,
          false
        ); // 处理月份
        dates[0].children = mouths;
        return dates;
      }
      // 处理开始月份
      const startIsLeap = this.isLeap(start_year);
      const start_mouths = this.generationMonths(
        start_year,
        start_mouth,
        13,
        startIsLeap,
        false
      );
      // 处理结束月份
      const endIsLeap = this.isLeap(end_year);
      const end_mouths = this.generationMonths(
        end_year,
        1,
        end_mouth + 1,
        endIsLeap,
        false
      );
      // 年间隔等于一年
      if (year_diff === 1) {
        dates[0].children = start_mouths;
        dates.push({
          name: `${end_year}年`,
          date: end_year,
          children: end_mouths,
          id: uuidv4(),
        });
        return dates;
      }
      // 年间隔大于1年
      if (year_diff > 1) {
        dates[0].children = start_mouths;
        for (let i = 1; i < year_diff; i++) {
          const item_year = start_year + i;
          const isLeap = this.isLeap(item_year);
          const month_and_day = this.generationMonths(
            item_year,
            1,
            13,
            isLeap,
            false
          );
          dates.push({
            name: `${item_year}年`,
            date: item_year,
            id: uuidv4(),
            children: month_and_day,
          });
        }
        dates.push({
          name: `${end_year}年`,
          date: end_year,
          children: end_mouths,
          id: uuidv4(),
        });
        return dates;
      }
    },
  },
};
</script>

<style scoped lang="scss">
.box {
  width: max-content;
  position: relative;
  min-width: 100%;
}
.placeholder {
  display: flex;
  height: 100%;
  width: 100%;
  align-items: center;
  justify-content: center;
  border-right: 1px solid #ccc;
}
.top {
  display: flex;
  position: sticky;
  top: 0;
  z-index: 99;

  > div + div {
    border-left-width: 0;
  }
}
.top > div {
  min-width: 150px;
  border: 1px solid #ccc;
  border-top-width: 0;

  flex: 1;
  text-align: center;
  font-size: 14px;
  font-weight: 700;
  // padding: 10px 0;
  background: #fff;
  height: 50px;
  display: flex;
  align-items: center;
  justify-content: center;
}
.row {
  display: flex;
  position: relative;

  > div.cell + div.cell {
    border-left-width: 0;
  }
}
.row > div.cell {
  min-width: 150px;
  border: 1px solid #ccc;
  flex: 1;
  height: 60px;
  border-top-width: 0;
}
.posx {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 10;
  height: 100%;
  width: 100%;
  // overflow: hidden;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.progress {
  height: 16px;
  background: #409eff;
}
.progress.success {
  background: #cff1c0;
  margin-top: 5px;
}
.progress.danger {
  background: #fcd9d9;
  margin-top: 5px;
}
.progress.warning {
  background: #f9ecd9;
  margin-top: 5px;
}
.progress.primary {
  background: #c1e0ff;
  margin-top: 5px;
}
</style>
<style lang="scss">
.vc-toolbox {
  max-width: 600px;
  p {
    word-break: break-all;
  }
}
.el-popover.el-popper.success {
  color: #67c23a;
  background: #f0f9eb;
  border-color: #c2e7b0;
}
.el-popover.el-popper.primary {
  color: #409eff;
  background: #ecf5ff;
  border-color: #b3d8ff;
}
.el-popover.el-popper.danger {
  color: #f56c6c;
  background: #fef0f0;
  border-color: #fbc4c4;
}
.el-popover.el-popper.emptyState {
  color: #fff;
  background: #7d7d7d;
  border-color: #7d7d7d;
}
.point_line {
  background: #4881fd;
  width: 2px;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0px;
  z-index: 20;
}
.fixed1 {
  width: 50px;
  height: 20px;
  background: #409eff;
  position: absolute;
  left: 10px;
}
.fixed2 {
  width: 50px;
  height: 20px;
  background: #409eff;
  position: absolute;
  left: 10px;
  top: 20px;
}
</style>

标签:end,item,gantt,new,start,year,const
From: https://www.cnblogs.com/7c89/p/17659614.html

相关文章

  • 产品介绍:普加甘特图PlusGantt
    PlusGantt是一款先进的项目管理中间件,旨在帮助项目团队高效地规划、执行和监控项目。它集成了强大的功能和直观的用户界面,为项目管理人员和团队成员提供了全面的工具来实现项目的成功交付。主要特点:智能Gantt图:PlusGantt提供了智能的Gantt图功能,以直观的方式展示项目的时间计划和进......
  • 普加资源甘特图Rgantt简介
    资源甘特图(RGantt)是Web环境功能最强大的生产排程甘特图控件,具有强大的定制能力,适用于生产制造类系统开发。支持.Net、Java等开发平台。它可以显示一个生产线或设备的多道工序排布情况,并且可以进行干涉调整,达到最佳生产效益。无需安装插件,兼容所有主流浏览器,支持JAVA、.Net等任意开......
  • vue2项目中使用dhtmlx gantt甘特图插件
    官网示例地址:https://docs.dhtmlx.com/gantt/samples/可以在这里查看绑定数据的格式安装依赖npminstalldhtmlx-gantt--save创建一个甘特图组件<template><el-scrollbarref="gantt_scrollbar"class="gantt-box"><divref="gantt"class=&qu......
  • 普加甘特图 vs. jQuery Gantt:两种甘特图工具的对比
    简介:甘特图是项目管理中常用的工具之一,它可帮助团队可视化项目进度和任务安排。在众多的甘特图工具中,普加甘特图和jQueryGantt都是备受关注的选项。本文将对这两种甘特图工具进行比较,以帮助你选择适合自己项目管理需求的工具。功能和特点:普加甘特图:普加甘特图提供直观的界面和丰......
  • #甘特图# DHTMLXGantt 组件笔记
    配置配置缩放单位gantt.config.scale,示例gantt.config.scale=[{unit:"day",step:1,format:"%d%M"}]需要注意的是,当显示比较小的刻度如天、小时甚至时分钟......
  • Excel Gantt chart 甘特图
    Excel条件格式设置甘特图,公式终于弄明白了,记录下;◆甘特图中的菱形块:代表不需要时间的活动类似于"获得批准";▼▼横道图两端的倒实心三角:代表自己是上级任务,......
  • dhtmlxgantt甘特图示例
    官方文档:https://docs.dhtmlx.com/gantt/api__refs__gantt.html示例图片:代码:<!DOCTYPEhtml><head><metahttp-equiv="Content-type"content="text/html......