# 使用文档

# 介绍

# 安装

npm install gantt-elastic-h --save # yarn add gantt-elastic-h # pnpm add gantt-elastic-h

新标签页中打开

点击查看代码
<!--
/**
 * @fileoverview GanttElastic standalone version component
 * @license MIT
 * @author Rafal Pospiech <neuronet.io@gmail.com>
 * @package
 */
-->
<template>
  <div>
    <gantt-elastic ref="elastic" :tasks="tasks" :options="options" :dynamicStyle="dynamicStyle">
      <div slot="header">
        <el-form size="mini" inline>
          <el-form-item label="显示模式:">
            <el-radio-group v-model="modeRadio">
              <el-radio :label="false">图表</el-radio>
              <el-radio :label="true">列表 + 图表</el-radio>
            </el-radio-group>
          </el-form-item>

          <el-form-item>
            <el-button @click="goNow">Go Now</el-button>
          </el-form-item>

          <el-form-item label="zoom-Y">
            <el-slider v-model="height" :min="7" :max="100" style="width: 200px"></el-slider>
          </el-form-item>
        </el-form>
      </div>
      <component v-if="components.footer" :is="components.footer" slot="footer"></component>

      <template slot="label" slot-scope="{ task }">
        <div>{{ task.label }}</div>
      </template>

      <template slot="operation" slot-scope="row">
        <el-button size="mini" @click="onUpdate(row)">修改</el-button>
      </template>
    </gantt-elastic>

    <el-dialog title="修改" :visible.sync="dialogFormVisible">
      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
        <el-form-item label="Description" prop="label">
          <el-input v-model="form.label" placeholder="Description"></el-input>
        </el-form-item>
        <el-form-item label="Assigned to" prop="user">
          <el-input v-model="form.user" placeholder="Assigned to"></el-input>
        </el-form-item>
        <el-form-item label="Start" prop="timeArr">
          <el-date-picker
            v-model="form.timeArr"
            type="daterange"
            placeholder="选择日期"
            format="yyyy 年 MM 月 dd 日"
            value-format="timestamp"
          >
          </el-date-picker>
        </el-form-item>
        <el-form-item label="progress" prop="progress">
          <el-input-number
            v-model="form.progress"
            controls-position="right"
            :min="0"
            :max="100"
            placeholder="progress"
          ></el-input-number>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="dialogFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="onSubmit">确 定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import dayjs from 'dayjs';
import GanttElastic from '../src/GanttElastic.vue';

function getDate(hours) {
  const currentDate = new Date();
  const currentYear = currentDate.getFullYear();
  const currentMonth = currentDate.getMonth() + 1;
  const currentDay = currentDate.getDate();
  const timeStamp = new Date(`${currentYear}-${currentMonth}-${currentDay} 00:00:00`).getTime();
  return new Date(timeStamp + hours * 60 * 60 * 1000).getTime();
}

export default {
  name: 'GanttElasticStandalone',
  components: {
    'gantt-elastic': GanttElastic,
  },
  props: ['header', 'footer'],
  data() {
    return {
      dialogFormVisible: false,
      form: {
        id: '',
        label: '',
        user: '',
        progress: '',
        timeArr: [],
      },
      rules: {
        label: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
        user: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
        progress: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
        timeArr: [{ required: true, message: '不能为空', trigger: ['blur', 'change'] }],
      },
      // modeRadio: '',

      components: {},
      tasks: [
        {
          id: 1,
          label: 'Make some noise',
          user: '<a href="https://www.google.com/search?q=John+Doe" target="_blank" style="color:#0077c0;">John Doe</a>',
          start: getDate(-24 * 5),
          duration: 15 * 24 * 60 * 60 * 1000,
          percent: 85,
          type: 'project',
          // collapsed: false,
        },
        {
          id: 2,
          label: 'With great power comes great responsibility',
          user: '<a href="https://www.google.com/search?q=Peter+Parker" target="_blank" style="color:#0077c0;">Peter Parker</a>',
          parentId: 1,
          start: getDate(-24 * 4),
          duration: 4 * 24 * 60 * 60 * 1000,
          percent: 50,
          type: 'milestone',
          collapsed: true,
          style: {
            base: {
              fill: '#1EBC61',
              stroke: '#0EAC51',
            },
            /*'tree-row-bar': {
              fill: '#1EBC61',
              stroke: '#0EAC51'
            },
            'tree-row-bar-polygon': {
              stroke: '#0EAC51'
            }*/
          },
        },
        {
          id: 3,
          label: 'Courage is being scared to death, but saddling up anyway.',
          user: '<a href="https://www.google.com/search?q=John+Wayne" target="_blank" style="color:#0077c0;">John Wayne</a>',
          parentId: 2,
          start: getDate(-24 * 3),
          duration: 2 * 24 * 60 * 60 * 1000,
          percent: 100,
          type: 'task',
        },
        {
          id: 4,
          label: 'Put that toy AWAY!',
          user: '<a href="https://www.google.com/search?q=Clark+Kent" target="_blank" style="color:#0077c0;">Clark Kent</a>',
          start: getDate(-24 * 2),
          duration: 2 * 24 * 60 * 60 * 1000,
          percent: 50,
          type: 'task',
        },
        {
          id: 5,
          label: 'One billion, gajillion, fafillion... shabadylu...mil...shabady......uh, Yen.',
          user: '<a href="https://www.google.com/search?q=Austin+Powers" target="_blank" style="color:#0077c0;">Austin Powers</a>',
          parentId: 4,
          start: getDate(0),
          duration: 2 * 24 * 60 * 60 * 1000,
          percent: 10,
          type: 'milestone',
          style: {
            base: {
              fill: '#0287D0',
              stroke: '#0077C0',
            },
          },
        },
        {
          id: 6,
          label: 'Butch Mario and the Luigi Kid',
          user: '<a href="https://www.google.com/search?q=Mario+Bros" target="_blank" style="color:#0077c0;">Mario Bros</a>',
          parentId: 5,
          start: getDate(24),
          duration: 1 * 24 * 60 * 60 * 1000,
          percent: 50,
          type: 'task',
          collapsed: true,
          style: {
            base: {
              fill: '#8E44AD',
              stroke: '#7E349D',
            },
          },
        },
        {
          id: 7,
          label: 'Devon, the old man wanted me, it was his dying request',
          user: '<a href="https://www.google.com/search?q=Knight+Rider" target="_blank" style="color:#0077c0;">Knight Rider</a>',
          parentId: 2,
          dependentOn: [6],
          start: getDate(24 * 2),
          duration: 4 * 60 * 60 * 1000,
          percent: 20,
          type: 'task',
          collapsed: true,
        },
        {
          id: 8,
          label: 'Hey, Baby! Anybody ever tell you I have beautiful eyes?',
          user: '<a href="https://www.google.com/search?q=Johhny+Bravo" target="_blank" style="color:#0077c0;">Johhny Bravo</a>',
          parentId: 7,
          dependentOn: [7],
          start: dayjs().startOf('day').valueOf(),
          end: dayjs().startOf('day').add(1, 'day').valueOf(),
          percent: 0,
          type: 'task',
        },
        {
          id: 9,
          label: 'This better be important, woman. You are interrupting my very delicate calculations.',
          user: '<a href="https://www.google.com/search?q=Dexter\'s+Laboratory" target="_blank" style="color:#0077c0;">Dexter\'s Laboratory</a>',
          parentId: 8,
          dependentOn: [8, 7],
          start: getDate(24 * 4),
          duration: 4 * 60 * 60 * 1000,
          percent: 20,
          type: 'task',
          style: {
            base: {
              fill: '#8E44AD',
              stroke: '#7E349D',
            },
            /*'tree-row-bar-polygon': {
              stroke: '#7E349D'
            },
            'tree-row-bar': {
              fill: '#8E44AD',
              stroke: '#7E349D'
            }*/
          },
        },
        {
          id: 10,
          label: 'current task',
          user: '<a href="https://www.google.com/search?q=Johnattan+Owens" target="_blank" style="color:#0077c0;">Johnattan Owens</a>',
          start: getDate(24 * 5),
          duration: 24 * 60 * 60 * 1000,
          percent: 0,
          type: 'task',
        },
        {
          id: 11,
          label: 'test task',
          user: '<a href="https://www.google.com/search?q=Johnattan+Owens" target="_blank" style="color:#0077c0;">Johnattan Owens</a>',
          start: getDate(24 * 6),
          duration: 24 * 60 * 60 * 1000,
          percent: 0,
          type: 'task',
        },
        {
          id: 12,
          label: 'test task',
          user: '<a href="https://www.google.com/search?q=Johnattan+Owens" target="_blank" style="color:#0077c0;">Johnattan Owens</a>',
          start: getDate(24 * 7),
          duration: 24 * 60 * 60 * 1000,
          percent: 0,
          type: 'task',
          parentId: 11,
        },
        {
          id: 13,
          label: 'test task',
          user: '<a href="https://www.google.com/search?q=Johnattan+Owens" target="_blank" style="color:#0077c0;">Johnattan Owens</a>',
          start: getDate(24 * 8),
          duration: 24 * 60 * 60 * 1000,
          percent: 0,
          type: 'task',
        },
        {
          id: 14,
          label: 'test task',
          user: '<a href="https://www.google.com/search?q=Johnattan+Owens" target="_blank" style="color:#0077c0;">Johnattan Owens</a>',
          start: getDate(24 * 9),
          end: getDate(24 * 12),
          percent: 0,
          type: 'task',
        },
        {
          id: 15,
          label: 'test task',
          user: '<a href="https://www.google.com/search?q=Johnattan+Owens" target="_blank" style="color:#0077c0;">Johnattan Owens</a>',
          start: getDate(24 * 16),
          duration: 24 * 60 * 60 * 1000,
          percent: 0,
          type: 'task',
        },
      ],
      options: {
        taskMapping: {
          progress: 'percent',
        },
        // maxRows: 2,
        // maxHeight: 500,
        title: {
          label: 'Your project title as html (link or whatever...)',
          html: false,
        },
        row: {
          height: 24,
        },
        calendar: {
          // hour: {
          //   // display: true,
          //   format: {
          //     short(date) {
          //       return '1';
          //     },
          //   },
          // },
        },
        chart: {
          progress: {
            bar: false,
          },
          expander: {
            display: true,
          },
        },
        taskList: {
          display: true,
          expander: {
            straight: false,
          },
          columns: [
            {
              id: 1,
              label: 'ID',
              value: 'id',
              width: 40,
            },
            {
              id: 2,
              label: 'Description',
              value: 'label',
              width: 200,
              expander: true,
              // html: true,
              slot: 'label',
              events: {
                click({ data, column }) {
                  alert('description clicked!\n' + data.label);
                },
              },
            },
            {
              id: 3,
              label: 'Assigned to',
              value: 'user',
              width: 130,
              html: true,
            },
            {
              id: 3,
              label: 'Start',
              value: (task) => dayjs(task.start).format('YYYY-MM-DD'),
              width: 78,
            },
            {
              id: 4,
              label: 'Type',
              value: 'type',
              width: 68,
            },
            {
              id: 5,
              label: '%',
              value: 'progress',
              width: 70,
              html: true,
              style: {
                'task-list-header-label': {
                  'text-align': 'center',
                  width: '100%',
                },
                'task-list-item-value-container': {
                  'text-align': 'center',
                  width: '100%',
                },
              },
            },
            {
              id: 6,
              label: '操作',
              value: 'operation',
              slot: 'operation',
              width: 80,
              style: {
                'task-list-header-label': {
                  'text-align': 'center',
                  width: '100%',
                },
                'task-list-item-value-container': {
                  'text-align': 'center',
                  width: '100%',
                },
              },
            },
          ],
        },
      },
      dynamicStyle: {},
    };
  },

  computed: {
    modeRadio: {
      get() {
        return this.options.taskList.display;
      },
      set(val) {
        console.log(val);
        this.options.taskList.display = val;
      },
    },

    height: {
      get() {
        console.log(this.options.row.height);
        return this.options.row.height;
      },
      set(value) {
        const elastic = this.$refs.elastic;
        elastic.$emit('row-height-change', Number(value));
      },
    },
  },

  methods: {
    onUpdate(row) {
      console.log(row);
      Object.keys(this.form).forEach((item) => {
        if (item === 'timeArr') {
          this.form[item] = [row.task['startTime'], row.task['endTime']];
        } else {
          this.form[item] = row.task[item];
        }
      });
      this.dialogFormVisible = true;
    },

    onSubmit() {
      const elastic = this.$refs.elastic;
      this.$refs['form'].validate((valid) => {
        if (valid) {
          const time = this.form.timeArr;
          elastic.updateTask(this.form.id, this.form);
          elastic.updateTaskTime(this.form.id, time[0], time[1]);
          this.dialogFormVisible = false;
          console.log(this.tasks);
        } else {
          console.log('error submit!!');
          return false;
        }
      });
    },

    goNow(type) {
      const elastic = this.$refs.elastic;
      elastic.goCurrentTime();
    },
  },
};
</script>

# 配置

# props

参数 说明 类型 可选值 默认值
tasks 任务列表 Object -- --
options 配置项
dynamicStyle 自定义样式设置,样式字典 (opens new window) Object -- --

# tasks

任务列表

参数 说明 类型 可选值 默认值
parentId 父 ID -- 用于生成树 Number / String -- --
type 任务类型, 可根据 options.taskMapping 修改字段名 必填 String milestone,task,project --
start 开始时间, 可根据 options.taskMapping 修改字段名 必填 date -- --
duration 任务持续时间(时间戳),可根据 options.taskMapping 修改字段名 必填 Number -- --
progress 任务完成百分比 ,可根据 options.taskMapping 修改字段名 必填 Number -- --
collapsed 是否默认收起,可根据 options.taskMapping 修改字段名 必填 Boolean -- false
style 当前任务,样式设置。参考 (opens new window)样式字典 (opens new window) Object -- --

# options

配置项

参数 说明 类型 可选值 默认值
taskList 左侧列表相关配置 Object -- --
taskMapping 组件需要用到的字段枚举。具体看下表 Object -- --
scroll 内容滚动相关配置。具体看下表 Object -- --
scope 右侧图表前后日期范围。具体看下表 Object -- --
times 右侧图表时间相关配置。具体看下表 Object -- --
row 左侧列表,行配置。具体看下表 Object -- --
maxRows 最多显示行数 Number -- 20
maxHeight 最大高度 (maxHeight 权重大于 MaxRows, maxHeight = 0 则不考虑该参数) Number -- 0
chart 右侧图表内容相关配置。具体看下表 Object -- --
calendar 右侧图表日历(日期)相关配置。具体看下表 Object -- --
locale i18n(语言包)配置,和 dayjs 的语言包一样; Object -- --

# options.taskList

左侧列表相关配置

参数 说明 类型 可选值 默认值
display 是否显示左侧任务列表 Boolean -- true
widthThreshold -- Number -- --
columns 任务列表 -- 列配置, 详细配置见下表 Arrary -- [{
id: 0,
label: 'ID',
value: 'id',
width: 40,
}]
percent -- Number -- 100
minWidth 列最小宽度 Number -- 18
expander 任务列表 -- 折叠按钮配置,详细配置见下表 Object -- {
type: 'task-list',
size: 50,
columnWidth: 24,
padding: 16,
margin: 10,
straight: false,
}

# options.taskList.columns

左侧列表 ,列配置

参数 说明 类型 可选值 默认值
id
label 显示的标题 String -- --
value 对应列内容的字段名 String -- --
width 列宽度 Number -- 40
html 解析为 html Boolean -- false
slot 自定义 Vue 插槽, slot 权重大于 html String -- --
expander 是否显示折叠按钮 Boolean -- false
events 事件注册 Object -- --
style 列样式 -- 参考 (opens new window) -- 字典 (opens new window) Object -- --

# options.taskList.expander

左侧列表,折叠按钮配置

参数 说明 类型 可选值 默认值
size 按钮大小 Number -- 16
padding 子节点与父节点之间的缩进距离 Number -- 16
margin 整体与左侧的间隔距离 Number -- 10

# options.taskMapping

组件需要用到的字段枚举

参数 说明 类型 可选值 默认值
id 任务 id String -- id
start 任务开始时间 date -- start
label 任务名称 String -- label
duration 任务持续时间 date -- duration
progress 任务完成进度 Number -- progress
type 任务类型 String -- type
style 任务样式,具体看下表 Object -- style
collapsed 是否默认展开 Boolean -- collapsed

# options.scroll

内容滚动相关配置

参数 说明 类型 可选值 默认值
dragXMoveMultiplier 推拽速度,X 轴 Number -- 3
dragYMoveMultiplier 推拽速度,Y 轴 Number -- 2

# options.scope

右侧图表前后日期范围

参数 说明 类型 可选值 默认值
before 前置天数间隔 Number -- 1
after 后置天数间隔 Number -- 1

# options.times

右侧图表时间相关配置

参数 说明 类型 可选值 默认值
timeZoom 时间默认缩放层级 Number 1~21 17

# options.row

左侧列表,行配置

参数 说明 类型 可选值 默认值
height 行高度 Number -- 24

# options.chart

右侧图表内容相关配置

参数 说明 类型 可选值 默认值
grid 网格配置 Object -- --
text 任务条右侧文本配置。具体看下表 Object -- --
expander 图表左侧折叠按钮配置 Object -- --

# options.chart.text

任务条右侧文本配置

参数 说明 类型 可选值 默认值
offset 文本左侧偏移量 Number -- 4
xPadding 文本内容内边距 Number -- 10
display 是否显示文本 Boolean -- true

# options.chart.expander

图表左侧折叠按钮配置

参数 说明 类型 可选值 默认值
display 是否显示折叠按钮 Boolean -- false
offset 距离右侧偏移量 Number -- 12
size 按钮大小 Number -- 18

# options.calendar

右侧图表日历(日期)相关配置

参数 说明 类型 可选值 默认值
workingDays 工作日配置 Array [1, 2, 3, 4, 5, 6, 7] [1, 2, 3, 4, 5]
gap 表格与表头之间的间距 Number -- 6
hour 表头时间节点行配置 Object -- --
day 表头日期节点行配置 Object -- --
month 表头月份节点行配置 Object -- --

# options.calendar.hour

表头时间节点行配置

参数 说明 类型 可选值 默认值
height 高度 Number -- 20
display 是否显示时间 Boolean -- true
format 时间显示内容格式。具体看下表

# options.calendar.day

表头日期节点行配置

参数 说明 类型 可选值 默认值
height 高度 Number -- 20
display 是否显示时间 Boolean -- true
format 时间显示内容格式。具体看下表

# options.calendar.month

表头月份节点行配置

参数 说明 类型 可选值 默认值
height 高度 Number -- 20
display 是否显示时间 Boolean -- true
format 时间显示内容格式。具体看下表

# format

date 为 dayjs 实例

参数 说明 类型 可选值 默认值
long 缩放层级,小 Function(date) -- --
medium 缩放层级,中 Function(date) -- --
short 缩放层级,大 Function(date) -- --

# Methods

方法名称 说明 参数
updateTask 更新任务, 该方法不能更新任务时间,如需更新时间使用 updateTaskTime
taskid: 任务 ID;
data: 修改后的数据;
Function(taskId, data, props = {})
updateTaskTime 更新任务时间;
taskid: 任务 ID;
start: 开始时间;
end:结束时间
Function(taskId, start, end)
getTask 根据任务 id,获取任务数据; Function(taskId)
getChildren 根据任务 id,获取当前任务的子任务; Function(taskId)
goCurrentTime 跳转到当前时间线 Function()

# Events

事件名称 说明 回调参数
calendar-recalculate 当大小发生变化(resize)触发 --
tasks-changed 任务数据(tasks)发生变化触发 tasks
options-changed 配置项(options)发生变化触发 options
dynamic-style-changed 样式发生变化触发 style
created created 组件实例
before-mount before-mount 组件实例
ready ready 组件实例
mounted mounted 组件实例
before-update before-update
updated updated
before-destroy before-destroy
destroyed destroyed
main-view-mousemove 鼠标移动 event
main-view-mouseup 鼠标松开 event
chart-scroll-horizontal 水平滚动事件 event
chart-scroll-vertical 垂直滚动事件 event
chart-wheel 鼠标滚轮在元素上下滚动时触发 event
chart-task-click 任务条事件,对应 Html Dom 事件 {...event, data: task}
chart-task-dblclick 同上 同上
chart-task-mouseenter 同上 同上
chart-task-mouseleave 同上 同上
chart-task-mouseover 同上 同上
chart-task-mouseout 同上 同上
chart-task-mousemove 同上 同上
chart-task-mousedown 同上 同上
chart-task-mouseup 同上 同上
chart-task-mousewheel 同上 同上
chart-task-touchstart 同上 同上
chart-task-touchmove 同上 同上
chart-task-touchend 同上 同上
task-list-click 表格行事件,对应 Html Dom 事件 {...event, data: task, column: column}
task-list-dblclick 同上 同上
task-list-mouseenter 同上 同上
task-list-mouseover 同上 同上
task-list-mouseout 同上 同上
task-list-mousemove 同上 同上
task-list-mousedown 同上 同上
task-list-mouseup 同上 同上
task-list-mousewheel 同上 同上
task-list-touchstart 同上 同上
task-list-touchmove 同上 同上
task-list-touchend 同上 同上