--添加测试模块

This commit is contained in:
2025-10-13 11:53:54 +08:00
parent c3c93fe308
commit e1326c7ce8
146 changed files with 11171 additions and 807 deletions

View File

@ -225,41 +225,54 @@
}
async function saveDraftData() {
let formModes = {};
try {
let formModes = {};
for (let index = 0; index < forms.configs.length; index++) {
const ele = forms.configs[index];
if (ele.formType == FormType.SYSTEM) {
let values = await itemRefs.value[index].validate();
formModes[ele.formKey] = values;
} else {
formModes[ele.formKey] = ele.formModel;
for (let index = 0; index < forms.configs.length; index++) {
const ele = forms.configs[index];
if (ele.formType == FormType.SYSTEM) {
let values = await itemRefs.value[index].getFormModels();
formModes[ele.formKey] = values;
} else {
formModes[ele.formKey] = ele.formModel;
}
}
return formModes;
} catch (e) {
console.error(e)
throw new Error(e);
}
return formModes;
}
async function getFormModels(saveRowKey) {
let formModes = {};
for (let index = 0; index < forms.configs.length; index++) {
const ele = forms.configs[index];
if (ele.formType == FormType.SYSTEM) {
let values = await itemRefs.value[index].workflowSubmit(saveRowKey);
formModes[ele.formKey] = values;
} else {
formModes[ele.formKey] = ele.formModel;
try {
let formModes = {};
for (let index = 0; index < forms.configs.length; index++) {
const ele = forms.configs[index];
if (ele.formType == FormType.SYSTEM) {
let values = await itemRefs.value[index].workflowSubmit(saveRowKey);
if(!values) {
return
}
formModes[ele.formKey] = values;
} else {
formModes[ele.formKey] = ele.formModel;
}
}
}
// forms.configs.forEach((ele) => {
// formModes[ele.formKey] = ele.formModel;
// });
forms.formEventConfigs.forEach(async (ele, i) => {
//此组件 获取数据 就是为了提交表单 所以 表单提交数据 事件 就此处执行
await submitFormEvent(ele, forms.configs[i]?.formModel);
});
return formModes;
// forms.configs.forEach((ele) => {
// formModes[ele.formKey] = ele.formModel;
// });
forms.formEventConfigs.forEach(async (ele, i) => {
//此组件 获取数据 就是为了提交表单 所以 表单提交数据 事件 就此处执行
await submitFormEvent(ele, forms.configs[i]?.formModel);
});
return formModes;
} catch (e) {
console.error(e)
throw new Error(e);
}
}
function getSystemType() {
@ -273,13 +286,23 @@
return system;
}
function handleInnerFun(funcName) {
for (let index = 0; index < forms.configs.length; index++) {
const ele = forms.configs[index];
if (ele.formType == FormType.SYSTEM) {
itemRefs.value[index].handleInnerFun(funcName)
}
}
}
defineExpose({
validateForm,
getFormModels,
saveDraftData,
setFormData,
getUploadComponentIds,
getSystemType
getSystemType,
handleInnerFun
});
</script>

View File

@ -48,30 +48,7 @@
redirect = list[1];
}
let tenantCode='';
if(getAppEnvConfig().VITE_GLOB_TENANT_ENABLED=='true'){
let url='';
if(targetURL){
url=targetURL;
}else{
url=redirect;
}
if(url.includes('tenantCode')){
const fullString = decodeURIComponent(url);
const queryString = fullString.split('?')[1];
tenantCode = (queryString.match(/tenantCode=([^&]+)/) || [])[1]
}
if(!tenantCode){
notification.error({
message: t('提示'),
description: t('租户码不能为空!'),
});
return;
}
}
let params = {...currentRoute.value.query, targetURL: targetURL,redirect: redirect, mode: 'none',tenantCode:tenantCode }; //不要默认的错误提示
let params = {...currentRoute.value.query, targetURL: targetURL,redirect: redirect, mode: 'none'}; //不要默认的错误提示
await userStore.singleLogin(params);
}

View File

@ -10,38 +10,32 @@
</slot>
关闭
</a-button>
<a-button @click="saveDraft" v-if="!readonly">
暂存
</a-button>
<a-button @click="setDraft" v-if="rDraftsId && !readonly">
从草稿导入
</a-button>
<a-button v-if="!readonly && hasBtnApprove" type="primary" @click="onApproveClick()">
<slot name="icon">
<check-circle-outlined />
</slot>
同意
</a-button>
<a-button v-if="!readonly && hasBtnReject" @click="onDenyClick">
<slot name="icon">
<stop-outlined />
</slot>
拒绝
</a-button>
<a-dropdown>
<template #overlay>
<a-menu @click="onMoreClick">
<a-menu-item v-if="!readonly && hasBtnFinish" key="finish">终止</a-menu-item>
<a-menu-item v-if="!readonly" key="transfer">转办</a-menu-item>
<a-menu-item v-if="readonly && drawNode" key="drawBack">撤回</a-menu-item>
<a-menu-item key="flowchart">查看流程图</a-menu-item>
</a-menu>
</template>
<a-button>
更多
<down-outlined />
<template v-for="(btn, index) in buttonMap.normal">
<a-button @click="onClickBtn(btn)" :type="btn.buttonCode === ApproveCode.AGREE ? 'primary' : ''">
<slot name="icon" v-if="btn.buttonCode === ApproveCode.AGREE">
<check-circle-outlined />
</slot>
<slot name="icon" v-if="btn.buttonCode === ApproveCode.REJECT">
<stop-outlined />
</slot>
{{btn.buttonName}}
</a-button>
</a-dropdown>
</template>
<template v-for="(btnGroup, btnGroupKey) in buttonMap">
<a-dropdown v-if="btnGroupKey!=='normal' && btnGroup.length">
<template #overlay>
<a-menu>
<a-menu-item v-for="(btn, index) in btnGroup" :key="btn.buttonCode" @click="onClickBtn(btn)">
{{btn.buttonName}}
</a-menu-item>
</a-menu>
</template>
<a-button>
{{btnGroupKey}}
<down-outlined />
</a-button>
</a-dropdown>
</template>
</a-space>
</div>
<FormInformation
@ -67,6 +61,15 @@
<a-button type="primary" @click="closeFlowChart">关闭</a-button>
</template>
</a-modal>
<a-modal :closable="false" v-if="showRecord" visible="true" centered class="geg" title="流程记录" width="1200px" @cancel="closeFlowRecord">
<div class="flow-record-box">
<FlowRecord :list="data.taskRecords" :processId="processId"/>
</div>
<template #footer>
<a-button type="primary" @click="closeFlowRecord">关闭</a-button>
</template>
</a-modal>
<SelectUserV2 ref="selectUser" v-model:value="addStepUser" @change="changeAddStepUser" just-dialog title="加签减签"/>
</div>
</div>
</a-spin>
@ -74,14 +77,15 @@
<script setup>
import { useRouter } from 'vue-router';
import { onMounted, reactive, ref, unref, createVNode } from 'vue';
import { onMounted, reactive, ref, unref, createVNode, provide } from 'vue';
import FormInformation from '/@/views/secondDev/FormInformation.vue';
import userTaskItem from '/@/views/workflow/task/hooks/userTaskItem';
import { getApprovalProcess, postApproval, postGetNextTaskMaybeArrival, postTransfer, getDrawNode, withdraw } from '/@/api/workflow/task';
import { ApproveCode, ApproveType } from '/@/enums/workflowEnum';
import { ApproveCode, ApproveType, ButtonType } from '/@/enums/workflowEnum';
import { CheckCircleOutlined, StopOutlined, CloseOutlined, DownOutlined, ExclamationCircleOutlined } from '@ant-design/icons-vue';
import OpinionDialog from '/@/components/SecondDev/OpinionDialog.vue';
import TransferDialog from '/@/components/SecondDev/TransferDialog.vue';
import SelectUserV2 from '/@/components/Form/src/components/SelectUserV2.vue';
import { separator } from '/@bpmn/config/info';
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
import Title from '/@/components/Title/src/Title.vue';
@ -94,6 +98,8 @@
import { useUserStore } from '/@/store/modules/user';
import { deleteDraft, postDraft, putDraft, getDraftInfo } from '/@/api/workflow/process';
import { TaskTypeUrl } from '/@/enums/workflowEnum';
import { postSetSign, postSetSignV2 } from '/@/api/workflow/task';
import FlowRecord from '/@/views/workflow/task/components/flow/FlowRecord.vue';
const spinning = ref(false);
@ -121,10 +127,29 @@
const transferDlg = ref();
const validateSuccess = ref(false);
const formInformation = ref();
const selectUser = ref()
const addStepUser = ref('')
const lastAddStepUser = ref('')
const hasBtnAddStep = ref(false)
const hasBtnTransfer = ref(false)
const showFlowChart = ref(false);
const hasBtnApprove = ref(true);
const showRecord = ref(false)
const hasBtnApprove = ref(false);
const hasBtnDisagree = ref(false)
const hasBtnReject = ref(false);
const hasBtnFinish = ref(false);
const hasBtnDraft = ref(false)
const approveBtnName = ref('同意')
const transferBtnName = ref('转办')
const addStepBtnName = ref('会签')
const finishBtnName = ref('终止')
const rejectBtnName = ref('拒绝')
const drawBackBtnName = ref('撤回')
const draftBtnName = ref('暂存')
const disagreeBtnName = ref('不同意')
const buttonMap = ref({
normal: []
})
let draftData = {}
const drawNode = ref('');
const props = defineProps({
@ -132,6 +157,8 @@
type: String
}
});
const processInfo = ref();
provide("processInfo", processInfo);
let approvalData = reactive({
isCountersign: false,
@ -151,21 +178,51 @@
nextTaskUser: {} // 格式为taskKey: 用户id逗号分隔
});
let approvedType = ref(ApproveType.AGREE);
function showButton(btn) {
// 撤回有drawNode才显示流程图任何情况下都显示
let show = (btn.checked && ((!readonly.value && btn.buttonCode !== ApproveCode.DRAWBACK) || (readonly.value && btn.buttonCode === ApproveCode.DRAWBACK && drawNode.value))) || (btn.buttonCode === ApproveCode.FLOWBPMN || btn.buttonCode === ApproveCode.FLOWRECORD)
return show
}
function onClickBtn(btn) {
const key = btn.buttonCode;
if(btn.buttonType === ButtonType.DEFAULT) {
let funName = `handle${key}`
methods[funName](btn)
} else if(btn.buttonType === ButtonType.SCRIPT) {
handleFunction(btn)
}
}
function onMoreClick(e) {
const key = e.key;
if (key === 'flowchart') {
openFlowChart();
} else if (key === 'finish') {
Modal.confirm({
title: () => '提示',
content: () => '确定终止吗?',
onOk: () => {
onFinishClick();
}
});
} else if (key === 'transfer') {
onTransferClick();
} else if (key === 'drawBack') {
if(btn.buttonType === ButtonType.DEFAULT) {
let funName = `handle${key}`
methods[funName](btn)
} else if(btn.buttonType === ButtonType.SCRIPT) {
handleFunction(btn)
}
}
function handleFunction(btn) {
// eval(script)
if (btn.handleFuncName) {
formInformation.value?.handleInnerFun(btn.handleFuncName)
}
}
const methods = {
handlesetDraft() {
setDraft()
},
handledraft() {
saveDraft();
},
handleaddStep() {
selectUser.value.show()
},
handledrawBack() {
Modal.confirm({
title: t('提示'),
icon: createVNode(ExclamationCircleOutlined),
@ -195,19 +252,49 @@
},
onCancel() {}
});
}
},
handlefinish(btn) {
Modal.confirm({
title: () => '提示',
content: () => `确定${btn.buttonName}吗?`,
onOk: () => {
onFinishClick();
}
});
},
handleflowBpmn() {
showFlowChart.value = true;
},
handletransfer() {
onTransferClick();
},
handleagree() {
onApproveClick()
},
handledisagree() {
onDisagreeClick()
},
handlereject() {
onDenyClick()
},
handleflowRecord() {
showRecord.value = true;
},
}
function closeFlowChart() {
showFlowChart.value = false;
}
function openFlowChart() {
showFlowChart.value = true;
function closeFlowRecord() {
showRecord.value = false
}
function close() {
tabStore.closeTab(currentRoute, router);
if(window?.isOnlyShowContent=='Y') {
window.close();
}
}
async function setDraft(needModal = true) {
let formData = [];
@ -259,35 +346,103 @@
notificationError(title);
}
}
async function changeAddStepUser (ids, memberList) {
try {
spinning.value = true;
let idList = memberList.map(item => {
return item.id
})
let lastIdList = lastAddStepUser.value.split(',')
let addUserIds = idList.filter(item => {
return lastIdList.indexOf(item) == -1
})
let subUserIds = lastIdList.filter(item => {
return idList.indexOf(item) == -1
})
let data = {
addUserIds,
subUserIds,
schemaId: schemaId.value,
taskId: taskId.value
}
await postSetSignV2(data);
let res = await getApprovalProcess(unref(taskId), unref(processId));
initProcessData(res);
spinning.value = false;
message.success('操作成功');
} catch (e) {
message.error(e)
spinning.value = false;
message.error('操作失败,请稍后再试');
}
}
async function onApproveClick(isAutoAgreeBreak = false) {
openSpinning();
if (!isAutoAgreeBreak) {
await submit();
}
if (!validateSuccess.value) {
closeSpinning();
return;
}
const params = await getApproveParams();
const nextNodes = await postGetNextTaskMaybeArrival(params);
approvalData.approvedType = ApproveType.AGREE;
approvalData.approvedResult = ApproveCode.AGREE;
//如果是自动同意触发的关闭弹层的loading
if (isAutoAgreeBreak) {
opinionDlg.value.stopLoading()
}
closeSpinning();
opinionDlg.value.toggleDialog({
action: 'agree',
nextNodes,
callback: (args) => {
approvalData.approvedContent = args.opinion;
approvalData.nextTaskUser = args.nextTaskUser;
approvalData.isEnd=args.isEnd;
onFinish('approve');
try {
openSpinning();
if (!isAutoAgreeBreak) {
await submit();
}
});
if (!validateSuccess.value) {
closeSpinning();
return;
}
const params = await getApproveParams();
const nextNodes = await postGetNextTaskMaybeArrival(params);
approvalData.approvedType = ApproveType.AGREE;
approvalData.approvedResult = ApproveCode.AGREE;
//如果是自动同意触发的关闭弹层的loading
if (isAutoAgreeBreak) {
opinionDlg.value.stopLoading()
}
closeSpinning();
opinionDlg.value.toggleDialog({
action: 'agree',
nextNodes,
callback: (args) => {
approvalData.approvedContent = args.opinion;
approvalData.nextTaskUser = args.nextTaskUser;
approvalData.isEnd=args.isEnd;
onFinish('approve');
}
});
} catch (e) {
console.error(e)
closeSpinning();
throw new Error(e);
}
}
async function onDisagreeClick() {
try {
openSpinning();
await submit();
if (!validateSuccess.value) {
closeSpinning();
return;
}
const params = await getApproveParams();
const nextNodes = await postGetNextTaskMaybeArrival(params);
approvalData.approvedType = ApproveType.DISAGREE;
approvalData.approvedResult = ApproveCode.DISAGREE;
closeSpinning();
opinionDlg.value.toggleDialog({
action: 'disagree',
nextNodes,
callback: (args) => {
approvalData.approvedContent = args.opinion;
onFinish('disagree');
}
});
} catch (e) {
console.error(e)
closeSpinning();
throw new Error(e);
}
}
async function onDenyClick() {
@ -379,12 +534,37 @@
function setBtnStatus() {
const btnConfigs = approvalData.buttonConfigs;
let draftBtn = btnConfigs.find((item) => item.buttonCode === ApproveCode.DRAFT)
if(draftBtn && rDraftsId.value) {
btnConfigs.push({
...draftBtn,
buttonName: t('从草稿导入'),
buttonCode: 'setDraft',
approveType: ApproveType.DRAFT,
index: 1
})
}
btnConfigs.forEach((btn) => {
const code = btn.buttonCode;
if (code === 'reject') {
hasBtnReject.value = true;
} else if (code === 'finish') {
hasBtnFinish.value = true;
const index = btn.index
const buttonGroup = btn?.buttonGroup
if(buttonGroup) {
if(!buttonMap.value[buttonGroup]) {
buttonMap.value[buttonGroup] = []
}
if(showButton(btn)) {
buttonMap.value[buttonGroup].push(btn)
}
} else {
if(showButton(btn)) {
buttonMap.value['normal'].push(btn)
}
}
for(let key in buttonMap.value) {
buttonMap.value[key].sort((a, b) => {
let aIndex = a?.index || 0
let bIndex = b?.index || 0
return aIndex - bIndex
})
}
});
}
@ -399,16 +579,26 @@
try {
let res = await getApprovalProcess(unref(taskId), unref(processId));
initProcessData(res);
await getBackNode();
processInfo.value = res;
const title = res?.schemaInfo?.name;
if (title) {
const tabPrefix = readonly.value ? '查看' : '审批';
tabStore.changeTitle(fullPath, `${tabPrefix}${title}`);
}
if(taskId.value) {
let ids = ''
res.currentTaskAssignees[res.taskInfo.taskDefinitionKey].forEach((item, index) => {
ids = `${ids}${index == 0 ? '' : ','}${item.assigneeIdStr}`
})
addStepUser.value = ids
lastAddStepUser.value = ids
}
if (res.buttonConfigs) {
approvalData.buttonConfigs = res.buttonConfigs;
setBtnStatus();
}
if (!readonly.value) {
if (res.buttonConfigs) {
approvalData.buttonConfigs = res.buttonConfigs;
setBtnStatus();
}
if (res.relationTasks) {
data.predecessorTasks = res.relationTasks;
}
@ -424,13 +614,12 @@
approvalData.circulateConfigs = [];
}
renderKey.value = Math.random() + '';
getBackNode();
setDraft()
} catch (error) {}
});
function getBackNode() {
getDrawNode(processId.value).then((res) => {
async function getBackNode() {
await getDrawNode(processId.value).then((res) => {
if (res.length) {
drawNode.value = res[0].activityId;
} else {
@ -481,29 +670,33 @@
}
async function getApproveParams() {
let formModels = await formInformation.value.getFormModels();
let system = formInformation.value.getSystemType();
let fileFolderIds = getUploadFileFolderIds(formModels);
return {
approvedType: approvalData.approvedType,
approvedResult: approvalData.approvedResult, // approvalData.approvedType 审批结果 如果为 4 就需要传buttonCode
approvedContent: approvalData.approvedContent,
formData: formModels,
rejectNodeActivityId: approvalData.rejectNodeActivityId,
taskId: taskId.value,
fileFolderIds,
circulateConfigs: approvalData.circulateConfigs,
/*stampId: values.stampId,
stampPassword: values.password,*/
isOldSystem: system,
isEnd:approvalData.isEnd,
nextTaskUser: approvalData.nextTaskUser
};
try {
let formModels = await formInformation.value.getFormModels();
let system = formInformation.value.getSystemType();
let fileFolderIds = getUploadFileFolderIds(formModels);
return {
approvedType: approvalData.approvedType,
approvedResult: approvalData.approvedResult, // approvalData.approvedType 审批结果 如果为 4 就需要传buttonCode
approvedContent: approvalData.approvedContent,
formData: formModels,
rejectNodeActivityId: approvalData.rejectNodeActivityId,
taskId: taskId.value,
fileFolderIds,
circulateConfigs: approvalData.circulateConfigs,
/*stampId: values.stampId,
stampPassword: values.password,*/
isOldSystem: system,
isEnd:approvalData.isEnd,
nextTaskUser: approvalData.nextTaskUser
};
} catch (e) {
console.error(e)
}
}
async function onFinish(values) {
try {
if (validateSuccess.value || values === 'reject' || values === 'finish') {
if (validateSuccess.value || values === 'reject' || values === 'finish' || values === 'disagree') {
let params = await getApproveParams();
let response = await postApproval(params);
// 判断返回值是否带有isAutoAgree 来判断中间是否有自动审批的业务如有再执行判断待审人员是否包含自己不包含就直接flowSuccess
@ -513,6 +706,7 @@
}
} catch (error) {
flowFail();
throw new Error(error);
}
}
@ -525,7 +719,6 @@
&& response.length != 0
&& response[0].isAutoAgree == true //
&& response[0].approveUserIds.includes(userStore.getUserInfo.id)) {
console.error('will reSelect user=', response[0].taskId);
// 注入新得taskId
taskId.value = response[0].taskId;
data.submitLoading = false;
@ -544,3 +737,10 @@
spinning.value = false;
}
</script>
<style lang="less" scoped>
.flow-record-box {
height: 500px;
overflow: auto;
}
</style>

View File

@ -1,6 +1,6 @@
<template>
<div class="page-bg-wrap">
<a-spin :spinning="loading" tip="加载中...">
<a-spin :spinning="loading" tip="加载中...">
<div class="page-bg-wrap">
<div class="geg-flow-page">
<div class="top-toolbar">
<a-space :size="10" wrap>
@ -22,11 +22,8 @@
</slot>
暂存
</a-button>
<a-button>
<slot name="icon">
<printer-outlined />
</slot>
打印
<a-button @click="handleDelete" danger v-if="rDraftsId!='0'">
删除
</a-button>
<a-button @click="openFlowChart">
<slot name="icon">
@ -47,8 +44,8 @@
</template>
</a-modal>
<opinion-dialog ref="opinionDlg" />
</a-spin>
</div>
</div>
</a-spin>
</template>
<script setup>
@ -63,7 +60,7 @@
import { deleteDraft, postDraft, putDraft } from '/@/api/workflow/process';
import { useI18n } from '/@/hooks/web/useI18n';
import { separator } from '/@bpmn/config/info';
import { message } from 'ant-design-vue';
import { message, Modal } from 'ant-design-vue';
import OpinionDialog from '/@/components/SecondDev/OpinionDialog.vue';
import { ApproveCode, ApproveType } from '/@/enums/workflowEnum';
import useEventBus from '/@/hooks/event/useEventBus';
@ -80,7 +77,7 @@
const fullPath = currentRoute.fullPath;
const rQuery = currentRoute.query;
const rSchemaId = rParams.arg1;
const rDraftsId = rParams.arg2;
let rDraftsId = ref(rParams.arg2);
const taskId = ref();
const loading = ref(false);
const draftsJsonStr = localStorage.getItem('draftsJsonStr');
@ -183,11 +180,12 @@
try {
disableSubmit.value = true;
let formModels = await formInformation.value.saveDraftData();
if (rDraftsId !== '0') {
let res = await putDraft(rSchemaId, formModels, rDraftsId, props.rowKeyData);
if (rDraftsId.value !== '0') {
let res = await putDraft(rSchemaId, formModels, rDraftsId.value, props.rowKeyData);
showResult(res, '保存草稿');
} else {
let res = await postDraft(rSchemaId, formModels, props.rowKeyData);
rDraftsId.value = res
showResult(res, '保存草稿');
}
} catch (error) {
@ -291,11 +289,27 @@
stampPassword: values.password,*/
isOldSystem: system,
nextTaskUser: approvalData.nextTaskUser,
draftId: rDraftsId,
draftId: rDraftsId.value,
};
}
async function saveLaunch() {
if (!taskId.value && rDraftsId.value!='0') {
try {
await new Promise((resolve, reject) => {
Modal.confirm({
title: '提示',
content: '请确认是否提交流程,提交后流程不能删除',
okText: '确定',
cancelText: '取消',
onOk: () => resolve(),
onCancel: () => reject()
});
});
} catch {
return;
}
}
data.submitLoading = true;
loading.value = true;
try {
@ -306,6 +320,7 @@
let successValidate = validateForms.filter((ele) => {
return ele.validate;
});
console.info("validateForms:"+JSON.stringify(validateForms));
if (successValidate.length == validateForms.length) {
mainFormModels.value = await formInformation.value.getFormModels(true);
/*for (let i in mainFormModels.value) {
@ -388,6 +403,29 @@
});
return fileFolderIds;
}
async function handleDelete() {
Modal.confirm({
title: '提示信息',
content: '是否确认删除?删除后无法恢复数据!',
okText: '确认',
cancelText: '取消',
async onOk() {
try {
let res = await deleteDraft([rDraftsId.value]);
if (res) {
message.success('删除成功');
setTimeout(() => {
bus.emit(CREATE_FLOW, {});
close();
}, 500);
} else {
message.error('删除失败');
}
} catch (error) {}
},
onCancel() {},
});
}
</script>
<style lang="less"></style>

View File

@ -0,0 +1,493 @@
<template>
<!-- 表单信息 -->
<div class="form-container">
<div class="box">
<div class="form-right">
<div v-for="(item, index) in forms.configs" :key="index" :tab="item.formName">
<div v-show="activeIndex == index">
<div class="page-bg-wrap">
<div class="top-toolbar" style="display: flex;margin-bottom: 10px">
<div id="adminButtons" v-show="activeIndex == index" style="margin-right:10px">
<a-button @click="handleCancel" v-if="forms.modes[index] == 'edit'">取消</a-button>
<a-button @click="handleSave" v-if="forms.modes[index] == 'edit'" type="primary" style="margin-left: 12px">保存</a-button>
<a-button @click="handleEdit" v-if="forms.modes[index] == 'view'">编辑</a-button>
<a-button @click="handleDelete" type="danger" style="margin-left: 12px">删除</a-button>
</div>
<div id="approveExtendButton"></div>
<div id="approveRightButton"></div>
<div id="approveExtendButtonLeft" v-show="false"></div>
</div>
<SystemForm class="form-box" v-if="item.formType == FormType.SYSTEM"
:systemComponent="item.systemComponent" :isViewProcess="props.disabled" :formModel="item.formModel"
:workflowConfig="item" :ref="setItemRef" />
<SimpleForm v-else-if="item.formType == FormType.CUSTOM" class="form-box testClass" :ref="setItemRef"
:formProps="item.formProps" :formModel="item.formModel" :isWorkFlow="true" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import SimpleForm from '/@/components/SimpleForm/src/SimpleForm.vue';
import { FewerLeft, FewerRight } from '/@/components/ModalPanel';
import { NodeHead } from '/@/components/ModalPanel/index';
import IconFontSymbol from '/@/components/IconFontSymbol/Index.vue';
import { onBeforeUpdate, nextTick, onMounted, reactive, computed, ref, inject, createVNode } from 'vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { TaskApproveOpinion, ValidateForms } from '/@/model/workflow/bpmnConfig';
import { cloneDeep } from 'lodash-es';
import { useI18n } from '/@/hooks/web/useI18n';
import { GeneratorConfig } from '/@/model/generator/generatorConfig';
import { FormEventColumnConfig } from '/@/model/generator/formEventConfig';
import { changeFormJson } from '/@/hooks/web/useWorkFlowForm';
import { SystemForm } from '/@/components/SystemForm/index';
import { FormType } from '/@/enums/workflowEnum';
import { createFormEvent, loadFormEvent, submitFormEvent } from '/@/hooks/web/useFormEvent';
import { message, Modal } from "ant-design-vue";
import { updateWorkflow } from '/@/api/workflow/adminOperation';
import { useMultipleTabStore } from '/@/store/modules/multipleTab';
import { useRouter } from 'vue-router';
import useEventBus from '/@/hooks/event/useEventBus';
const tabStore = useMultipleTabStore();
const { bus, FLOW_PROCESSED } = useEventBus();
const router = useRouter();
const { currentRoute } = useRouter();
const { t } = useI18n();
const props = withDefaults(
defineProps<{
disabled: boolean | undefined;
formInfos: Array<any>;
opinions?: Array<TaskApproveOpinion> | undefined;
opinionsComponents?: Array<string> | undefined;
formAssignmentData?: null | Recordable;
processId: string;
}>(),
{
disabled: false,
formInfos: () => {
return [];
},
processId: ''
},
);
const flowInfo = inject('flowInfo')
const emits = defineEmits(['getFormConfigs']);
let uploadComponent: { ids: Array<string> } = reactive({ ids: [] });
let activeIndex = ref(0);
let itemRefs = ref([]) as any;
const setItemRef = (el: never) => {
itemRefs.value.push(el);
};
onBeforeUpdate(() => {
itemRefs.value = [];
});
let forms: {
formModels: Array<Recordable>;
configs: Array<{
formName: string;
formProps: {};
formModel: Recordable;
formKey: string;
validate: boolean;
formType: FormType;
workflowPermissions?: Array<any>;
opinions?: Array<any>;
opinionsComponents?: Array<any>;
systemComponent?: {
functionalModule: string;
functionName: string;
functionFormName: string;
};
formJson?: Array<any>;
isOldSystem?: boolean;
}>;
formEventConfigs: FormEventColumnConfig[];
modes: string[];
} = reactive({
formModels: [],
configs: [],
formEventConfigs: [],
modes: []
});
onMounted(async () => {
for await (let element of props.formInfos) {
let formModels = {};
if (element.formData) {
formModels = cloneDeep(element.formData);
}
// 参数赋值[赋值权限最大]
if (props.formAssignmentData) {
if (props.formAssignmentData[element.formConfig.formId]) {
formModels = { ...formModels, ...props.formAssignmentData[element.formConfig.formId] };
}
}
forms.formModels.push(formModels);
// 默认赋值view
forms.modes.push('view');
// 系统表单
if (element.formType == FormType.SYSTEM) {
forms.configs.push({
formName: element.formConfig.formName,
formProps: {},
formModel: formModels,
formKey: element.formConfig.key,
validate: true,
formType: element.formType,
workflowPermissions: element.formConfig.children,
opinions: props.opinions,
opinionsComponents: props.opinionsComponents,
systemComponent: {
functionalModule: element.functionalModule,
functionName: element.functionName,
functionFormName: 'Form',
},
formJson: element.formJson,
isOldSystem: false,
});
// 上传组件Id集合
setTimeout(() => {
getSystemUploadComponentIds();
}, 0);
} else {
const model = JSON.parse(element.formJson) as GeneratorConfig;
const { formJson, formEventConfig } = model;
if (formEventConfig) {
forms.formEventConfigs.push(formEventConfig);
//初始化表单
await createFormEvent(formEventConfig, formModels, true);
//加载表单
await loadFormEvent(formEventConfig, formModels, true);
//TODO 暂不放开 工作流没有获取表单数据这个步骤 获取表单数据
// getFormDataEvent(formEventConfig, formModels,true);
}
let formKey = element.formConfig.key;
let config = {
formName: element.formConfig.formName,
formProps: {},
formModel: {},
formKey,
validate: true,
formType: element.formType,
};
let isViewProcess = props.disabled;
let { buildOptionJson, uploadComponentIds } = changeFormJson(
{
formJson,
formConfigChildren: element.formConfig.children,
formConfigKey: element.formConfig.key,
opinions: props.opinions,
opinionsComponents: props.opinionsComponents,
},
isViewProcess,
uploadComponent.ids,
);
uploadComponent.ids = uploadComponentIds;
if (buildOptionJson.schemas) {
config.formProps = buildOptionJson;
forms.configs.push(config);
}
}
// });
}
await nextTick();
setTimeout(() => {
setFormModel();
}, 0);
emits('getFormConfigs', forms.configs.length ? forms.configs[activeIndex.value] : null);
});
function setFormModel() {
for (let index = 0; index < itemRefs.value.length; index++) {
if (itemRefs.value[index]) {
itemRefs.value[index].setFieldsValue(forms.formModels[index]);
}
}
}
async function setFormData(formData) {
await nextTick();
forms.formModels = formData;
setFormModel();
}
function changeActiveIndex(index: number) {
activeIndex.value = index;
emits('getFormConfigs', forms.configs[activeIndex.value]);
}
function getSystemUploadComponentIds() {
for (let index = 0; index < itemRefs.value.length; index++) {
if (itemRefs.value[index] && itemRefs.value[index].getUploadComponentIds) {
let ids = itemRefs.value[index].getUploadComponentIds();
uploadComponent.ids = [...uploadComponent.ids, ...ids];
}
}
}
// 获取上传组件的字段值集合
function getUploadComponentIds() {
return uploadComponent.ids;
}
async function validateForm() {
let validateForms: ValidateForms = [];
for (let index = 0; index < itemRefs.value.length; index++) {
if (itemRefs.value[index]) {
try {
await itemRefs.value[index]?.validate();
validateForms.push({
validate: true,
msgs: [],
isOldSystem: forms.configs[index].isOldSystem,
});
forms.configs[index].validate = true;
} catch (error: any | Array<{ errors: Array<string>; name: Array<string> }>) {
validateForms.push({
validate: false,
msgs: error?.errorFields,
});
forms.configs[index].validate = false;
}
}
}
return validateForms;
}
async function saveDraftData() {
let formModes = {};
for (let index = 0; index < forms.configs.length; index++) {
const ele = forms.configs[index];
if (ele.formType == FormType.SYSTEM) {
let values = await itemRefs.value[index].validate();
formModes[ele.formKey] = values;
} else {
formModes[ele.formKey] = ele.formModel;
}
}
return formModes;
}
async function getFormModels(saveRowKey,isOnlyActive) {
let formModes = {};
for (let index = 0; index < forms.configs.length; index++) {
if (isOnlyActive && index != activeIndex.value) {
continue;
}
const ele = forms.configs[index];
if (ele.formType == FormType.SYSTEM) {
let values = await itemRefs.value[index].workflowSubmit(saveRowKey);
formModes[ele.formKey] = values;
} else {
formModes[ele.formKey] = ele.formModel;
}
}
// forms.configs.forEach((ele) => {
// formModes[ele.formKey] = ele.formModel;
// });
forms.formEventConfigs.forEach(async (ele, i) => {
if (isOnlyActive && i != activeIndex.value) {
return true;
}
//此组件 获取数据 就是为了提交表单 所以 表单提交数据 事件 就此处执行
await submitFormEvent(ele, forms.configs[i]?.formModel);
});
return formModes;
}
function getSystemType() {
let system = {};
for (let index = 0; index < forms.configs.length; index++) {
const ele = forms.configs[index];
if (ele.formType == FormType.SYSTEM) {
system[ele.formKey] = itemRefs.value[index].getIsOldSystem();
}
}
return system;
}
function handleCancel() {
itemRefs.value[activeIndex.value].setDisabledForm(true);
forms.modes[activeIndex.value] = 'view';
itemRefs.value[activeIndex.value].setFieldsValue(forms.formModels[activeIndex.value]);
}
async function handleDelete() {
Modal.confirm({
title: '提示信息',
icon: createVNode(ExclamationCircleOutlined),
content: '是否确认删除?',
okText: '确认',
cancelText: '取消',
async onOk() {
try{
let formVal = await itemRefs.value[activeIndex.value].getFormModels();
await itemRefs.value[activeIndex.value].handleDelete(formVal.id)
message.success('删除成功')
setTimeout(() => {
tabStore.closeTab(currentRoute.value, router);
bus.emit(FLOW_PROCESSED);
}, 1000)
} catch (err){
message.error('删除失败,请稍后再试');
}
},
onCancel() {},
});
}
async function handleSave() {
const params = await getFormModels(true, true);
const code = await updateWorkflow({ 'variables': params, 'processInstanceId': flowInfo.value.processId })
if (code) {
message.success(t('保存成功'));
} else {
message.success(t('保存失败,请稍后再试'));
}
itemRefs.value[activeIndex.value].setDisabledForm(true);
forms.modes[activeIndex.value] = 'view';
}
function handleEdit() {
itemRefs.value[activeIndex.value].setDisabledForm(false);
forms.modes[activeIndex.value] = 'edit';
}
defineExpose({
validateForm,
getFormModels,
saveDraftData,
setFormData,
getUploadComponentIds,
getSystemType,
handleEdit,
handleSave,
handleCancel,
handleDelete
});
</script>
<style lang="less" scoped>
.form-container {
display: flex;
height: 100%;
margin-top: -10px;
}
.box {
width: 100%;
.form-left {
float: left;
height: 100vh;
box-shadow: 5px 5px 5px rgb(0 0 0 / 10%);
z-index: 9998;
.resize-shrink-sidebar {
cursor: col-resize;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
bottom: 0;
right: 0;
z-index: 9999;
.shrink-sidebar-text {
padding: 0 2px;
background: #f2f2f2;
border-radius: 10px;
}
}
.left-box {
margin-right: 10px;
border-right: 1px solid #f0f0f0;
height: 80vh;
}
span {
font-size: 16px;
font-weight: 500;
padding-left: 4px;
}
.form-name {
height: 36px;
display: flex;
align-items: center;
font-size: 14px;
cursor: pointer;
color: rgb(102 102 102 / 99.6%);
margin-right: -2px;
padding-left: 4px;
}
.actived {
border-right: 1px solid #5e95ff;
}
.dot {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: transparent;
margin-right: 4px;
}
.validate {
background-color: @clear-color;
}
.icon-box {
font-size: 16px;
margin-right: 4px;
}
.left-title {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
padding: 10px 4px 10px 0;
top: 0;
left: 0;
.in-or-out {
position: absolute;
right: 10px;
}
}
}
.form-box {
overflow: auto;
height: calc(100vh - 280px);
}
.form-right {
width: 100%;
padding-top: 20px;
}
}
.page-bg-wrap {
background-color: #fff;
}
.top-toolbar {
min-height: 44px;
margin-bottom: 12px;
border-bottom: 1px solid #eee;
}
</style>

View File

@ -1,23 +1,25 @@
<template>
<div class="page-bg-wrap">
<div class="top-toolbar">
<a-space :size="10" wrap style="gap:0">
<a-button style="margin-right: 10px" @click="close">
<slot name="icon">
<close-outlined />
</slot>
关闭
</a-button>
<a-button v-if="mode != 'view'" type="primary" @click="handleSubmit">
<slot name="icon">
<check-circle-outlined />
</slot>
确认
</a-button>
</a-space>
<a-spin :spinning="spinning" tip="请稍后...">
<div class="page-bg-wrap">
<div class="top-toolbar" id="formViewPage">
<a-space :size="10" wrap style="gap:0">
<a-button style="margin-right: 10px" @click="close">
<slot name="icon">
<close-outlined />
</slot>
关闭
</a-button>
<a-button v-if="mode != 'view'" type="primary" @click="handleSubmit">
<slot name="icon">
<check-circle-outlined />
</slot>
确认
</a-button>
</a-space>
</div>
<component :is="dynamicComponent" ref="formRef" :fromPage="FromPageType.MENU" @form-mounted="onFormMounted" />
</div>
<component :is="dynamicComponent" ref="formRef" :fromPage="FromPageType.MENU" @form-mounted="onFormMounted" />
</div>
</a-spin>
</template>
<script setup>
@ -46,6 +48,8 @@ const tabStore = useMultipleTabStore();
const formProps = ref(null);
const formId = ref(currentRoute.value?.params?.id);
const spinning = ref(false)
const { notification } = useMessage();
const { t } = useI18n();
const hash = location.hash||location.pathname;
@ -84,6 +88,7 @@ function close() {
}
async function handleSubmit() {
spinning.value = true
try {
const saveSuccess = await saveModal();
if (saveSuccess) {
@ -98,6 +103,7 @@ async function handleSubmit() {
}, 1000);
}
} finally {
spinning.value = false
}
}
@ -105,7 +111,8 @@ async function saveModal() {
let saveSuccess = false;
const _mode = mode.value;
try {
const values = await formRef.value?.validate();
await formRef.value?.validate();
const values = (formRef.value?.getFormModal && formRef.value.getFormModal()) || await formRef.value?.validate();
//添加隐藏组件
if (formProps.hiddenComponent?.length) {
formProps.hiddenComponent.forEach((component) => {

View File

@ -0,0 +1,82 @@
<template>
<div class="flow-page">
<FormInformation
v-if="!isSelfForm"
:key="renderKey"
ref="formInformation"
:disabled="readonly"
:formAssignmentData="data.formAssignmentData"
:formInfos="data.formInfos"
:opinions="data.opinions"
:opinionsComponents="data.opinionsComponents"
@get-form-configs="(config) => (formConfigs = config)"
/>
<component v-else-if="data" :is="componentName" :formData="data" :flowData="flowData"/>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router';
import { onMounted, reactive, ref, unref, createVNode, provide, defineExpose, computed, defineAsyncComponent } from 'vue';
import FormInformation from './flowFormInformation.vue';
import { getApprovalProcess } from '/@/api/workflow/task';
import Title from '/@/components/Title/src/Title.vue';
import { message, Modal } from 'ant-design-vue';
const selfFormList = [
// 流程编码
// 'exchange_opinion',
// 'audit_programme'
]
const selfFormMap = {
// 流程编码对应文件路径
// 'exchange_opinion': 'auditOperationsCenter/exchangeViews/index.vue',
// 'audit_programme': 'auditOperationsCenter/preAuditProgramme/index.vue'
}
const isSelfForm = computed(() => {
return selfFormList.includes(props.data.item.code)
})
const componentName = computed(() => {
return defineAsyncComponent({
loader: () =>
import(
`../${selfFormMap[props.data.item.code]}`
),
})
})
const readonly = ref(true); // 查看流程会触发只读模式
const renderKey = ref('');
const formConfigs = ref();
const formInformation = ref();
const props = defineProps({
data: {
type: Object,
default: () => null
},
flowData: {
type: Object,
default: () => null
}
});
onMounted(async () => {
});
defineExpose({
})
</script>
<style lang="less" scoped>
.flow-page {
position: relative;
box-sizing: border-box;
width: 100%;
height: 100%;
}
</style>

View File

@ -0,0 +1,144 @@
<template>
<div class="process-monitoring-page">
<a-spin :spinning="spinning" tip="请稍后...">
<a-tabs tab-position="top" v-model:activeKey="activeKey" type="card">
<a-tab-pane :key="1" :tab="'表单信息'" force-render>
</a-tab-pane>
<a-tab-pane :key="2" :tab="'流程信息'">
</a-tab-pane>
<a-tab-pane :key="3" :tab="'流转记录'" style="overflow: auto">
</a-tab-pane>
<!-- <a-tab-pane :key="4" :tab="'附件汇总'">
<SummaryOfAttachments :processId="processId"/>
</a-tab-pane> -->
<a-tab-pane :key="5" :tab="'流程变更记录'">
</a-tab-pane>
<a-tab-pane :key="6" :tab="'审批记录'">
</a-tab-pane>
<a-tab-pane :key="7" :tab="'流程变量'">
</a-tab-pane>
<!-- <a-tab-pane :key="8 + index" v-for="(item, index) in predecessorTasks" :tab="item.schemaName">
<LookRelationTask
v-show="activeKey === 8 + index"
:taskId="item.taskId"
:processId="item.processId"
position="left"
/>
</a-tab-pane> -->
</a-tabs>
<div class="tab-content" v-if="dataComplete">
<processMonitoringFlowPage :data="data" :flowData="flowData" v-show="activeKey == '1'"></processMonitoringFlowPage>
<ProcessInformationWithInfo :process-id="processId" :data="data" :flowData="flowData" v-if="activeKey == '2'" :canClick="true" :key="ProcessInformationKey"/>
<FlowRecord :list="data.taskRecords" :processId="processId" v-show="activeKey == '3'"/>
<ChangeRecord v-if="activeKey == 5" :processId="processId" :formDataId="data.formInfos[0].formData.id" :formInfos="data.formInfos" />
<AuditRecord :processId="processId" :schemaId="schemaId" :xml="xml" v-show="activeKey == '6'"/>
<ProcVarPage :processId="processId" :schemaId="schemaId" :xml="xml" v-show="activeKey == '7'"/>
</div>
</a-spin>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, unref, provide } from 'vue';
import FlowRecord from '/@/views/workflow/task/components/flow/FlowRecord.vue';
import ProcessInformationWithInfo from '/@/views/workflow/task/components/flow/ProcessInformationWithInfo.vue';
import SummaryOfAttachments from '/@/views/workflow/task/components/flow/SummaryOfAttachments.vue';
import LookRelationTask from '/@/views/workflow/task/components/flow/LookRelationTask.vue';
import { SchemaTaskItem } from '/@/model/workflow/bpmnConfig';
import ChangeRecord from '/@/views/formChange/formChangeLog/index.vue';
import AuditRecord from '/@/views/auditOpt/auditRecord/index.vue';
import processMonitoringFlowPage from './processMonitoringFlowPage.vue'
// import ProcVarPage from '/@/views/editProVar/procVarManage/index.vue';
import { getApprovalProcess } from '/@/api/workflow/task';
import { Modal } from 'ant-design-vue';
import userTaskItem from '/@/views/workflow/task/hooks/userTaskItem';
const { data, initProcessData } = userTaskItem();
import { useRouter } from 'vue-router';
const router = useRouter();
const currentRoute = router.currentRoute.value;
const fullPath = currentRoute.fullPath;
const rQuery = currentRoute.query;
const rParams = currentRoute.params;
const schemaId = ref(rParams.arg1);
const processId = ref(rParams.arg2);
const ProcessInformationKey = ref(0)
const spinning = ref(false);
const dataComplete = ref(false);
const flowInfo = ref({
schemaId: schemaId.value,
processId: processId.value
})
const flowData = ref({})
provide('flowInfo', flowInfo)
provide('refreshApproveInfo', getApproveInfo)
const activeKey = ref(1);
onMounted(async () => {
await getApproveInfo()
});
async function getApproveInfo() {
try {
spinning.value = true
let res = await getApprovalProcess('', unref(processId.value));
initProcessData(res);
flowData.value = res
spinning.value = false
dataComplete.value = true
ProcessInformationKey.value++
} catch (error) {
console.error('processMonitoringPage Error', error)
}
}
</script>
<style lang="less" scoped>
.process-monitoring-page {
height: 100%;
width: 100%;
:deep(.ant-spin-nested-loading) {
height: 100%;
width: 100%;
display: flex;
.ant-spin-container {
height: 100%;
display: flex;
flex-direction: column;
flex: 1;
padding: 20px;
.ant-tabs-nav {
margin: 0;
}
.top-toolbar {
display: flex;
border-bottom: none;
padding: 16px 0;
}
.tab-content {
flex: 1;
min-height: 0;
overflow: auto;
box-sizing: border-box;
display: flex;
.process-information-with-info {
flex: 1;
display: flex;
flex-direction: column;
.flow-record-box {
flex: 1;
}
}
}
}
}
}
</style>