commit:支持服务、接收任务,支持串行会签,修复bug

This commit is contained in:
Jerry
2024-09-19 10:01:32 +08:00
parent 634ac49fd1
commit e9a39c24ba
22 changed files with 1017 additions and 898 deletions

View File

@@ -256,6 +256,8 @@ const getButtonType = (type: string) => {
case SysFlowTaskOperationType.TRANSFER:
case SysFlowTaskOperationType.CO_SIGN:
case SysFlowTaskOperationType.SIGN_REDUCTION:
case SysFlowTaskOperationType.BFORE_CONSIGN:
case SysFlowTaskOperationType.AFTER_CONSIGN:
case SysFlowTaskOperationType.MULTI_AGREE:
case SysFlowTaskOperationType.MULTI_SIGN:
case SysFlowTaskOperationType.SET_ASSIGNEE:

View File

@@ -68,6 +68,7 @@ watch(
},
{
immediate: true,
deep: true,
},
);
</script>

View File

@@ -108,9 +108,13 @@
<el-col
:span="24"
v-if="
dialogParams.operation.type !== SysFlowTaskOperationType.CO_SIGN &&
dialogParams.operation.type !== SysFlowTaskOperationType.SIGN_REDUCTION &&
dialogParams.operation.type !== SysFlowTaskOperationType.MULTI_SIGN
[
SysFlowTaskOperationType.CO_SIGN,
SysFlowTaskOperationType.SIGN_REDUCTION,
SysFlowTaskOperationType.BFORE_CONSIGN,
SysFlowTaskOperationType.AFTER_CONSIGN,
SysFlowTaskOperationType.MULTI_SIGN,
].indexOf(dialogParams.operation.type) === -1
"
>
<el-form-item label="审批意见" prop="message">
@@ -206,7 +210,7 @@ const loadTaskWidgetDropdownData = (): Promise<ListData<ANY_OBJECT>> => {
taskId: dialogParams.value.taskId,
})
.then(res => {
resolve({ dataList: res.data.dataList });
resolve({ dataList: res.data });
})
.catch(e => {
reject(e);
@@ -238,6 +242,8 @@ const showAssignSelect = computed(() => {
[
SysFlowTaskOperationType.TRANSFER,
SysFlowTaskOperationType.CO_SIGN,
SysFlowTaskOperationType.BFORE_CONSIGN,
SysFlowTaskOperationType.AFTER_CONSIGN,
SysFlowTaskOperationType.SET_ASSIGNEE,
].indexOf(dialogParams.value.operation.type) !== -1 || showAssignSelect
);
@@ -248,6 +254,8 @@ const multiSelect = computed(() => {
[
SysFlowTaskOperationType.CO_SIGN,
SysFlowTaskOperationType.MULTI_SIGN,
SysFlowTaskOperationType.BFORE_CONSIGN,
SysFlowTaskOperationType.AFTER_CONSIGN,
SysFlowTaskOperationType.TRANSFER,
SysFlowTaskOperationType.SET_ASSIGNEE,
].indexOf(dialogParams.value.operation.type) !== -1
@@ -287,7 +295,9 @@ const onSelectAssignee = () => {
showAssignee: false,
showStartUser:
dialogParams.value.operation.type !== SysFlowTaskOperationType.CO_SIGN &&
dialogParams.value.operation.type !== SysFlowTaskOperationType.SIGN_REDUCTION,
dialogParams.value.operation.type !== SysFlowTaskOperationType.SIGN_REDUCTION &&
dialogParams.value.operation.type !== SysFlowTaskOperationType.BFORE_CONSIGN &&
dialogParams.value.operation.type !== SysFlowTaskOperationType.BFORE_CONSIGN,
multiple: multiSelect.value,
usedUserIdList,
filterObject: otherFilterObject,

View File

@@ -403,6 +403,7 @@ const onAddFlowEntryClick = () => {
})
.catch(e => {
console.log(e);
refreshFormFlowEntry();
});
};
/**
@@ -430,6 +431,7 @@ const onEditFlowEntryClick = (row: ANY_OBJECT) => {
})
.catch(e => {
console.warn(e);
refreshFormFlowEntry();
});
};
/**

View File

@@ -44,19 +44,23 @@ export const useFlowAction = (props: IProp) => {
: props.variableList || thirdParams.value.variableList,
};
});
// 加签
const submitConsign = (assignee: Array<string> | string, isAdd = true) => {
const submitConsign = (
assignee: Array<string> | string,
isAdd = true,
before: boolean | undefined = undefined,
) => {
return new Promise((resolve, reject) => {
const params = {
taskId: dialogParams.value.taskId,
processInstanceId: dialogParams.value.processInstanceId,
newAssignees: Array.isArray(assignee) ? assignee : assignee.split(','),
isAdd,
before,
};
FlowOperationController.submitConsign(params)
.then(() => {
ElMessage.success(isAdd ? '加签成功!' : '减签成功!');
ElMessage.success(before != null || isAdd ? '加签成功!' : '减签成功!');
resolve(true);
})
.catch(e => {
@@ -102,6 +106,8 @@ export const useFlowAction = (props: IProp) => {
switch (operation.type) {
case SysFlowTaskOperationType.CO_SIGN:
case SysFlowTaskOperationType.SIGN_REDUCTION:
case SysFlowTaskOperationType.BFORE_CONSIGN:
case SysFlowTaskOperationType.AFTER_CONSIGN:
title = SysFlowTaskOperationType.getValue(operation.type);
break;
default:

View File

@@ -178,10 +178,22 @@ const preHandlerOperationThen = (
) => {
// 加签、减签操作
if (
operation.type === SysFlowTaskOperationType.CO_SIGN ||
operation.type === SysFlowTaskOperationType.SIGN_REDUCTION
[
SysFlowTaskOperationType.CO_SIGN,
SysFlowTaskOperationType.SIGN_REDUCTION,
SysFlowTaskOperationType.BFORE_CONSIGN,
SysFlowTaskOperationType.AFTER_CONSIGN,
].includes(operation.type)
) {
submitConsign((res || {}).assignee, operation.type === SysFlowTaskOperationType.CO_SIGN)
// 串行会签前后加签参数
let before;
if (
operation.type === SysFlowTaskOperationType.BFORE_CONSIGN ||
operation.type === SysFlowTaskOperationType.AFTER_CONSIGN
) {
before = operation.type === SysFlowTaskOperationType.BFORE_CONSIGN;
}
submitConsign((res || {}).assignee, operation.type === SysFlowTaskOperationType.CO_SIGN, before)
.then(() => {
handlerClose();
})

View File

@@ -230,7 +230,7 @@ import camundaModdleDescriptor from './plugins/descriptor/camundaDescriptor.json
import activitiModdleDescriptor from './plugins/descriptor/activitiDescriptor.json';
import flowableModdleDescriptor from './plugins/descriptor/flowableDescriptor.json';
// 标签解析 Extension
import flowableModdleExtension from './plugins/extension-moddle/flowable/index';
import flowableModdleExtension from './plugins/extension-moddle/flowable/index.js';
const emit = defineEmits<{
[key: string]: [ANY_OBJECT | string | null, (ANY_OBJECT | null)?, (ANY_OBJECT | null)?];
@@ -553,7 +553,9 @@ const onSave = () => {
emit('save', xml);
});
})
.catch(() => {});
.catch(() => {
console.log('取消保存');
});
} else {
bpmnModeler.saveXML({ format: true }).then(({ xml }: { xml: string }) => {
emit('save', xml);

View File

@@ -1169,6 +1169,23 @@
}
]
},
{
"name": "TaskInfo",
"superClass": [ "Element" ],
"meta": {
"allowedIn": [
"bpmn:ServiceTask",
"bpmn:ReceiveTask"
]
},
"properties": [
{
"name": "data",
"type": "String",
"isAttr": true
}
]
},
{
"name": "UserCandidateGroups",
"superClass": ["Element"],

View File

@@ -0,0 +1,74 @@
"use strict";
var some = require("min-dash").some;
var ALLOWED_TYPES = {
FailedJobRetryTimeCycle: ["bpmn:StartEvent", "bpmn:BoundaryEvent", "bpmn:IntermediateCatchEvent", "bpmn:Activity"],
Connector: ["bpmn:EndEvent", "bpmn:IntermediateThrowEvent"],
Field: ["bpmn:EndEvent", "bpmn:IntermediateThrowEvent"]
};
function is(element, type) {
return element && typeof element.$instanceOf === "function" && element.$instanceOf(type);
}
function exists(element) {
return element && element.length;
}
function includesType(collection, type) {
return (
exists(collection) &&
some(collection, function(element) {
return is(element, type);
})
);
}
function anyType(element, types) {
return some(types, function(type) {
return is(element, type);
});
}
function isAllowed(propName, propDescriptor, newElement) {
var name = propDescriptor.name,
types = ALLOWED_TYPES[name.replace(/flowable:/, "")];
return name === propName && anyType(newElement, types);
}
function FlowableModdleExtension(eventBus) {
eventBus.on(
"property.clone",
function(context) {
var newElement = context.newElement,
propDescriptor = context.propertyDescriptor;
this.canCloneProperty(newElement, propDescriptor);
},
this
);
}
FlowableModdleExtension.$inject = ["eventBus"];
FlowableModdleExtension.prototype.canCloneProperty = function(newElement, propDescriptor) {
if (isAllowed("flowable:FailedJobRetryTimeCycle", propDescriptor, newElement)) {
return (
includesType(newElement.eventDefinitions, "bpmn:TimerEventDefinition") ||
includesType(newElement.eventDefinitions, "bpmn:SignalEventDefinition") ||
is(newElement.loopCharacteristics, "bpmn:MultiInstanceLoopCharacteristics")
);
}
if (isAllowed("flowable:Connector", propDescriptor, newElement)) {
return includesType(newElement.eventDefinitions, "bpmn:MessageEventDefinition");
}
if (isAllowed("flowable:Field", propDescriptor, newElement)) {
return includesType(newElement.eventDefinitions, "bpmn:MessageEventDefinition");
}
};
module.exports = FlowableModdleExtension;

View File

@@ -1,78 +0,0 @@
import { ANY_OBJECT } from '@/types/generic';
import { some } from 'min-dash';
const ALLOWED_TYPES: ANY_OBJECT = {
FailedJobRetryTimeCycle: [
'bpmn:StartEvent',
'bpmn:BoundaryEvent',
'bpmn:IntermediateCatchEvent',
'bpmn:Activity',
],
Connector: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'],
Field: ['bpmn:EndEvent', 'bpmn:IntermediateThrowEvent'],
};
function is(element: ANY_OBJECT, type: string) {
return element && typeof element.$instanceOf === 'function' && element.$instanceOf(type);
}
function exists(element: ANY_OBJECT[]) {
return element && element.length;
}
function includesType(collection: ANY_OBJECT[], type: string) {
return (
exists(collection) &&
some(collection, function (element: ANY_OBJECT) {
return is(element, type);
})
);
}
function anyType(element: ANY_OBJECT, types: string[]) {
return some(types, function (type: string) {
return is(element, type);
});
}
function isAllowed(propName: string, propDescriptor: ANY_OBJECT, newElement: ANY_OBJECT) {
var name = propDescriptor.name,
types = ALLOWED_TYPES[name.replace(/flowable:/, '')];
return name === propName && anyType(newElement, types);
}
class FlowableModdleExtension {
// 定义属性
$inject = ['eventBus'];
// 定义构造函数:为了将来实例化对象的时候,可以直接对属性的值进行初始化
constructor(eventBus: ANY_OBJECT) {
eventBus.on('property.clone', (context: ANY_OBJECT) => {
var newElement = context.newElement,
propDescriptor = context.propertyDescriptor;
this.canCloneProperty(newElement, propDescriptor);
});
}
canCloneProperty(newElement: ANY_OBJECT, propDescriptor: ANY_OBJECT) {
if (isAllowed('flowable:FailedJobRetryTimeCycle', propDescriptor, newElement)) {
return (
includesType(newElement.eventDefinitions, 'bpmn:TimerEventDefinition') ||
includesType(newElement.eventDefinitions, 'bpmn:SignalEventDefinition') ||
is(newElement.loopCharacteristics, 'bpmn:MultiInstanceLoopCharacteristics')
);
}
if (isAllowed('flowable:Connector', propDescriptor, newElement)) {
return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
}
if (isAllowed('flowable:Field', propDescriptor, newElement)) {
return includesType(newElement.eventDefinitions, 'bpmn:MessageEventDefinition');
}
}
}
export default FlowableModdleExtension;

View File

@@ -0,0 +1,9 @@
/*
* @author igdianov
* address https://github.com/igdianov/activiti-bpmn-moddle
* */
module.exports = {
__init__: ["FlowableModdleExtension"],
FlowableModdleExtension: ["type", require("./flowableExtension")]
};

View File

@@ -1,9 +0,0 @@
/*
* @author igdianov
* address https://github.com/igdianov/activiti-bpmn-moddle
* */
export default {
__init__: ['FlowableModdleExtension'],
FlowableModdleExtension: ['type', (await import('./flowableExtension')).default],
};

View File

@@ -28,9 +28,21 @@
:type="elementType"
:key="elementBusinessObject.id"
v-if="
elementType && (elementType.indexOf('Task') !== -1 || elementType === 'SubProcess')
formVisible &&
elementType &&
(elementType.indexOf('Task') !== -1 || elementType === 'SubProcess')
"
/>
<service-task
:id="elementId"
:type="elementType"
:tabType="activeName"
v-if="elementType === 'ServiceTask' && activeName === 'baseInfo'"
/>
</div>
<div v-if="formVisible" key="goback">
<GoBack :id="elementId" :isCountersign="isCountersign" />
</div>
<div v-if="conditionFormVisible" key="condition">
@@ -133,6 +145,7 @@
* @Date 2021年3月31日18:57:51
*/
import { ANY_OBJECT } from '@/types/generic';
import RightAddBtn from '@/components/Btns/RightAddBtn.vue';
import ElementBaseInfo from './base/ElementBaseInfo.vue';
import ElementTask from './task/ElementTask.vue';
import ElementMultiInstance from './multi-instance/ElementMultiInstance.vue';
@@ -142,12 +155,11 @@ import ElementListeners from './listeners/ElementListeners.vue';
import ElementProperties from './properties/ElementProperties.vue';
import SetApproveStatus from './properties/SetApproveStatus.vue';
import ElementForm from './form/flowFormConfig.vue';
import ServiceTask from './form/ServiceTask';
import FormVariable from './form-variable/index.vue';
import CopyForSelect from './copy-for/index.vue';
import UserTaskListeners from './listeners/UserTaskListeners.vue';
import AutoAgree from './autoAgree/index.vue';
import RightAddBtn from '@/components/Btns/RightAddBtn.vue';
const props = withDefaults(
defineProps<{
bpmnModeler: ANY_OBJECT;

View File

@@ -0,0 +1,71 @@
<template>
<div>
<el-form
v-if="tabType === 'baseInfo'"
ref="form"
label-position="top"
:size="layoutStore.defaultFormItemSize"
:model="formData"
@submit.prevent
>
<el-form-item label="Java委托类">
<el-input
v-model="formData.class"
clearable
placeholder="输入包含包名的完整委托类名"
@change="updateServiceTaskClass"
/>
</el-form-item>
</el-form>
</div>
</template>
<script setup lang="ts">
import { defineProps, ref, inject, watch, nextTick } from 'vue';
import { ANY_OBJECT } from '@/types/generic';
import { useLayoutStore } from '@/store';
const layoutStore = useLayoutStore();
const props = defineProps<{
id: string;
type: string;
tabType: string;
}>();
const flowEntry = inject('flowEntry', () => {
return {} as ANY_OBJECT;
});
const prefix = inject('prefix');
const win: ANY_OBJECT = window;
type FormDataType = {
class: string;
};
const formData = ref<FormDataType>({
class: '',
});
const taskInfoElement = ref();
let bpmnELement = win.bpmnInstances.bpmnElement;
const resetTaskInfo = () => {
formData.value = {
class: bpmnELement.businessObject.class,
};
};
const updateServiceTaskClass = () => {
win.bpmnInstances.modeling.updateProperties(bpmnELement, {
class: formData.value.class,
});
};
watch(
() => props.id,
val => {
val && val.length && nextTick(() => resetTaskInfo());
},
{
immediate: true,
},
);
</script>
<style></style>

View File

@@ -354,7 +354,7 @@ const onEditOperation = (operation: ANY_OBJECT) => {
},
{
rowData: operation,
validStatusList: getFlowEntryValidStatus,
validStatusList: getFlowEntryValidStatus.value,
path: 'thirdEditOperation',
},
{

View File

@@ -50,7 +50,7 @@
>
<el-option label="不更新" :value="undefined" />
<el-option
v-for="item in dialogParams.validStatusList.value"
v-for="item in dialogParams.validStatusList"
:key="item.id"
:label="item.name"
:value="item.id"
@@ -178,12 +178,12 @@ import { useLayoutStore } from '@/store';
const layoutStore = useLayoutStore();
interface IProps extends ThirdProps {
rowData?: ANY_OBJECT;
validStatusList?: Ref<ANY_OBJECT[]>;
validStatusList?: ANY_OBJECT[];
// 当使用Dialog.show弹出组件时须定义该prop属性以便对dialog进行回调
dialog?: DialogProp<ANY_OBJECT | ANY_OBJECT[] | undefined>;
}
const props = withDefaults(defineProps<IProps>(), {
validStatusList: () => ref([]),
validStatusList: () => [],
});
const { thirdParams, onCloseThirdDialog } = useThirdParty(props);
@@ -215,7 +215,7 @@ const userName = ref<ANY_OBJECT[]>([]);
const dialogParams = computed(() => {
return {
rowData: props.rowData || thirdParams.value.rowData,
validStatusList: props.validStatusList.value || thirdParams.value.validStatusList || [],
validStatusList: props.validStatusList || thirdParams.value.validStatusList || [],
};
});
const multiSignGroupList = computed(() => {

View File

@@ -11,6 +11,12 @@
/>
</template>
</el-form-item>
<el-form-item v-if="isCountersign" label="会签类型" style="margin-bottom: 4px">
<el-radio-group v-model="loopCharacteristics" @change="changeLoopCharacteristicsType">
<el-radio label="ParallelMultiInstance">并行会签</el-radio>
<el-radio label="SequentialMultiInstance">串行会签</el-radio>
</el-radio-group>
</el-form-item>
<div v-if="isCountersign">
<el-form-item label="内置变量" style="margin-bottom: 5px">
<template #label>
@@ -57,6 +63,7 @@
<el-input
v-model="loopInstanceForm.completionCondition"
clearable
:disabled="loopCharacteristics === 'SequentialMultiInstance'"
@change="updateLoopCondition"
/>
</el-form-item>
@@ -163,6 +170,7 @@ const defaultLoopInstanceForm = {
const loopInstanceForm = ref<ANY_OBJECT>({
collection: 'assigneeList',
elementVariable: 'assignee',
completionCondition: '',
});
const variableList = [
{
@@ -299,7 +307,10 @@ const getElementLoop = (businessObject: ANY_OBJECT) => {
}
};
const changeLoopCharacteristicsType = (type: string) => {
// loopInstanceForm.value = { ...defaultLoopInstanceForm }; // 切换类型取消原表单配置
if (type === 'SequentialMultiInstance') {
// eslint-disable-next-line no-template-curly-in-string
loopInstanceForm.value.completionCondition = '${nrOfInstances == nrOfCompletedInstances}';
}
// 取消多实例配置
if (type === 'Null') {
win.bpmnInstances.modeling.updateProperties(bpmnElement, {
@@ -332,6 +343,7 @@ const changeLoopCharacteristicsType = (type: string) => {
});
updateLoopBase();
updateLoopCondition(loopInstanceForm.value.completionCondition);
};
// 循环基数

View File

@@ -573,6 +573,7 @@ const loadUserInfo = (params: ANY_OBJECT) => {
}
});
};
let bpmnElement: ANY_OBJECT = {};
const resetTaskForm = () => {
userTaskForm.value = {
assignee: '',
@@ -585,16 +586,15 @@ const resetTaskForm = () => {
};
candidateGroupIds.value = [];
sendMessageType.value = [];
let data =
win.bpmnInstances.bpmnElement && win.bpmnInstances.bpmnElement.businessObject
? win.bpmnInstances.bpmnElement.businessObject.formKey
: '';
let formObj = data ? JSON.parse(data) : {};
bpmnElement = win.bpmnInstances.bpmnElement;
let formKey = bpmnElement.businessObject.formKey;
let formObj = formKey ? JSON.parse(formKey) : undefined;
if (formObj) {
formData.value = {
formId: formObj.formId,
routerName: formObj.routerName,
editable: !formObj.readOnly,
formAuth: formObj.formAuth || {},
groupType: formObj.groupType || 'ASSIGNEE',
};
} else {
@@ -712,15 +712,16 @@ const updateFormKey = () => {
if (formData.value == null) return;
let formKeyString = JSON.stringify({
formId:
flowEntry().bindFormType === SysFlowEntryBindFormType.ONLINE_FORM
flowEntry().value.bindFormType === SysFlowEntryBindFormType.ONLINE_FORM
? formData.value.formId
: undefined,
routerName:
flowEntry().bindFormType === SysFlowEntryBindFormType.ONLINE_FORM
flowEntry().value.bindFormType === SysFlowEntryBindFormType.ONLINE_FORM
? undefined
: formData.value.routerName,
readOnly: !formData.value.editable,
groupType: formData.value.groupType || 'ASSIGNEE',
formAuth: formData.value.formAuth,
});
win.bpmnInstances.modeling.updateProperties(win.bpmnInstances.bpmnElement, {
formKey: formKeyString,