FormHeader.vue 9.84 KB
<template>
  <div class="form-header">
    <h3 class="header-title" :class="{ 'open-title': isOpen }">
      {{ processInfo.subject }}
    </h3>
    <div
      class="emergency-box"
      v-if="processInfo.urgentState && mergencyConfig"
      :style="{ color: `${mergencyConfig.conf[urgentStateindex].color}` }"
    >
      <i class="icon-jinjizhuangtai ergency-icon"></i>
      {{ mergencyConfig.conf[urgentStateindex].name }}
      <span
        v-if="mergencyConfig.right === 'w'"
        class="edit-emergency"
        @click="openEmergency"
      >
        修改
      </span>
    </div>
    <div :class="isOpen ? 'opentext' : 'collapsetext'">
      <div class="process-info" v-for="(value, key) in taskInfo" :key="key">
        <span class="info-text">{{ value }}:{{ processInfo[key] }}</span>
        <span
          v-if="key === 'id'"
          class="copy"
          @click="(event) => clipboard(processInfo.id, event)"
        >
          复制
        </span>
      </div>
      <div v-if="isShowCurrentNode" class="process-info">
        <span class="info-text">
          {{ isDone && isHistory ? '历史处理节点:' : '当前节点:' }}
        </span>
        <div class="selected" v-if="isDone && isHistory">
          <span
            class="select-val"
            style="margin-left: 8px"
            @click="handleSelect"
          >
            {{ selectNode }}
          </span>
          <van-icon name="arrow-down" @click="handleSelect" />
        </div>
        <span v-if="!isDone" class="info-text" style="margin-left: 8px">
          {{ isDone ? currentSelectNodeName : currentNode }}
        </span>
      </div>
    </div>
    <span class="inTips" @click="inTips">
      <span>{{ isOpen ? '收起' : '更多信息' }}</span>
      <van-icon :name="isOpen ? 'arrow-up' : 'arrow-down'" />
    </span>
    <div
      class="status-wrap"
      :style="{ '--statusBg': getCurrentStatusColorOrText('color') }"
    >
      <span class="status-icon"></span>
      <i class="status-text">
        {{ getCurrentStatusColorOrText('text') }}
      </i>
    </div>
    <van-action-sheet
      v-model="showNode"
      close-on-click-action
      cancel-text="取消"
      :close-on-click-overlay="false"
      :actions="nodeOptionsList"
      @select="onSelect"
    />
  </div>
</template>

<script>
  import {
    getProcessInfo,
    getTaskInfo,
    getProcessRunByTaskId,
  } from '@/api/process'
  const TASK_INFO = {
    id: '流程编号',
    creator: '发起人',
    createTime: '发起时间',
  }

  import {
    STATUS,
    REQUEST_STATUS,
    STATUS_CLASS_NAME,
    STATUS_COLOR,
  } from '.././const.js'

  import clipboard from '@/utils/clipboard'
  export default {
    name: 'FormHeader',
    props: {
      doneDataVersion: {
        type: String,
        default: '',
      },
      nodeOptions: {
        type: Array,
        default: () => {
          return []
        },
      },
      currentSelectNode: {
        type: String,
        default: '',
      },
      mergencyConfig: {
        type: Object,
        default: () => {
          return {}
        },
      },
    },
    data() {
      return {
        processInfo: {},
        taskInfo: TASK_INFO,
        currentNode: '',
        approvalStatus: { ...STATUS, ...REQUEST_STATUS },
        statusClassName: STATUS_CLASS_NAME,
        isOpen: false, //默认展开|收起
        showNode: false,
        selectNode: '',
        proInstId: '',
      }
    },
    computed: {
      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)
        )
      },
      status() {
        return this.$route.query.status
      },
      isDone() {
        return (
          this.$route.query.type === 'done' || this.$route.params.type === 'done'
        )
      },
      isHistory() {
        return this.doneDataVersion === 'history'
      },
      nodeOptionsList() {
        return this.nodeOptions.map((item, index) => {
          return {
            name: item.taskName,
            ...item,
          }
        })
      },
      urgentStateindex() {
        return this.mergencyConfig.conf.findIndex(
          (item) => item.state == this.mergencyConfig.value
        )
      },
    },

    watch: {
      currentSelectNode(val) {
        if (this.nodeOptions.length > 0) {
          const result =
            this.nodeOptions.find((item) => item.taskKey === val) || {}
          this.currentSelectNodeName = result.taskName
          this.selectNode = this.currentSelectNodeName
        }
      },
    },
    mounted() {
      this.proInstId = this.$route.query.instId || this.$route.params.instId
      if (this.proInstId) {
        this.getProcessInfoByInstId()
      } else {
        this.taskId &&
          getProcessRunByTaskId(this.taskId).then((bpmProcessInstance) => {
            this.proInstId = bpmProcessInstance.id
            this.getProcessInfoByInstId()
          })
      }
    },
    methods: {
      openEmergency() {
        this.$emit('open-emergency')
      },
      getCurrentStatusColorOrText(type) {
        const currentStatus = this.processInfo.status
        return STATUS_COLOR[currentStatus]?.[type]
      },
      getProcessInfoByInstId() {
        getProcessInfo(this.proInstId).then((res) => {
          this.processInfo = res

          this.$emit('get-process-sponsor', res.createAccount)
          this.$emit('getInstStatus', res.status)

          this.$emit('updateHeaderData', {
            ...this.processInfo,
            currentNode: this.currentNode,
          })
          this.getCurrentNode(res.status)
        })
      },
      getCurrentNode(status) {
        getTaskInfo([this.proInstId]).then((res) => {
          if (!res.state)
            return this.$notify({ type: 'danger', message: res.message })
          const currentNodeList = res.value?.[this.proInstId]
          if (currentNodeList?.length > 0) {
            let nodeMap = {}
            currentNodeList.forEach((item) => {
              const { nodeId, name } = item
              nodeMap[nodeId] = name
            })
            if (
              this.$route.query.type === 'request' ||
              this.$route.params.type === 'request'
            ) {
              if (currentNodeList.length === 1) {
                this.currentNode = currentNodeList[0].name
              } else {
                this.currentNode = '请查看流程图'
              }
            } else {
              const currentTask = currentNodeList.find(
                (item) => item.id === this.taskId
              )
              this.currentNode = currentTask && currentTask.name
            }
            // 更新当前节点
            this.$emit('updateHeaderData', {
              ...this.processInfo,
              currentNode: this.currentNode,
              nodeId: currentNodeList[0].nodeId,
            })
          }
        })
      },
      //展开收起功能
      inTips() {
        this.isOpen = !this.isOpen
        this.$emit('show-more', this.isOpen)
      },
      onSelect(item) {
        console.log(item)
        this.showNode = false
        this.selectNode = item.taskName
        this.$emit('node-change', item.taskKey)
      },
      handleSelect(type) {
        this.showNode = true
      },
      //复制方法
      clipboard,
    },
  }
</script>

<style lang="scss" scoped>
  .form-header {
    padding: 0 16px;
    margin-top: 1px;
    padding-bottom: 10px;
    background: $base-white-color;
    position: relative;
    .header-title {
      margin: 0;
      padding-top: 16px;
      font-size: $base-font-size-big;
      color: $base-text-color;
      width: 100%;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
    .open-title {
      white-space: unset;
    }
    .process-info {
      padding-top: 8px;
      height: 17px;
      line-height: 17px;
      display: flex;
      align-items: center;
      .info-text {
        color: #a6a6a6;
        font-size: $base-font-size-small;
      }
      .copy {
        display: inline-block;
        width: 30px;
        height: 16px;
        line-height: 16px;
        text-align: center;
        border-radius: 8px;
        margin-left: 8px;
        font-size: 11px;
        background: #e6e6e6;
        color: $base-text-color;
      }
      .info-status {
        font-size: 12px;
      }
    }

    .status-wrap {
      position: absolute;
      right: 0px;
      top: -1px;
      .status-icon {
        mask: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSI1Ni43NjgiIGhlaWdodD0iNTYuNzY3IiB2aWV3Qm94PSIwIDAgNTYuNzY4IDU2Ljc2NyI+DQogIDxwYXRoIGlkPSLlrqHmibnnirbmgIHmoIfnrb4iIGQ9Ik0tNTc1MC41NjItMTYwOS41OTFoMzMuOTQxbDIyLjgyNiwyMi44Mjd2MzMuOTRaIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSg1NzUwLjU2MyAxNjA5LjU5MSkiLz4NCjwvc3ZnPg0K);
        mask-size: 100% 100%;
        mask-repeat: no-repeat;
        background-color: var(--statusBg);
        display: inline-block;
        width: 64px;
        height: 64px;
      }
      .status-text {
        position: absolute;
        right: -18px;
        transform: rotate(45deg);
        top: 20px;
        color: #fff;
        font-size: $base-font-size-default;
        user-select: none;
        display: block;
        width: 64px;
        font-style: normal;
      }
    }
  }
  .opentext {
    height: 100%;
  }
  .collapsetext {
    height: 0;
    overflow: hidden;
  }
  .inTips {
    font-size: 12px;
    color: #8c8c8c;
  }
  .selected {
    font-size: 12px;
    color: #8c8c8c;
    margin-left: 8px;
  }
  .emergency-box {
    display: inline-block;
    font-size: 11px;
    max-width: 106px;
    border-radius: 4px;
    padding: 2px 4px;
    margin-top: 8px;
    .ergency-icon {
      font-size: 16px;
    }
    .edit-emergency {
      color: $--color-primary;
      padding-left: 8px;
    }
  }
</style>