可调日期的任务甘特图

cybozu发表于:2020年06月04日 15:36:25更新于:2021年04月07日 14:37:23

Index

概要

之前我们介绍过一篇甘特图插件,这次我们要介绍的是另一款甘特图(使用了开源库frappe-gantt)。与之前相比,这次最大的不同在于日期可以在图形界面上直接拖动来修改。而且还提供进度的管理。

如果您喜欢这些新功能,或是喜欢这款的UI风格,我们推荐您不妨尝试一下。

完成后的样子

下面是使用这个插件后,应用上显示的样子。

根据记录列表里的记录数据来显示甘特图。

0015ee6ef4262af27411592def43918

下载源码,打包,导入

为了方便之后的自定义开发,建议您下载源代码后自行打包上传。

有关打包和导入的方法,可参见kintone 插件开发流程中的打包导入章节。


应用的准备

创建一个适配插件的应用


应用本身非常简单,我们依照最低需求按下表中的字段创建应用

字段名类型code备注
任务名称单行文本框title
开始日期日期start_date
结束日期日期end_date
进度数值progress最小值为0;最大值为100

表单设计效果大致如下

7253a22f8aafd17a053426584cdd58bbdc250330278446a402c383b758b8cd84.png

开启应用的流程管理

本范例可以根据流程的执行人分组显示,所以需要开启应用的流程管理。

ca41877d80b65bd8acaaac65bbfd3e1bf55892a8005bee3f411832f81dd5eedf.png

d0373c973a9c25d4815d9f30c8d0baa134c9b70e74659d3b3c894e66574a8b01.png

流程的状态和执行动作可以根据情况自行设定。

本范例没有使用这些参数。


插件配置

  • 在设置中找到插件

  • 按下添加插件,找到之前取名为(任务进度管理插件)的插件后添加

  • 点击插件设置(插件的齿轮图标)

  • 配置好相应的字段代码(刚才在建立字段时赋予的code)

  • 点击Save保存

ab91bf280f0c6118d0fc027f2fe307fe3d0fd2d352e16c5d5df3637a162d686a.png

配置名称内容
标题代码title
开始日期代码start_date
结束日期代码end_date
进度代码progress

到这一步为止应用和插件的配置全部完成,点击更新应用,并试着追加几条数据来看看效果吧。


插件源码的简单介绍


现在我们来介绍一下主要功能是如何实现的吧。


添加视图切换的按钮,用天视图按钮举例


let ganttButtonElDayTop = document.createElement('button');
ganttButtonElDayTop.innerHTML = 'Day';
ganttButtonElDayTop.classList.add('gantt-date');
ganttButtonElDayTop.onclick = () => {
  ganttChart.change_view_mode('Day');
}


根据kintone的record制作图像用的任务object


function getFirstAssigneeName(r) {
  // 由于执行者可以是多人,这里只取第一个人
  for (const i in r) {
    if (r[i].type === 'STATUS_ASSIGNEE') {
      if (r[i].value.length > 0) {
        return r[i].value[0].name;
      } else {
        return 'nobody';
      }
    }
  }
}
function makeTask(records, config) {
  // 创建给甘特图用的对象
  let tasks = [];
  for (let i = 0; i < records.length; i++) {
    tasks.push({
      start: records[i][config.startDate].value,
      end: records[i][config.endDate].value,
      name: records[i][config.title].value,
      id: records[i].$id.value,
      progress: records[i][config.progress].value,
      assignee: getFirstAssigneeName(records[i])
    });
  }
  // 根据当前执行者排序,为了把同一人的任务显示在一起
  tasks.sort((one, two) => {
    if (one.assignee < two.assignee) {
      return 1;
    } else {
      return -1;
    }
  })
  return tasks;
}


更新开始和结束时间的方法


function updateTaskDate(ko, rocodeId, startDate, endDate) {
  var params = {
    app: 60,
    id: rocodeId,
    record: {
      start_date: {
        value: startDate
      },
      end_date: {
        value: endDate
      }
    }
  };
  ko.api(ko.api.url('/k/v1/record', true), 'PUT', params).then(function (resp) {}, function (error) {});
}

更新进度的方法和上面类似,这里不赘述,具体请参照完整的源代码。


执行者名和颜色对应提示


function unique(arr) {
  if (!Array.isArray(arr)) {
    console.log('type error!');
    return;
  }
  let arrry = [arr[0]];
  for (let i = 1; i < arr.length; i++) {
    if (arr[i].assignee !== arr[i - 1].assignee) {
      arrry.push(arr[i]);
    }
  }
  return arrry;
}
// 取得唯一的执行者
let uniChargeTasks = unique(tasks);
// 循环添加执行者的颜色
for (let i = 0; i < uniChargeTasks.length; i++) {
  let rectColor = document.createElement('div');
  rectColor.classList.add('fav-color');
  rectColor.classList.add(uniChargeTasks[i].assignee)
  let chargeName = document.createElement('span');
  chargeName.classList.add('charge-name');
  chargeName.innerText = uniChargeTasks[i].assignee;
  let rectColor2 = rectColor.cloneNode(true);
  let chargeName2 = chargeName.cloneNode(true);
  insertAfter(chargeName2, ganttButtonElMonthTop);
  insertAfter(rectColor2, ganttButtonElMonthTop);
  insertAfter(chargeName, ganttButtonElMonthBottom);
  insertAfter(rectColor, ganttButtonElMonthBottom);
}


显示甘特图


// 创建甘特图对象
let ganttChart = new Gantt('#gantt-target', tasks, {
    on_click: function (task) {
    },
    on_date_change: function (task, start, end) {
      let formatStart = start.Format('yyyy-MM-dd');
      let formatEnd = end.Format('yyyy-MM-dd');
      updateTaskDate(kintone, task.id, formatStart, formatEnd);
    },
    on_progress_change: function (task, progress) {
      updateTaskProgress(kintone, task.id, config.progress, progress);
    },
    on_view_change: function (mode) {
      if (mode === 'Day') {
        ganttButtonElDayTop.classList.add('button-selected');
        ganttButtonElDayBottom.classList.add('button-selected');
        ganttButtonElWeekTop.classList.remove('button-selected');
        ganttButtonElWeekBottom.classList.remove('button-selected');
        ganttButtonElMonthTop.classList.remove('button-selected');
        ganttButtonElMonthBottom.classList.remove('button-selected');
      } else if (mode === 'Week') {
        ganttButtonElDayTop.classList.remove('button-selected');
        ganttButtonElDayBottom.classList.remove('button-selected');
        ganttButtonElWeekTop.classList.add('button-selected');
        ganttButtonElWeekBottom.classList.add('button-selected');
        ganttButtonElMonthTop.classList.remove('button-selected');
        ganttButtonElMonthBottom.classList.remove('button-selected');
      } else if (mode === 'Month') {
        ganttButtonElDayTop.classList.remove('button-selected');
        ganttButtonElDayBottom.classList.remove('button-selected');
        ganttButtonElWeekTop.classList.remove('button-selected');
        ganttButtonElWeekBottom.classList.remove('button-selected');
        ganttButtonElMonthTop.classList.add('button-selected');
        ganttButtonElMonthBottom.classList.add('button-selected');
      } else if (mode === 'Year') {
        ganttButtonElDayTop.classList.remove('button-selected');
        ganttButtonElDayBottom.classList.remove('button-selected');
        ganttButtonElWeekTop.classList.remove('button-selected');
        ganttButtonElWeekBottom.classList.remove('button-selected');
        ganttButtonElMonthTop.classList.remove('button-selected');
        ganttButtonElMonthBottom.classList.remove('button-selected');
      }
    },
    bar_height: 24,
    view_mode: 'Day',
    language: 'zh'
  });
});


注意事项

  • 此插件是用于演示如何开发插件的范例,才望子不予以保证可正常运行。

  • 不对此范例提供技术支持。

  • kintone的插件功能只可在标准版使用,简易版不可使用这点请大家注意一下。

  • 此范例推荐在Chrome、firefox中使用,在其他浏览器中的行为可能有异常。