<template>
  <div
    class="app-container"
    style="width: 100%"
    :style="{
      maxHeight: customTableMaxHeight && customTableMaxHeight + 'px',
      height: tableMaxHeight,
    }"
    @mouseleave="mousemove"
  >
    <div ref="gantt" class="left-container" />
  </div>
</template>
<script>
  import gantt from 'dhtmlx-gantt' // 引入模块
  import 'dhtmlx-gantt/codebase/dhtmlxgantt.css'
  import 'dhtmlx-gantt/codebase/skins/dhtmlxgantt_terrace.css' //皮肤
  import 'dhtmlx-gantt/codebase/locale/locale_cn' // 本地化
  import 'dhtmlx-gantt/codebase/ext/dhtmlxgantt_tooltip.js' //任务条悬浮提示
  import { debounce } from 'lodash'
  const { Base64 } = require('js-base64')
  export default {
    props: {
      // 需要渲染的数据
      data: Array,
      ganttConfig: {
        type: String,
        default: '',
      },
      tableMaxHeight: {
        default: '400px',
      },
      //自定义gantt图最大高度
      customTableMaxHeight: {
        type: [String, Number],
      },
      bpmDataTemplate: Object,
      managePermission: {
        type: String,
        default: '',
      },
      pkField: {
        type: String,
        default: 'id_',
      },
    },
    // name: 'Gantt',
    data() {
      return {
        tasks: {
          data: [],
        },
        ganttConObj: {},
        btnPermission: {},
        btnMap: {},
      }
    },
    watch: {
      data: {
        handler(val) {
          this.renderData()
        },
        deep: true,
        immediate: true,
        showFields: [],
      },
    },
    created() {
      let this_ = this

      if (this.bpmDataTemplate.manageField) {
        const btns = JSON.parse(this.bpmDataTemplate.manageField)
        btns.forEach((b) => {
          this.btnMap[b.name] = b
        })
      }

      window.handleDelete = function (id) {
        const manage = this_.btnMap.del
        this_.$emit('gantt-btn-click', 'del', manage.afterScriptValue, {
          pkField: id,
        })
      }

      window.handleEdit = function (id, isStartFlow) {
        const manage = this_.btnMap.edit
        this_.$emit('gantt-btn-click', 'edit', manage.afterScriptValue, {
          templateId: this_.bpmDataTemplate.id,
          id: id,
          action: 'edit',
          defId: this_.bpmDataTemplate.defId,
          isStartFlow: isStartFlow,
          openType: manage.openType,
          quitAfterSaving: manage.quitAfterSaving,
          dialogWidth: manage.dialogWidth,
          dialogHeight: manage.dialogHeight,
        })
      }
    },
    beforeDestroy() {
      this.mousemove()
    },
    mounted() {
      if (this.ganttConfig) {
        this.ganttConObj = JSON.parse(Base64.decode(this.ganttConfig))
      }
      if (this.managePermission) {
        this.btnPermission = JSON.parse(Base64.decode(this.managePermission))
      }
      this.initGantt()
    },
    methods: {
      mousemove: debounce(function () {
        this.$nextTick(() => {
          const myNodeList = document.querySelectorAll('.gantt_tooltip')
          if (myNodeList.length > 0) {
            setTimeout(() => {
              myNodeList.forEach((value) => {
                document.body.removeChild(value)
              })
            }, 300)
          }
        })
      }, 300),
      percentToFixed(progress, fixNum = 0) {
        if (!progress && progress !== 0 && progress !== '0') {
          return ''
        }
        if (progress === 0 || progress === '0') {
          return '0%'
        }
        return Number((progress * 100).toFixed(fixNum)) + '%'
      },
      formartPercentByConfig(progress) {
        if (!progress && progress !== 0 && progress !== '0') {
          return ''
        }

        let result = progress
        if (progress.constructor != Number) {
          result = result.replaceAll('%', '')
        }
        if (!this.ganttConObj.process100) {
          result = Number(result) / 100
        }
        return result
      },
      originalPercentByConfig(progress) {
        if (!progress && progress !== 0 && progress !== '0') {
          return ''
        }
        let result = progress

        if (!this.ganttConObj.process100) {
          result = Number((result * 100).toFixed(0))
        } else {
          result = Number(result.toFixed(2))
        }

        return result
      },
      initGantt: function () {
        let this_ = this
        //自适应甘特图的尺寸大小, 使得在不出现滚动条的情况下, 显示全部任务
        gantt.config.autosize = false

        //只读模式
        if (!this.btnPermission.edit) {
          gantt.config.readonly = true
          gantt.config.drag_progress = false
        } else {
          gantt.config.readonly = false
          // 拖动进度
          gantt.config.drag_progress = true
        }
        gantt.config.autofit = true
        //是否显示左侧树表格
        gantt.config.show_grid = true
        //表格列设置
        let columns = []
        gantt.config.scale_height = 64

        let startFiled = {
          name: 'start_date',
          label: this.ganttConObj.startTimeField.desc,
          width: 100,
          align: 'center',
          template: function (task) {
            if (task[this_.ganttConObj.startTimeField.name]) {
              return task['start_date']
            }
            return '<div style="border:1px solid red;width:100%;height:calc(100% - 2px)"> </div>'
          },
        }
        let completeTimeField = {
          name: 'end_date',
          label: this.ganttConObj.completeTimeField.desc,
          width: 100,
          align: 'center',
          template: function (task) {
            if (task[this_.ganttConObj.completeTimeField.name]) {
              return task['end_date']
            }
            return '<div style="border:1px solid red;width:100%;height:calc(100% - 2px)"> </div>'
          },
        }
        let processField = null
        if (this.ganttConObj.processField) {
          processField = {
            name: 'SpeedProgress',
            label: this.ganttConObj.processField.desc,
            resize: true,
            width: 100,
            align: 'center',
            template: function (task) {
              return this_.percentToFixed(task.progress)
            },
          }
        }
        if (this.ganttConObj.tipFields) {
          this.showFields = JSON.parse(this.ganttConObj.tipFields)
          this.showFields.forEach((t) => {
            let path = t.tableName
            if (t.tableName.toLowerCase().startsWith('w_')) {
              path += 'f_'
            }
            path = path.replaceAll('_', '').toLowerCase()
            // 处理关联字表的情况。关联字表的name居然是拼接好的。改了怕影响别的功能，此处处理下
            if (t.na.toLowerCase().indexOf(path) > -1) {
              path = t.na
            } else {
              path += t.na
            }
            path = path.replaceAll('_', '').toLowerCase()
            t.path = path
            t.width = t.width || '120'
            t.cp = t.cp || ''
            if (t.cp.indexOf('1') > -1) {
              if (t.path == this.ganttConObj.startTimeField.name) {
                startFiled.width = t.width
                startFiled.label = t.cm
              } else if (t.path == this.ganttConObj.completeTimeField.name) {
                completeTimeField.width = t.width
                completeTimeField.label = t.cm
              } else if (
                processField &&
                t.path == this.ganttConObj.processField.name
              ) {
                processField.width = t.width
                processField.label = t.cm
              } else {
                columns.push({
                  name: t.path,
                  label: this.$td(
                    `formTemplate.${this.bpmDataTemplate.alias}.gantt.${t.name}`,
                    t.cm
                  ),
                  width: t.width,
                  align: 'center',
                })
              }
            }
          })
        }

        columns.push(startFiled)
        columns.push(completeTimeField)

        if (this.ganttConObj.processField) {
          columns.push(processField)
        }

        if (this.btnPermission.del || this.btnPermission.edit) {
          let width = 50
          if (this.btnPermission.del && this.btnPermission.edit) {
            width = 80
          }
          columns.push({
            name: 'action',
            width: width,
            align: 'center',
            label: '操作',
            template: function (task) {
              let actionStr = ''
              if (this_.btnPermission.edit) {
                actionStr +=
                  '<el-button class="GanttMain-Button" style="cursor: pointer;padding-right: 7px;font-size: 14px;" type="text" onClick="handleEdit(\'' +
                  task.openId +
                  "','" +
                  task.isStartFlow +
                  '\')">编辑</el-button>'
              }

              if (this_.btnPermission.del) {
                actionStr +=
                  '<el-button class="GanttMain-Button" style="cursor: pointer;font-size: 14px;" type="text" onClick="handleDelete(\'' +
                  task.openId +
                  '\')">删除</el-button>'
              }

              return actionStr
            },
          })
        }

        gantt.attachEvent(
          'onTaskDblClick',
          function (id, e) {
            if (this_.btnPermission.edit) {
              const editTask = this_.tasks.data.filter((d) => d.id == id)
              if (editTask && editTask.length == 1) {
                e.preventDefault()
                handleEdit(editTask[0].openId, editTask[0].isStartFlow)
              }
            }
            e.preventDefault()
            return false
          },
          { once: true, id: 'my-db-click' }
        )

        gantt.config.columns = columns

        //箭头线宽
        gantt.config.link_line_width = 1
        //时间轴图表中，如果不设置，只有行边框，区分上下的任务，设置之后带有列的边框，整个时间轴变成格子状。
        gantt.config.show_task_cells = true

        //设置x轴日期
        gantt.config.scale_unit = 'month'
        gantt.config.step = 1
        gantt.config.date_scale = '%Y' + '年' + '%M'

        //当task的长度改变时，自动调整图表坐标轴区间用于适配task的长度
        gantt.config.fit_tasks = true
        // 在时间线上增加一行显示星期
        gantt.config.subscales = [
          //   { unit: "day", step: 6, date: "星期" + "%d" },
          { unit: 'day', step: 1, template: this.template },
        ]

        // 显示进度
        gantt.config.show_progress = true
        //进度文本
        gantt.templates.progress_text = function (start, end, task) {
          return ''
          // this_.percentToFixed(task.progress)
        }

        //时间轴图表中，任务条形图的高度
        gantt.config.task_height = 20
        //时间轴图表中，甘特图的高度
        gantt.config.row_height = 36
        //从后端过来的数据格式化
        gantt.config.xml_date = '%Y-%m-%d'
        //任务条显示内容
        gantt.templates.task_text = function (start, end, task) {
          return ''
          // (
          //   (task[this_.ganttConObj.taskField.name] || '') +
          //   '(' +
          //   task.duration +
          //   '天)'
          // )
        }
        // gantt.templates.task_class = function(start, end, task){return "xx";};
        //悬浮
        gantt.templates.tooltip_text = function (start, end, task) {
          let tipStr = ''
          this_.showFields.forEach((t) => {
            if (t.cp && t.cp.indexOf('2') > -1) {
              let value = task[t.path]
              if (
                this_.ganttConObj.processField &&
                t.path == this_.ganttConObj.processField.name
              ) {
                value = this_.percentToFixed(task.progress)
              }
              value = value || ''
              tipStr += '<b>' + t.cm + ':</b> ' + value + '<br/>'
            }
          })

          return tipStr

          // return "<span style='font-size: 14px'>" + task.toolTipsTxt + '</span>'
        }

        gantt.templates.scale_cell_class = function (date) {
          /*if(date.getDay()== 0 || date.getDay()== 6){
            return "weekend";
          }*/
          return 'weekend'
        }
        //任务栏周末亮色
        /*gantt.templates.task_cell_class = function(item,date){
          if(date.getDay()== 0 || date.getDay()== 6){
            return "weekend";
          }
        };*/

        var dp = gantt.createDataProcessor({
          task: {
            create: function (data) {},
            update: function (data, id) {
              data[this_.ganttConObj.startTimeField.name] = data['start_date']

              data[this_.ganttConObj.completeTimeField.name] = data['end_date']
              if (this_.ganttConObj.taskField) {
                data[this_.ganttConObj.taskField.name] = data['text']
              }
              if (this_.ganttConObj.processField) {
                data[this_.ganttConObj.processField.name] =
                  this_.originalPercentByConfig(data['progress'])
              }

              const originalRow = this_.data.filter(
                (d) => d[this_.pkField] == data[this_.pkField]
              )

              if (originalRow && originalRow.length > 0) {
                let hasChange = false
                for (const key in originalRow[0]) {
                  if (originalRow[0][key] != data[key]) {
                    hasChange = true
                  }
                }
                if (hasChange) {
                  this_.$emit('gantt-task-update', data, id)
                }
              }
            },
            delete: function (id) {
              this_.$emit('gantt-task-delete', id)
            },
          },
          link: {
            create: function (data) {},
            update: function (data, id) {},
            delete: function (id) {},
          },
        })

        // 初始化
        gantt.init(this.$refs.gantt)
      },
      template(data) {
        let Year = new Date(data).getFullYear()
        let Month = new Date(data).getMonth() + 1
        var Monthdate = new Date(Year, Month, 0)

        let nowDate = new Date(data).getDate()
        let afterDate = Number(nowDate + 6)
        if (afterDate < Monthdate.getDate()) {
          return nowDate + '日'
        } else {
          return nowDate + '日'
        }
      },
      //开始时间-结束时间参数
      DateDifference: function (strDateStart, strDateEnd) {
        var begintime_ms = Date.parse(new Date(strDateStart.replace(/-/g, '/'))) //begintime 为开始时间
        var endtime_ms = Date.parse(new Date(strDateEnd.replace(/-/g, '/'))) // endtime 为结束时间
        var date3 = endtime_ms - begintime_ms //时间差的毫秒数
        var days = Math.floor(date3 / (24 * 3600 * 1000))
        return days
      },
      renderData: function () {
        let tempData = JSON.parse(JSON.stringify(this.data || []))
        let this_ = this
        let preMap = {}
        let preField = ''
        if (this_.ganttConObj.preTaskField) {
          preField = this_.ganttConObj.preTaskField.name
        }
        tempData.forEach((t) => {
          t.openId = t[this_.pkField]
          // 双击监听事件onTaskDblClick只返回了id，需要根据id找到编辑的那条数据，所以此处先给每条数据赋上id属性和值。
          // 主键有值就使用主键的值，没有就使用uuid
          t.id = t.openId || this_.$Formulas.UUID()
          t.start_date = t[this_.ganttConObj.startTimeField.name]
          t.end_date = t[this_.ganttConObj.completeTimeField.name]

          if (this_.ganttConObj.processField) {
            t.progress = this_.formartPercentByConfig(
              t[this_.ganttConObj.processField.name]
            )
          }

          if (this_.ganttConObj.taskField) {
            t.text = t[this_.ganttConObj.taskField.name]
          }
          if (preField && t[preField]) {
            preMap[t[preField]] = preMap[t[preField]] || []
            preMap[t[preField]].push(t.id)
          }
        })
        this.$set(this.tasks, 'data', tempData)

        //任务条上的文字大小 以及取消border自带样式
        gantt.templates.task_class = function (start, end, task) {
          let classStr = ''
          if (
            !task[this_.ganttConObj.startTimeField.name] ||
            !task[this_.ganttConObj.completeTimeField.name]
          ) {
            classStr += ' hide_task_text '
          }
          return task.$level == 0
            ? classStr + 'firstLevelTask'
            : classStr + 'secondLevelTask'
        }

        gantt.clearAll()
        // 数据解析
        gantt.parse(this.tasks)

        for (const key in preMap) {
          preMap[key].forEach((k) => {
            gantt.addLink({
              id: key + k,
              source: key,
              target: k,
              type: 1,
            })
          })
        }
      },
    },
  }
</script>
<style lang="scss">
  .firstLevelTask {
    border: none;
    .gantt_task_content {
      // font-weight: bold;
      font-size: 13px;
    }
  }

  .secondLevelTask {
    border: none;
  }

  .thirdLevelTask {
    border: 2px solid #da645d;
    color: #da645d;
    background: #da645d;
  }

  .milestone-default {
    border: none;
    background: rgba(0, 0, 0, 0.45);
  }

  .milestone-unfinished {
    border: none;
    background: #5692f0;
  }

  .milestone-finished {
    border: none;
    background: #84bd54;
  }

  .milestone-canceled {
    border: none;
    background: #da645d;
  }

  html,
  body {
    margin: 0px;
    padding: 0px;
    height: 100%;
    overflow: hidden;
  }

  .container {
    height: 900px;
    .left-container {
      height: 100%;
    }
  }

  .left-container {
    height: 100%;
    // max-height: fit-content;
    // min-height: 560px;
  }

  .gantt_scale_line {
    height: 32px !important;
    line-height: 32px !important;
  }

  .weekend {
    //background:#f4f7f4!important;
    color: #000000 !important;
  }

  .gantt_selected .weekend {
    background: #f7eb91 !important;
  }

  .gantt_task_content {
    //text-align: center;
    //padding-left: 10px;

    color: #262626;
    font-size: 12px;
    width: auto;
    background: #d1e8ff;
    width: 100%;
  }

  .gantt_task_progress {
    color: #fff !important;
    text-align: left;
    // background: rgba(0, 0, 0, 0.15) !important;
    background: #409eff !important;
    opacity: 1 !important;
  }
  .gantt_task_progress_wrapper {
    z-index: 1;
  }

  .gantt_delete_btn_set {
    display: none;
  }

  .gantt_save_btn_set {
    margin-left: 360px !important;
    background-color: #409eff !important;
  }

  .gantt_tree_content > i {
    padding: 10px;
  }
  .gantt_tree_content {
    width: 100%;
    height: 100%;
  }
  .gantt_scale_cell {
    color: #262626;
    font-size: 14px;
    height: 32px !important;
    line-height: 32px !important;
  }
  .gantt_link_point,
  .dhtmlx_modal_box,
  .dhx_modal_cover {
    display: none !important;
  }
</style>
<style>
  .dhtmlx_message_area .gantt-error {
    position: relative;
    background-color: transparent;
    border: none;
    box-shadow: none;
  }
  .dhtmlx_message_area .gantt-error::before {
    position: absolute;
    top: 15px;
    left: 10px;
    content: '';
    width: 12px;
    height: 12px;
    background-image: url(../image/bg-warning.png);
    background-size: 12px 12px;
    background-repeat: no-repeat;
  }
  .dhtmlx_message_area .gantt-error div {
    padding: 14px 26px 14px 26px;
    border-radius: 8px;
    border: none;
    box-sizing: border-box;
    background-color: #fff7ec;
    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
    color: #ffae44;
    border-color: transparent;
  }

  .hide_task_text {
    display: none;
  }
</style>
