388 lines
14 KiB
Vue
388 lines
14 KiB
Vue
|
|
<template>
|
|||
|
|
<a-modal :mask-closable="false" :title="dialogTitle" :visible="isOpen" :width="500" centered class="geg"
|
|||
|
|
@cancel="onClickCancel">
|
|||
|
|
<template #footer>
|
|||
|
|
<a-button :disabled="loading || isStart" @click="onClickCancel">{{ isStart ? '请注意流程已发起' : '取消' }}</a-button>
|
|||
|
|
<a-button :loading="loading" type="primary" @click="onClickOK">确定</a-button>
|
|||
|
|
</template>
|
|||
|
|
<div class="dialog-wrap">
|
|||
|
|
<a-form :label-col="{ span: 6 }" :model="formState" autocomplete="off">
|
|||
|
|
<a-form-item v-if="_action === 'agree'" label="下一节点" name="nextNodeName">
|
|||
|
|
<span>{{ getNextNodesName() }}</span>
|
|||
|
|
</a-form-item>
|
|||
|
|
<!--选择任意节点 start-->
|
|||
|
|
<a-form-item v-if="_action === 'select'" label="审批节点" name="selectNextNodeName">
|
|||
|
|
<a-select v-model:value="selected.taskId" :options="allTaskNodes" placeholder="请选择审批节点"
|
|||
|
|
:filterOption="search" :field-names="{ label: 'taskName', value: 'taskId' }"
|
|||
|
|
:disabled="editable"></a-select>
|
|||
|
|
</a-form-item>
|
|||
|
|
<a-form-item v-if="_action === 'select'" label="审批人">
|
|||
|
|
<SelectUser :selectedIds="selected.userId" :multiple="selected.multiple" @change="getUserList"
|
|||
|
|
placeholder="请选择审核人">
|
|||
|
|
<a-input v-model:value="selected.userName" placeholder="请选择审批人" :disabled="editable" />
|
|||
|
|
</SelectUser>
|
|||
|
|
</a-form-item>
|
|||
|
|
<a-form-item v-if="_action === 'select' && selected.choseTime" label="审批时间">
|
|||
|
|
<a-date-picker show-time format="YYYY-MM-DD HH:mm:ss" placeholder="请选择时间"
|
|||
|
|
v-model:value="selected.time" @change="onChange" @ok="onOk" />
|
|||
|
|
</a-form-item>
|
|||
|
|
<!--选择任意节点 end-->
|
|||
|
|
<template v-for="node in flowNextNodes">
|
|||
|
|
<a-form-item v-if="_action === 'agree' && !isEnd"
|
|||
|
|
:label="flowNextNodes.length > 1 ? node.activityName + '审批人' : '审批人'">
|
|||
|
|
<a-select v-show="node.chooseAssign" v-model:value="node.assignees"
|
|||
|
|
:options="node.nextAssignees" :placeholder="'请选择' + node.activityName + '的审批人'"
|
|||
|
|
max-tag-count="responsive" mode="multiple" :filterOption="search"></a-select>
|
|||
|
|
<span v-show="!node.chooseAssign">{{ getAssigneeText(node) }}</span>
|
|||
|
|
</a-form-item>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<a-form-item v-if="_action === 'reject'" label="退回至" name="rejectNode">
|
|||
|
|
<a-select v-model:value="rejectNodeId">
|
|||
|
|
<a-select-option v-for="(item, index) in rejectNodeList" :key="index"
|
|||
|
|
:value="item.activityId">{{
|
|||
|
|
item.activityName }}</a-select-option>
|
|||
|
|
</a-select>
|
|||
|
|
</a-form-item>
|
|||
|
|
<!--选择任意节点:填写备注-->
|
|||
|
|
<a-form-item v-if="_action === 'select'" :label="selected.choseTime ? '审批意见' : '备注'"
|
|||
|
|
name="selectOpinion">
|
|||
|
|
<a-dropdown placement="bottom" v-if="selected.choseTime">
|
|||
|
|
<a-button type="link" class="opinion-but">常用审批意见</a-button>
|
|||
|
|
<template #overlay>
|
|||
|
|
<a-menu>
|
|||
|
|
<a-menu-item v-for="item in normalOpinionList" @click="clickMenu(item.text)">
|
|||
|
|
<a href="javascript:;">{{ item.text }}</a>
|
|||
|
|
</a-menu-item>
|
|||
|
|
</a-menu>
|
|||
|
|
</template>
|
|||
|
|
</a-dropdown>
|
|||
|
|
<a-textarea v-model:value="selected.opinion" :maxlength="200" :rows="3" style="margin-top: 35px;"
|
|||
|
|
placeholder="请输入内容,不超过200字" :rules="[{ required: true, message: '必须填写!' }]" />
|
|||
|
|
</a-form-item>
|
|||
|
|
<!--选择任意节点:填写备注-->
|
|||
|
|
<a-form-item v-if="_action != 'select'" label="审批意见" name="opinion"
|
|||
|
|
:rules="[{ required: true, message: '审批意见必须填写!' }]">
|
|||
|
|
<a-dropdown placement="bottom">
|
|||
|
|
<a-button type="link" class="opinion-but">常用审批意见</a-button>
|
|||
|
|
<template #overlay>
|
|||
|
|
<a-menu>
|
|||
|
|
<a-menu-item v-for="item in normalOpinionList" @click="clickMenu(item.text)">
|
|||
|
|
<a href="javascript:;">{{ item.text }}</a>
|
|||
|
|
</a-menu-item>
|
|||
|
|
</a-menu>
|
|||
|
|
</template>
|
|||
|
|
</a-dropdown>
|
|||
|
|
<a-textarea v-model:value="formState.opinion" :maxlength="200" :rows="3" style="margin-top: 35px;"
|
|||
|
|
placeholder="请输入审批意见,不超过200字" />
|
|||
|
|
</a-form-item>
|
|||
|
|
</a-form>
|
|||
|
|
</div>
|
|||
|
|
</a-modal>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup>
|
|||
|
|
import { computed, reactive, ref } from 'vue';
|
|||
|
|
import { getRejectNodeList } from '/@/api/workflow/task';
|
|||
|
|
import { getUserMulti } from '/@/api/system/user';
|
|||
|
|
import { SelectUser } from '/@/components/SelectOrganizational/index';
|
|||
|
|
import { message, Dropdown } from 'ant-design-vue';
|
|||
|
|
import dayjs, { Dayjs } from 'dayjs';
|
|||
|
|
|
|||
|
|
const aDropdown = Dropdown;
|
|||
|
|
|
|||
|
|
const dialogTitle = ref('审批');
|
|||
|
|
const isOpen = ref(false);
|
|||
|
|
const rejectNodeList = ref([]);
|
|||
|
|
const rejectNodeId = ref('');
|
|||
|
|
const loading = ref(false);
|
|||
|
|
const isEnd = ref(false);
|
|||
|
|
const isStart = ref(false);
|
|||
|
|
|
|||
|
|
let _action = ref('agree');
|
|||
|
|
let _processId = '';
|
|||
|
|
let _taskId = '';
|
|||
|
|
let flowNextNodes = ref([]);
|
|||
|
|
let allTaskNodes = ref([]);
|
|||
|
|
let selectedNode = ref('');
|
|||
|
|
let _callback = null;
|
|||
|
|
let _onCancel = null;
|
|||
|
|
const normalOpinionList = [
|
|||
|
|
{
|
|||
|
|
text: '请批准。'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
text: '同意。'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
text: '批准。'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
text: '不同意。'
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
text: '请修改。'
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
const formState = reactive({
|
|||
|
|
opinion: '',
|
|||
|
|
opinionList: ['同意。', '请领导审批。']
|
|||
|
|
});
|
|||
|
|
function clickMenu(val) {
|
|||
|
|
formState.opinion = val
|
|||
|
|
selected.opinion = val
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const selected = reactive({
|
|||
|
|
taskId: '',
|
|||
|
|
userId: [],
|
|||
|
|
userName: '',
|
|||
|
|
opinion: '',
|
|||
|
|
selectedList: [],
|
|||
|
|
time: '',
|
|||
|
|
choseTime: false,
|
|||
|
|
multiple: true
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const editable = ref(false);
|
|||
|
|
|
|||
|
|
const taskName = computed(() => {
|
|||
|
|
let name = allTaskNodes.value.map((ele) => {
|
|||
|
|
if (ele.taskId === selected.taskId) {
|
|||
|
|
return ele.taskName
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
return name;
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
function getAssigneeText(node) {
|
|||
|
|
// 注意这里用的是下拉框的数据结构 所以字段是value和label
|
|||
|
|
return (node.nextAssignees || [])
|
|||
|
|
.filter((item) => node.assignees.includes(item.value))
|
|||
|
|
.map((item) => item.label)
|
|||
|
|
.join('、');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function getNextNodesName() {
|
|||
|
|
return flowNextNodes.value.length > 1 ? '多个并行节点' : flowNextNodes.value[0].activityName;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function toggleDialog({ isClose, isCreateFlow, action, callback, rejectCancel, processId, taskId, nextNodes, schemaId, choseTime, title, multiple, taskNode, edit, record } = {}) {
|
|||
|
|
if (isClose) {
|
|||
|
|
isOpen.value = false;
|
|||
|
|
loading.value = false;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
isOpen.value = true;
|
|||
|
|
_action.value = action;
|
|||
|
|
_callback = callback;
|
|||
|
|
_onCancel = rejectCancel;
|
|||
|
|
_processId = processId;
|
|||
|
|
_taskId = taskId;
|
|||
|
|
flowNextNodes.value = nextNodes;
|
|||
|
|
isStart.value = isCreateFlow;
|
|||
|
|
formState.opinion = '';
|
|||
|
|
dialogTitle.value = title ? title : '审批';
|
|||
|
|
selected.choseTime = choseTime;
|
|||
|
|
selected.multiple = multiple == false ? multiple : true;
|
|||
|
|
editable.value = edit ? edit : false
|
|||
|
|
|
|||
|
|
if (action === 'select') {
|
|||
|
|
allTaskNodes.value = taskNode
|
|||
|
|
console.log(11111111111, taskNode);
|
|||
|
|
console.log(22222222222, allTaskNodes);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (taskId)
|
|||
|
|
selected.taskId = taskId
|
|||
|
|
|
|||
|
|
if (record != null) {
|
|||
|
|
console.log(record);
|
|||
|
|
selected.selectedList = record.approveUserId
|
|||
|
|
selected.opinion = record.approveComment
|
|||
|
|
selected.time = dayjs(record.approveTime, 'YYYY-MM-DD HH:mm:ss')
|
|||
|
|
selected.userName = record.approveUserName
|
|||
|
|
}
|
|||
|
|
if (nextNodes && nextNodes.length) {
|
|||
|
|
// 下一个节点唯一时(可能有并行节点)
|
|||
|
|
const nNode = nextNodes[0];
|
|||
|
|
//formState.nextNodeName = nNode.activityName;
|
|||
|
|
isEnd.value = nNode.isEnd;
|
|||
|
|
nextNodes.forEach((nNode) => {
|
|||
|
|
if (!nNode.userList?.length) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const selected = [];
|
|||
|
|
nNode.nextAssignees = nNode.userList.map((item) => {
|
|||
|
|
if (item.checked || nNode.userList.length === 1) {
|
|||
|
|
// 只有一个人的时候必须选他
|
|||
|
|
selected.push(item['F_UserId']);
|
|||
|
|
}
|
|||
|
|
return {
|
|||
|
|
value: item['F_UserId'],
|
|||
|
|
label: item['F_RealName'],
|
|||
|
|
item: item
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
nNode.assignees = selected;
|
|||
|
|
if (!nNode.chooseAssign) {
|
|||
|
|
// 不需要选审批人的时候 所有备选人都要放到下个节点
|
|||
|
|
nNode.assignees = nNode.userList.map((item) => item['F_UserId']);
|
|||
|
|
}
|
|||
|
|
nNode.chooseAssign = nNode.chooseAssign;
|
|||
|
|
});
|
|||
|
|
flowNextNodes.value = nextNodes;
|
|||
|
|
}
|
|||
|
|
if (action === 'reject') {
|
|||
|
|
loadRejectNodeList();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function search(inputValue, option) {
|
|||
|
|
return inputValue ? (option.item.F_Account.indexOf(inputValue) > -1 || option.item.F_RealName.indexOf(inputValue) > -1) : true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function loadRejectNodeList() {
|
|||
|
|
rejectNodeId.value = '';
|
|||
|
|
let res = await getRejectNodeList(_processId, _taskId);
|
|||
|
|
if (res && Array.isArray(res) && res.length > 0) {
|
|||
|
|
rejectNodeList.value = res;
|
|||
|
|
dialogTitle.value = `退回`;
|
|||
|
|
if (res?.length) {
|
|||
|
|
res.forEach((nNode) => {
|
|||
|
|
if (!nNode.userList?.length) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
const selected = [];
|
|||
|
|
nNode.nextAssignees = nNode.userList.map((item) => {
|
|||
|
|
if (item.checked || nNode.userList.length === 1) {
|
|||
|
|
// 只有一个人的时候必须选他
|
|||
|
|
selected.push(item['F_UserId']);
|
|||
|
|
}
|
|||
|
|
return {
|
|||
|
|
value: item['F_UserId'],
|
|||
|
|
label: item['F_RealName'] + (item.remarks ? "(" + item.remarks + ")" : ""),
|
|||
|
|
item: item
|
|||
|
|
};
|
|||
|
|
});
|
|||
|
|
nNode.assignees = selected;
|
|||
|
|
if (!nNode.chooseAssign) {
|
|||
|
|
// 不需要选审批人的时候 所有备选人都要放到下个节点
|
|||
|
|
nNode.assignees = nNode.userList.map((item) => item['F_UserId']);
|
|||
|
|
}
|
|||
|
|
nNode.chooseAssign = nNode.chooseAssign;
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function onClickOK() {
|
|||
|
|
if (_action.value === 'select') {
|
|||
|
|
// if (!selected.choseTime) {
|
|||
|
|
// if (selected.opinion === null || selected.opinion.trim() === '') {
|
|||
|
|
// return message.error('请填写备注');
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
if (selected.opinion === null || selected.opinion.trim() === '') {
|
|||
|
|
return message.error('请填写备注');
|
|||
|
|
}
|
|||
|
|
if (_callback && typeof _callback === 'function') {
|
|||
|
|
_callback({
|
|||
|
|
info: selected,
|
|||
|
|
taskName: taskName,
|
|||
|
|
});
|
|||
|
|
isOpen.value = false;
|
|||
|
|
clearSelectVal();
|
|||
|
|
} else {
|
|||
|
|
isOpen.value = false;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
const nextTaskUser = {};
|
|||
|
|
if (_action.value === 'agree' && !isEnd.value) {
|
|||
|
|
const isEmpty = flowNextNodes.value.find((node) => !node.assignees?.length);
|
|||
|
|
if (isEmpty) {
|
|||
|
|
return message.error('请选择审批人');
|
|||
|
|
}
|
|||
|
|
flowNextNodes.value.forEach((nNode) => {
|
|||
|
|
nextTaskUser[nNode.activityId] = isEnd.value ? '' : nNode.assignees.join(',');
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
if (_action.value === 'reject') {
|
|||
|
|
const isChoose = rejectNodeList.value.find((node) => node.activityId == rejectNodeId.value && node.assignees?.length);
|
|||
|
|
if (!isChoose) {
|
|||
|
|
return message.error('请选择审批人');
|
|||
|
|
}
|
|||
|
|
rejectNodeList.value.forEach((nNode) => {
|
|||
|
|
if (nNode.activityId == rejectNodeId.value) {
|
|||
|
|
nextTaskUser[nNode.activityId] = isEnd.value ? '' : nNode.assignees.join(',');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
if (formState.opinion === null || formState.opinion.trim() === '') {
|
|||
|
|
return message.error('请填写审批意见');
|
|||
|
|
}
|
|||
|
|
if (_callback && typeof _callback === 'function') {
|
|||
|
|
loading.value = true;
|
|||
|
|
_callback({
|
|||
|
|
opinion: formState.opinion,
|
|||
|
|
rejectNodeId: rejectNodeId.value,
|
|||
|
|
nextTaskUser,
|
|||
|
|
isEnd
|
|||
|
|
});
|
|||
|
|
} else {
|
|||
|
|
isOpen.value = false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function onClickCancel() {
|
|||
|
|
clearSelectVal();
|
|||
|
|
if (isStart.value) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (_onCancel && typeof _onCancel === 'function') {
|
|||
|
|
_onCancel();
|
|||
|
|
}
|
|||
|
|
isOpen.value = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function clearSelectVal() {
|
|||
|
|
selected.taskId = '';
|
|||
|
|
selected.userId = [];
|
|||
|
|
selected.opinion = '';
|
|||
|
|
selected.userName = '';
|
|||
|
|
selected.time = null;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function stopLoading() {
|
|||
|
|
loading.value = false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function getUserList(list) {
|
|||
|
|
selected.selectedList = await getUserMulti(list.join(','));
|
|||
|
|
selected.userName = selected.selectedList
|
|||
|
|
.map((ele) => {
|
|||
|
|
return ele.name;
|
|||
|
|
})
|
|||
|
|
.join(',');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
defineExpose({
|
|||
|
|
toggleDialog,
|
|||
|
|
stopLoading,
|
|||
|
|
clearSelectVal
|
|||
|
|
});
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style lang="less" scoped>
|
|||
|
|
.dialog-wrap {
|
|||
|
|
padding: 10px 15px 0 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.opinion-but {
|
|||
|
|
position: absolute;
|
|||
|
|
top: 0;
|
|||
|
|
right: 0;
|
|||
|
|
}
|
|||
|
|
</style>
|