548 lines
18 KiB
Vue
548 lines
18 KiB
Vue
<template>
|
||
<div class="app-approveDrawer">
|
||
<el-drawer
|
||
v-if="onOpen"
|
||
:visible.sync="onOpen"
|
||
ref="drawer"
|
||
direction="rtl"
|
||
@close="closeCallBack"
|
||
size="80%"
|
||
>
|
||
<template slot="title">
|
||
<div>流程详情 【{{ title }}】 - 当前进度【{{ jdtitle }}】<span v-if="showjd"> - 当前节点【{{options.taskName}}】</span></div>
|
||
</template>
|
||
<div class="drawer">
|
||
<div class="drawerLeft block containers">
|
||
<div class="canvas" ref="flowCanvas"></div>
|
||
<div class="maskLayer" />
|
||
<el-timeline>
|
||
<el-timeline-item
|
||
v-for="(item,index ) in flowRecordList"
|
||
:key="index"
|
||
:icon="setIcon(item)"
|
||
:color="setColor(item)"
|
||
>
|
||
<p style="font-weight: 700"> {{ getSort(index) }}{{item.taskName}}{{item.commentResult}}</p>
|
||
<el-card :body-style="{ padding: '6px' }">
|
||
<el-descriptions class="margin-top" :column="1" size="small" border>
|
||
<el-descriptions-item v-if="item.assigneeName" label-class-name="my-label" :labelStyle="labelStyle">
|
||
<template slot="label"><i class="el-icon-user"></i>办理人</template>
|
||
{{item.assigneeName}}
|
||
<el-tag type="info" size="mini">{{item.deptName}}</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item v-if="item.candidate" label-class-name="my-label" :labelStyle="labelStyle">
|
||
<template slot="label"><i class="el-icon-user"></i>候选办理</template>
|
||
{{item.candidate}}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item v-if="item.deleteReason" label-class-name="my-label" :labelStyle="labelStyle">
|
||
<template slot="label"><i class="el-icon-user"></i>驳回节点</template>
|
||
{{getDeleteReason(item.deleteReason)}}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label-class-name="my-label" :labelStyle="labelStyle">
|
||
<template slot="label"><i class="el-icon-date"></i>接收时间</template>
|
||
{{item.startTime}}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item v-if="item.endTime" label-class-name="my-label" :labelStyle="labelStyle">
|
||
<template slot="label"><i class="el-icon-date"></i>处理时间</template>
|
||
{{item.endTime}}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item v-if="item.duration" label-class-name="my-label" :labelStyle="labelStyle">
|
||
<template slot="label"><i class="el-icon-time"></i>处理耗时</template>
|
||
{{getDurationDate(item.duration)}}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item v-if="item.message" label-class-name="my-label" :labelStyle="labelStyle">
|
||
<template slot="label"><i class="el-icon-tickets"></i>处理意见</template>
|
||
{{item.message}}
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
</el-card>
|
||
</el-timeline-item>
|
||
</el-timeline>
|
||
</div>
|
||
<div class="drawerRight">
|
||
<el-form
|
||
ref="form" :model="form" :rules="rules" label-width="100px" v-loading="loading">
|
||
<el-form-item label="所属项目">
|
||
{{ initData.projectName }}
|
||
</el-form-item>
|
||
<el-form-item label="发起单位">
|
||
{{ options.startDeptName }}
|
||
</el-form-item>
|
||
<el-form-item label="发起人">
|
||
{{ options.startUserName }}
|
||
</el-form-item>
|
||
<el-form-item label="审批事项">
|
||
{{ title }}
|
||
</el-form-item>
|
||
<el-form-item label="审批内容">
|
||
<div v-for="(file,index ) in initData.files" :key="index">
|
||
<el-button
|
||
size="mini"
|
||
type="text"
|
||
icon="el-icon-paperclip"
|
||
@click="handledownload(file)"
|
||
>{{file.substring(file.lastIndexOf('/')+1)}}</el-button
|
||
>
|
||
</div>
|
||
</el-form-item>
|
||
<el-form-item label="申请说明">
|
||
{{initData.remark}}
|
||
</el-form-item>
|
||
<el-form-item label="审批意见" prop="comment">
|
||
<el-input
|
||
type="textarea"
|
||
v-model="form.comment"
|
||
placeholder="请输入审批意见(最多500字)"
|
||
rows="5"
|
||
/>
|
||
</el-form-item>
|
||
<div style="text-align: center">
|
||
<el-button icon="el-icon-check" type="success" size="mini" @click="handlePass">审批通过</el-button>
|
||
<el-button icon="el-icon-circle-close" type="danger" size="mini" @click="handleReject">审批驳回</el-button>
|
||
</div>
|
||
</el-form>
|
||
</div>
|
||
</div>
|
||
</el-drawer>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import store from "@/store";
|
||
import { flowXmlAndNode } from "@/api/flowable/definition";
|
||
import { CustomViewer as BpmnViewer } from "@/components/customBpmn";
|
||
import { complete, rejectTask } from "@/api/flowable/todo";
|
||
import {findCommentByProcInsId,findFormDatasByProcInsId} from "@/api/flowable/businessKey";
|
||
export default {
|
||
components: {},
|
||
props: {
|
||
closeCallBack: {
|
||
type: Function,
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
// 抽屉层
|
||
onOpen: false,
|
||
// 遮罩层
|
||
loading: false,
|
||
// 标题
|
||
title: "",
|
||
jdtitle:"",
|
||
// 表单参数
|
||
form: {},
|
||
// 表单校验
|
||
rules: {
|
||
comment: [
|
||
{ required: true, message: "请输入审批意见", trigger: "blur" },
|
||
{
|
||
max: 500,
|
||
message: "审批意见最多输入500字",
|
||
trigger: "blur",
|
||
},
|
||
],
|
||
},
|
||
bpmnViewer: null,
|
||
options: {},
|
||
flowRecordList: [],
|
||
showjd:false,
|
||
//label样式
|
||
labelStyle: { width: "180px" },
|
||
initData:{}
|
||
};
|
||
},
|
||
computed: {},
|
||
watch: {},
|
||
created() {},
|
||
mounted() {},
|
||
beforeDestroy() {},
|
||
methods: {
|
||
/** 通过任务 */
|
||
handlePass() {
|
||
this.$refs["form"].validate((valid) => {
|
||
if (valid) {
|
||
this.form.taskId = this.options.taskId;
|
||
this.form.taskName = this.options.taskName;
|
||
this.form.userId = store.getters.userId;
|
||
this.form.deployId = this.options.deployId;
|
||
this.form.procInsId = this.options.procInsId;
|
||
this.form.instanceId = this.options.procInsId;
|
||
this.form.executionId = this.options.procInsId;
|
||
this.$confirm('是否确认通过当前流程申请?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.loading=true;
|
||
complete(this.form).then(res => {
|
||
this.$modal.msgSuccess("任务审批成功");
|
||
this.loading = false;
|
||
//关闭并刷新列表
|
||
this.$refs.drawer.closeDrawer();
|
||
});
|
||
}).catch(() => {
|
||
console.log("取消操作");
|
||
});
|
||
}
|
||
});
|
||
},
|
||
/** 驳回任务 */
|
||
handleReject() {
|
||
this.$refs["form"].validate((valid) => {
|
||
if (valid) {
|
||
this.form.taskId = this.options.taskId;
|
||
this.form.taskName = this.options.taskName;
|
||
this.form.userId = store.getters.userId;
|
||
this.form.deployId = this.options.deployId;
|
||
this.form.procInsId = this.options.procInsId;
|
||
this.form.instanceId = this.options.procInsId;
|
||
this.form.executionId = this.options.procInsId;
|
||
this.$confirm('是否确认驳回当前流程申请?', '提示', {
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}).then(() => {
|
||
this.loading=true;
|
||
rejectTask(this.form).then(res => {
|
||
this.$modal.msgSuccess("任务驳回成功");
|
||
this.loading = false;
|
||
//关闭并刷新列表
|
||
this.$refs.drawer.closeDrawer();
|
||
});
|
||
}).catch(() => {
|
||
console.log("取消操作");
|
||
});
|
||
}
|
||
});
|
||
},
|
||
setIcon(row) {
|
||
if (row.endTime) {
|
||
if(row.commentResult=="驳回")
|
||
{
|
||
return "el-icon-close";
|
||
}else{
|
||
return "el-icon-check";
|
||
}
|
||
} else {
|
||
return "el-icon-time";
|
||
}
|
||
},
|
||
setColor(row) {
|
||
if (row.endTime) {
|
||
if(row.commentResult=="驳回")
|
||
{
|
||
return "#f56c6c";
|
||
}else{
|
||
return "#2bc418";
|
||
}
|
||
} else {
|
||
return "#b3bdbb";
|
||
}
|
||
},
|
||
getSort(i) {
|
||
let num = this.flowRecordList.length - i;
|
||
if (num < 10) {
|
||
num = "0" + num;
|
||
}
|
||
return num + ". ";
|
||
},
|
||
doCanel() {
|
||
this.onOpen = false;
|
||
},
|
||
show(options) {
|
||
this.options = options;
|
||
this.title = options.procDefName;
|
||
this.deptName = options.startDeptName;
|
||
this.nickName = options.startUserName;
|
||
this.onOpen = true;
|
||
this.showjd = false;
|
||
this.form = {};
|
||
if(options.finishTime==null){
|
||
this.jdtitle="进行中";
|
||
this.showjd=true;
|
||
}else if(options.finishTime!=null && options.assigneeId==null){
|
||
this.jdtitle="已撤销";
|
||
}else{
|
||
this.jdtitle="已完成";
|
||
}
|
||
this.getFlowRecordList(options.procInsId, options.deployId);
|
||
this.getFormDatasList(options.procInsId, options.deployId);
|
||
flowXmlAndNode({ procInsId:options.procInsId, deployId: options.deployId }).then((res) => {
|
||
this.initFlowImage(res.data);
|
||
});
|
||
},
|
||
async initFlowImage(data) {
|
||
const self = this;
|
||
try {
|
||
self.bpmnViewer = new BpmnViewer({
|
||
container: this.$refs.flowCanvas,
|
||
height: "90px",
|
||
});
|
||
await self.bpmnViewer.importXML(data.xmlData);
|
||
// 自适应
|
||
self.bpmnViewer.get("canvas").zoom("fit-viewport", "auto");
|
||
if (data.nodeData !==undefined && data.nodeData.length > 0 ) {
|
||
self.fillColor(data.nodeData)
|
||
}
|
||
} catch (err) {
|
||
console.error(err.message, err.warnings);
|
||
}
|
||
},
|
||
/** 流程流转记录 */
|
||
getFlowRecordList(procInsId, deployId) {
|
||
// const that = this
|
||
// const params = {procInsId: procInsId, deployId: deployId}
|
||
// flowRecord(params).then(res => {
|
||
// that.flowRecordList = res.data.flowList;
|
||
// }).catch(res => {
|
||
// this.$message.error("数据异常,请联系管理员...");
|
||
// })
|
||
const that = this
|
||
const params = {procInsId: procInsId}
|
||
findCommentByProcInsId(params).then(res => {
|
||
that.flowRecordList = res.data;
|
||
}).catch(res => {
|
||
this.$message.error("数据异常,请联系管理员...");
|
||
})
|
||
},
|
||
/** 流程表单数据 */
|
||
getFormDatasList(procInsId, deployId) {
|
||
// const that = this
|
||
// const params = {procInsId: procInsId, deployId: deployId}
|
||
// flowRecord(params).then(res => {
|
||
// that.flowRecordList = res.data.flowList;
|
||
// }).catch(res => {
|
||
// this.$message.error("数据异常,请联系管理员...");
|
||
// })
|
||
const that = this
|
||
const params = {procInsId: procInsId}
|
||
findFormDatasByProcInsId(params).then(res => {
|
||
this.initData = res.data;
|
||
this.initData.files = res.data.files.split(',');
|
||
}).catch(res => {
|
||
this.$message.error("数据异常,请联系管理员...");
|
||
})
|
||
},
|
||
// 设置高亮颜色的
|
||
fillColor(nodeData) {
|
||
const canvas = this.bpmnViewer.get('canvas')
|
||
this.bpmnViewer.getDefinitions().rootElements[0].flowElements.forEach(n => {
|
||
const completeTask = nodeData.find(m => m.key === n.id)
|
||
const todoTask = nodeData.find(m => !m.completed)
|
||
const endTask = nodeData[nodeData.length - 1]
|
||
if (n.$type === 'bpmn:UserTask') {
|
||
if (completeTask) {
|
||
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
|
||
n.outgoing?.forEach(nn => {
|
||
const targetTask = nodeData.find(m => m.key === nn.targetRef.id)
|
||
if (targetTask) {
|
||
if (todoTask && completeTask.key === todoTask.key && !todoTask.completed){
|
||
canvas.addMarker(nn.id, todoTask.completed ? 'highlight' : 'highlight-todo')
|
||
canvas.addMarker(nn.targetRef.id, todoTask.completed ? 'highlight' : 'highlight-todo')
|
||
}else {
|
||
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
|
||
canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
// 排他网关
|
||
else if (n.$type === 'bpmn:ExclusiveGateway') {
|
||
if (completeTask) {
|
||
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
|
||
n.outgoing?.forEach(nn => {
|
||
const targetTask = nodeData.find(m => m.key === nn.targetRef.id)
|
||
if (targetTask) {
|
||
|
||
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
|
||
canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
|
||
}
|
||
|
||
})
|
||
}
|
||
|
||
}
|
||
// 并行网关
|
||
else if (n.$type === 'bpmn:ParallelGateway') {
|
||
if (completeTask) {
|
||
canvas.addMarker(n.id, completeTask.completed ? 'highlight' : 'highlight-todo')
|
||
n.outgoing?.forEach(nn => {
|
||
const targetTask = nodeData.find(m => m.key === nn.targetRef.id)
|
||
if (targetTask) {
|
||
canvas.addMarker(nn.id, targetTask.completed ? 'highlight' : 'highlight-todo')
|
||
canvas.addMarker(nn.targetRef.id, targetTask.completed ? 'highlight' : 'highlight-todo')
|
||
}
|
||
})
|
||
}
|
||
}
|
||
else if (n.$type === 'bpmn:StartEvent') {
|
||
n.outgoing.forEach(nn => {
|
||
const completeTask = nodeData.find(m => m.key === nn.targetRef.id)
|
||
if (completeTask) {
|
||
canvas.addMarker(nn.id, 'highlight')
|
||
canvas.addMarker(n.id, 'highlight')
|
||
return
|
||
}
|
||
})
|
||
}
|
||
else if (n.$type === 'bpmn:EndEvent') {
|
||
if (endTask.key === n.id && endTask.completed) {
|
||
canvas.addMarker(n.id, 'highlight')
|
||
return
|
||
}
|
||
}
|
||
})
|
||
},
|
||
getDurationDate(val){
|
||
// 计算出相差天数
|
||
let days = Math.floor(val / (24 * 3600 * 1000))
|
||
// 计算出小时数
|
||
let leave1 = val % (24 * 3600 * 1000) // 计算天数后剩余的毫秒数
|
||
let hours = Math.floor(leave1 / (3600 * 1000))
|
||
// 计算相差分钟数
|
||
let leave2 = leave1 % (3600 * 1000) // 计算小时数后剩余的毫秒数
|
||
let minutes = Math.floor(leave2 / (60 * 1000))
|
||
// 计算相差秒数
|
||
let leave3 = leave2 % (60 * 1000) // 计算分钟数后剩余的毫秒数
|
||
let seconds = Math.round(leave3 / 1000)
|
||
if(days>0){
|
||
if(days<10) days = "0"+days;
|
||
if(hours<10) hours = "0"+hours;
|
||
if(minutes<10) minutes = "0"+minutes;
|
||
if(seconds<10) seconds = "0"+seconds;
|
||
return days + '天' + hours + '小时' + minutes + '分钟' + seconds + '秒';
|
||
}
|
||
if(hours>0){
|
||
if(hours<10) hours = "0"+hours;
|
||
if(minutes<10) minutes = "0"+minutes;
|
||
if(seconds<10) seconds = "0"+seconds;
|
||
return hours + '小时' + minutes + '分钟' + seconds + '秒';
|
||
}
|
||
if(minutes>0){
|
||
if(minutes<10) minutes = "0"+minutes;
|
||
if(seconds<10) seconds = "0"+seconds;
|
||
return minutes + '分钟' + seconds + '秒';
|
||
}
|
||
if(seconds>0){
|
||
if(seconds<10) seconds = "0"+seconds;
|
||
return seconds + '秒';
|
||
}
|
||
},
|
||
handledownload(url){
|
||
this.$download.resource(url);
|
||
},
|
||
getDeleteReason(val){
|
||
val = val.replace("Change activity to ","");
|
||
let flowRecordList = this.flowRecordList;
|
||
for(let i=0;i<flowRecordList.length;i++){
|
||
if(flowRecordList[i].taskDefKey==val){
|
||
console.log(i);
|
||
return "驳回至"+flowRecordList[i].taskName;
|
||
}
|
||
}
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
<style lang="scss">
|
||
.app-approveDrawer{
|
||
.drawer {
|
||
width: 100%;
|
||
height: 100%;
|
||
padding-left: 20px;
|
||
padding-right: 20px;
|
||
padding-bottom: 20px;
|
||
.drawerLeft {
|
||
width: 60%;
|
||
min-width: 280px;
|
||
height: 100%;
|
||
float: left;
|
||
border-right: 1px solid #dcdfe6;
|
||
overflow-y: scroll;
|
||
padding-right: 20px;
|
||
.bjs-powered-by {
|
||
display: none !important;
|
||
|
||
}
|
||
.maskLayer {
|
||
width: 100%;
|
||
height: 90px;
|
||
position: absolute;
|
||
z-index: 9999;
|
||
top: 77px;
|
||
}
|
||
}
|
||
.drawerRight {
|
||
width: 40%;
|
||
min-width: 400px;
|
||
height: 100%;
|
||
float: left;
|
||
padding-left: 20px;
|
||
}
|
||
}
|
||
.containers {
|
||
.canvas {
|
||
width: 100%;
|
||
height: 100px;
|
||
}
|
||
.panel {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 50px;
|
||
width: 300px;
|
||
}
|
||
.load {
|
||
margin-right: 10px;
|
||
}
|
||
.el-form-item__label{
|
||
font-size: 13px;
|
||
}
|
||
|
||
.djs-palette{
|
||
left: 0px!important;
|
||
top: 0px;
|
||
border-top: none;
|
||
}
|
||
|
||
.djs-container svg {
|
||
min-height: 650px;
|
||
}
|
||
|
||
.highlight.djs-shape .djs-visual > :nth-child(1) {
|
||
fill: green !important;
|
||
stroke: green !important;
|
||
fill-opacity: 0.2 !important;
|
||
}
|
||
.highlight.djs-shape .djs-visual > :nth-child(2) {
|
||
fill: green !important;
|
||
}
|
||
.highlight.djs-shape .djs-visual > path {
|
||
fill: green !important;
|
||
fill-opacity: 0.2 !important;
|
||
stroke: green !important;
|
||
}
|
||
.highlight.djs-connection > .djs-visual > path {
|
||
stroke: green !important;
|
||
}
|
||
.highlight-todo.djs-connection > .djs-visual > path {
|
||
stroke: orange !important;
|
||
stroke-dasharray: 4px !important;
|
||
fill-opacity: 0.2 !important;
|
||
}
|
||
.highlight-todo.djs-shape .djs-visual > :nth-child(1) {
|
||
fill: orange !important;
|
||
stroke: orange !important;
|
||
stroke-dasharray: 4px !important;
|
||
fill-opacity: 0.2 !important;
|
||
}
|
||
.overlays-div {
|
||
font-size: 10px;
|
||
color: red;
|
||
width: 100px;
|
||
top: -20px !important;
|
||
}
|
||
}
|
||
}
|
||
</style>
|