YanZhuProject/yanzhu-ui/src/views/flowable/task/todo/detail/approveDrawer.vue

909 lines
29 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="app-approveDrawer">
<el-drawer
v-if="onOpen"
:visible.sync="onOpen"
ref="drawer"
direction="rtl"
@close="closeCallBack"
size="70%"
z-index="10002"
>
<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 style="padding-left: 0px !important">
<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.parProjName }}
</el-form-item>
<el-form-item label="项目名称">
{{ initData.projName }}
</el-form-item>
<el-form-item label="申请类型">
{{ title }}
</el-form-item>
<el-form-item label="申请人">
<label
>{{ initData.createBy }}
<el-tag type="info" size="mini">{{
initData.createByDeptName
}}</el-tag></label
>
</el-form-item>
<el-form-item label="申请原因">
{{ initData.applyReason }}
</el-form-item>
<el-form-item label="申请附件" v-if="initData.applyFiles">
<div v-for="(file, index) in initData.applyFiles.split(',')" :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="申请时间">
{{ options.createTime }}
</el-form-item>
<el-form-item label="使用时间" v-if="initData.useTime">
{{ parseTime(initData.useTime, "{y}-{m}-{d} {h}:{i}") }}
</el-form-item>
<el-divider content-position="left">申请明细信息</el-divider>
<el-table
ref="proProjectApplyDetail"
:data="initData.proProjectApplyDetailList"
>
<el-table-column
label="序号"
width="50"
type="index"
align="center"
></el-table-column>
<el-table-column label="申请明细" align="left">
<template slot-scope="scope">
<el-breadcrumb separator=">">
<el-breadcrumb-item>{{ scope.row.typeName }}</el-breadcrumb-item>
<el-breadcrumb-item class="assetsName">{{
scope.row.assetsName
}}</el-breadcrumb-item>
</el-breadcrumb>
</template>
</el-table-column>
<el-table-column
label="申请规格"
prop="assetsVersion"
width="180"
align="center"
v-if="showAssetsVersion"
></el-table-column>
<el-table-column label="申请数量" prop="number" width="180" align="center">
<template slot-scope="scope">
<label>{{ scope.row.number }} {{ scope.row.assetsUnit }}</label>
</template>
</el-table-column>
<el-table-column
label="使用说明"
prop="useReason"
align="center"
></el-table-column>
</el-table>
<el-divider content-position="left">申请办理信息</el-divider>
<el-form-item label="审批意见" prop="comment">
<el-input
type="textarea"
v-model="form.comment"
placeholder="请输入审批意见最多500字"
rows="5"
/>
</el-form-item>
<el-form-item label="退回节点" v-if="returnTask">
<el-select
v-model="form.targetKey"
placeholder="请选择退回节点"
style="width: 100%"
>
<el-option
v-for="(item, index) in returnTaskList"
:key="index"
:label="item.name"
:value="item.id"
>
</el-option>
</el-select>
</el-form-item>
<div style="text-align: center">
<el-button icon="el-icon-s-custom" plain size="mini" @click="handleDelegate"
>任务委派</el-button
>
<el-button
icon="el-icon-position"
type="primary"
plain
size="mini"
@click="handleAssign"
>任务转办</el-button
>
<el-button
icon="el-icon-check"
type="success"
size="mini"
@click="handlePass"
>审批通过</el-button
>
<el-button
icon="el-icon-back"
type="warning"
size="mini"
@click="handleBack"
>退回任务</el-button
>
<el-button
icon="el-icon-circle-close"
type="danger"
size="mini"
@click="handleReject"
>驳回上级</el-button
>
</div>
</el-form>
<div class="page-warning">
<p>
<strong style="color: #606266">“任务委派”</strong>
操作后,由指定人跟进处理,处理完成后继续由您进行审批。
</p>
<p>
<strong style="color: #a3d3ff">“任务转办”</strong>
操作后,任务由指定人进行处理。
</p>
<p>
<strong style="color: #67c23a">“审批通过”</strong>
操作后,代表您对当前任务满意,任务进入下一阶段。
</p>
<p>
<strong style="color: #e6a23c">“退回任务”</strong>
操作后,代表您对当前任务不满意,任务进入您选择的阶段或退回给申请人重新申请。
</p>
<p>
<strong style="color: #f56c6c">“驳回上级”</strong>
操作后,代表您对当前任务不满意,任务退回到上一阶段继续处理。
</p>
</div>
<!--占位使用...-->
<div style="width: 100%; height: 2px"></div>
</div>
</div>
</el-drawer>
<!--选择人员-->
<el-dialog
title="选择人员"
:visible.sync="userVisible"
width="60%"
:close-on-press-escape="false"
:show-close="false"
z-index="999999"
v-loading="delegateLoading"
>
<flow-user :checkType="checkType" @handleUserSelect="handleUserSelect"></flow-user>
<span slot="footer" class="dialog-footer">
<el-button @click="userVisible = false">取 消</el-button>
<el-button type="primary" @click="checkUserComplete">确 定</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import store from "@/store";
import { flowXmlAndNode } from "@/api/flowable/definition";
import { CustomViewer as BpmnViewer } from "@/components/customBpmn";
import {
complete,
rejectTask,
returnTask,
assignTask,
delegateTask,
returnList,
} from "@/api/flowable/todo";
import FlowUser from "@/components/flow/User";
import { findCommentByProcInsId } from "@/api/flowable/businessKey";
import { getProjectApply } from "@/api/project/projectApply";
export default {
components: { FlowUser },
props: {
closeCallBack: {
type: Function,
},
},
data() {
return {
// 抽屉层
onOpen: false,
// 遮罩层
loading: false,
delegateLoading: 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: "160px" },
initData: {},
returnTaskList: [], // 回退列表数据
returnTask: false,
returnTaskBtn: false,
showAssetsVersion: false,
userVisible: false,
// 选类型
checkType: "single",
// 选中的值
selectValue: null,
delegateType: null,
};
},
computed: {},
watch: {},
created() {},
mounted() {},
beforeDestroy() {},
methods: {
/** 通过任务 */
handlePass() {
this.$refs["form"].validate((valid) => {
if (valid) {
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.$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("取消操作");
});
}
});
},
/** 退回任务 */
handleBack() {
if (!this.returnTask) {
this.returnTask = true;
this.$modal.msgWarning("请选择退回节点");
return;
}
this.$refs["form"].validate((valid) => {
if (valid) {
if (!this.form.targetKey) {
this.$modal.msgWarning("请选择退回节点");
return;
}
this.$confirm("是否确认退回当前流程申请?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning",
})
.then(() => {
this.loading = true;
returnTask(this.form).then((res) => {
this.$modal.msgSuccess("任务退回成功");
this.loading = false;
//关闭并刷新列表
this.$refs.drawer.closeDrawer();
});
})
.catch(() => {
console.log("取消操作");
});
}
});
},
initReturnList() {
returnList(this.form).then((res) => {
this.returnTaskList = res.data;
//退回节点>1时可显示退回按钮
if (res.data.length > 1) {
this.returnTaskBtn = true;
}
});
},
initFormDate() {
let formData = {
taskId: this.options.taskId,
procInsId: this.options.procInsId,
instanceId: this.options.procInsId,
deployId: this.options.deployId,
executionId: this.options.procInsId,
};
this.form = formData;
this.returnTask = false;
this.returnTaskBtn = false;
this.initReturnList();
},
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;
//物资设备类需要输入规格
if (options.category == 1) {
this.showAssetsVersion = true;
} else {
this.showAssetsVersion = false;
}
this.title = options.procDefName;
this.deptName = options.startDeptName;
this.nickName = options.startUserName;
this.onOpen = true;
this.showjd = false;
this.initFormDate();
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.businessKey);
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: "100px",
});
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(businessKey) {
getProjectApply(businessKey)
.then((res) => {
if (res.code == 200) {
this.initData = res.data;
} else {
this.$message.error("数据异常,请联系管理员...");
}
})
.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;
}
}
},
// 任务委派
handleDelegate() {
this.delegateType = "Delegate";
this.userVisible = true;
},
//任务转办
handleAssign() {
this.delegateType = "Assign";
this.userVisible = true;
},
// 用户选中数据
handleUserSelect(selection) {
this.selectValue = null;
if (selection) {
this.selectValue = selection;
}
},
checkUserComplete() {
if (this.selectValue) {
let param = {
taskId: this.options.taskId,
procInsId: this.options.procInsId,
instanceId: this.options.procInsId,
assignee: this.selectValue.userId
};
const that = this;
if (this.delegateType == "Delegate") {
param.comment=this.$store.getters.nickname+" 将申请流程委派到 "+ this.selectValue.nickName;
this.$modal
.confirm(
"是否确认“委派”当前申请流程给 " + this.selectValue.nickName + "处理?"
)
.then(function () {
that.delegateLoading = true;
return delegateTask(param);
})
.then(() => {
this.delegateLoading = false;
this.userVisible = false;
//关闭并刷新列表
this.$refs.drawer.closeDrawer();
})
.catch(() => {});
} else {
param.comment=this.$store.getters.nickname+" 将申请流程转办到 "+ this.selectValue.nickName;
this.$modal
.confirm(
"是否确认“转办”当前申请流程给 " + this.selectValue.nickName + "处理?"
)
.then(function () {
that.delegateLoading = true;
return assignTask(param);
})
.then(() => {
this.delegateLoading = false;
this.userVisible = false;
//关闭并刷新列表
this.$refs.drawer.closeDrawer();
})
.catch(() => {});
}
} else {
this.$modal.msgWarning("请选择人员信息");
}
},
},
};
</script>
<style lang="scss">
.app-approveDrawer {
.drawer {
width: 100%;
height: 100%;
padding-left: 20px;
padding-right: 20px;
padding-bottom: 20px;
.drawerLeft {
width: 50%;
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: 100px;
position: absolute;
z-index: 9999;
top: 77px;
}
}
.drawerRight {
width: 50%;
min-width: 400px;
height: 100%;
float: left;
padding-left: 20px;
}
}
.containers {
width: 100%;
height: 100px;
.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: 100px;
}
.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;
}
}
}
.page-warning {
padding: 8px 16px;
background-color: #f0f9eb;
border-radius: 4px;
border-left: 5px solid #67c23a;
font-size: 13px;
margin: 20px 0;
}
.assetsName .el-breadcrumb__inner {
font-weight: 800 !important;
color: #409eff !important;
}
.el-divider .el-divider__text {
color: #000000;
font-weight: 600;
}
</style>