<template>
  <div
    class="flow-form"
    :style="{
      '--outHeight': `${outHeight}px`,
    }"
  >
    <!-- //为el-tooltip隐藏后能正常二次点击 -->
    <van-field
      style="
        z-index: -999;
        width: 0;
        height: 0;
        line-height: 0;
        position: absolute;
      "
      @focus.prevent="afocus"
      ref="adsaFocus"
    />
    <ht-online-form
      v-if="formType == 'INNER'"
      ref="flowOnlineForm"
      class="online-form"
      :class="{ 'readonly-form': isView }"
      :html="innerHtml"
      :data="data"
      :permission="permission"
      :extend-prop="recordData"
      :is-view="isView"
      :is-support-mobile="isSupportMobile"
      :inst-id="instId"
      :def-id="defId"
    />
    <iframe
      v-else-if="formType == 'FRAME'"
      name="flowFrameForm"
      :src="frameUrl"
    ></iframe>
    <!-- 设置紧急程度弹窗 -->
    <van-popup v-model="showMergencyConfig" position="bottom" round>
      <van-picker
        v-if="mergencyConfig && mergencyConfig.conf.length > 0"
        show-toolbar
        :columns="mergencyConfig.conf"
        value-key="state"
        ref="mergencyPickers"
        item-height="48px"
        @confirm="onConfirm"
        @cancel="showMergencyConfig = false"
      >
        <template #option="option">
          <div :style="{ color: `${option.color}` }">
            {{ option.name }}
          </div>
        </template>
      </van-picker>
    </van-popup>
  </div>
</template>

<script>
  import { mapState, mapGetters } from 'vuex'
  import { TASK_INFO } from './const.js'
  import {
    getProcessInfo,
    getUrgentStateConf,
    getProcessTitle,
    getTaskInfo,
    getCurNodeProperties,
  } from '@/api/process'
  export default {
    name: 'FlowForm',
    props: {
      form: {
        type: Object,
        default: () => {
          return {}
        },
      },
      data: {
        type: Object,
        default: () => {
          return null
        },
      },
      permission: {
        type: Object,
        default: () => {
          return null
        },
      },
      frameSaveDataTimeOut: {
        type: Number,
        default: 10,
      },
      instId: {
        type: String,
        default: '',
      },
      defId: {
        type: String,
        default: '',
      },
      isDialogModel: {
        type: Boolean,
        default: false,
      },
      recordData: {
        type: Object,
        default: () => {
          return {}
        },
      },
      doneDataVersion: {
        type: String,
        default: '',
      },
      nodeOptions: {
        type: Array,
        default: () => {
          return []
        },
      },
      currentSelectNode: {
        type: String,
        default: '',
      },
      outHeight: {
        type: Number,
        default: 112,
      },
    },
    data() {
      return {
        formType: 'INNER',
        innerHtml: null,
        frameUrl: null,
        frameRoger: null,
        frameCancelTimeout: null,
        frameSaveDataPromiseResolve: null,
        frameSaveDataPromiseReject: null,
        processInfo: {},
        isShowHelp: false,
        mergencyConfig: null,
        processName: '',
        currentNode: '',
        isSupportMobile: false,
        taskInfo: TASK_INFO,
        selectNode: '',
        currentSelectNodeName: '',
        flag: 0,
        showMergencyConfig: false,
      }
    },
    computed: {
      ...mapState('process', ['bpmTask']),
      ...mapGetters('login', ['token']),
      instStatus() {
        return this.$route.query.instStatus
      },
      taskId() {
        return this.$route.query.taskId || this.$route.params.taskId
      },
      isShowCurrentNode() {
        const { type, taskId } = this.$route.query
        return (
          ['request', 'todo'].includes(type) ||
          taskId ||
          (type === 'done' && this.isHistory)
        )
      },
      isDone() {
        return this.$route.query.type === 'done'
      },
      isView() {
        // 加签 征询 流转时禁止编辑
        const statusList = ['TRANSFORMEDINQU', 'USERADDSIGN', 'TRANSFORMED']
        const result =
          (this.instId && !this.taskId && !this.isDialogModel) ||
          statusList.includes(this.bpmTask.status)
        return result ? true : false
      },
      headers() {
        return { Authorization: `Bearer ${this.accessToken}` }
      },
      isShowTop() {
        return !this.instId && !this.isShowHelp && !this.mergencyConfig
      },
      isHistory() {
        return this.doneDataVersion === 'history'
      },
    },
    watch: {
      form: function (newVal) {
        if (newVal) {
          this.formType = newVal.type
          if (this.formType == 'INNER') {
            this.innerHtml = newVal.formHtml
          } else if (this.formType == 'FRAME') {
            this.frameUrl = newVal.formValue
          }
          this.$emit('formTypeChange', this.formType)
        }
        if (newVal.helpFile) {
          this.isShowHelp = true
        }
      },
      urgentStateValue(val) {
        const urgentStateValue = localStorage.getItem('urgentStateValue')
        if (urgentStateValue) {
          const savaData = { ...JSON.parse(urgentStateValue), new: val }
          localStorage.setItem('urgentStateValue', JSON.stringify(savaData))
        }
      },
      mergencyConfig: {
        handler(config) {
          const urgentStateValue = localStorage.getItem('urgentStateValue')
          if (urgentStateValue) {
            const savaData = {
              ...JSON.parse(urgentStateValue),
              new: config.value,
            }
            localStorage.setItem('urgentStateValue', JSON.stringify(savaData))
          }
        },
        deep: true,
      },
      'mergencyConfig.value': {
        handler(val) {
          let index = this.mergencyConfig.conf.findIndex(
            (item) => item.state == val
          )
          this.$emit('urgentStateValueChange', val, index)
        },
        deep: true,
      },
      showMergencyConfig: {
        handler(newVal) {
          let currentIndex = 0
          for (let i = 0; i < this.mergencyConfig.conf.length; i++) {
            if (
              this.mergencyConfig.value == this.mergencyConfig.conf[i].state
            ) {
              currentIndex = i
            }
          }
          // picker打开,默认选中已选择的那一栏
          this.$nextTick(() => {
            if (
              this.mergencyConfig.conf != null &&
              this.mergencyConfig.conf.length > 0
            ) {
              this.$refs.mergencyPickers.setColumnIndex(0, currentIndex)
              this.showBottomPop = newVal
            }
          })
        },
      },
      currentSelectNode(val) {
        this.selectNode = val
        if (this.nodeOptions.length > 0) {
          const result =
            this.nodeOptions.find((item) => item.taskKey === val) || {}
          this.currentSelectNodeName = result.taskName
        }
      },
    },
    created() {
      window.addEventListener('message', this.handleFrameMessage)
      this.instId && this.getProcessInfoByInstId()
      this.getEmergencyStatusConf()
      this.defId && this.getProcessTitleByDefId()
      // 流程初始化时获取流程节点配置信息
      this.getFlowConfig()
    },
    mounted() {
      window.addEventListener('scroll', this.scrollBackTop, true) //监听滚动条
    },
    beforeDestroy() {
      window.removeEventListener('message', this.handleFrameMessage)
      window.removeEventListener('scroll', this.scrollBackTop)
    },
    methods: {
      afocus() {
        this.$refs.adsaFocus.blur()
      },
      scrollBackTop() {
        const myNodeList = document.querySelectorAll('.el-tooltip__popper')

        if (myNodeList.length > 0) {
          myNodeList.forEach((value) => {
            value.style.display = 'none'
            value.ariaHidden = true
          })

          this.$refs.adsaFocus.afocus()
        }
      },
      getFlowConfig() {
        if (this.defId) {
          getCurNodeProperties({
            defId: this.defId,
            instId: '',
            taskId: '',
          }).then((res) => {
            if (
              res &&
              res.localProperties &&
              res.localProperties.initFillData
            ) {
              this.recordData.initFillData = true
            }
          })
        }
      },
      getData(verify) {
        if (this.formType == 'INNER') {
          return this.$refs.flowOnlineForm.getData(verify)
        } else if (this.formType == 'FRAME') {
          const frm = this.getFrameFormElement()
          // 发送保存数据的消息
          frm.contentWindow.postMessage('saveData', '*')
          this.frameRoger = false
          this.frameCancelTimeout = false
          // 1.未收到roger消息，认为url表单未引入hotent.helper.js文件
          const rogerJudge = new Promise((resolve, reject) => {
            setTimeout(() => {
              if (this.frameRoger === false) {
                this.$message.error(
                  `当前URL表单没有回复保存数据的消息，可能没有引入hotent.helper.js文件。`
                )
                reject()
              }
            }, 200)
          })
          // 2.超时未返回saveData消息
          var timeoutHandler
          const timeoutJudge = new Promise((resolve, reject) => {
            timeoutHandler = setTimeout(() => {
              if (this.frameCancelTimeout === false) {
                this.$message.error(
                  `当前URL表单保存数据超时，超时时间为：${this.frameSaveDataTimeOut}秒。`
                )
                reject()
              }
            }, this.frameSaveDataTimeOut * 1000)
          })

          // 3.接受到保存完数据的消息后resolve
          const receiveData = new Promise((resolve, reject) => {
            this.frameSaveDataPromiseResolve = resolve
            this.frameSaveDataPromiseReject = reject
          })

          return Promise.race([rogerJudge, timeoutJudge, receiveData]).finally(
            () => {
              this.frameRoger = null
              this.frameCancelTimeout = null
              this.frameSaveDataPromiseResolve = null
              this.frameSaveDataPromiseReject = null
              if (timeoutHandler) {
                clearTimeout(timeoutHandler)
              }
            }
          )
        } else {
          throw `不识别的表单类型${this.formType}`
        }
      },
      getFrameFormElement() {
        const frm = document.querySelectorAll("[name='flowFrameForm']")[0]
        const frmControl = document.querySelectorAll("[name='iframeControl']")
        if (!frm && !frmControl) {
          this.$message.error('当前URL表单加载不正确。')
        }
        return frm
      },
      // 处理iframe表单发送过来的消息
      handleFrameMessage(e) {
        if (
          !e ||
          !e.data ||
          !e.data.type ||
          e.data.type.startsWith?.('webpack')
        ) {
          return
        }
        const frm = this.getFrameFormElement()
        switch (e.data.type) {
          //调整iframe的高度
          case 'height': {
            frm.style.visibility = 'hidden'
            // 提前还原高度
            frm.setAttribute('height', 'auto')
            setTimeout(function () {
              frm.setAttribute('height', e.data.height)
              frm.style.visibility = 'visible'
              frm.dispatchEvent(new Event('mouseover'))
            }, 0)
            break
          }
          //获取到frame表单传过来的流程变量数据
          case 'saveData': {
            if (e.data.state && this.frameSaveDataPromiseResolve) {
              const iframeData = {
                businessKey: e.data.businessKey,
                sysCode: e.data.sysCode,
              }
              if (e.data.vars) {
                iframeData.vars = e.data.vars
              }
              this.$emit('update:data', iframeData)
              this.frameSaveDataPromiseResolve()
            } else if (!e.data.state && this.frameSaveDataPromiseReject) {
              this.frameSaveDataPromiseReject()
              this.$message.error('业务表单数据保存失败')
            }
            break
          }
          //表示frame表单收到了保存表单数据的消息
          case 'roger':
            this.frameRoger = true
            break
          //frame表单取消超时判断
          case 'cancelTimeout':
            this.frameCancelTimeout = true
            break
        }
      },
      getProcessInfoByInstId() {
        getProcessInfo(this.instId).then((res) => {
          this.processInfo = res
          this.isSupportMobile = res.supportMobile === 1 ? true : false
          this.$emit('get-process-sponsor', res.createAccount)
          this.$emit('getInstStatus', res.status)

          this.$emit('updateHeaderData', {
            ...this.processInfo,
            currentNode: this.currentNode,
          })
          this.getCurrentNode(res.status)
        })
      },
      getCurrentStatusColorOrText(type) {
        const currentStatus = this.processInfo.status || this.instStatus
        return STATUS_COLOR[currentStatus]?.[type]
      },
      preview() {
        const files = this.form?.helpFile ? JSON.parse(this.form.helpFile) : []
        window.open(
          `${this.$file.previewUrl}?fileId=${files[0]?.id}&token=${this.accessToken}`
        )
      },
      getEmergencyStatusConf() {
        const params = {
          defId: this.defId,
          instId: this.instId,
          taskId: this.taskId,
        }
        getUrgentStateConf(params).then((data) => {
          if (data.conf) {
            try {
              data.conf = JSON.parse(data.conf)
            } catch (error) {
              data.conf = eval('(' + data.conf + ')')
            }
            localStorage.setItem(
              'urgentStateValue',
              JSON.stringify({ old: data.value })
            )
            if (!data.value) {
              data.conf?.map?.((item) => {
                if (item.default) {
                  data.value = item.state
                }
              })
            }
            this.mergencyConfig = data
          } else {
            localStorage.removeItem('urgentStateValue')
          }
          this.$emit('urgentStateList', this.mergencyConfig)
        })
      },

      getProcessTitleByDefId() {
        getProcessTitle(this.defId).then((res) => {
          if (res.state) {
            this.processName = res.value.name
            this.$store.commit('process/setprocessName', res.value.name)
          }
        })
      },
      getCurrentNode(status) {
        getTaskInfo([this.instId]).then((res) => {
          if (!res.state) return this.$message.error(res.message)
          const currentNodeList = res.value?.[this.instId]
          if (currentNodeList?.length > 0) {
            let nodeMap = {}
            currentNodeList.forEach((item) => {
              const { nodeId, name } = item
              nodeMap[nodeId] = name
            })
            if (this.$route.query.type === 'request') {
              if (currentNodeList.length === 1) {
                this.currentNode = currentNodeList[0].name
              } else {
                this.currentNode = '请查看流程图'
              }
              this.isSupportMobile = currentNodeList[0].supportMobile === 1
            } else {
              const currentTask = currentNodeList.find(
                (item) => item.id === this.taskId
              )
              this.currentNode = currentTask && currentTask.name
              if (currentTask && currentTask.supportMobile) {
                this.isSupportMobile = currentTask.supportMobile === 1
              }
            }
            // 更新当前节点
            this.$emit('updateHeaderData', {
              ...this.processInfo,
              currentNode: this.currentNode,
              nodeId: currentNodeList[0].nodeId,
            })
          }
        })
      },
      handleNodeChange(taskKey) {
        this.$emit('node-change', taskKey)
      },
      onConfirm(value) {
        this.mergencyConfig.value = value.state
        this.showMergencyConfig = false
      },
    },
  }
</script>

<style lang="scss" scoped>
  .flow-form {
    background: #fff;
    height: calc(#{$vh} - var(--outHeight));
    ::v-deep {
      .el-form-item__label {
        line-height: 48px;
        text-align: left;
        span {
          margin-left: 0 !important;
          display: inline-block;
          line-height: 24px;
          padding: 12px 0;
        }
      }
      .el-form-item__content {
        line-height: 48px;
        .van-cell__right-icon {
          font-size: 18px;
          margin-right: -5px;
          line-height: 24px;
        }
      }
      .ht-form-item-top-label {
        .el-form-item__content {
          line-height: 24px;
          .field-tail {
            text-align: left;
            display: inline-block;
            line-height: 24px;
          }
        }
      }
    }
    .online-form {
      overflow-y: auto;
      overflow-x: hidden;
      padding-bottom: 24px;
      height: calc(#{$vh} - var(--outHeight) - 24px);
    }
    ::v-deep {
      .van-stepper__minus,
      .van-stepper__plus {
        background: #409eff;
      }
      iframe[name='flowFrameForm'] {
        min-height: 300px;
        width: 100%;
        border: none;
      }
    }
  }
</style>
