首页 前端知识 使用 Vue 3 TypeScript 集成 DHTMLX 甘特图

使用 Vue 3 TypeScript 集成 DHTMLX 甘特图

2025-02-25 13:02:57 前端知识 前端哥 209 995 我要收藏

使用 Vue 3 + TypeScript 集成 DHTMLX 甘特图


前言

在现代前端开发中,Vue 3 结合 TypeScript 是一个强大的组合,能够帮助开发者更好地构建类型安全、可扩展的应用。而 DHTMLX Gantt 作为一款高效的项目管理组件,可以轻松实现任务调度、时间跟踪等功能。本文将详细介绍如何在 Vue 3 项目中结合 TypeScript 和 DHTMLX 甘特图,实现一个项目管理系统的关键功能。
官网:https://docs.dhtmlx.com/gantt/


一、使用步骤

1.1 安装dhtmlx-gantt依赖

代码如下(示例):

npm install dhtmlx-gantt -save 

1.2 在页面中引入插件

import {gantt} from 'dhtmlx-gantt'
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
import demoData from './ganttData.json' //引入数据

二.页面代码

2.1 index.vue

代码如下(示例):

<template>
  <div style="height:100%;background-color: white">
    <section class="my-gantt">
      <div class="time-box" >
        <el-form :model="search" inline style="margin-left: 15px">
          <el-form-item style="margin-left: 0px">
            <el-radio-group v-model="fullData.timeState" @change="changeTime">
              <el-radio-button
                v-for="(time, t_index) in fullData.timeList"
                :key="t_index"
                :label="time.code"
                size="small"
                border
              >{{ time.name }}
              </el-radio-button
              >
            </el-radio-group>
          </el-form-item>
          <el-form-item>
            <div style="height: 40px;position: relative;float: left;">
              <el-tooltip content="已完成" placement="top">
                <div
                  style="background-color:#BEE4BE;width: 14px;height: 14px;position: relative;float: left;top: 13px;border: 1px solid;">
                </div>
              </el-tooltip>
              <el-tooltip content="进行中" placement="top">
                <div
                  style="background-color:#ffd28f;width: 14px;height: 14px;position: relative;float: left;left: 15%;top: 13px;border: 1px solid;">
                </div>
              </el-tooltip>
              <el-tooltip content="未开始" placement="top">
                <div
                  style="background-color: #d4d4d4;width: 14px;height: 14px;position: relative;float: left;left: 30%;top: 13px;border: 1px solid;">
                </div>
              </el-tooltip>
            </div>
          </el-form-item>
        </el-form>
      </div>
      <div id="gantt_here" class="gantt-container"></div>
    </section>
  </div>
</template>

2.2 typeScript

<script setup lang="ts">
  import {ref, reactive, toRefs, onBeforeMount, onMounted, watchEffect, defineExpose,nextTick} from 'vue'
  import { gantt } from 'dhtmlx-gantt'
  import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
  import {Search, Plus,Edit,Delete,ArrowDownBold } from "@element-plus/icons-vue"
  import demoData from "../progressManage/demoData.json"

  //此方法是通过父子节点的关联性设置并且与时间相关联,父节点 设置 render: 'split'拆分子任务过程,子任务过程关联上父节点id(用parent)。可以通过设置子进程的开始结束时间或持续时间(duration)来设置子进程的长度。
  // 注意id 的唯一性
  const startWidth = ref(0);
  const fullData = reactive({
    timeList: [
      {
        name: '日',
        code: 'day'
      },
      {
        name: '周',
        code: 'week'
      },
      {
        name: '月',
        code: 'month'
      }
    ],
    timeState: 'month',
    demoData:{data:[]}

  })
  const zoomConfig = {
    levels: [
      {
        name: 'day',
        scale_height: 60,
        min_column_width: 18,
        scales: [
          { unit: 'month', format: '%Y-%m' },
          {
            unit: 'day',
            step: 1,
            format: '%d',
            css: function (date) {
              if (date.getDay() == 0 || date.getDay() == 6) {
                return 'day-item weekend weekend-border-bottom'
              } else {
                return 'day-item'
              }
            }
          }
        ]
      },
      {
        name: 'week',
        height: 60,
        min_column_width: 110,
        scales: [
          {
            unit: 'quarter',
            step: 1,
            format: function (date) {
              let yearStr = new Date(date).getFullYear() + '年'
              let dateToStr = gantt.date.date_to_str('%M')
              let endDate = gantt.date.add(gantt.date.add(date, 3, 'month'), -1, 'day')
              return yearStr + dateToStr(date) + ' - ' + dateToStr(endDate)
            }
          },
          {
            unit: 'week',
            step: 1,
            format: function (date) {
              let dateToStr = gantt.date.date_to_str('%m-%d')
              let endDate = gantt.date.add(date, 6, 'day')
              let weekNum = gantt.date.date_to_str('%W')(date)
              return dateToStr(date) + ' 至 ' + dateToStr(endDate)
            }
          }
        ]
      },
      {
        name: 'month',
        scale_height: 50,
        min_column_width: 150,
        scales: [
          { unit: 'year', step: 1, format: '%Y年' },
          { unit: 'month', format: '%Y-%m' }
        ]
      }
    ]
  }

  //初始化甘特图
  const initGantt = () => {
    let dateToStr = gantt.date.date_to_str('%Y.%m.%d')
    gantt.config.grid_width = 350
    gantt.config.add_column = false //添加符号

    //时间轴图表中,如果不设置,只有行边框,区分上下的任务,设置之后带有列的边框,整个时间轴变成格子状。
    // gantt.config.autofit = true
    // gantt.config.grid_width = 300
    gantt.config.row_height = 34 // 行高
    gantt.config.bar_height = 28
    // gantt.config.fit_tasks = true //自动延长时间刻度,以适应所有显示的任务
    gantt.config.auto_types = true //将包含子任务的任务转换为项目,将没有子任务的项目转换回任务
    gantt.config.xml_date = '%Y.%m.%d' //甘特图时间数据格式
    gantt.config.readonly = true //是否只读
    gantt.config.show_errors = false //不显示错误信息
    gantt.config.show_empty_state = true //显示空状态
    //激活列表展开、折叠功能
    gantt.config.open_split_tasks = true

    //设置时间截止线
    gantt.locale.labels.deadline_enable_button = 'Set';
    gantt.locale.labels.deadline_disable_button = 'Remove';

    gantt.attachEvent("onTaskLoading", function (task) {
      if (task.end_date)
        task.end_date = gantt.date.parseDate(task.end_date, "xml_date");
      return true;
    });
    gantt.ext.draw = true
    //进度条
    gantt.templates.task_text = function (start, end, task) {
      let progressState = ''
      let progressPerson = ''
      if (task.state == '1') { //未开始
        progressState = 'lag'
      }else if(task.state == '3'){  //进行中
        progressState = 'ing'
      }else if(task.state == '2'){
        progressState = 'wanch'   //已完成
      }else{
        progressState = 'normal'
      }
      return ` <div class="project-progress-${progressState}">${task.text}
</div>`

    }
    //表格配置
    gantt.config.columns = [
      {
        name: "end_date", label: "", width: 30, template: function (obj) {
          if(obj.skip==0){
            if(obj.Long||obj.gap){
              return `<el-tooltip content="${obj.text}"placement="top" effect="dark"><div class="overdue-indicator" ></div></el-tooltip>`;
            }
          }else if(obj.skip==1){
            return `<el-tooltip content="跳过该节点"placement="top" effect="dark"><div class="overdue-indicator1" ></div></el-tooltip>`;
          }
        }
      },
      {
        name: 'text',
        label: '项目名称',
        resize:true,
        tree: true,
        align:'left',
        width: 250,
        template: function (obj) {
          return ` <el-tooltip content="${obj.text}"placement="bottom"> <span class="box-item">${obj.text}</span></el-tooltip>`
        }
      },
      {
        name: 'apply_unit',
        label: '项目地',
        resize:true,
        align:'left',
        width: 150,
        template: function (obj) {
          return `<el-tooltip content="${obj.apply_unit}"placement="bottom"> <span class="box-item">${obj.apply_unit}</span></el-tooltip>`
        }
      }
    ]
    //更改树状的图标
    // gantt.templates.grid_open = (item) => {
    //   // console.log('item', item)
    //   const res = "<div  class='gantt_" +
    //     (item.$open ? "close" : "open") + "'></div>"
    //   return res
    // }
    //更改父项图标
    gantt.templates.grid_folder = (item) => {
      return ""
    }
    //更改子项图标
    gantt.templates.grid_file = (item) => {
      return ""
    }
    gantt.i18n.setLocale('ZN') //设置语言
    gantt.ext.zoom.init(zoomConfig) //配置初始化扩展
    gantt.ext.zoom.setLevel('month') //切换到指定的缩放级别x
    // 开启marker插件
    gantt.plugins({ marker: true, tooltip: true });
    // const today = new Date(dayjs(new Date()).format("YYYY-MM-DD"));
    // const dateToStr2 = gantt.date.date_to_str(gantt.config.task_date);
    gantt.templates.tooltip_text=function (start,end,task) {
      var tooltip = "<span>项目名称:</span>"+ task.text+"<br><span>开始时间:</span>"+dateToStr(task.start_date)+
        "<br><span>结束时间:</span>"+dateToStr(task.end_date)
      if(task.Long){
        tooltip+=`<br><font color="red">预警信息:${task.Long}</font>`
      }
      if(task.gap){
        tooltip+=`<br><font color="red">预警信息:${task.gap}</font>`
      }
      if(task.skip){
        tooltip+=`<br><font color="blue">预警信息:跳过该节点</font>`
      }
      return tooltip
    }
    const todayMarker = gantt.addMarker({
      start_date: new Date(),
      css: "today", // 今日线注意定义在初始化和填充数据前
      text: "今日",
      title: new Date()
    });
    gantt.config.autofit = true
    gantt.init('gantt_here') //初始化
    gantt.parse(demoData) //填充数据
    scrollInit()
    gantt.showDate(new Date())


    scrollInit()
    gantt.showDate(new Date())
    gantt.attachEvent("onGridHeaderClick",function (name,e) {
      console.log("name",e,name)
    })
  }
  //拖拽滚动
  const scrollInit = () => {
    const nav = document.querySelectorAll('.gantt_task')[0]
    const parNav = document.querySelectorAll('.gantt_hor_scroll')[0]
    parNav.scrollLeft = 0
    let flag
    let downX
    let scrollLeft
    nav.addEventListener('mousedown', function (event) {
      flag = true
      downX = event.clientX // 获取到点击的x下标
      scrollLeft = this.scrollLeft // 获取当前元素滚动条的偏移量
    })
    nav.addEventListener('mousemove', function (event) {
      if (flag) {
        let moveX = event.clientX
        let scrollX = moveX - downX
        parNav.scrollLeft = scrollLeft - scrollX
      }
    })
    // 鼠标抬起停止拖动
    nav.addEventListener('mouseup', function () {
      flag = false
    })
    // 鼠标离开元素停止拖动
    nav.addEventListener('mouseleave', function (event) {
      flag = false
    })
  }

  const changeTime = () => {
    gantt.ext.zoom.setLevel(fullData.timeState)
    gantt.showDate(new Date())
  }
  //点击事件
  gantt.attachEvent('onTaskClick',function (id, e,parent) {
    // addResizeHandlers()
    // console.log("e",e.target.parentElement.dataset)
    // if(e.target.className === "gantt_task_content") { // 点击进度条
    if(e.target.className=='project-progress-wanch'||e.target.className=='project-progress-ing'||e.target.className=='project-progress-lag'){
      console.log("Task clicked:", id,e,parent)
      queryProgress.findNode({id:id}).then(res=>{
        nodeInfo.value = res[0]
        nodeTitle.value = "节点信息【"+nodeInfo.value.node_name +"】"
        reserveProject.getFileByID({id:id}).then(res=>{
          myfileList.value =res
        })
        console.log("myfileList.value",myfileList.value)
        nodeDetail.value = true
      })
    }else if(e.target.className=='project-progress-normal'){
      console.log("aaa",id,e)
      projectDetail.value = true
      queryProgress.findProject({id:id}).then(res=>{
        clickInfo.value = res[0]
        projectTitle.value = "项目信息【"+clickInfo.value.project_name +"】"
      })
    }
    else if(e.target.className=='gantt_open'||e.target.className=='gantt_close'){
      console.log('e', e.target.className)
      // 关闭所有任务
      gantt.eachTask(function(task){
        gantt.close(task.id);
      });
      //点击展开/收起按钮
      switch (e.target.className) {
        case 'gantt_open':
          gantt.open(id)
          break;
        case 'gantt_close':
          gantt.close(id)
          break;
        default:
          break;
      }
    }
    else{  //点击左侧表格定位到右边进度条
      var task = gantt.getTask(id)
      gantt.showTask(task.id)
      return true
    }
  })
  //点击网状表格
  gantt.attachEvent('onTaskRowClick',function (id,row) {
    console.log("点击",id,row)
  })
  //查看
  const toPdf =(id) => {
    loading1.value = true
    previewMmgzWj({id:id}).then(res=>{
      console.log("res",res)
      dialogtitle.value = res[0].file_name
      pdfUrl.value = res[0].url_yl
      downloadUrl.value = res[0].url
      pdfShow.value = true;
      loading1.value = false
    })
    pdfHeight.value = window.innerHeight - 200 + 'px'
  };
  function getColumn() {
    var col = 3
    return col
  }
  function save() {
    window.open(downloadUrl.value)
  }

  function print() {
    window.open(pdfUrl.value)
  }
  onMounted(() => {
    initGantt()
  })

  watchEffect(() => {})
  defineExpose({
    ...toRefs(fullData)
  })
</script>

2.3 css样式

代码如下(示例):

<style lang="scss" scoped>
  .my-gantt {
    height: 800px;
    width: 100vw;
    ::v-deep .gantt-container {
      border-radius: 8px 0px 0px 8px;
      overflow: hidden;
      height: 100%;

    }
  }
  .el-form--inline .el-form-item {
    margin-right: 10px !important;
    margin-top: 10px !important;
  }
  .el-form-item__label {
    font-weight: 500 !important;
  }
  .today1{
    background: red;
    color: red
  }
  //树状图标打开
  ::v-deep(.gantt_open) {
    width: 12px !important;
    height: 100%;
    background-image: url('../../assets/left.png') !important;
    background-repeat: no-repeat;
    background-position: center center;
    background-size: 100% auto;
    margin-right: 5px;
    transform: rotate(-90deg);//旋转90度
  }
  //树状图标关闭
  ::v-deep(.gantt_close) {
    width: 12px !important;
    height: 100%;
    background-image: url('../../assets/left.png') !important ;
    background-repeat: no-repeat;
    background-position: center center;
    background-size: 100% auto;
    margin-right: 10px;
  }

  /*表头隐藏*/
  ::v-deep(.box-item ){
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 100%;
    display: inline-block;
  }

  //任务条自定义内容
  ::v-deep(.project-progress-ing) {
    background: #ffd28f !important;
    color: black;
    /*border: 2px solid #ffce2a !important;*/
  }
  ::v-deep(.project-progress-warn) {
    background: #ff8f8f !important;
  }
  ::v-deep(.project-progress-wanch ){
    background: #BEE4BE !important;
    /*font-weight: bolder;*/
    /*border: 2px solid #8fe48c !important;*/
    color: black;
    /*font-size: 16px;*/
  }
  ::v-deep(.project-progress-lag){
    background: #d4d4d4 !important;
    color: black;
  }
  ::v-deep(.project-progress-normal){
    background: #7ea7f5 !important;
    color: black;
  }


  .deadline {
    position: absolute;
    border-radius: 12px;
    border: 2px solid #585858;
    -moz-box-sizing: border-box;
    box-sizing: border-box;
    width: 22px;
    height: 22px;
    margin-left: -11px;
    margin-top: 6px;
    z-index: 1;
    background: url("../../assets/warning.png") center no-repeat;
  }
  ::v-deep(.overdue-indicator) {
    width: 20px !important;
    height: 100%;
    background: url("../../assets/warning.png");
    background-repeat: no-repeat;
    background-position: center center;
    background-size: 100% auto;
  }
  ::v-deep(.overdue-indicator1) {
    width: 20px !important;
    height: 100%;
    background: url("../../assets/skip1.png");
    background-repeat: no-repeat;
    background-position: center center;
    background-size: 100% auto;
  }
  /*::v-deep(.tooltip-line){*/
  /*color:red !important;*/
  /*background: red !important;*/
  /*}*/

  ::v-deep(.gantt_task_line) {
    background-color: rgba(0,0,0,0) !important;
    border: 1px solid rgba(0,0,0,0) !important;
  }
  ::v-deep(.gantt_task_line.gantt_project) {
    background-color: #fff !important;
    border: 1px solid #fff !important;
  }
  //设置任务条高度,以及圆角
  ::v-deep(.gantt_task_content) {
    color: #fff;
    top: 0px;
    height: 16px;
    border-radius: 50px;
  }

  //调整字体高度,任务条距上边框的距离
  ::v-deep( .gantt_data_area div, .gantt_grid div) {
    -ms-touch-action: none;
    -webkit-tap-highlight-color: rgba(0,0,0,0);
    line-height: 18px;
    top: 5px;
  }
</style>

2.4 demoData.json数据

代码如下(示例):

{
  "data": [
    {
      "apply_unit": "成都",
      "end_date": "2024-12-31",
      "parent": "0",
      "id": "001",
      "text": "项目1",
      "type": "project",
      "render": "split",
      "open": false,
      "start_date": "2024-01-01"
    },
    {
      "apply_unit": "深圳",
      "end_date": "2024-12-31",
      "parent": "0",
      "id": "002",
      "text": "项目1",
      "type": "project",
      "render": "split",
      "open": false,
      "start_date": "2024-01-01"
    },
    {
      "apply_unit": "广东",
      "end_date": "2025-10-01",
      "parent": "0",
      "id": "003",
      "text": "项目3",
      "type": "project",
      "render": "split",
      "open": false,
      "start_date": "2024-01-01"
    },
    {
      "apply_unit": "北京",
      "end_date": "2024-12-30",
      "parent": "0",
      "id": "004",
      "text": "项目4",
      "type": "project",
      "render": "split",
      "open": false,
      "start_date": "2024-01-01"
    },
    {
      "apply_unit": "天津",
      "end_date": "2024-06-30",
      "parent": "0",
      "id": "005",
      "text": "项目5",
      "type": "project",
      "render": "split",
      "open": false,
      "start_date": "2024-01-01"
    },
    {
      "apply_unit": "山东",
      "end_date": "2024-06-30",
      "parent": "0",
      "id": "006",
      "text": "项目6",
      "type": "project",
      "render": "split",
      "open": false,
      "start_date": "2024-01-01"
    },
    {
      "apply_unit": "广西",
      "end_date": "2024-06-30",
      "parent": "0",
      "id": "007",
      "text": "项目7",
      "type": "project",
      "render": "split",
      "open": false,
      "start_date": "2024-01-01"
    },
    {
      "id": "0011",
      "text": "项目1节点1",
      "parent": "001",
      "start_date": "2024-01-01",
      "end_date": "2024-02-21",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 2,
      "skip": 0,
      "apply_unit": "成都",
      "open": false
    },
    {
      "id": "0012",
      "text": "项目1节点2",
      "parent": "001",
      "start_date": "2024-02-23",
      "end_date": "2024-03-31",
      "plan_start_date": "20240331",
      "plan_end_date": "20240531",
      "state": 2,
      "skip": 0,
      "apply_unit": "成都",
      "open": false,
      "gap": "该节点与前一节点时间间距过长"
    },
    {
      "id": "0013",
      "text": "项目1节点3",
      "parent": "001",
      "start_date": "2024-04-01",
      "end_date": "2024-06-30",
      "plan_start_date": "20240314",
      "plan_end_date": "20241231",
      "state": 2,
      "skip": 0,
      "apply_unit": "成都",
      "open": false,
      "Long": "该节点耗时过长",
      "gap": "该节点与前一节点时间间距过长"
    },
    {
      "id": "0014",
      "text": "项目1节点4",
      "parent": "001",
      "start_date": "2024-07-01",
      "end_date": "2024-10-24",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 3,
      "apply_unit": "成都",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0015",
      "text": "项目1节点5",
      "parent": "001",
      "start_date": "2024-10-01",
      "end_date": "2024-11-21",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "apply_unit": "成都",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0016",
      "text": "项目1节点6",
      "parent": "001",
      "start_date": "2024-11-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "apply_unit": "成都",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0021",
      "text": "项目2节点1",
      "parent": "002",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "apply_unit": "广东",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0022",
      "text": "项目2节点2",
      "parent": "002",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "apply_unit": "广东",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0023",
      "text": "项目2节点3",
      "parent": "002",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "apply_unit": "广东",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0024",
      "text": "项目2节点4",
      "parent": "002",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "skip": 0,
      "apply_unit": "广东",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0025",
      "text": "项目2节点5",
      "parent": "002",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "skip": 0,
      "apply_unit": "广东",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0026",
      "text": "项目2节点6",
      "parent": "002",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "skip": 0,
      "apply_unit": "广东",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0031",
      "text": "项目3节点1",
      "parent": "003",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "apply_unit": "深圳",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0032",
      "text": "项目3节点2",
      "parent": "003",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "apply_unit": "深圳",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0033",
      "text": "项目3节点3",
      "parent": "002",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "apply_unit": "深圳",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0034",
      "text": "项目3节点4",
      "parent": "003",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "skip": 0,
      "apply_unit": "深圳",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0035",
      "text": "项目3节点5",
      "parent": "003",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "skip": 0,
      "apply_unit": "深圳",
      "open": false,
      "Long": "该节点耗时过长"
    },
    {
      "id": "0036",
      "text": "项目3节点6",
      "parent": "003",
      "start_date": "2024-01-01",
      "end_date": "2024-12-31",
      "plan_start_date": "20240101",
      "plan_end_date": "20241231",
      "state": 1,
      "skip": 0,
      "apply_unit": "深圳",
      "open": false,
      "Long": "该节点耗时过长"
    }
  ]
}


三、效果图

在这里插入图片描述

转载请注明出处或者链接地址:https://www.qianduange.cn//article/21424.html
标签
甘特图
评论
发布的文章

python调用ollama库详解

2025-02-25 13:02:30

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!