656 lines
16 KiB
Vue
656 lines
16 KiB
Vue
<template>
|
||
<view>
|
||
<view class="fixedheader">
|
||
<headers :showBack="true">
|
||
<view class="headerName">
|
||
流程详情
|
||
</view>
|
||
</headers>
|
||
</view>
|
||
<view v-if="instanceData">
|
||
<scroll-view class="w-instace" scroll-y show-scrollbar @scroll="scroll"
|
||
:style="{ height: contentHeight + 'px' }">
|
||
<view class="w-instace-header" v-if="instanceData.staterUser">
|
||
<avatar :name="instanceData.staterUser.name" :src="getRes(instanceData.staterUser.avatar)" showY>
|
||
</avatar>
|
||
<view>
|
||
<view style="display: flex; align-items: center">
|
||
<text style="margin-right: 10px">{{
|
||
instanceData.processDefName
|
||
}}</text>
|
||
<uni-tag circle="true" :text="instanceData.status"
|
||
:type="getProcTag(instanceData.result).type" inverted></uni-tag>
|
||
</view>
|
||
<view>编号:{{ instanceData.instanceId }}</view>
|
||
</view>
|
||
<image v-if="instanceData.statusImg" :src="instanceData.statusImg" mode="aspectFill"></image>
|
||
</view>
|
||
<view class="w-instace-form">
|
||
<form-render :jsonConf="instanceData.formItems" :config="instanceData.formConfig"
|
||
v-model="instanceData.formData"></form-render>
|
||
</view>
|
||
<view class="w-instace-process">
|
||
<process-progress :progress="instanceData.progress" :status="instanceData.status"
|
||
:result="instanceData.result"></process-progress>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="w-instace-action" v-if="(instanceData.operationPerm || !instanceData.finishTime) && instanceData.result === 'RUNNING'">
|
||
<view class="w-action">
|
||
<view @click="doAction('comment')">
|
||
<uni-icons2 type="chat" class="uni-icons_34"></uni-icons2>
|
||
<view>评论</view>
|
||
</view>
|
||
<!-- <view class="w-action-dis" @click="urging" v-if="!instanceData.finishTime">
|
||
<uni-icons2 type="notification" size="34"></uni-icons2>
|
||
<view>催办</view>
|
||
</view> -->
|
||
<view @click="showMore">
|
||
<uni-icons2 type="more-filled" class="uni-icons_34"></uni-icons2>
|
||
<view>更多</view>
|
||
</view>
|
||
<view class="w-action-main">
|
||
<button v-if="instanceData.operationPerm?.refuse?.show && rejectionBtn"
|
||
@click="doAction('recall')">
|
||
退回
|
||
</button>
|
||
<button v-if="instanceData.operationPerm?.refuse?.show && !rejectionBtn"
|
||
@click="doAction('refuse')">
|
||
{{ instanceData.operationPerm.refuse.alisa }}
|
||
</button>
|
||
<button type="primary" v-if="instanceData.operationPerm?.agree?.show"
|
||
@click="doAction('agree')">
|
||
{{ instanceData.operationPerm.agree.alisa }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<uni-popup ref="actionPopup" type="bottom" background-color="#fff">
|
||
<view class="w-more-title">选择您的审批操作</view>
|
||
<view class="w-action-mores">
|
||
<view v-if="instanceData.operationPerm?.transfer?.show" @click="doAction('transfer')">
|
||
<uni-icons2 type="staff" size="34"></uni-icons2>
|
||
<view>转交</view>
|
||
</view>
|
||
<view v-if="instanceData.operationPerm?.recall?.show" @click="doAction('recall')">
|
||
<uni-icons2 type="undo" size="34"></uni-icons2>
|
||
<view>退回</view>
|
||
</view>
|
||
<view v-if="instanceData.operationPerm?.afterAdd?.show" @click="doAction('afterAdd')">
|
||
<uni-icons2 type="personadd" size="34"></uni-icons2>
|
||
<view>后加签</view>
|
||
</view>
|
||
<view v-if="enableCancel" @click="doAction('cancel')">
|
||
<uni-icons2 type="refreshempty" size="34"></uni-icons2>
|
||
<view>撤销</view>
|
||
</view>
|
||
</view>
|
||
<button class="w-more-close" type="default" @click="actionPopup.close()">
|
||
关 闭
|
||
</button>
|
||
</uni-popup>
|
||
<uni-popup ref="doActionPopup" type="bottom" background-color="#fff">
|
||
<view class="w-more-title">{{ action.title }}</view>
|
||
<view class="w-action-content">
|
||
<view style="display: flex; align-items: center"
|
||
v-if="action.type === 'afterAdd' || action.type === 'transfer'">
|
||
<text>目标人员:</text>
|
||
<view style="flex: 1">
|
||
<user-picker v-model="selectedUser" position="bottom"></user-picker>
|
||
</view>
|
||
</view>
|
||
<view style="display: flex; align-items: center" v-else-if="action.type === 'recall'">
|
||
<text>回退节点:</text>
|
||
<view style="flex: 1">
|
||
<select-picker labelKey="nodeName" valueKey="nodeId" v-model="handerParams.targetNode"
|
||
:options="recallNodes" />
|
||
</view>
|
||
</view>
|
||
|
||
<view style="display: flex; align-items: center" v-if="activeTasks.length > 1">
|
||
<text>处理节点:</text>
|
||
<view style="flex: 1">
|
||
<select-picker placeholder="要处理哪个任务" valueKey="taskId" labelKey="name"
|
||
v-model="handerParams.taskId" :options="activeTasks" />
|
||
</view>
|
||
</view>
|
||
|
||
<uni-easyinput type="textarea" v-model="handerParams.comment.text" :placeholder="action.tip" />
|
||
<view style="margin: 32rpx 0">
|
||
<file-upload ref="attachFile" :formProps="{ maxSize: 10, placeholder: '审批附件' }"
|
||
v-model="attachment.files" />
|
||
<image-upload :formProps="{ maxSize: 10, placeholder: '审批附图' }" v-model="attachment.images" />
|
||
<view style="margin-top: 32rpx" v-if="showSignCt">
|
||
<sign-panel position="bottom" v-model="handerParams.signature" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="w-action-confirm">
|
||
<button type="default" @click="doActionPopup.close()">取消</button>
|
||
<button type="primary" @click="submitAction">确 认</button>
|
||
</view>
|
||
</uni-popup>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {
|
||
computed,
|
||
nextTick,
|
||
onBeforeMount,
|
||
reactive,
|
||
ref,
|
||
watch
|
||
} from "vue";
|
||
import {
|
||
getFormAndProcessProgress,
|
||
approvalTask,
|
||
getEnableRecallNodes,
|
||
getTaskNodeSettings,
|
||
} from "@/api/task";
|
||
import {
|
||
onLoad
|
||
} from "@dcloudio/uni-app";
|
||
import {
|
||
$nEmpty,
|
||
debounce
|
||
} from "@/utils/tool.js";
|
||
import Avatar from "@/components/Avatar.vue";
|
||
import FormRender from "@/components/FormRender.vue";
|
||
import processProgress from "./processProgress.vue";
|
||
import {
|
||
getUserSign
|
||
} from "@/api/org";
|
||
import {
|
||
getProcTag
|
||
} from "@/utils/ProcessUtil.js";
|
||
import OrgPicker from "@/components/OrgPicker.vue";
|
||
import ImageUpload from "@/components/form/ImageUpload.vue";
|
||
import FileUpload from "@/components/form/FileUpload.vue";
|
||
import SignPanel from "@/components/form/SignPanel.vue";
|
||
import UserPicker from "@/components/form/UserPicker.vue";
|
||
import SelectPicker from "@/components/common/SelectPicker.vue";
|
||
import {
|
||
getRes
|
||
} from "@/utils/tool.js";
|
||
|
||
const nodeId = ref(null);
|
||
const instanceData = ref(null);
|
||
const selectedUser = ref([]);
|
||
const recallNodes = ref([]);
|
||
const actionPopup = ref();
|
||
const doActionPopup = ref();
|
||
const showSign = ref(false);
|
||
const attachFile = ref();
|
||
const action = ref({
|
||
type: "",
|
||
title: null,
|
||
tip: null,
|
||
});
|
||
//审批参数
|
||
const attachment = reactive({
|
||
files: [],
|
||
images: [],
|
||
});
|
||
const handerParams = reactive({
|
||
instanceId: null,
|
||
taskId: null,
|
||
comment: {
|
||
text: null,
|
||
attachments: [],
|
||
},
|
||
formData: {},
|
||
signature: null,
|
||
action: null,
|
||
updateSign: false,
|
||
targetNode: null,
|
||
targetUser: null,
|
||
});
|
||
|
||
onLoad((v) => {
|
||
nodeId.value = v.nodeId;
|
||
getInstanceData(v.instanceId, v.nodeId);
|
||
});
|
||
|
||
//当前登录的用户
|
||
const loginUser = JSON.parse(uni.getStorageSync("loginUser"));
|
||
|
||
const rejectionBtn = computed(() => {
|
||
if (["文明施工", "安全检查", "质量问题", "质量监督检查"].includes(instanceData.value.processDefName)) {
|
||
return instanceData.value.progress.length == 2 ? false : true;
|
||
}
|
||
return false;
|
||
})
|
||
|
||
const showSignCt = computed(() => {
|
||
return showSign.value && action.value.type === "agree";
|
||
});
|
||
|
||
const enableCancel = computed(() => {
|
||
try {
|
||
return instanceData.value?.externSetting?.enableCancel;
|
||
} catch (e) {
|
||
return false;
|
||
}
|
||
});
|
||
|
||
//过滤本人下待处理的节点
|
||
const activeTasks = computed(() => {
|
||
let tasks = [];
|
||
(instanceData.value.progress || []).forEach((task) => {
|
||
if (task.isFuture) return;
|
||
if (task.users) {
|
||
task.users.forEach((tk) => {
|
||
if (
|
||
tk.user &&
|
||
tk.user.id === loginUser.userId &&
|
||
!$nEmpty(tk.finishTime)
|
||
) {
|
||
tasks.push(tk);
|
||
}
|
||
});
|
||
} else {
|
||
if (
|
||
task.user &&
|
||
task.user.id === loginUser.userId &&
|
||
!$nEmpty(task.finishTime)
|
||
) {
|
||
tasks.push(task);
|
||
}
|
||
}
|
||
});
|
||
return tasks;
|
||
});
|
||
|
||
const contentHeight = computed(() => {
|
||
const h = uni.getSystemInfoSync().windowHeight;
|
||
// #ifdef APP-PLUS
|
||
if (plus) {
|
||
return instanceData.value.result != 'RUNNING' ?
|
||
h :
|
||
h - uni.upx2px(88) - uni.upx2px(44) - uni.upx2px(60) - uni.upx2px(88);;
|
||
}
|
||
// #endif
|
||
|
||
// console.log(11111111222222222, instanceData.value.finishTime);
|
||
return instanceData.value.result != 'RUNNING' ?
|
||
h :
|
||
h - uni.upx2px(88) - uni.upx2px(40) - uni.upx2px(44);
|
||
});
|
||
|
||
//滚动延时去抖动
|
||
const scDebounce = debounce(() => uni.$emit("showFp"), 500);
|
||
|
||
watch(
|
||
() => handerParams.taskId,
|
||
() => {
|
||
getTaskSet();
|
||
}
|
||
);
|
||
|
||
function getTaskSet() {
|
||
getTaskNodeSettings(handerParams.taskId)
|
||
.then((rsp) => {
|
||
showSign.value = rsp.data.enableSign || false;
|
||
})
|
||
.catch((e) => {});
|
||
}
|
||
|
||
function getInstanceData(instId, nodeId) {
|
||
getFormAndProcessProgress(instId, nodeId).then((rsp) => {
|
||
instanceData.value = rsp.data;
|
||
//设置状态图标
|
||
switch (instanceData.value.result) {
|
||
case "PASS":
|
||
instanceData.value.statusImg = "/static/image/agree.png";
|
||
break;
|
||
case "CANCEL":
|
||
instanceData.value.statusImg = "/static/image/recall.png";
|
||
break;
|
||
case "REFUSE":
|
||
instanceData.value.statusImg = "/static/image/refuse.png";
|
||
break;
|
||
}
|
||
});
|
||
}
|
||
|
||
function showMore() {
|
||
actionPopup.value.open();
|
||
}
|
||
|
||
function urging() {
|
||
uni.showModal({
|
||
title: "催办确认",
|
||
content: "您确认要提醒流程中人员尽快处理任务?",
|
||
success: function(res) {
|
||
if (res.confirm) {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "该功能暂未实现哈😘",
|
||
});
|
||
} else if (res.cancel) {}
|
||
},
|
||
});
|
||
}
|
||
|
||
function doAction(type) {
|
||
handerParams.taskId = getTask();
|
||
selectedUser.value = [];
|
||
handerParams.targetUser = null;
|
||
handerParams.comment.text = "";
|
||
handerParams.comment.attachments = [];
|
||
switch (type) {
|
||
case "comment":
|
||
action.value = {
|
||
type: type,
|
||
title: "添加评论",
|
||
tip: "评论内容",
|
||
};
|
||
break;
|
||
case "transfer":
|
||
action.value = {
|
||
type: type,
|
||
title: "转交流程",
|
||
tip: "转交意见",
|
||
};
|
||
break;
|
||
case "afterAdd":
|
||
action.value = {
|
||
type: type,
|
||
title: "流程节点加签",
|
||
tip: "加签意见",
|
||
};
|
||
break;
|
||
case "recall":
|
||
action.value = {
|
||
type: type,
|
||
title: "退回流程",
|
||
tip: "退回意见",
|
||
};
|
||
getEnableRecallNode();
|
||
break;
|
||
case "cancel":
|
||
action.value = {
|
||
type: type,
|
||
title: "撤销流程",
|
||
tip: "撤销原因",
|
||
};
|
||
break;
|
||
case "agree":
|
||
action.value = {
|
||
type: type,
|
||
title: "审批同意",
|
||
tip: "审批意见",
|
||
};
|
||
break;
|
||
case "refuse":
|
||
action.value = {
|
||
type: type,
|
||
title: "审批拒绝",
|
||
tip: "审批意见",
|
||
};
|
||
break;
|
||
}
|
||
doActionPopup.value.open();
|
||
//使文件选择生效
|
||
nextTick(() => attachFile.value.show());
|
||
}
|
||
|
||
function loadBeforeSign() {
|
||
getUserSign().then((rsp) => {
|
||
if (rsp.data === "") {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "您还没有保存过的签名",
|
||
});
|
||
} else {
|
||
handerParams.signature = rsp.data;
|
||
}
|
||
});
|
||
}
|
||
//获取要处理的任务id
|
||
function getTask() {
|
||
if ($nEmpty(handerParams.taskId)) {
|
||
return handerParams.taskId;
|
||
} else if (activeTasks.value.length > 0) {
|
||
return activeTasks.value[0].taskId;
|
||
} else if (action.value.type === "cancel") {
|
||
const pg = instanceData.value.progress || [];
|
||
for (let i = 0; i < pg.length; i++) {
|
||
if (pg[i].users) {
|
||
for (let j = 0; j < pg[i].users.length; j++) {
|
||
if (!$nEmpty(pg[i].users[j].finishTime)) {
|
||
return pg[i].users[j].taskId;
|
||
}
|
||
}
|
||
} else {
|
||
if (!$nEmpty(pg[i].finishTime)) {
|
||
return pg[i].taskId;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return null;
|
||
}
|
||
|
||
function getEnableRecallNode() {
|
||
getEnableRecallNodes(instanceData.value.instanceId, getTask())
|
||
.then((rsp) => {
|
||
recallNodes.value = rsp.data;
|
||
})
|
||
.catch((err) => {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "获取可回退节点失败",
|
||
});
|
||
});
|
||
}
|
||
|
||
function submitAction() {
|
||
handerParams.instanceId = instanceData.value.instanceId;
|
||
handerParams.action = action.value.type;
|
||
handerParams.taskId = getTask();
|
||
handerParams.formData = instanceData.value.formData;
|
||
handerParams.comment.attachments = [
|
||
...attachment.files,
|
||
...attachment.images,
|
||
];
|
||
if (
|
||
(action.value.type === "afterAdd" || action.value.type === "transfer") &&
|
||
!selectedUser.value.length > 0
|
||
) {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "请设置人员",
|
||
});
|
||
} else if (
|
||
action.value.type === "recall" &&
|
||
!$nEmpty(handerParams.targetNode)
|
||
) {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "请选择回退节点",
|
||
});
|
||
} else if (showSignCt.value && !$nEmpty(handerParams.signature)) {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "请签字后再提交",
|
||
});
|
||
} else {
|
||
handerParams.targetUser = selectedUser.value?.[0]?.id;
|
||
approvalTask(handerParams)
|
||
.then((rsp) => {
|
||
uni.showToast({
|
||
icon: "success",
|
||
title: "处理成功",
|
||
});
|
||
actionPopup.value.close();
|
||
doActionPopup.value.close();
|
||
if (action.value.type === "comment") {
|
||
getInstanceData(instanceData.value.instanceId, nodeId.value);
|
||
} else {
|
||
uni.reLaunch({
|
||
url: "/pages/workspace/workspace",
|
||
});
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: err.msg,
|
||
});
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
|
||
function scroll() {
|
||
// #ifdef APP-VUE
|
||
uni.$emit("wv:scorll");
|
||
// #endif
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
:deep(.headerBox) {
|
||
background: #2b8df3;
|
||
color: #fff;
|
||
}
|
||
|
||
page {
|
||
background-color: #f4f5f7;
|
||
}
|
||
|
||
:deep(.w-instace-form) {
|
||
.w-form-item {
|
||
margin-bottom: 0;
|
||
padding-bottom: 16rpx;
|
||
}
|
||
|
||
.w-form-item-r {
|
||
margin-bottom: 16rpx;
|
||
}
|
||
}
|
||
|
||
.w-instace {
|
||
font-size: 32rpx;
|
||
|
||
.w-instace-header,
|
||
.w-instace-form,
|
||
.w-instace-process {
|
||
margin: 32rpx 0;
|
||
padding: 32rpx 16rpx;
|
||
background-color: white;
|
||
}
|
||
|
||
.w-instace-header {
|
||
margin-top: 0;
|
||
position: relative;
|
||
|
||
image {
|
||
position: absolute;
|
||
z-index: 2;
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
right: 32rpx;
|
||
bottom: -96rpx;
|
||
}
|
||
}
|
||
|
||
:deep(.w-instace-form) {
|
||
padding: 32rpx 16rpx;
|
||
|
||
.w-form-item-r {
|
||
padding-bottom: 0;
|
||
font-size: 26rpx;
|
||
|
||
.w-form-title :last-child {
|
||
font-size: 29rpx !important;
|
||
color: #848484;
|
||
}
|
||
}
|
||
}
|
||
|
||
.w-instace-header {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 26rpx;
|
||
|
||
.uni-tag {
|
||
font-weight: 400;
|
||
}
|
||
}
|
||
}
|
||
|
||
.w-instace-action {
|
||
width: 100%;
|
||
height: 144rpx;
|
||
background-color: #efefef;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
|
||
.w-action {
|
||
width: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
font-size: 25rpx;
|
||
text-align: center;
|
||
padding: 0 32rpx;
|
||
|
||
.w-action-main {
|
||
width: 40%;
|
||
display: flex;
|
||
right: 32rpx;
|
||
|
||
&>button {
|
||
// width: 128rpx;
|
||
font-size: 32rpx;
|
||
line-height: 64rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.w-action-mores {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 64rpx;
|
||
font-size: 32rpx;
|
||
|
||
&>view {
|
||
text-align: center;
|
||
}
|
||
}
|
||
|
||
.w-more-close {
|
||
background-color: #e8e8e8 !important;
|
||
border: none !important;
|
||
margin: 0 32rpx 32rpx 32rpx;
|
||
border-radius: 48rpx;
|
||
line-height: 64rpx;
|
||
color: #848484;
|
||
}
|
||
|
||
.w-more-title {
|
||
font-size: 29rpx;
|
||
text-align: center;
|
||
margin-top: 16rpx;
|
||
color: #848484;
|
||
}
|
||
|
||
.w-action-content {
|
||
padding: 32rpx;
|
||
}
|
||
|
||
.w-action-confirm {
|
||
display: flex;
|
||
padding-bottom: 32rpx;
|
||
|
||
button {
|
||
width: 40%;
|
||
font-size: 32rpx;
|
||
line-height: 64rpx;
|
||
}
|
||
}
|
||
</style> |