<template> <div class="app-container"> <!-- 审批信息 --> <el-card class="box-card" v-loading="processInstanceLoading" v-for="(item, index) in runningTasks" :key="index"> <div slot="header" class="clearfix"> <span class="el-icon-picture-outline">{{$t('审批任务')}}【{{ item.name }}】</span> </div> <el-col :span="16" :offset="6"> <el-form :ref="'form' + index" :model="auditForms[index]" :rules="auditRule" label-width="100px"> <el-form-item :label="$t('流程名')" v-if="processInstance && processInstance.name"> {{ processInstance.name }} </el-form-item> <el-form-item :label="$t('流程发起人')" v-if="processInstance && processInstance.startUser"> {{ processInstance.startUser.nickname }} <el-tag type="info" size="mini">{{ processInstance.startUser.deptName }}</el-tag> </el-form-item> <el-form-item :label="$t('抄送人')" prop="copyUserIds"> <el-select v-model="auditForms[index].copyUserIds" clearable multiple filterable style="width: 100%"> <el-option v-for="item in userOptions" :key="parseInt(item.id)" :label="item.nickname" :value="parseInt(item.id)"/> </el-select> </el-form-item> <el-form-item :label="$t('审批建议')" prop="comment"> <el-input type="textarea" v-model="auditForms[index].comment" :placeholder="$t('请输入审批建议')"/> </el-form-item> </el-form> <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px;"> <el-button :loading="loading" icon="el-icon-edit-outline" type="success" size="mini" @click="handleAudit(item, true)">{{$t('通过')}} </el-button> <el-button :loading="loading" icon="el-icon-circle-close" type="danger" size="mini" @click="handleAudit(item, false)">{{$t('不通过')}} </el-button> <el-button :loading="loading" icon="el-icon-edit-outline" type="primary" size="mini" @click="handleUpdateAssignee(item)">{{$t('转办')}} </el-button> <!-- <el-button icon="el-icon-edit-outline" type="primary" size="mini" @click="handleDelegate(item)">{{ $t('委派') }}</el-button>--> <!-- <el-button icon="el-icon-refresh-left" type="warning" size="mini" @click="handleBack(item)">{{ $t('退回') }}</el-button>--> </div> </el-col> </el-card> <!-- 申请信息 --> <el-card class="box-card mt-10" v-loading="processInstanceLoading"> <div slot="header" class="clearfix"> <span class="el-icon-document">{{$t('申请信息')}}【{{ processInstance.name }}】</span> </div> <el-col v-if="this.processInstance.processDefinition && this.processInstance.processDefinition.formType === 10" :span="16" :offset="6"> <div> <parser :key="new Date().getTime()" :form-conf="detailForm" @submit="submitForm"/> </div> </el-col> <div v-if="this.processInstance.processDefinition && this.processInstance.processDefinition.formType === 20"> <component v-if="businessKeyToComponent" :is="businessKeyToComponent.component" v-bind="businessKeyToComponent" /> <div v-else> <router-link :to="this.processInstance.processDefinition.formCustomViewPath + '?id=' + this.processInstance.businessKey"> <el-button type="primary">{{$t('点击查看')}}</el-button> </router-link> </div> </div> </el-card> <el-card class="box-card mt-10" v-loading="tasksLoad"> <div slot="header" class="clearfix"> <span class="el-icon-picture-outline">{{$t('审批记录')}}</span> </div> <el-col :span="16" :offset="4"> <div class="block"> <el-timeline> <el-timeline-item v-for="(item, index) in tasks" :key="index" :icon="getTimelineItemIcon(item)" :type="getTimelineItemType(item)"> <p style="font-weight: 700">{{$t('任务')}}:{{ item.name }}</p> <el-card :body-style="{ padding: '10px' }"> <label v-if="item.assigneeUser" style="font-weight: normal; margin-right: 30px;"> {{$t('审批人')}}:{{ item.assigneeUser.nickname }} <el-tag type="info" size="mini">{{ item.assigneeUser.deptName }}</el-tag> </label> <label style="font-weight: normal">{{$t('创建时间')}}:</label> <label style="color:#8a909c; font-weight: normal">{{ parseTime(item.createTime) }}</label> <label v-if="item.endTime" style="margin-left: 30px;font-weight: normal">{{$t('审批时间')}}:</label> <label v-if="item.endTime" style="color:#8a909c;font-weight: normal"> {{ parseTime(item.endTime) }}</label> <label v-if="item.durationInMillis" style="margin-left: 30px;font-weight: normal">{{$t('耗时')}}:</label> <label v-if="item.durationInMillis" style="color:#8a909c;font-weight: normal"> {{ getDateStar(item.durationInMillis) }} </label> <p v-if="item.comment"> <el-tag :type="getTimelineItemType(item)">{{ item.comment }}</el-tag> </p> </el-card> </el-timeline-item> </el-timeline> </div> </el-col> </el-card> <!-- 高亮流程图 --> <el-card class="box-card mt-10" v-loading="processInstanceLoading"> <div slot="header" class="clearfix"> <span class="el-icon-picture-outline">{{$t('流程图')}}</span> </div> <my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" :activityData="activityList" :processInstanceData="processInstance" :taskData="tasks"/> </el-card> <!-- 对话框(转派审批人) --> <el-dialog :title="$t('转派审批人')" :visible.sync="updateAssignee.open" width="500px" append-to-body> <el-form ref="updateAssigneeForm" :model="updateAssignee.form" :rules="updateAssignee.rules" label-width="110px"> <el-form-item :label="$t('新审批人')" prop="assigneeUserId"> <el-select v-model="updateAssignee.form.assigneeUserId" clearable style="width: 100%"> <el-option v-for="item in userOptions" :key="parseInt(item.id)" :label="item.nickname" :value="parseInt(item.id)"/> </el-select> </el-form-item> </el-form> <div slot="footer" class="dialog-footer"> <el-button type="primary" @click="submitUpdateAssigneeForm">{{$t('确定')}}</el-button> <el-button @click="cancelUpdateAssigneeForm">{{$t('取消')}}</el-button> </div> </el-dialog> </div> </template> <script> import {getProcessDefinitionBpmnXML} from "@/api/bpm/definition"; import {DICT_TYPE, getDictDatas} from "@/utils/dict"; import store from "@/store"; import {decodeFields} from "@/utils/formGenerator"; import Parser from '@/components/parser/Parser' import {createProcessInstance, getProcessInstance} from "@/api/bpm/processInstance"; import {approveTask, getTaskListByProcessInstanceId, rejectTask, updateTaskAssignee} from "@/api/bpm/task"; import {getDate} from "@/utils/dateUtils"; import {listSimpleUsers} from "@/api/system/user"; import {getActivityList} from "@/api/bpm/activity"; import OfferSpecialDetail from "@/views/ecw/offer/components/SpecialDetail" import warehouseDetails from "@/views/ecw/order/components/warehouseDetails"; import shippingDetail from '@/views/ecw/box/shippingDetail' import SplitDetail from '@/views/ecw/order/components/SplitDetail' import MergeDetail from '@/views/ecw/order/components/MergeDetail' import CargoControlDetail from '@/views/ecw/order/components/CargoControlDetail' import BoxSplitDetail from '@/views/ecw/order/components/BoxSplitDetail' // 流程实例的详情页,可用于审批 export default { name: "ProcessInstanceDetail", components: { Parser, OfferSpecialDetail, warehouseDetails, shippingDetail, SplitDetail, MergeDetail, CargoControlDetail, BoxSplitDetail }, computed:{ matterNum(){ return this.$store.state.user.matterNum }, businessKeyToComponent(){ if(!this.processInstance.processDefinition || !this.processInstance.processDefinition.formCustomViewPath){ return false } const map = { "shippingDetail": { component: "shippingDetail", processId: this.processInstance.businessKey, }, // 报价单特价审核,原来配置的组件名 "special-discount": { component: 'OfferSpecialDetail', id: this.processInstance.businessKey, type: 1 }, // 报价单特价审核,符合命名规则的组件名 offer_special: { component: 'OfferSpecialDetail', id: this.processInstance.businessKey, type: 1 }, offer_commission: { component: 'OfferSpecialDetail', id: this.processInstance.businessKey, type: 2 }, // 原来的费用申请 free_apply: { component: "warehouseDetails", processId: this.processInstance.businessKey, type: 2 }, // 2.0空运加的批量申请 batch_free_apply: { component: () => import('@/views/ecw/order/components/BatchFeeApplication'), businessId: this.processInstance.businessKey, processInstanceId: this.$route.query.id }, retired_warehouse: { component: "warehouseDetails", processId: this.processInstance.businessKey, type: 3 }, warehouse_transfer: { component: "warehouseDetails", processId: this.processInstance.businessKey, type: 1 }, warehouse_update: { component: "warehouseDetails", processId: this.processInstance.businessKey, type: 4 }, container_modify: { component: "shippingDetail", processId: this.processInstance.businessKey, }, trailer_modify: { component: "shippingDetail", processId: this.processInstance.businessKey, }, ship_modify: { component: "shippingDetail", processId: this.processInstance.businessKey, }, customs_declare_modify: { component: "shippingDetail", processId: this.processInstance.businessKey, }, arrival_modify: { component: "shippingDetail", processId: this.processInstance.businessKey, }, customs_clearance_modify: { component: "shippingDetail", processId: this.processInstance.businessKey, }, customs_exit_part: { component: "shippingDetail", processId: this.processInstance.businessKey, }, customs_exit_all: { component: "shippingDetail", processId: this.processInstance.businessKey, }, start_port_modify: { component: "shippingDetail", processId: this.processInstance.businessKey, }, book_space_modify: { component: "shippingDetail", processId: this.processInstance.businessKey, }, sorting_apply_no: { component: "shippingDetail", processId: this.processInstance.businessKey, }, sorting_apply: { component: "shippingDetail", processId: this.processInstance.businessKey, }, unload_container_no: { component: "shippingDetail", processId: this.processInstance.businessKey, }, unload_container: { component: "shippingDetail", processId: this.processInstance.businessKey, type: "unload_container", }, close_container: { component: "shippingDetail", processId: this.processInstance.businessKey, type: "close_container", }, shipment_preassemble: { component: "shippingDetail", processId: this.processInstance.businessKey, }, close_container_no: { component: "shippingDetail", processId: this.processInstance.businessKey, }, // 拆单审核 split_detail: { component: "SplitDetail", id: this.processInstance.businessKey, }, // 出货拆单审核 shipment_split_detail: { component: "BoxSplitDetail", id: this.processInstance.businessKey, }, // 退场拆单,跟出货装柜拆单一样 exit_split: { component: "BoxSplitDetail", id: this.processInstance.businessKey, }, merge_detail: { component: "MergeDetail", id: this.processInstance.businessKey, }, // 放货修改 order_update_release: { component: 'CargoControlDetail', id: this.processInstance.businessKey, applyType: 7 }, // 反复核 order_fallback: { component: 'CargoControlDetail', id: this.processInstance.businessKey, applyType: 8 }, // 调货审核 order_transfer: { component: 'CargoControlDetail', id: this.processInstance.businessKey, applyType: 9 }, // 取消放货审核 order_cancel_release: { component: 'CargoControlDetail', id: this.processInstance.businessKey, applyType: 10 }, // 提单审核 order_landing_bill:{ component: () => import('@/views/ecw/order/components/LandingBillDetail'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, // 订单审核详情 order_approval:{ component: () => import('@/views/ecw/order/components/ApprovalDetail'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, // 订单修改审批 order_update:{ component: () => import('@/views/ecw/order/components/UpdateDetail'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, // 预付异常审核 prepay_excetion:{ component: () => import('@/views/ecw/order/components/PrepayExceptionDetail'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, // 客户延期 customer_delay:{ component: () => import('@/views/ecw/customer/components/Delay'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, //付款单审核-所有审核流程详情组件 finance_payment_approve:{ component: () => import('@/views/ecw/financial/components/PaymentApproval'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, //收款单审核 finance_receipt_approve:{ component: () => import('@/views/ecw/financial/components/CollectionApproval'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, //收款单核销 finance_receipt_write_off:{ component: () => import('@/views/ecw/financial/components/CollectionWriteoff'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, //收款单反核销审核 finance_receipt_write_off_no:{ component: () => import('@/views/ecw/financial/components/CollectionApproval'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, //收款单核销反审核 finance_receipt_approve_no:{ component: () => import('@/views/ecw/financial/components/CollectionApproval'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, //收款单银行实收明细核销 finance_receipt_item_write_off:{ component: () => import('@/views/ecw/financial/components/CollectionBankDetail'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, //收款单银行实收反核销 finance_receipt_item_write_off_no:{ component: () => import('@/views/ecw/financial/components/CollectionBankDetail'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, //佣金付款单审核详情-关于佣金付款单所有审核详情 commissionPaymentDetails:{ component: ()=>import('@/views/ecw/financial/components/commissionPaymentDetails.vue'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, /*撤销拆单*/ split_revoke:{ component: () => import("@/views/ecw/order/components/SplitRevokeDetail"), id: this.processInstance.businessKey }, // 不可出渠道异常审核 not_shiping_channel:{ component: () => import('@/views/ecw/order/components/NotShipingChannel'), id: this.processInstance.businessKey, path: this.processInstance.processDefinition?.formCustomViewPath }, // 出货批量加价审核 box_batch_markup:{ component: () => import('@/views/ecw/box/components/batchMakeUpDetail.vue'), processId: this.processInstance.businessKey, type: this.processInstance.processDefinition?.formCustomViewPath }, // 可获移交详情 customer_handover_details:{ component: () => import('@/views/ecw/customer/components/customer-handover-details.vue'), processId: this.processInstance.businessKey, type: this.processInstance.processDefinition?.formCustomViewPath }, // 出货审核 air_shipment: { component: "shippingDetail", processId: this.processInstance.businessKey, type: 'shipment' }, //排单分拣审核 shipment_order_sorting: { component: "shippingDetail", processId: this.processInstance.businessKey }, // 出货反审 shipment_review: { component: "shippingDetail", processId: this.processInstance.businessKey }, // 删单退场 customs_declare_remove: { component: "shippingDetail", processId: this.processInstance.businessKey, type: 'deleteExit' }, // 空运到仓审核 air_warehouse: { component: "shippingDetail", processId: this.processInstance.businessKey, type: 'air_warehouse' }, // 空运到仓审核 air_warehouse_no: { component: "shippingDetail", processId: this.processInstance.businessKey, type: 'air_warehouse' }, revoke_clear:{ component:() => import("@/views/ecw/box/components/RevokeClear"), id: this.processInstance.businessKey } } console.log('formCustomViewPath', this.processInstance.processDefinition.formCustomViewPath.trim()) return map[this.processInstance.processDefinition.formCustomViewPath.trim()] } }, data() { return { // 提交中 loading: false, // 遮罩层 processInstanceLoading: true, // 流程实例 id: undefined, // 流程实例的编号 processInstance: {}, // 流程表单详情 detailForm: { fields: [] }, // BPMN 数据 bpmnXML: null, bpmnControlForm: { prefix: "activiti" }, activityList: [], // 审批记录 tasksLoad: true, tasks: [], // 审批表单 runningTasks: [], auditForms: [], auditRule: { comment: [{required: true, message: this.$t("审批建议不能为空"), trigger: "blur"}], }, // 转派审批人 userOptions: [], updateAssignee: { open: false, form: { assigneeUserId: undefined, }, rules: { assigneeUserId: [{required: true, message: this.$t("新审批人不能为空"), trigger: "change"}], } }, // 数据字典 categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY), }; }, created() { this.id = this.$route.query.id; if (!this.id) { this.$message.error('未传递 id 参数,无法查看流程信息'); return; } this.getDetail(); // 获得用户列表 this.userOptions = []; listSimpleUsers().then(response => { this.userOptions.push(...response.data); }); }, methods: { /** 获得流程实例 */ getDetail() { // 获得流程实例相关 this.processInstanceLoading = true; getProcessInstance(this.id).then(response => { if (!response.data) { this.$message.error('查询不到流程信息!'); return; } // 设置流程信息 this.processInstance = response.data; console.log(this.processInstance) // 设置表单信息 if (this.processInstance.processDefinition.formType === 10) { this.detailForm = { ...JSON.parse(this.processInstance.processDefinition.formConf), disabled: true, // 表单禁用 formBtns: false, // 按钮隐藏 fields: decodeFields(this.processInstance.processDefinition.formFields) } // 设置表单的值 this.detailForm.fields.forEach(item => { const val = this.processInstance.formVariables[item.__vModel__] if (val) { item.__config__.defaultValue = val } }); } // 加载流程图 getProcessDefinitionBpmnXML(this.processInstance.processDefinition.id).then(response => { this.bpmnXML = response.data }); // 加载活动列表 getActivityList({ processInstanceId: this.processInstance.id }).then(response => { this.activityList = response.data; }); // 取消加载中 this.processInstanceLoading = false; }); // 获得流程任务列表(审批记录) this.tasksLoad = true; this.runningTasks = []; this.auditForms = []; getTaskListByProcessInstanceId(this.id).then(response => { // 审批记录 this.tasks = response.data; // 排序,将未完成的排在前面,已完成的排在后面; this.tasks.sort((a, b) => { // 有已完成的情况,按照完成时间倒序 if (a.endTime && b.endTime) { return b.endTime - a.endTime; } else if (a.endTime) { return 1; } else if (b.endTime) { return -1; // 都是未完成,按照创建时间倒序 } else { return b.createTime - a.createTime; } }); // 需要审核的记录 const userId = store.getters.userId; this.tasks.forEach(task => { if (task.result !== 1) { // 只有待处理才需要 return; } if (!task.assigneeUser || task.assigneeUser.id !== userId) { // 自己不是处理人 return; } this.runningTasks.push({...task}); this.auditForms.push({ comment: '' }) }); // 取消加载中 this.tasksLoad = false; }); }, /** 处理选择流程的按钮操作 **/ handleSelect(row) { // 设置选择的流程 this.selectProcessInstance = row; // 流程表单 if (row.formId) { // 设置对应的表单 this.detailForm = { ...JSON.parse(row.formConf), fields: decodeFields(row.formFields) } } else if (row.formCustomCreatePath) { this.$router.push({path: row.formCustomCreatePath}); // 这里暂时无需加载流程图,因为跳出到另外个 Tab; } }, /** 提交按钮 */ submitForm(params) { if (!params) { return; } // 设置表单禁用 const conf = params.conf; conf.disabled = true; // 表单禁用 conf.formBtns = false; // 按钮隐藏 // 提交表单,创建流程 const variables = params.values; createProcessInstance({ processDefinitionId: this.selectProcessInstance.id, variables: variables }).then(response => { this.$modal.msgSuccess("发起流程成功"); // 关闭当前窗口 this.$tab.closeOpenPage(); this.$router.go(-1); }).catch(() => { conf.disabled = false; // 表单开启 conf.formBtns = true; // 按钮展示 }) }, getDateStar(ms) { return getDate(ms); }, getTimelineItemIcon(item) { if (item.result === 1) { return 'el-icon-time'; } if (item.result === 2) { return 'el-icon-check'; } if (item.result === 3) { return 'el-icon-close'; } if (item.result === 4) { return 'el-icon-remove-outline'; } return ''; }, getTimelineItemType(item) { if (item.result === 1) { return 'primary'; } if (item.result === 2) { return 'success'; } if (item.result === 3) { return 'danger'; } if (item.result === 4) { return 'info'; } return ''; }, /** 处理审批通过和不通过的操作 */ handleAudit(task, pass) { const index = this.runningTasks.indexOf(task); this.$refs['form' + index][0].validate(valid => { if (!valid) { return; } const data = { id: task.id, comment: this.auditForms[index].comment, copyUserIds: this.auditForms[index].copyUserIds } this.loading = true if (pass) { approveTask(data).then(response => { let p = this.matterNum //this.$store.commit('GET_MAATER', --p) this.$modal.msgSuccess("审批通过成功!"); this.getDetail(); // 获得最新详情 }).finally(() => { this.loading = false }); } else { rejectTask(data).then(response => { let p = this.matterNum //this.$store.commit('GET_MAATER', --p) this.$modal.msgSuccess("审批不通过成功!"); this.getDetail(); // 获得最新详情 }).finally(() => { this.loading = false }); } }); }, /** 处理转派审批人 */ handleUpdateAssignee(task) { // 设置表单 this.resetUpdateAssigneeForm(); this.updateAssignee.form.id = task.id; // 设置为打开 this.updateAssignee.open = true; }, /** 提交转派审批人 */ submitUpdateAssigneeForm() { this.$refs['updateAssigneeForm'].validate(valid => { if (!valid) { return; } updateTaskAssignee(this.updateAssignee.form).then(response => { this.$modal.msgSuccess("转派任务成功!"); let p = this.matterNum // this.$store.commit('GET_MAATER', --p) this.updateAssignee.open = false; this.getDetail(); // 获得最新详情 }); }); }, /** 取消转派审批人 */ cancelUpdateAssigneeForm() { this.updateAssignee.open = false; this.resetUpdateAssigneeForm(); }, /** 重置转派审批人 */ resetUpdateAssigneeForm() { this.updateAssignee.form = { id: undefined, assigneeUserId: undefined, }; this.resetForm("updateAssigneeForm"); }, /** 处理审批退回的操作 */ handleDelegate(task) { this.$modal.msgError("暂不支持【委派】功能,可以使用【转派】替代!"); }, /** 处理审批退回的操作 */ handleBack(task) { this.$modal.msgError("暂不支持【退回】功能!"); } } }; </script> <style lang="scss" scoped> .my-process-designer { height: calc(100vh - 200px); } .box-card { width: 100%; margin-bottom: 20px; } </style>