Blame view

src/views/workflow/components/processViewer.vue 9.08 KB
任超 committed
1 2
<template>
  <div class="process-viewer">
任超 committed
3
    <div v-show="!isLoading" ref="processCanvas" class="process-canvas" style="height: 360px;" />
任超 committed
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
    <!-- 自定义箭头样式,用于成功状态下流程连线箭头 -->
    <defs ref="customSuccessDefs">
      <marker id="sequenceflow-end-white-success" view-box="0 0 20 20" ref-x="11" ref-y="10" marker-width="10"
        marker-height="10" orient="auto">
        <path class="success-arrow" d="M 1 5 L 11 10 L 1 15 Z"
          style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;" />
      </marker>
      <marker id="conditional-flow-marker-white-success" view-box="0 0 20 20" ref-x="-1" ref-y="10" marker-width="10"
        marker-height="10" orient="auto">
        <path class="success-conditional" d="M 0 10 L 8 6 L 16 10 L 8 14 Z"
          style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;" />
      </marker>
    </defs>
    <!-- 自定义箭头样式,用于失败状态下流程连线箭头 -->
    <defs ref="customFailDefs">
      <marker id="sequenceflow-end-white-fail" view-box="0 0 20 20" ref-x="11" ref-y="10" marker-width="10"
        marker-height="10" orient="auto">
        <path class="fail-arrow" d="M 1 5 L 11 10 L 1 15 Z"
          style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;" />
      </marker>
      <marker id="conditional-flow-marker-white-fail" view-box="0 0 20 20" ref-x="-1" ref-y="10" marker-width="10"
        marker-height="10" orient="auto">
        <path class="fail-conditional" d="M 0 10 L 8 6 L 16 10 L 8 14 Z"
          style="stroke-width: 1px; stroke-linecap: round; stroke-dasharray: 10000, 1;" />
      </marker>
    </defs>
    <!-- 已完成节点悬浮弹窗 -->
    <el-dialog class="comment-dialog" :title="dlgTitle || '审批记录'" :visible.sync="dialogVisible" append-to-body>
      <el-row>
        <el-table :data="taskCommentList" size="mini" border header-cell-class-name="table-header-gray">
          <el-table-column label="序号" header-align="center" align="center" type="index" width="55px" />
          <el-table-column label="候选办理" prop="candidate" width="150px" align="center" />
          <el-table-column label="实际办理" prop="assigneeName" width="100px" align="center" />
          <el-table-column label="处理时间" prop="createTime" width="140px" align="center" />
          <el-table-column label="办结时间" prop="finishTime" width="140px" align="center" />
          <el-table-column label="耗时" prop="duration" width="100px" align="center" />
          <el-table-column label="审批意见" align="center">
            <template slot-scope="scope">
              {{ scope.row.commentList&&scope.row.commentList[0]?scope.row.commentList[0].fullMessage:'' }}
            </template>
          </el-table-column>
        </el-table>
      </el-row>
    </el-dialog>
    <div style="position: absolute; top: 0px; left: 0px; width: 100%;">
      <el-row type="flex" justify="end">
        <el-button-group key="scale-control" size="medium">
          <el-button size="medium" type="default" :plain="true" :disabled="defaultZoom <= 0.3" icon="el-icon-zoom-out"
            @click="processZoomOut()" />
          <el-button size="medium" type="default" style="width: 90px;">{{ Math.floor(this.defaultZoom * 10 * 10) + "%"
          }}</el-button>
          <el-button size="medium" type="default" :plain="true" :disabled="defaultZoom >= 3.9" icon="el-icon-zoom-in"
            @click="processZoomIn()" />
          <el-button size="medium" type="default" icon="el-icon-c-scale-to-original" @click="processReZoom()" />
          <slot />
        </el-button-group>
      </el-row>
    </div>
  </div>
</template>

<script>
任超 committed
66
import '@/styles/package/theme/index.scss'
任超 committed
67 68 69 70
import BpmnViewer from 'bpmn-js/lib/Viewer'
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'
export default {
  props: {
71 72 73
    formData: {
      type: Object,
      default: {}
任超 committed
74 75 76 77 78 79 80 81
    }
  },
  data () {
    return {
      dialogVisible: false,
      dlgTitle: undefined,
      defaultZoom: 1,
      // 是否正在加载流程图
任超 committed
82
      isLoading: true,
任超 committed
83 84 85 86 87 88 89 90 91 92 93 94 95
      bpmnViewer: undefined,
      // 已完成流程元素
      processNodeInfo: undefined,
      // 当前任务id
      selectTaskId: undefined,
      // 任务节点审批记录
      taskCommentList: [],
      // 已完成任务悬浮延迟Timer
      hoverTimer: null
    }
  },
  created () {
    this.$nextTick(() => {
96 97
      this.importXML(this.formData.xml)
      this.setProcessStatus(this.formData.finishedInfo);
任超 committed
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
    })
  },
  destroyed () {
    this.clearViewer()
  },
  methods: {
    processReZoom () {
      this.defaultZoom = 1
      this.bpmnViewer.get('canvas').zoom('fit-viewport', 'auto')
    },
    processZoomIn (zoomStep = 0.1) {
      const newZoom = Math.floor(this.defaultZoom * 100 + zoomStep * 100) / 100
      if (newZoom > 4) {
        throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4')
      }
      this.defaultZoom = newZoom
      this.bpmnViewer.get('canvas').zoom(this.defaultZoom)
    },
    processZoomOut (zoomStep = 0.1) {
      const newZoom = Math.floor(this.defaultZoom * 100 - zoomStep * 100) / 100
      if (newZoom < 0.2) {
任超 committed
119
        throw new Error('[Process Designer Warn ]: The zoom ratio cannot be scss than 0.2')
任超 committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
      }
      this.defaultZoom = newZoom
      this.bpmnViewer.get('canvas').zoom(this.defaultZoom)
    },
    getOperationTagType (type) {
      return 'success'
    },
    // 流程图预览清空
    clearViewer (a) {
      if (this.$refs.processCanvas) {
        this.$refs.processCanvas.innerHTML = ''
      }
      if (this.bpmnViewer) {
        this.bpmnViewer.destroy()
      }
      this.bpmnViewer = null
    },
    // 添加自定义箭头
    addCustomDefs () {
      const canvas = this.bpmnViewer.get('canvas')
      const svg = canvas._svg
      const customSuccessDefs = this.$refs.customSuccessDefs
      const customFailDefs = this.$refs.customFailDefs
      svg.appendChild(customSuccessDefs)
      svg.appendChild(customFailDefs)
    },
    // 任务悬浮弹窗
    onSelectElement (element) {
      this.selectTaskId = undefined
      this.dlgTitle = undefined

      if (this.processNodeInfo == null || this.processNodeInfo.finishedTaskSet == null) return

      if (element == null || this.processNodeInfo.finishedTaskSet.indexOf(element.id) === -1) {
        return
      }

      this.selectTaskId = element.id
      this.dlgTitle = element.businessObject ? element.businessObject.name : undefined
      // 计算当前悬浮任务审批记录,如果记录为空不显示弹窗
      this.taskCommentList = (this.allCommentList || []).filter(item => {
        return item.taskDefKey === this.selectTaskId
      })
      this.dialogVisible = true
    },
    // 显示流程图
    async importXML (xml) {
      this.clearViewer('a')
      if (xml != null && xml !== '') {
        try {
          this.bpmnViewer = new BpmnViewer({
            additionalModules: [
              // 移动整个画布
              MoveCanvasModule
            ],
            container: this.$refs.processCanvas
          })
          // 任务节点悬浮事件
          this.bpmnViewer.on('element.click', ({ element }) => {
            this.onSelectElement(element)
          })
          const c = await this.bpmnViewer.importXML(xml)
任超 committed
182
          this.isLoading = true
任超 committed
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
          this.addCustomDefs()
        } catch (e) {
          // this.clearViewer('b')
        } finally {
          this.isLoading = false
          this.setProcessStatus(this.processNodeInfo)
          this.$nextTick(() => {
            this.processReZoom()
          })
        }
      }
    },
    // 设置流程图元素状态
    setProcessStatus (processNodeInfo) {
      this.processNodeInfo = processNodeInfo
      if (this.isLoading || this.processNodeInfo == null || this.bpmnViewer == null) return
      const { finishedTaskSet, rejectedTaskSet, unfinishedTaskSet, finishedSequenceFlowSet } = this.processNodeInfo
      const canvas = this.bpmnViewer.get('canvas')
      const elementRegistry = this.bpmnViewer.get('elementRegistry')
      if (Array.isArray(finishedSequenceFlowSet)) {
        finishedSequenceFlowSet.forEach(item => {
          if (item != null) {
            canvas.addMarker(item, 'success')
            const element = elementRegistry.get(item)
            const conditionExpression = element.businessObject.conditionExpression
            if (conditionExpression) {
              canvas.addMarker(item, 'condition-expression')
            }
          }
        })
      }
      if (Array.isArray(finishedTaskSet)) {
        finishedTaskSet.forEach(item => canvas.addMarker(item, 'success'))
      }
      if (Array.isArray(unfinishedTaskSet)) {
        unfinishedTaskSet.forEach(item => canvas.addMarker(item, 'primary'))
      }
      if (Array.isArray(rejectedTaskSet)) {
        rejectedTaskSet.forEach(item => {
          if (item != null) {
            const element = elementRegistry.get(item)
            if (element.type.includes('Task')) {
              canvas.addMarker(item, 'danger')
            } else {
              canvas.addMarker(item, 'warning')
            }
          }
        })
      }
    }
  }
}
</script>