<template> <div> <div class="workflow-button" :style="{top: styleTop, right: styleRight}" @click="showDrawer = true"> <div class="icon"> <svg width="24px" height="24px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> <use xlink:href="#icon-a-24_BILLEXPAND_C_RBC_shenpiguiji"></use> </svg> </div> <div class="text">审批轨迹</div> </div> <Drawer :transfer="false" title="审批轨迹" :closable="true" v-model="showDrawer" :width="styleDrawerWidth" :inner="true" :mask="false" > <div v-if="!data || !data.steps">暂无数据</div> <div v-if="data && data.steps" class="workflow-step-wrapper"> <div class="workflow-step" v-for="step, k in data.steps" :key="k" > <div class="step-head"> <div class="step-icon"> <svg :class="`step-icon ${isCurrentStep(step, k) ? 'waiting-icon' : 'finish-icon'}`" aria-hidden="true"> <!-- 未到达 --> <use v-if="step.state == 1" xlink:href="#icon-a-24_BILLEXPAND_C_RBC_weidaoda"></use> <!-- 通过 --> <use v-else-if="step.result == 1" xlink:href="#icon-a-24_BILLEXPAND_C_RBC_shenpitongguo"></use> <!-- 提交 --> <use v-else-if="step.result == -1" xlink:href="#icon-a-24_BILLEXPAND_C_RBC_shenpitijiao"></use> <!-- 驳回 --> <use v-else-if="isRejectStep(step)" xlink:href="#icon-a-24_BILLEXPAND_C_RBC_shenpibohui"></use> <!-- 末端节点 --> <use v-else-if="step.result == -2" xlink:href="#icon-a-24_BILLEXPAND_C_RBC_moduanjiedian"></use> </svg> </div> <div :class="`step-line ${step.state === 2 ? 'finish-line' : 'waiting-line'}`" v-if="k != data.steps.length - 1"></div> </div> <div class="step-main"> <div class="main-body"> <div :class="`${isCurrentStep(step, k) ? 'text-black' : isRejectStep(step) ? 'text-error' : 'text-grey'}`"> <div v-if="step.result == -2"> <span class="result">审批结束</span> </div> <div v-else-if="step.state == 2"> <span v-if="step.actualOwner" class="participants">{{step.actualOwner}} </span> <span class="result">{{step.result == -1 ? '提交' : step.result == 1 ? '通过' : '驳回'}}</span> </div> <div v-else> <span v-if="isCurrentStep(step, k)" class="participants">等待</span> <span class="participants">{{step.participants.join('、')}}</span> <span class="result"> 审批</span> </div> </div> <div v-if="isCurrentStep(step, k) || step.state == 2">{{formatStepTime(step)}}</div> <div v-if="step.comments">审批意见:{{step.comments}}</div> <div v-if="step.title" :class="isRejectStep(step) ? 'text-error' : 'text-main'">环节名称: {{step.title}}</div> </div> </div> </div> </div> </Drawer> </div> </template> <script> export default { data() { return { showDrawer: false, data: { "procState": 2, "startTime": "2021-10-12T07:28:04Z", "finishTime": "2021-10-13T02:15:40Z", "steps": [], "submitter": "ylq" }, } }, props: { define: { type: Object, required: true, }, context: { type: Object, default: () => { } } }, methods:{ isCurrentStep(step, k) { if (k != 0) return false return step.result != -2 }, isRejectStep(step) { return step.result === 3 }, formatDate(datetime) { const t = new Date(datetime) return t.format('yyyy-MM-dd HH:mm:ss') }, /** * 根据节点状态返回完成时间或时间差 * @param {object} step * @param {string} step.createTime - 创建时间 * @param {string | null} step.completeTime - 结束时间 * @param {number} step.result - 状态 * @returns {string} */ formatStepTime(step) { const currentTimestamp = Date.now() if (step.state === 1) { let ans = '已等待' const createTimestamp = (new Date(step.createTime)).getTime() let seconds = (currentTimestamp - createTimestamp) / 1000 const formatList = [ { rate: 60 * 60 * 24, label: '天', }, { rate: 60 * 60, label: '小时', }, { rate: 60, label: '分钟', }, ] // for (const o of formatList) { // const num = Math.floor(seconds / o.rate) // if (num > 0 || o.alwaysShow) { // ans += `${num}${o.label}` // seconds -= num * o.rate // } // } for (let i = 0; i < formatList.length; ++i) { const o = formatList[i] const num = Math.floor(seconds / o.rate) if (num > 0 || (!ans && i == formatList.length - 1)) { ans += `${num}${o.label}` seconds -= num * o.rate } } return ans } else { return this.formatDate(step.completeTime) } }, }, mounted() { window.GAMS.Util.invokeServer({ path: 'gms/workflow/task/approved', type: 'GET', contentType: 'application/json', params: { bizName: this.context.bill.getBillDefine(), bizObjId: this.context.bill.getMasterData().getValue('id'), }, }).then((res) => { res = res || [] let count = 0 this.data = res[0] for (let o of res) { if (o.procState == 1) { this.data = o ;++count } } if (count > 1) { this.$Message.error('工作流数据异常') } if (!this.data) return this.data.steps.push({ title: "提交申请", state: 2, createTime: this.data.startTime, completeTime: this.data.startTime, comments: null, result: -1, actualOwner: this.data.submitter, }) // 审批流程结束贴一个结束节点 if (this.data.procState === 2) { this.data.steps.unshift({ state: this.data.procState, createTime: this.data.finishTime, completeTime: this.data.finishTime, result: -2, }) } // 否则从当前节点切断, else { for (let i = 0; i < this.data.steps.length - 1; ++i) { if (this.data.steps[i].state === 1 && this.data.steps[i + 1].state === 2) { this.data.steps = this.data.steps.slice(i) break } } } }) }, computed: { styleTop() { return this.define.layout.top || '0px' }, styleRight() { return this.define.layout.right || '0px' }, styleDrawerWidth() { return this.define.layout.drawerWidth || '400px' }, }, beforeDestroy() { }, } </script> <style lang="less" scoped> @icon-width: 24px; @main-color: #0DA2E6; .workflow-button { cursor: pointer; position: absolute; z-index: 1000; width: 56px; height: 64px; padding-top: 10px; background: #FFA938; box-shadow: 0px 1px 6px 0px rgba(255, 169, 56, 0.8); border-radius: 6px; opacity: 0.9; .icon { width: 24px; height: 24px; margin-bottom: 8px; margin: 0 auto; } .text { margin-top: 2px; text-align: center; height: 17px; font-size: 12px; font-family: PingFangSC-Medium, PingFang SC; font-weight: 500; color: #FFFFFF; line-height: 17px; text-shadow: 0px 1px 6px rgba(255, 169, 56, 0.8); } } .workflow-step-wrapper { width: 100%; .workflow-step { width: 100%; position: relative; .step-head { width: @icon-width; position: absolute; height: 100%; .step-icon { width: 100%; fill: currentColor; height: @icon-width; } .waiting-icon { color: @main-color; } .finish-icon { color: #888; } .step-line { margin-left: @icon-width / 2; width: 1px; height: calc(100% - @icon-width); } .finish-line { background-color: #C8C8C8; } .waiting-line { background-color: @main-color; } } .step-main { width: 100%; min-height: calc(@icon-width + 10px); padding-left: @icon-width; font-size: 12px; color: #888888; line-height: 17px; font-family: PingFangSC-Regular, PingFang SC; font-weight: 400; .main-body { padding-bottom: 30px; padding-left: 5px; > div { margin-bottom: 5px; } .text-gray { color: #888888; } .text-black { color: #222222; } .text-main { color: @main-color; } .text-error { color: #ED664B; } .participants { height: 20px; font-size: 14px; line-height: 20px; } .result { height: 20px; font-size: 14px; line-height: 20px; } } } } } </style>