--添加测试模块

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

@ -115,10 +115,14 @@
import { BasicTable, useTable, FormSchema, BasicColumn } from '/@/components/Table';
import { useI18n } from '/@/hooks/web/useI18n';
import { useRouter } from 'vue-router';
import { usePermissionStore } from '/@/store/modules/permission';
const permissionStore = usePermissionStore();
const { currentRoute } = useRouter();
const router = useRouter();
const definitionKey = ref('');
const menuId = currentRoute.value.meta.menuId;
const perm = permissionStore.getPermCodeList.find((x) => x.menuId === menuId);
onMounted(() => {
definitionKey.value = currentRoute.value.query.definitionKey as string;
});
@ -427,8 +431,13 @@
processId.value = record.processId;
taskId.value = record.taskId;//改版 一个流程中可能有多个taskId 已不返回
schemaId.value = record.schemaId;
await nextTick()
await lookProcess.value.look();
let appointedAuditor = 'N'
if(perm?.buttonAuthCode.includes('monitor:appointedAuditor')) {
appointedAuditor = 'Y'
}
router.push(`/processMonitoring/${record.schemaId}/${record.processId}/processMonitoringFlow?taskId=${record.taskId || ''}&appointedAuditor=${appointedAuditor}`)
// await nextTick()
// await lookProcess.value.look();
}
const [registerUpdateProcessVersionModal, { openModal: openUpdateProcessVersionModal }] = useModal();

View File

@ -0,0 +1,87 @@
<template>
<span>
<a-button class="mr-2" @click="show">{{ t('修改审批人') }}</a-button>
<SelectUserV2 ref="selectUser" v-model:value="addStepUser" :submitClose="false" @change="submit" just-dialog title="加签减签"/>
</span>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue';
import SelectUserV2 from '/@/components/Form/src/components/SelectUserV2.vue';
import { notification, message, Modal } from 'ant-design-vue';
import { useI18n } from '/@/hooks/web/useI18n';
import { postSetSign, postSetSignV2, postSetAssignee } from '/@/api/workflow/task';
const { t } = useI18n();
const props = defineProps({
lastAddStepUser: {
type: String || Array,
},
schemaId: {
type: String,
},
processId: {
type: String,
},
taskId: {
type: String,
},
});
const addStepUser = ref()
const selectUser = ref()
const emits = defineEmits(['change']);
function show() {
if(Array.isArray(props.lastAddStepUser)) {
let ids = props.lastAddStepUser.map(item => {
return item?.id || item
})
addStepUser.value = ids.join(',')
} else {
addStepUser.value = props.lastAddStepUser
}
selectUser.value.show()
}
function cancel() {
}
async function submit(ids, memberList) {
try {
let idList = memberList.map(item => {
return item.id
})
// let lastIdList = []
// if(Array.isArray(props.lastAddStepUser)) {
// lastIdList = props.lastAddStepUser.map(item => {
// return item?.id || item
// })
// } else {
// lastIdList = props.lastAddStepUser.split(',')
// }
// let addUserIds = idList.filter(item => {
// return lastIdList.indexOf(item) == -1
// })
// let subUserIds = lastIdList.filter(item => {
// return idList.indexOf(item) == -1
// })
// if(!addUserIds.length && !subUserIds.length) {
// message.error('请修改审批人员')
// return
// }
let data = {
// addUserIds,
// subUserIds,
schemaId: props.schemaId,
taskId: props.taskId
}
await postSetAssignee(props.taskId, idList);
message.success('操作成功')
selectUser.value.close()
emits('change')
} catch (e) {
message.error(e)
// message.error('操作失败,请稍后再试');
}
}
</script>
<style lang="less" scoped>
</style>

View File

@ -2,14 +2,31 @@
<div style="margin:20px;">
当前流程审批人{{currentTaskAssigneeNames.replaceAll(",","、")}}
</div>
<div style="margin:20px;" v-if="currentTaskAssignees">
<div style="margin:20px;" v-if="currentTaskMap">
节点审批人
<div v-for="(assignees,taskKey) in currentTaskAssignees" :key="taskKey">
<span>{{assignees[0].taskName}}{{currentTaskInfo?.taskDefinitionKey==taskKey?'(当前审批节点)':''}}</span>
<span :class="canClick ? 'custom-cursor' : ''" v-for="(assignee,index) in assignees" :key="index" @click="openView(assignee)">
{{assignee.assigneeNameStr?(assignee.assigneeNameStr?.replaceAll(",","/") + (index<assignees.length-1?'':'')):('')}}
</span>
</div>
<template v-if="isSubTask">
<div v-for="(process,taskKey) in currentTaskMap" :key="taskKey" class="task">
<span>{{currentTaskName?.[taskKey] || ''}}{{currentTaskInfo?.taskDefinitionKey==taskKey?'(当前审批节点)':''}}</span>
<div v-for="(subProcess,subProcessKey) in process" :key="subProcessKey" class="sub-task">
<span v-if="subProcess?.name">{{subProcess.name}}</span>
<span v-for="(item, index) in subProcess.list" :key="index" @click="openView(item)">
{{item.assigneeNameStr?(item.assigneeNameStr?.replaceAll(",","/") + (index < subProcess.list.length-1?'':'')):('')}}
</span>
<a-button type="link" @click="addTask(subProcess.list)" v-if="canClick && isMultiple(subProcess.list)">加签</a-button>
<a-button type="link" @click="subTask(subProcess.list)" v-if="canClick && isMultiple(subProcess.list)">减签</a-button>
</div>
</div>
</template>
<template v-else>
<div v-for="(assignees,taskKey) in currentTaskAssignees" :key="taskKey" class="task">
<span>{{assignees[0].taskName}}{{currentTaskInfo?.taskDefinitionKey==taskKey?'(当前审批节点)':''}}</span>
<span :class="canClick ? 'custom-cursor' : ''" v-for="(assignee,index) in assignees" :key="index" @click="openView(assignee)">
{{assignee.assigneeNameStr?(assignee.assigneeNameStr?.replaceAll(",","/") + (index<assignees.length-1?'':'')):('')}}
</span>
<a-button type="link" @click="addTask(assignees)" v-if="canClick && isMultiple(assignees)">加签</a-button>
<a-button type="link" @click="subTask(assignees)" v-if="canClick && isMultiple(assignees)">减签</a-button>
</div>
</template>
</div>
<!-- 流程信息 -->
<div class="flow-record-box">
@ -29,16 +46,25 @@
initBpmnModeler()
}">
<CurrentNode :schemaId="schemaId" :currentTaskAssigneeNames="currentTaskAssigneeNames"
:clickedTaskAssignees="clickedTaskAssignees" :processId="processId">
:clickedTaskAssignees="clickedTaskAssignees" :processId="processId" :canClick="canClick">
</CurrentNode>
</a-modal>
<SelectUserV2 ref="selectUser" v-model:value="addStepUser" :submitClose="false" @change="submitAdd" just-dialog title="加签减签" lastSelectedDisabled :confirmLoading="confirmLoading"/>
<ModalPanel title="减签" :visible="subVisible" :width="500" class="select-user-model" @close="closeSub" @submit="submitSub" :confirmLoading="confirmLoading">
<div class="sub-task-box">
<a-checkbox v-for="(item, index) in subCheckList" :key="index" v-model:checked="item.selected">{{ item.assigneeNameStr }}</a-checkbox>
</div>
</ModalPanel>
</template>
<script lang="ts" setup>
import CustomModeler from '/@bpmn/modeler';
import { ZoomInOrOut } from '/@/components/ModalPanel';
import { getFinishedTask } from '/@/api/workflow/task';
import { ref, reactive, onMounted, provide } from 'vue';
import { getFinishedTask, postAddTask, postSubTask } from '/@/api/workflow/task';
import { ref, reactive, onMounted, provide, computed, inject } from 'vue';
import CurrentNode from '../../../../actHiTaskinst/components/Form.vue';
import SelectUserV2 from '/@/components/Form/src/components/SelectUserV2.vue';
import { ModalPanel } from '/@/components/ModalPanel/index';
import { message } from 'ant-design-vue';
const visibleFlowRecordModal = ref(false);
const props = withDefaults(
@ -65,6 +91,8 @@ const props = withDefaults(
);
const taskNode = ref<Array<{ 'taskId': string, 'taskName': string }>>([]);
const bpmnCanvas = ref();
const refreshInfo = inject('refreshApproveInfo', () => {})
const selectUser = ref()
let data: {
bpmnViewer: any;
zoom: number;
@ -74,11 +102,62 @@ let data: {
zoom: 1,
xmlString: '',
});
const currentHandleTask = ref({});
const currentTaskName = ref({});
const isSubTask = ref(false);
const addStepUser = ref('')
const lastIds = ref([])
const subCheckList = ref([])
const confirmLoading = ref(false)
const subVisible = ref(false)
const currentTaskMap = computed(() => {
if(!props.currentTaskAssignees) return null
let taskMap = {}
for(let key in props.currentTaskAssignees){
taskMap[key] = {}
// 循环处理currentTaskAssignees 对没审批人的task 进行assigneeNameStr赋值
props.currentTaskAssignees[key].forEach((item:any) => {
if(!item.assigneeNameStr){
item.assigneeNameStr = '无审批人'
}
if(!item.assigneeVoList) {
item.assigneeVoList = []
}
})
props.currentTaskAssignees[key].forEach((item:any) => {
if(item.subProcessInstId) {
if(taskMap[key][item.subProcessInstId]){
taskMap[key][item.subProcessInstId].list.push(item)
}else{
let val = {
name: item.subProcessInstName,
executionId: item.executionId,
list: [item]
}
taskMap[key][item.subProcessInstId] = val
currentTaskName.value[key] = item.taskName
}
isSubTask.value = true
} else {
isSubTask.value = false
return props.currentTaskAssignees
}
})
}
return taskMap
});
onMounted(() => {
data.xmlString = props.xml;
if (data.xmlString) initBpmnModeler();
});
function isMultiple(list) {
return list.length > 0 && list[0].nodeMultipleInstancesType != 0
}
async function initBpmnModeler() {
data.bpmnViewer = await new CustomModeler({
container: bpmnCanvas.value,
@ -118,6 +197,92 @@ async function redrawing() {
provide('taskNode', taskNode);
function addTask(val) {
let list = []
currentHandleTask.value = val[0]
val.forEach(item => {
item.assigneeVoList.forEach(m => {
list.push(m.id)
})
})
lastIds.value = list
addStepUser.value = list.join(',')
selectUser.value.show()
}
async function submitAdd(ids, memberList) {
confirmLoading.value = true
let idList = []
memberList.forEach(item => {
if(!lastIds.value.includes(item.id)){
idList.push(item.id)
}
})
let params = {
processId: props.processId,
nodeKey: currentHandleTask.value.taskDefKey,
userIds: idList,
type: 'add'
}
if(isSubTask.value){
params.executionId = currentHandleTask.value.executionId
params.subProcessId = currentHandleTask.value.subProcessInstId
}
try {
await postAddTask(params)
message.success('加签成功')
lastIds.value = []
selectUser.value.close()
refreshInfo()
} catch (e) {
console.error(e)
message.error('加签失败')
} finally {
confirmLoading.value = false
}
}
function subTask(val) {
let ids = []
subCheckList.value = val.map(item => {
ids.push(item.taskId)
return {...item, selected: true}
})
subVisible.value = true
}
function closeSub() {
subVisible.value = false
}
async function submitSub() {
confirmLoading.value = true
let subIds = []
subCheckList.value.forEach(item => {
if(!item.selected){
subIds.push(item.taskId)
}
})
let params = {
processId: props.processId,
taskIds: subIds,
type: 'sub'
}
try {
await postSubTask(params)
message.success('减签成功')
lastIds.value = []
closeSub()
refreshInfo()
} catch (e) {
console.error(e)
message.success('减签失败')
} finally {
confirmLoading.value = false
}
}
function setColors(finishedIds: Array<string>, currentIds: Array<string>) {
// finishedIds 完成的节点id
// currentIds 进行中节点id
@ -165,6 +330,28 @@ if (props.canClick) {
}
}
</script>
<style lang="less" scoped>
.task {
padding-left: 15px;
:deep(.ant-btn) {
padding: 0 10px;
}
.sub-task {
padding-left: 15px;
display: flex;
align-items: center;
}
}
.sub-task-box {
padding: 20px;
display: flex;
flex-direction: column;
:deep(.ant-checkbox-wrapper) {
margin-left: 0;
margin-bottom: 10px;
}
}
</style>
<style lang="less">
@import '/@/assets/style/bpmn-js/diagram-js.css';
@import '/@/assets/style/bpmn-js/bpmn-font/css/bpmn.css';
@ -191,9 +378,9 @@ if (props.canClick) {
/* 按钮(放大 缩小 清除) */
.fixed-bottom {
position: absolute;
top: 110px;
bottom: 110px;
font-size: 30px;
left: 40%;
left: 20%;
display: flex;
}

View File

@ -0,0 +1,80 @@
<template>
<div class="process-information-with-info">
<div class="base-info" v-if="actorNames">
<Row>
<Col :span="24" class="info-col">
<div class="info-label">
创建日期
</div>
{{flowData.taskRecords[0].startTime}}
</Col>
<Col :span="24" class="info-col" v-if="flowData.taskInfo">
<div class="info-label">
当前环节
</div>
{{flowData.taskInfo.name}}
</Col>
</Row>
</div>
<process-information
:process-id="processId"
:xml="data.xml"
:schemaId="flowData.schemaId || flowData.schemaInfo.id"
:currentTaskAssigneeNames="flowData.currentTaskAssigneeNames"
:currentTaskAssignees="flowData.currentTaskAssignees"
:canClick="canClick" />
</div>
</template>
<script lang="ts" setup>
import { ref, reactive, onMounted, computed } from 'vue'
import { Row, Col } from 'ant-design-vue'
import ProcessInformation from '/@/views/workflow/task/components/flow/ProcessInformation.vue';
const props = defineProps({
data: {
type: Object,
default: {}
},
processId: {
type: String,
default: ''
},
flowData: {
type: Object,
default: {}
},
canClick: {
type: Boolean,
default: false
}
});
const actorNames = computed(() => {
let names = ''
names = props.flowData.currentTaskAssigneeNames
return names
})
onMounted(() => {
});
</script>
<style lang="less" scoped>
.process-information-with-info {
.base-info {
padding: 20px 20px 0px 20px;;
.info-label {
width: 70px;
text-align: right;
}
.info-col {
display:flex;
margin-bottom: 12px;
}
}
}
</style>
<style lang="less">
.process-information-with-info {
.flow-record-box{
margin-top: 0;
height: 50vh;
}
}
</style>

View File

@ -40,6 +40,8 @@
import { notification } from 'ant-design-vue';
import { TaskTypeUrl } from '/@/enums/workflowEnum';
import { useI18n } from '/@/hooks/web/useI18n';
import useEventBus from '/@/hooks/event/useEventBus';
const { bus, CREATE_FLOW } = useEventBus();
const { t } = useI18n();
const configColumns: BasicColumn[] = [
{
@ -105,6 +107,8 @@
fixed: undefined,
},
});
bus.on(CREATE_FLOW, reload);
async function handleEdit(record: Recordable) {
try {
let res = await getDraftInfo(record.id);

View File

@ -29,7 +29,7 @@
<script setup lang="ts">
import userTaskTable from './../../hooks/userTaskTable';
import { ref, createVNode } from 'vue';
import { ref, createVNode,h } from 'vue';
import { useRouter } from 'vue-router';
import LookProcess from './../LookProcess.vue';
@ -44,10 +44,19 @@
import { useMessage } from '/@/hooks/web/useMessage';
import { Modal } from 'ant-design-vue';
import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import {useTenantManager} from "/@/utils/tenantManager";
import { getAppEnvConfig } from '/@/utils/env';
import { useUserStore } from '/@/store/modules/user';
import { storeToRefs } from 'pinia';
const router = useRouter();
const { notification } = useMessage();
const { t } = useI18n();
const restartProcessVisible = ref(false);
const router = useRouter();
const tenantEnabled=getAppEnvConfig().VITE_GLOB_TENANT_ENABLED;
const {toggleLocal}=useTenantManager();
const userStore = useUserStore();
const { userInfo } = storeToRefs(userStore);
const configColumns: BasicColumn[] = [
{
title: t('流水号'),
@ -169,15 +178,66 @@
}
}
const onRowDblClick = (record, index) => {
const {tenantId,tenantCode,tenantName} = record;
if(tenantEnabled =='true'&&tenantId){
let currentTenantId=userInfo.value.tenantId;
if(tenantId!=currentTenantId){
if(window.autoToggleTenant=='Y'){
switchTenant(tenantCode).then(() => {
openDetailPage(record, true);
})
}else {
const {createConfirm} = useMessage();
createConfirm({
iconType: 'warning',
title: () => h('span', t('温馨提醒')),
content: () => h('div', [
h('span', t(`当前流程的发起租户是"${tenantName}",需要切换到该租户才能查看该流程详情信息`)),
h('br'),
h('span', t('是否确认切换租户?未保存的数据可能会丢失!'))
]),
width: '600px',
onOk: async () => {
switchTenant(tenantCode).then(() => {
openDetailPage(record, true);
})
},
okText: () => t('确认'),
cancelText: () => t('取消'),
});
}
}else{
openDetailPage(record);
};
}else{
openDetailPage(record);
}
};
function openDetailPage(record,isTenantSwitch){
const { processId, taskId, schemaId } = record;
router.push({
path: `/flow/${schemaId}/${processId}/approveFlow`,
query: {
taskId,
readonly: 1
}
}).then(()=> {
if (isTenantSwitch) {
const {notification} = useMessage();
const {tenantName} = record;
notification.success({
message: 'Tip',
description: t('已切换到租户“' + tenantName + '"'),
});
}
});
};
}
async function switchTenant(tenantCode: string) {
await toggleLocal({tenantCode:tenantCode, goHome:false,tabCloseAction:"closeOther"});
}
// function restartProcess() {
// restartProcessVisible.value = true;
// }

View File

@ -40,6 +40,7 @@
import {useTenantManager} from "/@/utils/tenantManager";
const router = useRouter();
const tenantEnabled=getAppEnvConfig().VITE_GLOB_TENANT_ENABLED;
const { currentRoute } = router;
const { t } = useI18n();
const userStore = useUserStore();
@ -142,30 +143,33 @@
);
const onRowDblClick = (record, index) => {
const {tenantId,tenantCode,tenantName} = record;
let tenantEnabled=getAppEnvConfig().VITE_GLOB_TENANT_ENABLED;
if(tenantEnabled =='true'&&tenantId){
let currentTenantId=userInfo.value.tenantId;
if(tenantId!=currentTenantId){
const { createConfirm} = useMessage();
createConfirm({
iconType: 'warning',
title: () => h('span', t('温馨提醒')),
content: () => h('div', [
h('span', t(`当前流程的发起租户是"${tenantName}",需要切换到该租户才能对该流程进行审批`)),
h('br'),
h('span', t('是否确认切换租户?未保存的数据可能会丢失!'))
]),
width:'600px',
onOk: async () => {
switchTenant(tenantCode).then(()=>{
openDetailPage(record,true);
})
},
okText: () => t('确认'),
cancelText: () => t('取消'),
});
if(window.autoToggleTenant=='Y'){
switchTenant(tenantCode).then(() => {
openDetailPage(record, true);
})
}else {
const {createConfirm} = useMessage();
createConfirm({
iconType: 'warning',
title: () => h('span', t('温馨提醒')),
content: () => h('div', [
h('span', t(`当前流程的发起租户是"${tenantName}",需要切换到该租户才能对该流程进行审批`)),
h('br'),
h('span', t('是否确认切换租户?未保存的数据可能会丢失!'))
]),
width: '600px',
onOk: async () => {
switchTenant(tenantCode).then(() => {
openDetailPage(record, true);
})
},
okText: () => t('确认'),
cancelText: () => t('取消'),
});
}
}else{
openDetailPage(record);
};