甘特图开发代码(测试版)

场景:要实现的功能就是单行数据能左右拖动。

流程五个:ABCDE。(对应:Charter开发、概念和计划、初样开发、正样开发、验证)

1、A有开始时间,结束时间。B的开始时间必须是A的结束时间(相等或者大于A的结束时间)。CDE以此类推。

2、一条数据有父子级的话,父级数据的进度条取A的开始时间,E的结束时间,如果当前流程没到E,就是取现有的流程的结束时间。流程阶段影响父级

3、单个流程进度条移动会影响前后进度条,如果没有前后节点就不影响

一、代码实现

https://juejin.cn/post/7451884906761486372

https://juejin.cn/post/7273096710826967092

问题版:

复制代码
const tooltipConfig = {
    grid: true,
    bars: true,
    cells: false,
    scales: false,
    links: false,
}

// window.addEventListener('click', function (e) {
//     for (key in tooltipConfig) {
//         if (e.target.closest("." + key)) {
//             tooltipConfig[key] = !tooltipConfig[key];
//             gantt.message(`Tooltip config changed. ${key}: ${tooltipConfig[key]}`)
//         }
//     }
// });

const zoomLevels = [
  // {
  //   name: 'hour',
  //   label: '小时',
  //   scale_height: 50,
  //   min_column_width: 30,
  //   scales: [
  //     { unit: 'day', format: '%Y-%m-%d' },
  //     { unit: 'hour', format: '%H' },
  //   ],
  // },
  // {
  //   name: 'day',
  //   label: '日',
  //   scale_height: 70,
  //   min_column_width: 30,
  //   scales: [
  //     { unit: 'month', format: '%Y年 %F' },
  //     // { unit: "day", step: 1, format: "%j %D" },
  //     {
  //       unit: 'day',
  //       step: 1,
  //       format: date => {
  //         const weekDays = ['日', '一', '二', '三', '四', '五', '六'];
  //         const day = new Date(date).getDate();
  //         const weekDay = new Date(date).getDay();
  //         return `<div class='scale-formate-date'>
  //         <span class='formate-date'>${day}</span>
  //         <span class='formate-weekDay'>${weekDays[weekDay]}</span>
  //         </div>`;
  //         // return "<strong>Day " + dayNumber(date) + "</strong><br/>" + dateFormat(date);
  //       },
  //     },
  //   ],
  // },
  // {
  //   name: 'week',
  //   label: '周',
  //   scale_height: 50,
  //   min_column_width: 50,
  //   scales: [
  //     { unit: 'month', format: '%Y年 %F' },
  //     { unit: 'week', step: 1, date: '%W周' },
  //   ],
  // },
  {
    name: 'month',
    label: '月',
    scale_height: 50,
    min_column_width: 50,
    scales: [
      // { unit: "year", step: 1, format: "%Y年" },
      {
        unit: 'quarter',
        step: 1,
        format: date => {
          const year = new Date(date).getFullYear();
          const month = new Date(date).getMonth();
          const quarter = Math.floor(month / 3 + 1);
          return `${year}年-Q${quarter}`;
          // return `Q${quarter}`;
        },
      },
      { unit: 'month', step: 1, format: '%F' },
    ],
  },
  {
    name: 'quarter',
    label: '季',
    scale_height: 50,
    min_column_width: 50,
    scales: [
      { unit: 'year', step: 1, format: '%Y年' },
      {
        unit: 'quarter',
        step: 1,
        format: date => {
          // const year = new Date(date).getFullYear();
          const month = new Date(date).getMonth();
          const quarter = Math.floor(month / 3 + 1);
          // return `${year}年-Q${quarter}`;
          return `Q${quarter}`;
        },
      },
    ],
  },
  {
    name: 'year',
    label: '年',
    scale_height: 50,
    min_column_width: 50,
    scales: [{ unit: 'year', step: 1, format: '%Y年' }],
  },
];



/**
 * 禁用系列
 */
// 禁止双击创建任务
gantt.config.dblclick_create = false;
// 禁止创建任务时弹出编辑弹窗
gantt.config.edit_on_create = false;
// 禁止双击任务时弹出详情弹窗
gantt.config.details_on_dblclick = false;
// 禁止双击创建链接
gantt.config.dblclick_create_link = false;
// 禁止创建链接时弹出编辑弹窗
gantt.config.edit_on_create_link = false;
// 禁止任务进度调整
gantt.config.drag_progress = false;
// 禁止拖拽连线
gantt.config.drag_link = false;
// 关闭报错弹窗
gantt.config.show_errors = false;
/**
 * end
 */


// 语言设置-中文
gantt.i18n.setLocale('cn');
// 行高
gantt.config.row_height = 48;
// 日期格式
gantt.config.date_format = '%Y-%m-%d %H:%i';
// 初始化缩放插件
gantt.ext.zoom.init({
  levels: zoomLevels, // 刻度级别配置
});

// 设置默认刻度级别为月视图
gantt.ext.zoom.setLevel('month');

/** 定义:第二网格列的配置 */
const secondGridColumns = {
    columns: [
    {
        name: 'actions',
        label: '操作',
        width: 120,
        align: 'center',
        template: task => {
        // 自定义列内容的模板函数,接收任务对象作为参数
        return `
            <div class="gantt-actions">
                <!-- 编辑按钮,绑定任务的 id -->
                <div class="edit-btn" data-task-id="${task.id}">编辑</div>
                <!-- 详情按钮,绑定任务的 id -->
                <div class="detail-btn" data-task-id="${task.id}">详情</div>
            </div>
            `;
        },
    },
    ],
};

/**
 * 设置列(第一列)
 * name - 数据字段;label-表头显示的名称;width-列宽;align-对齐方式;templage-自定义列内容的模板函数;
 */
gantt.config.columns = [
    {
        name: 'selected', // 勾选框列
        label: '', // 表头为空
        width: 50,
        align: 'center',
        template: task => {
        return `
            <div class="gantt-checkbox">
            <input
                type="checkbox"
                data-task-id="${task.id}"
                ${task.selected ? 'checked' : ''}
            />
            </div>
        `;
        },
    },
    {
        name: 'name',
        label: '姓名',
        width: 150,
        align: 'left',
        tree: true,
    },
    { name: 'start_date', label: '开始日期', width: 130, align: 'center' },
    { name: 'end_date', label: '持续时间', width: 80, align: 'center' },
];

// 布局
gantt.config.layout = {
    css: 'gantt_container', // 容器的 CSS 类名
    rows: [
        {
        cols: [
            // 左侧网格(任务列表)
            { view: 'grid', width: 320, scrollY: 'scrollVer' }, //  // 网格视图,宽度为 320,垂直滚动条绑定到 'scrollVer'
            { resizer: true, width: 1 }, // 可调整宽度的分隔条,宽度为
            // 时间线视图
            { view: 'timeline', scrollX: 'scrollHor', scrollY: 'scrollVer' }, // 时间线视图,水平和垂直滚动条分别绑定到 'scrollHor' 和 'scrollVer'
            { resizer: true, width: 1 },
            // 右侧网格(自定义操作列)
            {
            view: 'grid', // 网格视图
            width: 120,
            bind: 'task', // 绑定到任务数据
            scrollY: 'scrollVer', // 垂直滚动条绑定到 'scrollVer'
            config: secondGridColumns, // 使用上面定义的 secondGridColumns 配置
            },
            // 垂直滚动条
            { view: 'scrollbar', id: 'scrollVer' }, // 垂直滚动条视图,id 为 'scrollVer'
        ],
        },
        // 水平滚动条
        { view: 'scrollbar', id: 'scrollHor', height: 20 }, // 水平滚动条视图,id 为 'scrollHor',高度为 20
    ],
};

gantt.plugins({
    tooltip: true
});


const tasks = {
    "data": [
    // 第一条数据
    {
      pCode: '0',
      type: 1,
      code: 'm1234',
      name: 'doudou',
      open: true, // 默认展开
      id: 'm1234',
      color: '#c7cfd8',
      text: '',
    },
    {
      id: 'm1234567-1',
      pCode: 'm1234567',
      startDate: '2025-01-02',
      endDate: '2025-02-02',
      text: 'Charter开发',
      milestonskey: 'CHARTER_DEVELOPMENT',
      parent: 'm1234567',
      start_date: '2025-01-02',
      end_date: '2025-02-02',
      color: '#adbbff',
    },
    {
      id: 'm1234567-2',
      pCode: 'm1234567',
      startDate: '2025-02-02',
      endDate: '2025-03-02',
      text: '概念和计划',
      milestonskey: 'CONCEPT_AND_PLAN',
      parent: 'm1234567',
      start_date: '2025-02-02',
      end_date: '2025-03-02',
      color: '#ffe179',
    },
    {
      id: 'm1234567-3',
      pCode: 'm1234567',
      startDate: '2025-03-02',
      endDate: '2025-04-02',
      text: '初样开发',
      milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
      parent: 'm1234567',
      start_date: '2025-03-02',
      end_date: '2025-04-02',
      color: '#adbbff',
    },
    {
      id: 'm1234567-4',
      pCode: 'm1234567',
      startDate: '2025-04-02',
      endDate: '2025-05-02',
      text: '正样开发',
      milestonskey: 'PROTOTYPE_DEVELOPMENT',
      parent: 'm1234567',
      start_date: '2025-04-02',
      end_date: '2025-05-02',
      color: '#92d9fb',
    },
    {
      id: 'm1234567-5',
      pCode: 'm1234567',
      startDate: '2025-05-02',
      endDate: '2025-09-02',
      text: '验证',
      milestonskey: 'VERIFY',
      parent: 'm1234567',
      start_date: '2025-05-02',
      end_date: '2025-09-02',
      color: '#64e0b7',
    },
    // 第一条数据的:第一个子级
    {
      bid: 4,
      pCode: 'm1234',
      type: 2,
      code: 'm1234567',
      name: 'ububuu',
      milestones: [
        {
          id: 'm1234567-1',
          pCode: 'm1234567',
          startDate: '2025-01-02',
          endDate: '2025-02-02',
          text: 'Charter开发',
          milestonskey: 'CHARTER_DEVELOPMENT',
          parent: 'm1234567',
          start_date: '2025-01-02',
          end_date: '2025-02-02',
        },
        {
          id: 'm1234567-2',
          pCode: 'm1234567',
          startDate: '2025-02-02',
          endDate: '2025-03-02',
          text: '概念和计划',
          milestonskey: 'CONCEPT_AND_PLAN',
          parent: 'm1234567',
          start_date: '2025-02-02',
          end_date: '2025-03-02',
        },
        {
          id: 'm1234567-3',
          pCode: 'm1234567',
          startDate: '2025-03-02',
          endDate: '2025-04-02',
          text: '初样开发',
          milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
          parent: 'm1234567',
          start_date: '2025-03-02',
          end_date: '2025-04-02',
        },
        {
          id: 'm1234567-4',
          pCode: 'm1234567',
          startDate: '2025-04-02',
          endDate: '2025-05-02',
          text: '正样开发',
          milestonskey: 'PROTOTYPE_DEVELOPMENT',
          parent: 'm1234567',
          start_date: '2025-04-02',
          end_date: '2025-05-02',
        },
        {
          id: 'm1234567-5',
          pCode: 'm1234567',
          startDate: '2025-05-02',
          endDate: '2025-09-02',
          text: '验证',
          milestonskey: 'VERIFY',
          parent: 'm1234567',
          start_date: '2025-05-02',
          end_date: '2025-09-02',
        },
      ],
      children: null,
      parent: 'm1234',
      render: 'split',
      isChildrenNode: true,
      id: 'm1234567',
    },
    {
      id: 'm12345678-1',
      pCode: 'm12345678',
      startDate: '2025-01-02',
      endDate: '2025-02-02',
      text: 'Charter开发',
      milestonskey: 'CHARTER_DEVELOPMENT',
      parent: 'm12345678',
      start_date: '2025-01-02',
      end_date: '2025-02-02',
      color: '#adbbff',
    },
    {
      id: 'm12345678-2',
      pCode: 'm12345678',
      startDate: '2025-02-02',
      endDate: '2025-03-02',
      text: '概念和计划',
      milestonskey: 'CONCEPT_AND_PLAN',
      parent: 'm12345678',
      start_date: '2025-02-02',
      end_date: '2025-03-02',
      color: '#ffe179',
    },
    {
      id: 'm12345678-3',
      pCode: 'm12345678',
      startDate: '2025-03-02',
      endDate: '2025-04-02',
      text: '初样开发',
      milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
      parent: 'm12345678',
      start_date: '2025-03-02',
      end_date: '2025-04-02',
      color: '#adbbff',
    },
    {
      id: 'm12345678-4',
      pCode: 'm12345678',
      startDate: '2025-04-02',
      endDate: '2025-05-02',
      text: '正样开发',
      milestonskey: 'PROTOTYPE_DEVELOPMENT',
      parent: 'm12345678',
      start_date: '2025-04-02',
      end_date: '2025-05-02',
      color: '#92d9fb',
    },
    {
      id: 'm12345678-5',
      pCode: 'm12345678',
      startDate: '2025-05-02',
      endDate: '2025-07-02',
      text: '验证',
      milestonskey: 'VERIFY',
      parent: 'm12345678',
      start_date: '2025-05-02',
      end_date: '2025-07-02',
      color: '#64e0b7',
    },
    // 第一第的第二子级
    {
      bid: 5,
      pCode: 'm1234',
      type: 2,
      code: 'm12345678',
      name: 'm1234',
      // 任务节点
      milestones: [
        {
          id: 'm12345678-1',
          pCode: 'm12345678',
          startDate: '2025-01-02',
          endDate: '2025-02-02',
          text: 'Charter开发',
          milestonskey: 'CHARTER_DEVELOPMENT',
          parent: 'm12345678',
          start_date: '2025-01-02',
          end_date: '2025-02-02',
        },
        {
          id: 'm12345678-2',
          pCode: 'm12345678',
          startDate: '2025-02-02',
          endDate: '2025-03-02',
          text: '概念和计划',
          milestonskey: 'CONCEPT_AND_PLAN',
          parent: 'm12345678',
          start_date: '2025-02-02',
          end_date: '2025-03-02',
        },
        {
          id: 'm12345678-3',
          pCode: 'm12345678',
          startDate: '2025-03-02',
          endDate: '2025-04-02',
          text: '初样开发',
          milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
          parent: 'm12345678',
          start_date: '2025-03-02',
          end_date: '2025-04-02',
        },
        {
          id: 'm12345678-4',
          pCode: 'm12345678',
          startDate: '2025-04-02',
          endDate: '2025-05-02',
          text: '正样开发',
          milestonskey: 'PROTOTYPE_DEVELOPMENT',
          parent: 'm12345678',
          start_date: '2025-04-02',
          end_date: '2025-05-02',
        },
        {
          id: 'm12345678-5',
          pCode: 'm12345678',
          startDate: '2025-05-02',
          endDate: '2025-07-02',
          text: '验证',
          milestonskey: 'VERIFY',
          parent: 'm12345678',
          start_date: '2025-05-02',
          end_date: '2025-07-02',
        },
      ],
      children: null,
      parent: 'm1234',
      render: 'split',
      isChildrenNode: true,
      id: 'm12345678',
    },
    {
      id: 'm12345-1',
      pCode: 'm12345',
      startDate: '2025-01-02',
      endDate: '2025-02-02',
      text: 'Charter开发',
      milestonskey: 'CHARTER_DEVELOPMENT',
      parent: 'm12345',
      start_date: '2025-01-02',
      end_date: '2025-02-02',
      color: '#adbbff',
    },
    {
      id: 'm12345-2',
      pCode: 'm12345',
      startDate: '2025-02-02',
      endDate: '2025-03-02',
      text: '概念和计划',
      milestonskey: 'CONCEPT_AND_PLAN',
      parent: 'm12345',
      start_date: '2025-02-02',
      end_date: '2025-03-02',
      color: '#ffe179',
    },
    {
      id: 'm12345-3',
      pCode: 'm12345',
      startDate: '2025-03-02',
      endDate: '2025-04-02',
      text: '初样开发',
      milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
      parent: 'm12345',
      start_date: '2025-03-02',
      end_date: '2025-04-02',
      color: '#adbbff',
    },
    {
      id: 'm12345-4',
      pCode: 'm12345',
      startDate: '2025-04-02',
      endDate: '2025-05-02',
      text: '正样开发',
      milestonskey: 'PROTOTYPE_DEVELOPMENT',
      parent: 'm12345',
      start_date: '2025-04-02',
      end_date: '2025-05-02',
      color: '#92d9fb',
    },
    {
      id: 'm12345-5',
      pCode: 'm12345',
      startDate: '2025-05-02',
      endDate: '2025-06-02',
      text: '验证',
      milestonskey: 'VERIFY',
      parent: 'm12345',
      start_date: '2025-05-02',
      end_date: '2025-06-02',
      color: '#64e0b7',
    },
    // 第二条
    {
      bid: 2,
      pCode: '0',
      type: 2,
      code: 'm12345',
      name: 'demo',
      projectRank: 'm1234',
      marketPositioning: 'm1234',
      costBenchmarking: 'm1234',
      performanceBenchmark: 'm1234',
      targetAudienceName: 'm1234',
      focusedIndustriesAndClientsName: 'm1234',
      newReachableCapacity: 'm1234',
      preparationOfKeyTechnologies: null,
      valueProposition: 'm1234',
      keyCompetitive: 'm1234',
      charterInitDate: '2025-01-02 00:00:00',
      charterTransferDate: '2025-02-02 00:00:00',
      pdcpDate: '2025-03-02 00:00:00',
      tr4aDate: '2025-04-02 00:00:00',
      edcpDate: '2025-05-02 00:00:00',
      adcpDate: '2025-06-02 00:00:00',
      insightFinishDate: '2025-07-02 00:00:00',
      relateOfferingName: null,
      rerlationshipName: null,
      serviceSolution: null,
      belongSolutionName: null,
      productClassL3Name: null,
      productSeriesL4Name: null,
      productOfferingL5Name: null,
      productBelongGroupsName: null,
      productBelongSpdtName: null,
      charterClass: null,
      charterTransferVersionTypeName: null,
      decisionLevelName: null,
      priorityName: null,
      businessPropertyName: null,
      bak: null,
      verison: null,
      state: 1,
      createUser: null,
      updatedUser: null,
      milestones: [
        {
          id: 'm12345-1',
          pCode: 'm12345',
          startDate: '2025-01-02',
          endDate: '2025-02-02',
          text: 'Charter开发',
          milestonskey: 'CHARTER_DEVELOPMENT',
          parent: 'm12345',
          start_date: '2025-01-02',
          end_date: '2025-02-02',
        },
        {
          id: 'm12345-2',
          pCode: 'm12345',
          startDate: '2025-02-02',
          endDate: '2025-03-02',
          text: '概念和计划',
          milestonskey: 'CONCEPT_AND_PLAN',
          parent: 'm12345',
          start_date: '2025-02-02',
          end_date: '2025-03-02',
        },
        {
          id: 'm12345-3',
          pCode: 'm12345',
          startDate: '2025-03-02',
          endDate: '2025-04-02',
          text: '初样开发',
          milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
          parent: 'm12345',
          start_date: '2025-03-02',
          end_date: '2025-04-02',
        },
        {
          id: 'm12345-4',
          pCode: 'm12345',
          startDate: '2025-04-02',
          endDate: '2025-05-02',
          text: '正样开发',
          milestonskey: 'PROTOTYPE_DEVELOPMENT',
          parent: 'm12345',
          start_date: '2025-04-02',
          end_date: '2025-05-02',
        },
        {
          id: 'm12345-5',
          pCode: 'm12345',
          startDate: '2025-05-02',
          endDate: '2025-06-02',
          text: '验证',
          milestonskey: 'VERIFY',
          parent: 'm12345',
          start_date: '2025-05-02',
          end_date: '2025-06-02',
        },
      ],
      children: null,
      render: 'split',
      id: 'm12345',
    },
    {
      id: 'm12346-1',
      pCode: 'm12346',
      startDate: '2025-01-02',
      endDate: '2025-02-02',
      text: 'Charter开发',
      milestonskey: 'CHARTER_DEVELOPMENT',
      parent: 'm12346',
      start_date: '2025-01-02',
      end_date: '2025-02-02',
      color: '#adbbff',
    },
    {
      id: 'm12346-2',
      pCode: 'm12346',
      startDate: '2025-02-02',
      endDate: '2025-03-02',
      text: '概念和计划',
      milestonskey: 'CONCEPT_AND_PLAN',
      parent: 'm12346',
      start_date: '2025-02-02',
      end_date: '2025-03-02',
      color: '#ffe179',
    },
    {
      id: 'm12346-3',
      pCode: 'm12346',
      startDate: '2025-03-02',
      endDate: '2025-04-02',
      text: '初样开发',
      milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
      parent: 'm12346',
      start_date: '2025-03-02',
      end_date: '2025-04-02',
      color: '#adbbff',
    },
    {
      id: 'm12346-4',
      pCode: 'm12346',
      startDate: '2025-04-02',
      endDate: '2025-05-02',
      text: '正样开发',
      milestonskey: 'PROTOTYPE_DEVELOPMENT',
      parent: 'm12346',
      start_date: '2025-04-02',
      end_date: '2025-05-02',
      color: '#92d9fb',
    },
    {
      id: 'm12346-5',
      pCode: 'm12346',
      startDate: '2025-05-02',
      endDate: '2025-08-02',
      text: '验证',
      milestonskey: 'VERIFY',
      parent: 'm12346',
      start_date: '2025-05-02',
      end_date: '2025-08-02',
      color: '#64e0b7',
    },
    {
      bid: 3,
      pCode: '0',
      type: 2,
      code: 'm12346',
      name: 'karla',
      milestones: [
        {
          id: 'm12346-1',
          pCode: 'm12346',
          startDate: '2025-01-02',
          endDate: '2025-02-02',
          text: 'Charter开发',
          milestonskey: 'CHARTER_DEVELOPMENT',
          parent: 'm12346',
          start_date: '2025-01-02',
          end_date: '2025-02-02',
        },
        {
          id: 'm12346-2',
          pCode: 'm12346',
          startDate: '2025-02-02',
          endDate: '2025-03-02',
          text: '概念和计划',
          milestonskey: 'CONCEPT_AND_PLAN',
          parent: 'm12346',
          start_date: '2025-02-02',
          end_date: '2025-03-02',
        },
        {
          id: 'm12346-3',
          pCode: 'm12346',
          startDate: '2025-03-02',
          endDate: '2025-04-02',
          text: '初样开发',
          milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
          parent: 'm12346',
          start_date: '2025-03-02',
          end_date: '2025-04-02',
        },
        {
          id: 'm12346-4',
          pCode: 'm12346',
          startDate: '2025-04-02',
          endDate: '2025-05-02',
          text: '正样开发',
          milestonskey: 'PROTOTYPE_DEVELOPMENT',
          parent: 'm12346',
          start_date: '2025-04-02',
          end_date: '2025-05-02',
        },
        {
          id: 'm12346-5',
          pCode: 'm12346',
          startDate: '2025-05-02',
          endDate: '2025-08-02',
          text: '验证',
          milestonskey: 'VERIFY',
          parent: 'm12346',
          start_date: '2025-05-02',
          end_date: '2025-08-02',
        },
      ],
      children: null,
      render: 'split',
      id: 'm12346',
    },
  ],
}


gantt.init("gantt_here");
gantt.parse(tasks);

function showTaskTooltip(event, domElement, key) {
    if (!tooltipConfig[key]) {
        return
    }
    let targetTaskId = gantt.locate(event);
    if (key == "cells") {
        targetTaskId = domElement.closest(".gantt_task_row").dataset.taskId
    }
    if (gantt.isTaskExists(targetTaskId)) {
        const task = gantt.getTask(targetTaskId);
        return gantt.templates.tooltip_text(task.start_date, task.end_date, task);
    }
}
gantt.ext.tooltips.tooltipFor({
    selector: ".gantt_row",
    html: function (event, domElement) {
        return showTaskTooltip(event, domElement, "grid")
    }
});

gantt.ext.tooltips.tooltipFor({
    selector: ".gantt_task_line",
    html: function (event, domElement) {
        return showTaskTooltip(event, domElement, "bars")
    }
});

gantt.ext.tooltips.tooltipFor({
    selector: ".gantt_task_row",
    html: function (event, domElement) {
        return showTaskTooltip(event, domElement, "cells")
    }
});

gantt.ext.tooltips.tooltipFor({
    selector: ".gantt_task_link",
    html: function (event, domElement) {
        if (!tooltipConfig.links) {
            return
        }
        let targetLinkId = domElement.closest(".gantt_task_link").dataset.linkId
        if (gantt.isLinkExists(targetLinkId)) {
            const link = gantt.getLink(targetLinkId);
            const source = gantt.getTask(link.source)
            const target = gantt.getTask(link.target)
            return `Link from <b>${source.text}</b> to <b>${target.text}</b>`
        }
    }
});

gantt.ext.tooltips.tooltipFor({
    selector: ".gantt_scale_cell",
    html: function (event, domElement) {
        if (!tooltipConfig.scales) {
            return
        }
        return event.target.innerHTML
    }
});

1、html页面

javascript 复制代码
import React, {
  forwardRef,
  useEffect,
  useRef,
  useState,
  useCallback,
} from 'react';
import { observer } from 'mobx-react';
import { gantt } from '@/lib/dhtmlx-gantt/dhtmlxgantt.js';
import '@/lib/dhtmlx-gantt/dhtmlxgantt.css';
import { zoomLevels } from './store';
import './index.less';

import { languageConfig } from '@/language/language';
import moment from 'moment';

const GanttChart = observer(
  forwardRef(<HTMLDivElement, GanttChartProps>(props: any, ref) => {
    const { infoData, onSelect } = props;
    const { timeScale, tableColunm, tableData } = infoData;
    console.log('甘特图接收参数', infoData);

    const ganttRef = useRef<any>(null);
    const currentZoomRef = useRef<string>('month'); // 用于存储当前时间刻度的引用

    /** 定义:第二网格列的配置 */
    const secondGridColumns = {
      columns: [
        {
          name: 'actions',
          label: '操作',
          width: 120,
          align: 'center',
          template: task => {
            // 自定义列内容的模板函数,接收任务对象作为参数
            return `
                <div class="gantt-actions">
                  <!-- 编辑按钮,绑定任务的 id -->
                  <div class="edit-btn" data-task-id="${task.id}">编辑</div>
                  <!-- 详情按钮,绑定任务的 id -->
                  <div class="detail-btn" data-task-id="${task.id}">详情</div>
                </div>
              `;
          },
        },
      ],
    };

    /** 初始化:配置 Gantt */
    const initConfig = useCallback(() => {
      /**
       * 禁用系列
       */
      // 禁止双击创建任务
      gantt.config.dblclick_create = false;
      // 禁止创建任务时弹出编辑弹窗
      gantt.config.edit_on_create = false;
      // 禁止双击任务时弹出详情弹窗
      gantt.config.details_on_dblclick = false;
      // 禁止双击创建链接
      gantt.config.dblclick_create_link = false;
      // 禁止创建链接时弹出编辑弹窗
      gantt.config.edit_on_create_link = false;
      // 禁止任务进度调整
      gantt.config.drag_progress = false;
      // 禁止拖拽连线
      gantt.config.drag_link = false;
      // 关闭报错弹窗
      gantt.config.show_errors = false;
      /**
       * end
       */

      // 语言设置-中文
      gantt.i18n.setLocale('cn');
      // 行高
      gantt.config.row_height = 48;
      // 日期格式
      gantt.config.date_format = '%Y-%m-%d %H:%i';
      // 自适应
      gantt.config.autosize = 'y';

      /**
       * 设置列(第一列)
       * name - 数据字段;label-表头显示的名称;width-列宽;align-对齐方式;templage-自定义列内容的模板函数;
       */
      gantt.config.columns = [
        {
          name: 'selected', // 勾选框列
          label: '', // 表头为空
          width: 50,
          align: 'center',
          template: task => {
            return `
              <div class="gantt-checkbox">
                <input
                  type="checkbox"
                  data-task-id="${task.id}"
                  ${task.selected ? 'checked' : ''}
                />
              </div>
            `;
          },
        },
        ...tableColunm,
      ];

      // 布局
      gantt.config.layout = {
        css: 'gantt_container', // 容器的 CSS 类名
        rows: [
          {
            cols: [
              // 左侧网格(任务列表)
              { view: 'grid', width: 320, scrollY: 'scrollVer' }, //  // 网格视图,宽度为 320,垂直滚动条绑定到 'scrollVer'
              { resizer: true, width: 1 }, // 可调整宽度的分隔条,宽度为
              // 时间线视图
              { view: 'timeline', scrollX: 'scrollHor', scrollY: 'scrollVer' }, // 时间线视图,水平和垂直滚动条分别绑定到 'scrollHor' 和 'scrollVer'
              { resizer: true, width: 1 },
              // 右侧网格(自定义操作列)
              {
                view: 'grid', // 网格视图
                width: 120,
                bind: 'task', // 绑定到任务数据
                scrollY: 'scrollVer', // 垂直滚动条绑定到 'scrollVer'
                config: secondGridColumns, // 使用上面定义的 secondGridColumns 配置
              },
              // 垂直滚动条
              { view: 'scrollbar', id: 'scrollVer' }, // 垂直滚动条视图,id 为 'scrollVer'
            ],
          },
          // 水平滚动条
          { view: 'scrollbar', id: 'scrollHor', height: 20 }, // 水平滚动条视图,id 为 'scrollHor',高度为 20
        ],
      };

      // 设置 Tooltip
      gantt.templates.tooltip_text = function(start, end, task) {
        return `
          <div class="gantt_tooltip_box">
            <div><span>价值主张:</span> ${gantt.templates.tooltip_date_format(
              start,
            )}</div>
            <div><span>关键竞争力:</span> ${gantt.templates.tooltip_date_format(
              end,
            )}</div>
          </div>
        `;
      };

      // 启用当前日期标识插件
      gantt.plugins({
        marker: true, // 启用标记功能
        tooltip: true, // 启用tooltip插件功能
      });
    }, []);

    /** 初始化:缩放配置 */
    const initZoom = useCallback(() => {
      const zoomConfig = {
        levels: zoomLevels,
        element: () => ganttRef.current,
      };

      gantt.ext.zoom.init(zoomConfig); // 设置默认时间刻度
      gantt.ext.zoom.setLevel(currentZoomRef.current); // 设置默认时间刻度
    }, []);

    /** 初始化:加载任务数据 */
    const loadTasks = useCallback(() => {
      const tasks = tableData;
      gantt.parse(tasks);
    }, []);

    /** change:操作按钮点击事件 */
    const handleActionClick = useCallback(event => {
      const target = event.target;
      const taskId = target.getAttribute('data-task-id');

      if (!taskId) return;

      const actionMap = {
        'edit-btn': () => {
          console.log('点击了编辑:', taskId);
          onSelect({
            operationType: 'edit',
            operationId: taskId,
          });
        },
        'detail-btn': () => {
          console.log('点击了详情:', taskId);
          onSelect({
            operationType: 'detail',
            operationId: taskId,
          });
        },
      };

      // 调用对应的处理逻辑
      const action = actionMap[target.className];
      if (action) {
        action();
      }
    }, []);

    /** change:勾选框点击事件 */
    const handleCheckboxClick = useCallback(event => {
      const target = event.target;
      if (target.tagName === 'INPUT' && target.type === 'checkbox') {
        const taskId = target.getAttribute('data-task-id');
        const isChecked = target.checked;

        // 更新任务数据中的选中状态
        const task = gantt.getTask(taskId);
        task.selected = isChecked;
        gantt.updateTask(taskId);

        console.log('任务选中状态:', taskId, isChecked);
      }
    }, []);

    /** 监听 timeScale 的变化并动态更新时间刻度 */
    useEffect(() => {
      if (timeScale && timeScale !== currentZoomRef.current) {
        gantt.ext.zoom.setLevel(timeScale);
        currentZoomRef.current = timeScale; // 更新当前时间刻度
      }
    }, [timeScale]);

    /** 初始化 Gantt */
    useEffect(() => {
      // 甘特图配置
      initConfig();

      // 缩放配置
      initZoom();

      // 初始化
      gantt.init(ganttRef.current);

      // 加载数据
      loadTasks();

      // 设置初始时间刻度
      if (timeScale) {
        gantt.ext.zoom.setLevel(timeScale);
        currentZoomRef.current = timeScale; // 更新当前时间刻度
      }

      // 绑定操作按钮的点击事件
      const ganttContainer = ganttRef.current;
      ganttContainer.addEventListener('click', handleActionClick);

      // 绑定勾选框的点击事件
      ganttContainer.addEventListener('change', handleCheckboxClick);

      // 清理事件监听器
      return () => {
        ganttContainer.removeEventListener('click', handleActionClick);
        ganttContainer.removeEventListener('change', handleCheckboxClick);
      };
    }, [
      initConfig,
      initZoom,
      loadTasks,
      handleActionClick,
      handleCheckboxClick,
    ]);

    function formatTime(date) {
      return moment(date).format('YYYY-MM-DD');
    }
    (gantt as any).attachEvent('onTaskDrag', function(id, mode, task) {
      const currentTask = gantt.getTask(id);
      console.log(
        'currentTask',
        currentTask,
        formatTime(currentTask.start_date),
        formatTime(currentTask.end_date),
      );

      // 1. 获取上一个任务Id
      let preTask: any = null;
      const preTaskId = gantt.getPrevSibling(id);
      if (preTaskId) {
        preTask = gantt.getTask(preTaskId);
        console.log(
          'preTask',
          preTask,
          formatTime(preTask.start_date),
          formatTime(preTask.end_date),
        );
      }

      // 1. 获取下一个任务Id
      let nextTask: any = null;
      const nextTaskId = gantt.getNextSibling(id);
      if (nextTaskId) {
        nextTask = gantt.getTask(nextTaskId);
        console.log(
          'nextTask',
          nextTask,
          formatTime(nextTask.start_date),
          formatTime(nextTask.end_date),
        );
      }

      if (mode === 'move') {
        return false;
      }

      if (mode === 'resize') {
        // 首个任务
        if (nextTask && !preTask) {
          // console.log('start_date', currentTask.start_date);
          // console.log('end_date', currentTask.end_date);
          // console.log('计算', currentTask.end_date - currentTask.start_date);
          // if ((currentTask.end_date - currentTask.start_date) / (3600 * 24 * 1000) < 35) {
          //   return;

          // }
          gantt.updateTask(nextTask.id, {
            ...nextTask,
            start_date: currentTask.end_date,
          });
          // // 5. 自动刷新视图
          gantt.refreshTask(nextTask.id);
        }
        // 最后一个任务
        if (preTask && !nextTask) {
          gantt.updateTask(preTask.id, {
            ...preTask,
            end_date: currentTask.start_date,
          });
          // // 5. 自动刷新视图
          gantt.refreshTask(preTask.id);
        }
        // 中间任务
        if (preTask && nextTask) {
          gantt.updateTask(preTask.id, {
            ...preTask,
            end_date: currentTask.start_date,
          });
          gantt.updateTask(nextTask.id, {
            ...nextTask,
            start_date: currentTask.end_date,
          });
        }
      }

      return true;
    });

    return (
      <>
        {/* Gantt 容器 */}
        <div ref={ganttRef} style={{ width: '100%', height: '100%' }}></div>
      </>
    );
  }),
);

export default GanttChart;
javascript 复制代码
import React, {
  forwardRef,
  useEffect,
  useRef,
  useState,
  useCallback,
} from 'react';
import { Button } from 'choerodon-ui/pro';
import { ButtonColor, FuncType } from 'choerodon-ui/pro/lib/button/enum';
import { observer } from 'mobx-react';
import { gantt } from '@/lib/dhtmlx-gantt/dhtmlxgantt.js';
import '@/lib/dhtmlx-gantt/dhtmlxgantt.css';
import { zoomLevels } from './store';
import './index.less';
import DetailModal from '../detail/main';
import RecordModal from '../record/main';
import { languageConfig } from '@/language/language';

const GanttChart = observer(
  forwardRef((props, ref) => {
    const [visible, setVisible] = useState(false); // 弹框(详情/编辑)
    const [recordVisible, setRecordVisible] = useState(false); // 记录弹框
    const [operationType, setOperationType] = useState('detail'); // 弹框类型
    const [operationId, setOperationId] = useState(''); // 操作ID(用于调用详情)

    const ganttRef = useRef<any>(null);
    const [currentZoom, setCurrentZoom] = useState('day'); // 时间刻度(默认值: day)
    const currentZoomRef = useRef('day');

    /** 配置 Gantt */
    const initConfig = useCallback(() => {
      // 语言设置-中文
      gantt.i18n.setLocale('cn');
      // 行高
      gantt.config.row_height = 48;
      // 日期格式
      gantt.config.date_format = '%Y-%m-%d %H:%i';
      // 自适应
      gantt.config.autosize = 'y';

      // 设置列(name为数据字段,label为表头显示的名称,width为列宽,align为对齐方式)
      gantt.config.columns = [
        {
          name: 'selected', // 勾选框列
          label: '', // 表头为空
          width: 50,
          align: 'center',
          template: task => {
            return `
              <div class="gantt-checkbox">
                <input
                  type="checkbox"
                  data-task-id="${task.id}"
                  ${task.selected ? 'checked' : ''}
                />
              </div>
            `;
          },
        },
        {
          name: 'text',
          label: '姓名',
          width: 150,
          tree: true,
        },
        { name: 'start_date', label: '开始日期', width: 130, align: 'center' },
        { name: 'duration', label: '持续时间', width: 80, align: 'center' },
        { name: 'progress', label: '进度', width: 80, align: 'center' },
        {
          name: 'actions',
          label: '操作',
          width: 200,
          align: 'center',
          template: task => {
            return `
              <div class="gantt-actions">
                <div class="edit-btn" data-task-id="${task.id}">编辑</div>
                <div class="detail-btn" data-task-id="${task.id}">详情</div>
                <div class="record-btn" data-task-id="${task.id}">修改记录</div>
              </div>
            `;
          },
        },
      ];

      // 布局
      gantt.config.layout = {
        css: 'custom-gantt-style',
        rows: [
          {
            cols: [
              { view: 'grid', width: 450 }, // 任务列表
              { view: 'timeline', scrollX: 50, scrollY: 60 }, // 时间轴
            ],
          },
        ],
        resizer: {
          width: 10,
          handler: true,
        },
      };

      // 启用当前日期标识插件
      gantt.plugins({
        marker: true,
      });

      // 设置时间列的样式
      gantt.templates.timeline_cell_class = (task, date) => {
        const isDisabledZoom = ['month', 'year', 'quarter'].includes(
          currentZoomRef.current,
        );
        if (!isDisabledZoom && !gantt.isWorkTime(date)) return 'week_end';
        return '';
      };
    }, []);

    /** 设置当前日期标识线 */
    const initCurrentDateMarker = useCallback(() => {
      const dateToStr = gantt.date.date_to_str(gantt.config.task_date);
      const today = new Date(new Date().setHours(0, 0, 0, 0)); // 当天零点
      gantt.addMarker({
        start_date: today,
        css: 'today',
        text: '今日',
        title: `Today: ${dateToStr(today)}`,
      });
    }, []);

    /** 初始化缩放配置 */
    const initZoom = useCallback(() => {
      const zoomConfig = {
        levels: zoomLevels,
        element: () => gantt.$root.querySelector('.gantt_task'),
      };
      gantt.ext.zoom.init(zoomConfig);
      gantt.ext.zoom.setLevel('day');
    }, []);

    /** 加载任务数据 */
    const loadTasks = useCallback(() => {
      const tasks = {
        data: [
          {
            id: 1,
            text: 'Karla',
            start_date: '2019-08-01 00:00',
            duration: 3,
            parent: 0,
            progress: 0.5,
            open: true,
          },
          {
            id: 11,
            text: 'Karla-01',
            start_date: '2019-08-01 10:00',
            duration: 3,
            parent: 1,
            progress: 0.8,
          },
          {
            id: 2,
            text: '前端',
            start_date: '2019-08-02 00:00',
            duration: 2,
            parent: 1,
            progress: 0.7,
          },
          {
            id: 3,
            text: 'York',
            start_date: '2019-08-03 00:00',
            duration: 4,
            parent: 0,
            progress: 0.3,
            open: true,
          },
          {
            id: 4,
            text: '后端',
            start_date: '2019-08-04 00:00',
            duration: 1,
            parent: 3,
            progress: 0.6,
            open: true,
          },
          {
            id: 5,
            text: 'Coco',
            start_date: '2019-08-05 00:00',
            duration: 2,
            parent: 0,
            progress: 0.8,
            open: true,
          },
          {
            id: 6,
            text: '测试',
            start_date: '2019-08-06 00:00',
            duration: 5,
            parent: 5,
            progress: 0.4,
          },
          {
            id: 7,
            text: 'Happy',
            start_date: '2019-08-07 00:00',
            duration: 3,
            parent: 0,
            progress: 0.9,
          },
          {
            id: 9,
            text: 'Rose',
            start_date: '2019-08-09 00:00',
            duration: 1,
            parent: 0,
            progress: 0.1,
          },
        ],
      };
      gantt.parse(tasks);
    }, []);

    /** change 操作按钮点击事件 */
    const handleActionClick = useCallback(event => {
      const target = event.target;
      const taskId = target.getAttribute('data-task-id');

      if (!taskId) return;

      setOperationId(taskId);
      const actionMap = {
        'edit-btn': () => {
          console.log('点击了编辑:', taskId);

          setOperationType('edit');
          setVisible(true);
        },
        'detail-btn': () => {
          console.log('点击了详情:', taskId);
          setOperationType('detail');
          setVisible(true);
        },
        'record-btn': () => {
          console.log('点击了修改记录:', taskId);
          setRecordVisible(true);
        },
      };

      // 调用对应的处理逻辑
      const action = actionMap[target.className];
      if (action) {
        action();
      }
    }, []);

    /**change 时间刻度视图 */
    const handleZoomChange = useCallback(zoom => {
      setCurrentZoom(zoom);
      currentZoomRef.current = zoom;
      gantt.ext.zoom.setLevel(zoom);
    }, []);

    /** 处理勾选框点击事件 */
    const handleCheckboxClick = useCallback(event => {
      const target = event.target;
      if (target.tagName === 'INPUT' && target.type === 'checkbox') {
        const taskId = target.getAttribute('data-task-id');
        const isChecked = target.checked;

        // 更新任务数据中的选中状态
        const task = gantt.getTask(taskId);
        task.selected = isChecked;
        gantt.updateTask(taskId);

        console.log('任务选中状态:', taskId, isChecked);
      }
    }, []);

    /** 初始化 Gantt */
    useEffect(() => {
      initConfig();
      initZoom();
      initCurrentDateMarker();

      // 初始化
      gantt.init(ganttRef.current);

      // 加载数据
      loadTasks();

      // 绑定操作按钮的点击事件
      const ganttContainer = ganttRef.current;
      ganttContainer.addEventListener('click', handleActionClick);

      // 绑定勾选框的点击事件
      ganttContainer.addEventListener('change', handleCheckboxClick);

      // 清理事件监听器
      return () => {
        ganttContainer.removeEventListener('click', handleActionClick);
        ganttContainer.removeEventListener('change', handleCheckboxClick);
      };
    }, [
      initConfig,
      initZoom,
      initCurrentDateMarker,
      loadTasks,
      handleActionClick,
      handleCheckboxClick,
    ]);

    return (
      <div>
        {/* 时间刻度:切换按钮 */}
        <div style={{ margin: '12px 0' }}>
          {zoomLevels.map(item => (
            <Button
              key={item.name}
              color={ButtonColor.primary}
              funcType={FuncType.raised}
              disabled={item.name === currentZoom}
              onClick={() => handleZoomChange(item.name)}
              style={{ marginRight: 6 }}
            >
              {item.label}
            </Button>
          ))}
        </div>

        {/* Gantt 容器 */}
        <div style={{ height: '500px', overflow: 'auto' }}>
          <div ref={ganttRef} style={{ width: '100%', height: '100%' }}></div>
        </div>

        {/* 详情/编辑弹框 */}
        {visible && (
          <DetailModal
            visible={visible}
            setVisible={setVisible}
            onSelect={() => {}}
            title={
              operationType === 'detail'
                ? languageConfig('platformCharter.title.detail', '详情')
                : languageConfig('platformCharter.title.edit', '编辑')
            }
            infoData={{
              operationId,
              operationType,
            }}
          />
        )}

        {/* 记录 */}
        {recordVisible && (
          <RecordModal
            visible={recordVisible}
            setVisible={setRecordVisible}
            onSelect={() => {}}
            title={languageConfig(
              'platformCharter.title.editRecord',
              '修改记录',
            )}
            infoData={{
              operationId,
            }}
          />
        )}
      </div>
    );
  }),
);

export default GanttChart;

3、mock数据(暂时没用上)

javascript 复制代码
  data: [
    // 第一条数据
    {
      pCode: '0',
      type: 1,
      code: 'm1234',
      name: 'doudou',
      open: true, // 默认展开
      id: 'm1234',
      color: '#c7cfd8',
      text: '',
    },
    {
      id: 'm1234567-1',
      pCode: 'm1234567',
      startDate: '2025-01-02',
      endDate: '2025-02-02',
      text: 'Charter开发',
      milestonskey: 'CHARTER_DEVELOPMENT',
      parent: 'm1234567',
      start_date: '2025-01-02',
      end_date: '2025-02-02',
      color: '#adbbff',
    },
    {
      id: 'm1234567-2',
      pCode: 'm1234567',
      startDate: '2025-02-02',
      endDate: '2025-03-02',
      text: '概念和计划',
      milestonskey: 'CONCEPT_AND_PLAN',
      parent: 'm1234567',
      start_date: '2025-02-02',
      end_date: '2025-03-02',
      color: '#ffe179',
    },
    {
      id: 'm1234567-3',
      pCode: 'm1234567',
      startDate: '2025-03-02',
      endDate: '2025-04-02',
      text: '初样开发',
      milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
      parent: 'm1234567',
      start_date: '2025-03-02',
      end_date: '2025-04-02',
      color: '#adbbff',
    },
    {
      id: 'm1234567-4',
      pCode: 'm1234567',
      startDate: '2025-04-02',
      endDate: '2025-05-02',
      text: '正样开发',
      milestonskey: 'PROTOTYPE_DEVELOPMENT',
      parent: 'm1234567',
      start_date: '2025-04-02',
      end_date: '2025-05-02',
      color: '#92d9fb',
    },
    {
      id: 'm1234567-5',
      pCode: 'm1234567',
      startDate: '2025-05-02',
      endDate: '2025-09-02',
      text: '验证',
      milestonskey: 'VERIFY',
      parent: 'm1234567',
      start_date: '2025-05-02',
      end_date: '2025-09-02',
      color: '#64e0b7',
    },
    // 第一条数据的:第一个子级
    {
      bid: 4,
      pCode: 'm1234',
      type: 2,
      code: 'm1234567',
      name: 'ububuu',
      milestones: [
        {
          id: 'm1234567-1',
          pCode: 'm1234567',
          startDate: '2025-01-02',
          endDate: '2025-02-02',
          text: 'Charter开发',
          milestonskey: 'CHARTER_DEVELOPMENT',
          parent: 'm1234567',
          start_date: '2025-01-02',
          end_date: '2025-02-02',
        },
        {
          id: 'm1234567-2',
          pCode: 'm1234567',
          startDate: '2025-02-02',
          endDate: '2025-03-02',
          text: '概念和计划',
          milestonskey: 'CONCEPT_AND_PLAN',
          parent: 'm1234567',
          start_date: '2025-02-02',
          end_date: '2025-03-02',
        },
        {
          id: 'm1234567-3',
          pCode: 'm1234567',
          startDate: '2025-03-02',
          endDate: '2025-04-02',
          text: '初样开发',
          milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
          parent: 'm1234567',
          start_date: '2025-03-02',
          end_date: '2025-04-02',
        },
        {
          id: 'm1234567-4',
          pCode: 'm1234567',
          startDate: '2025-04-02',
          endDate: '2025-05-02',
          text: '正样开发',
          milestonskey: 'PROTOTYPE_DEVELOPMENT',
          parent: 'm1234567',
          start_date: '2025-04-02',
          end_date: '2025-05-02',
        },
        {
          id: 'm1234567-5',
          pCode: 'm1234567',
          startDate: '2025-05-02',
          endDate: '2025-09-02',
          text: '验证',
          milestonskey: 'VERIFY',
          parent: 'm1234567',
          start_date: '2025-05-02',
          end_date: '2025-09-02',
        },
      ],
      children: null,
      parent: 'm1234',
      render: 'split',
      isChildrenNode: true,
      id: 'm1234567',
    },
    {
      id: 'm12345678-1',
      pCode: 'm12345678',
      startDate: '2025-01-02',
      endDate: '2025-02-02',
      text: 'Charter开发',
      milestonskey: 'CHARTER_DEVELOPMENT',
      parent: 'm12345678',
      start_date: '2025-01-02',
      end_date: '2025-02-02',
      color: '#adbbff',
    },
    {
      id: 'm12345678-2',
      pCode: 'm12345678',
      startDate: '2025-02-02',
      endDate: '2025-03-02',
      text: '概念和计划',
      milestonskey: 'CONCEPT_AND_PLAN',
      parent: 'm12345678',
      start_date: '2025-02-02',
      end_date: '2025-03-02',
      color: '#ffe179',
    },
    {
      id: 'm12345678-3',
      pCode: 'm12345678',
      startDate: '2025-03-02',
      endDate: '2025-04-02',
      text: '初样开发',
      milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
      parent: 'm12345678',
      start_date: '2025-03-02',
      end_date: '2025-04-02',
      color: '#adbbff',
    },
    {
      id: 'm12345678-4',
      pCode: 'm12345678',
      startDate: '2025-04-02',
      endDate: '2025-05-02',
      text: '正样开发',
      milestonskey: 'PROTOTYPE_DEVELOPMENT',
      parent: 'm12345678',
      start_date: '2025-04-02',
      end_date: '2025-05-02',
      color: '#92d9fb',
    },
    {
      id: 'm12345678-5',
      pCode: 'm12345678',
      startDate: '2025-05-02',
      endDate: '2025-07-02',
      text: '验证',
      milestonskey: 'VERIFY',
      parent: 'm12345678',
      start_date: '2025-05-02',
      end_date: '2025-07-02',
      color: '#64e0b7',
    },
    // 第一第的第二子级
    {
      bid: 5,
      pCode: 'm1234',
      type: 2,
      code: 'm12345678',
      name: 'm1234',
      // 任务节点
      milestones: [
        {
          id: 'm12345678-1',
          pCode: 'm12345678',
          startDate: '2025-01-02',
          endDate: '2025-02-02',
          text: 'Charter开发',
          milestonskey: 'CHARTER_DEVELOPMENT',
          parent: 'm12345678',
          start_date: '2025-01-02',
          end_date: '2025-02-02',
        },
        {
          id: 'm12345678-2',
          pCode: 'm12345678',
          startDate: '2025-02-02',
          endDate: '2025-03-02',
          text: '概念和计划',
          milestonskey: 'CONCEPT_AND_PLAN',
          parent: 'm12345678',
          start_date: '2025-02-02',
          end_date: '2025-03-02',
        },
        {
          id: 'm12345678-3',
          pCode: 'm12345678',
          startDate: '2025-03-02',
          endDate: '2025-04-02',
          text: '初样开发',
          milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
          parent: 'm12345678',
          start_date: '2025-03-02',
          end_date: '2025-04-02',
        },
        {
          id: 'm12345678-4',
          pCode: 'm12345678',
          startDate: '2025-04-02',
          endDate: '2025-05-02',
          text: '正样开发',
          milestonskey: 'PROTOTYPE_DEVELOPMENT',
          parent: 'm12345678',
          start_date: '2025-04-02',
          end_date: '2025-05-02',
        },
        {
          id: 'm12345678-5',
          pCode: 'm12345678',
          startDate: '2025-05-02',
          endDate: '2025-07-02',
          text: '验证',
          milestonskey: 'VERIFY',
          parent: 'm12345678',
          start_date: '2025-05-02',
          end_date: '2025-07-02',
        },
      ],
      children: null,
      parent: 'm1234',
      render: 'split',
      isChildrenNode: true,
      id: 'm12345678',
    },
    {
      id: 'm12345-1',
      pCode: 'm12345',
      startDate: '2025-01-02',
      endDate: '2025-02-02',
      text: 'Charter开发',
      milestonskey: 'CHARTER_DEVELOPMENT',
      parent: 'm12345',
      start_date: '2025-01-02',
      end_date: '2025-02-02',
      color: '#adbbff',
    },
    {
      id: 'm12345-2',
      pCode: 'm12345',
      startDate: '2025-02-02',
      endDate: '2025-03-02',
      text: '概念和计划',
      milestonskey: 'CONCEPT_AND_PLAN',
      parent: 'm12345',
      start_date: '2025-02-02',
      end_date: '2025-03-02',
      color: '#ffe179',
    },
    {
      id: 'm12345-3',
      pCode: 'm12345',
      startDate: '2025-03-02',
      endDate: '2025-04-02',
      text: '初样开发',
      milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
      parent: 'm12345',
      start_date: '2025-03-02',
      end_date: '2025-04-02',
      color: '#adbbff',
    },
    {
      id: 'm12345-4',
      pCode: 'm12345',
      startDate: '2025-04-02',
      endDate: '2025-05-02',
      text: '正样开发',
      milestonskey: 'PROTOTYPE_DEVELOPMENT',
      parent: 'm12345',
      start_date: '2025-04-02',
      end_date: '2025-05-02',
      color: '#92d9fb',
    },
    {
      id: 'm12345-5',
      pCode: 'm12345',
      startDate: '2025-05-02',
      endDate: '2025-06-02',
      text: '验证',
      milestonskey: 'VERIFY',
      parent: 'm12345',
      start_date: '2025-05-02',
      end_date: '2025-06-02',
      color: '#64e0b7',
    },
    // 第二条
    {
      bid: 2,
      pCode: '0',
      type: 2,
      code: 'm12345',
      name: 'demo',
      projectRank: 'm1234',
      marketPositioning: 'm1234',
      costBenchmarking: 'm1234',
      performanceBenchmark: 'm1234',
      targetAudienceName: 'm1234',
      focusedIndustriesAndClientsName: 'm1234',
      newReachableCapacity: 'm1234',
      preparationOfKeyTechnologies: null,
      valueProposition: 'm1234',
      keyCompetitive: 'm1234',
      charterInitDate: '2025-01-02 00:00:00',
      charterTransferDate: '2025-02-02 00:00:00',
      pdcpDate: '2025-03-02 00:00:00',
      tr4aDate: '2025-04-02 00:00:00',
      edcpDate: '2025-05-02 00:00:00',
      adcpDate: '2025-06-02 00:00:00',
      insightFinishDate: '2025-07-02 00:00:00',
      relateOfferingName: null,
      rerlationshipName: null,
      serviceSolution: null,
      belongSolutionName: null,
      productClassL3Name: null,
      productSeriesL4Name: null,
      productOfferingL5Name: null,
      productBelongGroupsName: null,
      productBelongSpdtName: null,
      charterClass: null,
      charterTransferVersionTypeName: null,
      decisionLevelName: null,
      priorityName: null,
      businessPropertyName: null,
      bak: null,
      verison: null,
      state: 1,
      createUser: null,
      updatedUser: null,
      milestones: [
        {
          id: 'm12345-1',
          pCode: 'm12345',
          startDate: '2025-01-02',
          endDate: '2025-02-02',
          text: 'Charter开发',
          milestonskey: 'CHARTER_DEVELOPMENT',
          parent: 'm12345',
          start_date: '2025-01-02',
          end_date: '2025-02-02',
        },
        {
          id: 'm12345-2',
          pCode: 'm12345',
          startDate: '2025-02-02',
          endDate: '2025-03-02',
          text: '概念和计划',
          milestonskey: 'CONCEPT_AND_PLAN',
          parent: 'm12345',
          start_date: '2025-02-02',
          end_date: '2025-03-02',
        },
        {
          id: 'm12345-3',
          pCode: 'm12345',
          startDate: '2025-03-02',
          endDate: '2025-04-02',
          text: '初样开发',
          milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
          parent: 'm12345',
          start_date: '2025-03-02',
          end_date: '2025-04-02',
        },
        {
          id: 'm12345-4',
          pCode: 'm12345',
          startDate: '2025-04-02',
          endDate: '2025-05-02',
          text: '正样开发',
          milestonskey: 'PROTOTYPE_DEVELOPMENT',
          parent: 'm12345',
          start_date: '2025-04-02',
          end_date: '2025-05-02',
        },
        {
          id: 'm12345-5',
          pCode: 'm12345',
          startDate: '2025-05-02',
          endDate: '2025-06-02',
          text: '验证',
          milestonskey: 'VERIFY',
          parent: 'm12345',
          start_date: '2025-05-02',
          end_date: '2025-06-02',
        },
      ],
      children: null,
      render: 'split',
      id: 'm12345',
    },
    {
      id: 'm12346-1',
      pCode: 'm12346',
      startDate: '2025-01-02',
      endDate: '2025-02-02',
      text: 'Charter开发',
      milestonskey: 'CHARTER_DEVELOPMENT',
      parent: 'm12346',
      start_date: '2025-01-02',
      end_date: '2025-02-02',
      color: '#adbbff',
    },
    {
      id: 'm12346-2',
      pCode: 'm12346',
      startDate: '2025-02-02',
      endDate: '2025-03-02',
      text: '概念和计划',
      milestonskey: 'CONCEPT_AND_PLAN',
      parent: 'm12346',
      start_date: '2025-02-02',
      end_date: '2025-03-02',
      color: '#ffe179',
    },
    {
      id: 'm12346-3',
      pCode: 'm12346',
      startDate: '2025-03-02',
      endDate: '2025-04-02',
      text: '初样开发',
      milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
      parent: 'm12346',
      start_date: '2025-03-02',
      end_date: '2025-04-02',
      color: '#adbbff',
    },
    {
      id: 'm12346-4',
      pCode: 'm12346',
      startDate: '2025-04-02',
      endDate: '2025-05-02',
      text: '正样开发',
      milestonskey: 'PROTOTYPE_DEVELOPMENT',
      parent: 'm12346',
      start_date: '2025-04-02',
      end_date: '2025-05-02',
      color: '#92d9fb',
    },
    {
      id: 'm12346-5',
      pCode: 'm12346',
      startDate: '2025-05-02',
      endDate: '2025-08-02',
      text: '验证',
      milestonskey: 'VERIFY',
      parent: 'm12346',
      start_date: '2025-05-02',
      end_date: '2025-08-02',
      color: '#64e0b7',
    },
    {
      bid: 3,
      pCode: '0',
      type: 2,
      code: 'm12346',
      name: 'karla',
      milestones: [
        {
          id: 'm12346-1',
          pCode: 'm12346',
          startDate: '2025-01-02',
          endDate: '2025-02-02',
          text: 'Charter开发',
          milestonskey: 'CHARTER_DEVELOPMENT',
          parent: 'm12346',
          start_date: '2025-01-02',
          end_date: '2025-02-02',
        },
        {
          id: 'm12346-2',
          pCode: 'm12346',
          startDate: '2025-02-02',
          endDate: '2025-03-02',
          text: '概念和计划',
          milestonskey: 'CONCEPT_AND_PLAN',
          parent: 'm12346',
          start_date: '2025-02-02',
          end_date: '2025-03-02',
        },
        {
          id: 'm12346-3',
          pCode: 'm12346',
          startDate: '2025-03-02',
          endDate: '2025-04-02',
          text: '初样开发',
          milestonskey: 'INITIAL_SAMPLE_DEVELOPMENT',
          parent: 'm12346',
          start_date: '2025-03-02',
          end_date: '2025-04-02',
        },
        {
          id: 'm12346-4',
          pCode: 'm12346',
          startDate: '2025-04-02',
          endDate: '2025-05-02',
          text: '正样开发',
          milestonskey: 'PROTOTYPE_DEVELOPMENT',
          parent: 'm12346',
          start_date: '2025-04-02',
          end_date: '2025-05-02',
        },
        {
          id: 'm12346-5',
          pCode: 'm12346',
          startDate: '2025-05-02',
          endDate: '2025-08-02',
          text: '验证',
          milestonskey: 'VERIFY',
          parent: 'm12346',
          start_date: '2025-05-02',
          end_date: '2025-08-02',
        },
      ],
      children: null,
      render: 'split',
      id: 'm12346',
    },
  ],
相关推荐
bingbingyihao5 分钟前
在线终端(一个基于 Spring Boot 的在线终端模拟器,实现了类 Linux 命令行操作功能)
linux·javascript
user775742973531512 分钟前
Echarts-Vue3-多图表联动
前端
清弦居士12 分钟前
解锁 Ant Design MCP 组件查询新姿势:大模型组件查询新范式
前端·mcp
天天扭码16 分钟前
LeetCode 题解 | 1.两数之和(最优解)
前端·javascript·算法
冉冉同学19 分钟前
【HarmonyOS NEXT】解决微信浏览器无法唤起APP的问题
android·前端·harmonyos
广龙宇23 分钟前
【Web API系列】Web Shared Storage API之WorkletSharedStorage深度解析与实践指南
前端
逍遥德28 分钟前
前端工程化-包管理NPM-package.json 和 package-lock.json 详解
前端·npm·json
一只小风华~29 分钟前
Web前端 (CSS篇)
前端·css·html·html5
HelloRevit33 分钟前
npm install 版本过高引发错误,请添加 --legacy-peer-deps
前端·npm·node.js
工九度36 分钟前
2025前端社招最新面试题汇总- 场景题篇
前端·javascript