<template>
  <div class="dingflow-design" @mousedown.prevent="mousedown" @mousemove.prevent="mousemove">
    <div class="dingflow-design-header">
      <div class="img" @click="openFIle" v-show="!readonly">
        <img src="@/assets/img/import.png" alt="">
      </div>
      <div class="img" @click="downloadProcessAsXml" v-show="!readonly">
        <img src="@/assets/img/down.png" alt="">
      </div>
      <div class="img" @click="previewProcessXML" v-show="!readonly">
        <img src="@/assets/img/preview.png" alt="">
      </div>
      <span class="line" v-show="!readonly"></span>
      <div class="zoom">
        <div class="img" :class="'zoom-out'+ (nowVal === 50?' disabled':'')" @click="zoomSize(1)"><img src="@/assets/img/reduce.png" alt=""></div>
        <span>{{ nowVal }}%</span>
        <div class="img" :class="'zoom-in'+ (nowVal === 300?' disabled':'')" @click="zoomSize(2)"><img src="@/assets/img/add.png" alt=""></div>
      </div>
    </div>
    <div class="design-scroll" ref="scroll">
      <div v-if="nodeConfig && onload" class="box-scale" id="box-scale" :key="nowVal/100" :style="'transform: scale('+nowVal/100+'); transform-origin: 50% 0px 0px;'">
        <nodeWrap :nodeConfig.sync="nodeConfig" :bpmnModeler="bpmnModeler" @elementClick="elementClick" :readonly="readonly"></nodeWrap>
        <div class="end-node" :class="{active: finishedTaskSet.indexOf(endId) > -1}">
          <div class="end-node-circle"></div>
          <div class="end-node-text">流程结束</div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
import { treeDataTranslate } from '@/utils'
import { SysCommonBizController } from '@/api'
import { mapGetters, mapMutations } from 'vuex'

export default {
  props: {
    bpmnModeler: {
      type: Object
    },
    readonly: {
      type: Boolean,
      default: false
    },
    finishedInfo: {
      type: Object
    },
    downloadProcessAsXml: {
      type: Function,
      default: () => {}
    },
    previewProcessXML: {
      type: Function,
      default: () => {}
    },
    openFIle: {
      type: Function,
      default: () => {}
    }
  },
  provide () {
    return {
      bpmnModeler: this.bpmnModeler,
      groupMap: this.groupMap,
      deptPostMap: this.deptPostMap,
      postMap: this.postMap,
      roleMap: this.roleMap,
      finishedSequenceFlowSet: this.finishedSequenceFlowSet,
      finishedTaskSet: this.finishedTaskSet,
      unfinishedTaskSet: this.unfinishedTaskSet
    }
  },
  data () {
    return {
      nodeConfig: null,
      nowVal: 100,
      users: [],
      roleList: [],
      roleMap: new Map(),
      groupList: [],
      groupMap: new Map(),
      deptPostMap: new Map(),
      deptPostList: [],
      postMap: new Map(),
      postList: [],
      onload: false,
      isDown: false,
      top: 0,
      left: 0,
      dx: 0,
      dy: 0,
      hasElementIds: [],
      endId: ''
    }
  },
  computed: {
    ...mapGetters(['getUserShowNameData']),
    finishedSequenceFlowSet () {
      return this.finishedInfo ? this.finishedInfo.finishedSequenceFlowSet : []
    },
    finishedTaskSet () {
      return this.finishedInfo ? this.finishedInfo.finishedTaskSet : []
    },
    unfinishedTaskSet () {
      return this.finishedInfo ? this.finishedInfo.unfinishedTaskSet : []
    }
  },
  mounted () {
    this.endId = this.bpmnModeler.get('elementRegistry').find(el => el.type === 'bpmn:EndEvent').id
    window.addEventListener('mouseup', this.mouseup)
    let httpCall = [
      this.loadDeptWidgetDropdownList(),
      this.loadDeptPostList(),
      this.loadSysPostList(),
      this.loadSysRoleList()
    ]
    Promise.all(httpCall).then(() => {
      this.initData()
      this.getUserShowName().then(() => {
        this.onload = true
      })
    })
  },
  destroyed () {
    window.removeEventListener('mouseup', this.mouseup)
  },
  methods: {
    zoomSize (type) {
      if (type === 1) {
        if (this.nowVal === 50) return
        this.nowVal -= 10
      } else {
        if (this.nowVal === 300) return
        this.nowVal += 10
      }
    },
    refresh () {
      this.endId = this.bpmnModeler.get('elementRegistry').find(el => el.type === 'bpmn:EndEvent').id
      this.initData()
      this.getUserShowName()
    },
    initData () {
      const StartEvent = this.bpmnModeler.get('elementRegistry').find(el => el.type === 'bpmn:StartEvent')
      const nodeConfig = {
        nodeName: StartEvent.outgoing[0]?.target?.businessObject?.name || '发起人',
        type: this.FlowNodeType.ORIGINATOR,
        element: StartEvent.outgoing[0]?.target,
        groupType: '',
        nodeUserList: [],
        childNode: null
      }
      nodeConfig.nodeUserList = this.getNodeUserList(StartEvent.outgoing[0].target, nodeConfig)
      this.setNodeConfig(nodeConfig)
      this.nodeConfig = nodeConfig
    },
    // 设置节点数据
    setNodeConfig (nodeConfig, childElement) {
      let childNode = {}
      const element = childElement || (nodeConfig.type === this.FlowNodeType.CONNECTING_LINE ? nodeConfig.element : nodeConfig.element.outgoing[0])
      if (element?.target.type === 'bpmn:UserTask' && (element.target.incoming.length === 1 || childElement)) {
        let formKey = {}
        try {
          formKey = JSON.parse(element?.target?.businessObject?.formKey)
        } catch (error) {
        }
        childNode.nodeName = element.target?.businessObject?.name || '审核人'
        childNode.type = this.FlowNodeType.APPROVED_BY
        childNode.groupType = formKey ? formKey.groupType : 'ASSIGNEE'
        childNode.element = element.target
        childNode.nodeUserList = this.getNodeUserList(element.target, childNode)
        this.setNodeConfig(childNode)
      } else if (((element?.target?.type === 'bpmn:ExclusiveGateway' || element?.target?.type === 'bpmn:ParallelGateway') && element.target.outgoing.length > 1) && (element.target.incoming.length === 1 || childElement)) {
        childNode = this.setConditionData(nodeConfig, childElement)
      } else {
        childNode = null
      }
      nodeConfig.childNode = childNode
    },
    // 设置分支条件数据
    setConditionData (nodeConfig, childElement) {
      const childNode = {}
      const conditionElement = childElement?.target || (nodeConfig.type === this.FlowNodeType.CONNECTING_LINE ? nodeConfig.element.target : nodeConfig.element.outgoing[0].target)
      childNode.nodeName = conditionElement?.businessObject?.name || (conditionElement.type === 'bpmn:ExclusiveGateway' ? '网关' : '并行网关')
      childNode.type = conditionElement.type === 'bpmn:ExclusiveGateway' ? this.FlowNodeType.CONDITIONAL_BRANCH : this.FlowNodeType.PARALLEL_BRANCH
      childNode.element = conditionElement
      childNode.conditionNodes = []
      childNode.childNode = null
      conditionElement.outgoing.forEach((element, index) => {
        let extensionElements = element.businessObject.get('extensionElements') || this.bpmnModeler.get('moddle').create('bpmn:ExtensionElements', { values: [] })
        let customCondition = (extensionElements.values || []).filter(ex => ex.$type === 'flowable:CustomCondition' || ex.$type === 'flowable:customCondition')[0]
        let conditionStr = ''
        if (conditionElement.type === 'bpmn:ParallelGateway') {
          conditionStr = '并行任务（同时进行）'
        } else if (element.source.businessObject?.default?.id === element?.id) {
          conditionStr = '默认流转路径'
        } else if (customCondition && customCondition.type === 'operation') {
          conditionStr = '内置按钮'
        } else if (!element.businessObject.conditionExpression) {
          conditionStr = '普通流转路径'
        } else {
          conditionStr = '条件流转路径'
        }
        let priorityLevel = index
        element?.businessObject?.extensionElements?.values && element.businessObject.extensionElements.values.forEach(row => {
          if (row.$type === 'flowable:Properties' || row.$type === 'flowable:properties') {
            (row.values || row.$children).forEach(item => {
              if (item.name === 'priorityLevel') {
                priorityLevel = item.value
              }
            })
          }
        })
        let obj = {
          nodeName: element?.businessObject?.name || '',
          type: this.FlowNodeType.CONNECTING_LINE,
          priorityLevel: priorityLevel,
          element: element,
          conditionStr: conditionStr,
          extensionElements: element.businessObject.extensionElements,
          conditionExpression: element.businessObject.conditionExpression,
          childNode: null
        }
        childNode.conditionNodes.push(obj)
      })

      childNode.conditionNodes.sort((a, b) => a.priorityLevel * 1 - b.priorityLevel * 1)

      nodeConfig.childNode = childNode
      // 查找条件分支下的目标节点
      const element = this.findConditionTargetElement(childNode)
      if (element) {
        if ((element.type === 'bpmn:ExclusiveGateway' || element.type === 'bpmn:ParallelGateway') && element.outgoing.length === 1) {
          childNode.endElement = element
          element.outgoing[0]?.target?.incoming.length === 1 && this.setNodeConfig(childNode, element.outgoing[0])
        } else {
          this.setNodeConfig(childNode, element.incoming[0])
        }
      }

      childNode.conditionNodes.forEach(row => {
        this.setNodeConfig(row)
      })

      return childNode
    },
    getNodeUserList (element, nodeConfig) {
      let formKey = {}
      try {
        formKey = element?.businessObject?.formKey ? JSON.parse(element?.businessObject?.formKey) : JSON.parse(element?.businessObject?.$attrs['flowable:formKey'])
        nodeConfig.groupType = formKey.groupType
      } catch (error) {
      }
      if (formKey.groupType === 'ASSIGNEE') {
        return (element?.businessObject?.assignee?.split(',') || element?.businessObject?.$attrs['flowable:assignee']?.split(',') || []).map(row => {
          this.users.indexOf(row) === -1 && this.users.push(row)
          return {
            id: row,
            name: row
          }
        }) || []
      } else if (formKey.groupType === 'USERS') {
        return (element?.businessObject?.candidateUsers?.split(',') || element?.businessObject?.$attrs['flowable:candidateUsers']?.split(',') || []).map(row => {
          this.users.indexOf(row) === -1 && this.users.push(row)
          return {
            id: row,
            name: row
          }
        }) || []
      } else if (formKey.groupType === 'DEPT') {
        return (element?.businessObject?.candidateGroups?.split(',') || element?.businessObject?.$attrs['flowable:candidateGroups']?.split(',') || []).map(row => {
          return {
            id: row,
            name: row
          }
        }) || []
      } else if (formKey.groupType === 'ROLE') {
        return (element?.businessObject?.candidateGroups?.split(',') || element?.businessObject?.$attrs['flowable:candidateGroups']?.split(',') || []).map(row => {
          return {
            id: row,
            name: row
          }
        }) || []
      } else if (formKey.groupType === 'POST') {
        return (element?.businessObject?.candidateGroups?.split(',') || element?.businessObject?.$attrs['flowable:candidateGroups']?.split(',') || []).map(row => {
          const arr = row.split('__')
          return {
            id: arr[1],
            name: arr[1],
            type: arr[0]
          }
        }) || []
      } else if (formKey.groupType === 'DEPT_POST_LEADER') {
        return [{ name: '流程发起人部门领导', type: 'DEPT_POST_LEADER' }];
      } else if (formKey.groupType === 'UP_DEPT_POST_LEADER') {
        return [{ name: '流程发起人上级部门领导', type: 'DEPT_POST_LEADER' }];
      }
      return []
    },
    getUserShowName () {
      return new Promise(resolve => {
        let params = {
          widgetType: 'upms_user',
          fieldName: 'loginName',
          fieldValues: this.users.join(',')
        }

        // 去掉流程启动人和指定审批人
        params.fieldValues = params.fieldValues.split(',').filter(row => {
          // eslint-disable-next-line no-template-curly-in-string
          return ['${startUserName}', '${appointedAssignee}'].indexOf(row) === -1
        }).join(',')

        if (params.fieldValues) {
          SysCommonBizController.viewByIds(this, params).then(res => {
            let userShowNameData = { ...this.getUserShowNameData };
            (res.data || []).forEach(item => {
              userShowNameData[item.loginName] = item.showName
            })
            this.setUserShowNameData(userShowNameData)
            resolve()
          }).catch(e => {
            resolve()
          })
        } else {
          resolve()
        }
      })
    },
    loadSysRoleList () {
      return new Promise((resolve, reject) => {
        let params = {
          widgetType: 'upms_role',
          filter: {}
        }
        SysCommonBizController.list(this, params, null, {
          showMask: false
        }).then(res => {
          this.roleList = res.data.dataList.map(item => {
            let obj = {
              id: String(item.roleId),
              name: item.roleName,
              ...item
            }
            this.roleMap.set(obj.id, obj)
            return obj
          })
          resolve()
        }).catch(e => {
          reject(e)
        })
      })
    },
    loadDeptWidgetDropdownList () {
      return new Promise((resolve, reject) => {
        let params = {
          widgetType: 'upms_dept',
          filter: {}
        }
        SysCommonBizController.list(this, params, null, {
          showMask: false
        }).then(res => {
          let groupList = []
          res.data.dataList.forEach(item => {
            const obj = {
              id: String(item.deptId),
              name: item.deptName,
              parentId: String(item.parentId),
              ...item
            }
            this.groupMap.set(obj.id, obj)
            groupList.push(obj)
          })
          this.groupList = treeDataTranslate(groupList)
          resolve()
        }).catch(e => {
          reject(e)
        })
      })
    },
    loadDeptPostList () {
      return new Promise((resolve, reject) => {
        let params = {
          widgetType: 'upms_dept_post',
          filter: {}
        }
        SysCommonBizController.list(this, params, null, {
          showMask: false
        }).then(res => {
          res.data.dataList.forEach(item => {
            this.deptPostMap.set(item.deptPostId, item)
          })
          this.deptPostList = res.data.dataList
          resolve()
        }).catch(e => {
          reject(e)
        })
      })
    },
    loadSysPostList () {
      return new Promise((resolve, reject) => {
        let params = {
          widgetType: 'upms_post',
          filter: {}
        }
        SysCommonBizController.list(this, params, null, {
          showMask: false
        }).then(res => {
          res.data.dataList.forEach(item => {
            const obj = {
              id: String(item.postId),
              name: item.postName,
              ...item
            }
            this.postMap.set(obj.id, obj)
          })
          this.postList = res.data.dataList
          resolve()
        }).catch(e => {
          reject(e)
        })
      })
    },
    elementClick (element) {
      this.$emit('elementClick', element)
    },
    // 查找分支条件的目标节点
    findConditionTargetElement (childNode) {
      const obj = {}
      const findEndElement = (element, arr) => {
        let el = element?.incoming?.length > 1 ? element : null
        el && arr.indexOf(el.id) === -1 && arr.push(el.id)
        element && element.outgoing.forEach(row => {
          if (row.target) {
            findEndElement(row.target, arr)
          }
        })
        return arr
      }
      childNode.conditionNodes.forEach(row => {
        const arr = findEndElement(row.element.target, [])
        arr.forEach(key => {
          obj[key] = Number.isInteger(obj[key]) ? (obj[key] + 1) : 1
        })
      })
      let elementKey = ''
      Object.keys(obj).forEach(key => {
        if (obj[key] === childNode.conditionNodes.length && !elementKey && this.hasElementIds.indexOf(key) === -1) {
          elementKey = key
        }
      })
      let element = this.bpmnModeler.get('elementRegistry').find(el => ['bpmn:UserTask', 'bpmn:ExclusiveGateway', 'bpmn:ParallelGateway'].indexOf(el.type) > -1 && el.id === elementKey)
      element && this.hasElementIds.push(element.id)
      return element
    },
    mousedown (e) {
      this.top = this.$refs.scroll.scrollTop
      this.left = this.$refs.scroll.scrollLeft
      this.dx = e.clientX
      this.dy = e.clientY
      this.isDown = true
    },
    mousemove (e) {
      let {
        dx,
        dy,
        top,
        left
      } = this
      if (this.isDown) {
        let x = dx - e.clientX
        let y = dy - e.clientY

        this.$refs.scroll.scrollTop = y + top
        this.$refs.scroll.scrollLeft = x + left
      }
    },
    mouseup () {
      this.isDown = false
    },
    ...mapMutations(['setUserShowNameData'])
  }
}
</script>

<style scoped>
.dingflow-design {
  width: 100%;
  height: 100%;
  background-color: #f5f5f7;
  position: relative;
}
.design-scroll {
  position: relative;
  overflow: auto;
  width: 100%;
  height: 100%;
}
.dingflow-design-header{
  position: absolute;
  top: 24px;
  right: 26px;
  z-index: 10;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: white;
  padding-left: 10px;
}
.dingflow-design-header>div{
  padding: 0 6px;
}
.dingflow-design-header .img{
  cursor: pointer;
}

.dingflow-design-header .img img {
  width: 20px;
  height: 20px;
}

.dingflow-design-header .line{
  width: 1px;
  height: 26px;
  background-color: #D9DBDD;
  margin: 0 10px;
}
.zoom {
  display: flex;
  -webkit-box-align: center;
  -ms-flex-align: center;
  align-items: center;
  -webkit-box-pack: justify;
  -ms-flex-pack: justify;
  justify-content: space-between;
  height: 40px;
  width: 125px;
}
.zoom .zoom-in,
.zoom .zoom-out {
  width: 30px;
  height: 30px;
  background: #fff;
  color: #c1c1cd;
  cursor: pointer;
  background-size: 100%;
  background-repeat: no-repeat;
  display: flex;
  align-items: center;
  justify-content: center;
}
.zoom .zoom-out.disabled {
  opacity: .5
}
.zoom .zoom-in.disabled {
  opacity: .5
}
.active.end-node .end-node-circle {
  background: #999999;
}
</style>
