refactor: 重构 bpmnProcessDesigner => bpmn-process-designer

This commit is contained in:
puhui999
2025-09-15 09:43:52 +08:00
parent 85de19a422
commit 26f00f3d37
67 changed files with 35 additions and 11 deletions

View File

@@ -0,0 +1,58 @@
<script lang="ts" setup>
import type { Component } from 'vue';
import { defineOptions, defineProps, ref, watch } from 'vue';
import { CustomConfigMap } from './data';
defineOptions({ name: 'ElementCustomConfig' });
const props = defineProps({
id: {
type: String,
default: '',
},
type: {
type: String,
default: '',
},
businessObject: {
type: Object as () => BusinessObject,
default: () => ({}),
},
});
interface BusinessObject {
eventDefinitions?: Array<{ $type: string }>;
[key: string]: any;
}
// const bpmnInstances = () => (window as any)?.bpmnInstances;
const customConfigComponent = ref<Component | null>(null);
watch(
() => props.businessObject,
() => {
if (props.type && props.businessObject) {
let val = props.type;
if (props.businessObject.eventDefinitions) {
val +=
props.businessObject.eventDefinitions[0]?.$type.split(':')[1] || '';
}
// @ts-ignore
customConfigComponent.value = (
CustomConfigMap as Record<string, { component: Component }>
)[val]?.component;
}
},
{ immediate: true },
);
</script>
<template>
<div class="panel-tab__content">
<component :is="customConfigComponent" v-bind="$props" />
</div>
</template>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,307 @@
<script lang="ts" setup>
import {
defineOptions,
defineProps,
inject,
nextTick,
ref,
toRaw,
watch,
} from 'vue';
import {
Divider,
FormItem,
InputNumber,
RadioButton,
RadioGroup,
Select,
SelectOption,
Switch,
} from 'ant-design-vue';
import { convertTimeUnit } from '#/components/simple-process-design/components/nodes-config/utils';
import {
TIME_UNIT_TYPES,
TIMEOUT_HANDLER_TYPES,
TimeUnitType,
} from '#/components/simple-process-design/consts';
defineOptions({ name: 'ElementCustomConfig4BoundaryEventTimer' });
const props = defineProps({
id: {
type: String,
default: '',
},
type: {
type: String,
default: '',
},
});
const prefix = inject('prefix');
const bpmnElement = ref<any>();
const bpmnInstances = () => (window as Record<string, any>)?.bpmnInstances;
const timeoutHandlerEnable = ref(false);
const boundaryEventType = ref<any>();
const timeoutHandlerType = ref<{
value: number | undefined;
}>({
value: undefined,
});
const timeModdle = ref<any>();
const timeDuration = ref(6);
const timeUnit = ref(TimeUnitType.HOUR);
const maxRemindCount = ref(1);
const elExtensionElements = ref<any>();
const otherExtensions = ref<any[]>();
const configExtensions = ref<any[]>([]);
const eventDefinition = ref<any>();
const resetElement = () => {
bpmnElement.value = bpmnInstances().bpmnElement;
eventDefinition.value = bpmnElement.value.businessObject.eventDefinitions[0];
// 获取元素扩展属性 或者 创建扩展属性
elExtensionElements.value =
bpmnElement.value.businessObject?.extensionElements ??
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] });
// 是否开启自定义用户任务超时处理
boundaryEventType.value = elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:BoundaryEventType`,
)?.[0];
if (boundaryEventType.value && boundaryEventType.value.value === 1) {
timeoutHandlerEnable.value = true;
configExtensions.value.push(boundaryEventType.value);
}
// 执行动作
timeoutHandlerType.value = elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:TimeoutHandlerType`,
)?.[0];
if (timeoutHandlerType.value) {
configExtensions.value.push(timeoutHandlerType.value);
if (eventDefinition.value.timeCycle) {
const timeStr = eventDefinition.value.timeCycle.body;
const maxRemindCountStr = timeStr.split('/')[0];
const timeDurationStr = timeStr.split('/')[1];
maxRemindCount.value = Number.parseInt(maxRemindCountStr.slice(1));
timeDuration.value = Number.parseInt(timeDurationStr.slice(2, -1));
timeUnit.value = convertTimeUnit(timeDurationStr.slice(-1));
timeModdle.value = eventDefinition.value.timeCycle;
}
if (eventDefinition.value.timeDuration) {
const timeDurationStr = eventDefinition.value.timeDuration.body;
timeDuration.value = Number.parseInt(timeDurationStr.slice(2, -1));
timeUnit.value = convertTimeUnit(timeDurationStr.slice(-1));
timeModdle.value = eventDefinition.value.timeDuration;
}
}
// 保留剩余扩展元素,便于后面更新该元素对应属性
otherExtensions.value =
elExtensionElements.value.values?.filter(
(ex: any) =>
ex.$type !== `${prefix}:BoundaryEventType` &&
ex.$type !== `${prefix}:TimeoutHandlerType`,
) ?? [];
};
const timeoutHandlerChange = (checked: any) => {
timeoutHandlerEnable.value = checked;
if (checked) {
// 启用自定义用户任务超时处理
// 边界事件类型 --- 超时
boundaryEventType.value = bpmnInstances().moddle.create(
`${prefix}:BoundaryEventType`,
{
value: 1,
},
);
configExtensions.value.push(boundaryEventType.value);
// 超时处理类型
timeoutHandlerType.value = bpmnInstances().moddle.create(
`${prefix}:TimeoutHandlerType`,
{
value: 1,
},
);
configExtensions.value.push(timeoutHandlerType.value);
// 超时时间表达式
timeDuration.value = 6;
timeUnit.value = 2;
maxRemindCount.value = 1;
timeModdle.value = bpmnInstances().moddle.create(`bpmn:Expression`, {
body: 'PT6H',
});
eventDefinition.value.timeDuration = timeModdle.value;
} else {
// 关闭自定义用户任务超时处理
configExtensions.value = [];
delete eventDefinition.value.timeDuration;
delete eventDefinition.value.timeCycle;
}
updateElementExtensions();
};
const onTimeoutHandlerTypeChanged = () => {
maxRemindCount.value = 1;
updateElementExtensions();
updateTimeModdle();
};
const onTimeUnitChange = () => {
// 分钟,默认是 60 分钟
if (timeUnit.value === TimeUnitType.MINUTE) {
timeDuration.value = 60;
}
// 小时,默认是 6 个小时
if (timeUnit.value === TimeUnitType.HOUR) {
timeDuration.value = 6;
}
// 天, 默认 1天
if (timeUnit.value === TimeUnitType.DAY) {
timeDuration.value = 1;
}
updateTimeModdle();
updateElementExtensions();
};
const updateTimeModdle = () => {
if (maxRemindCount.value > 1) {
timeModdle.value.body = `R${maxRemindCount.value}/${isoTimeDuration()}`;
if (!eventDefinition.value.timeCycle) {
delete eventDefinition.value.timeDuration;
eventDefinition.value.timeCycle = timeModdle.value;
}
} else {
timeModdle.value.body = isoTimeDuration();
if (!eventDefinition.value.timeDuration) {
delete eventDefinition.value.timeCycle;
eventDefinition.value.timeDuration = timeModdle.value;
}
}
};
const isoTimeDuration = () => {
let strTimeDuration = 'PT';
if (timeUnit.value === TimeUnitType.MINUTE) {
strTimeDuration += `${timeDuration.value}M`;
}
if (timeUnit.value === TimeUnitType.HOUR) {
strTimeDuration += `${timeDuration.value}H`;
}
if (timeUnit.value === TimeUnitType.DAY) {
strTimeDuration += `${timeDuration.value}D`;
}
return strTimeDuration;
};
const updateElementExtensions = () => {
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
values: [...(otherExtensions.value || []), ...configExtensions.value],
});
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
extensionElements: extensions,
});
};
watch(
() => props.id,
(val) => {
val &&
val.length > 0 &&
nextTick(() => {
resetElement();
});
},
{ immediate: true },
);
</script>
<template>
<div>
<Divider orientation="left">审批人超时未处理时</Divider>
<FormItem label="启用开关" name="timeoutHandlerEnable">
<Switch
v-model:checked="timeoutHandlerEnable"
checked-children="开启"
un-checked-children="关闭"
@change="timeoutHandlerChange"
/>
</FormItem>
<FormItem
label="执行动作"
name="timeoutHandlerType"
v-if="timeoutHandlerEnable"
>
<RadioGroup
v-model:value="timeoutHandlerType.value"
@change="onTimeoutHandlerTypeChanged"
>
<RadioButton
v-for="item in TIMEOUT_HANDLER_TYPES"
:key="item.value"
:value="item.value"
>
{{ item.label }}
</RadioButton>
</RadioGroup>
</FormItem>
<FormItem label="超时时间设置" v-if="timeoutHandlerEnable">
<span class="mr-2">当超过</span>
<FormItem name="timeDuration">
<InputNumber
class="mr-2"
:style="{ width: '100px' }"
v-model:value="timeDuration"
:min="1"
:controls="true"
@change="
() => {
updateTimeModdle();
updateElementExtensions();
}
"
/>
</FormItem>
<Select
v-model:value="timeUnit"
class="mr-2"
:style="{ width: '100px' }"
@change="onTimeUnitChange"
>
<SelectOption
v-for="item in TIME_UNIT_TYPES"
:key="item.value"
:value="item.value"
>
{{ item.label }}
</SelectOption>
</Select>
未处理
</FormItem>
<FormItem
label="最大提醒次数"
name="maxRemindCount"
v-if="timeoutHandlerEnable && timeoutHandlerType.value === 1"
>
<InputNumber
v-model:value="maxRemindCount"
:min="1"
:max="10"
@change="
() => {
updateTimeModdle();
updateElementExtensions();
}
"
/>
</FormItem>
</div>
</template>
<style lang="scss" scoped></style>

View File

@@ -0,0 +1,783 @@
<!-- UserTask 自定义配置
1. 审批人与提交人为同一人时
2. 审批人拒绝时
3. 审批人为空时
4. 操作按钮
5. 字段权限
6. 审批类型
7. 是否需要签名
-->
<script lang="ts" setup>
import type { SystemUserApi } from '#/api/system/user';
import type { ButtonSetting } from '#/components/simple-process-design/consts';
import { inject, nextTick, onMounted, ref, toRaw, watch } from 'vue';
import { BpmModelFormType } from '@vben/constants';
import {
Button,
Divider,
Form,
Radio,
RadioGroup,
Select,
SelectOption,
Switch,
} from 'ant-design-vue';
import { getSimpleUserList } from '#/api/system/user';
import {
APPROVE_TYPE,
ApproveType,
ASSIGN_EMPTY_HANDLER_TYPES,
ASSIGN_START_USER_HANDLER_TYPES,
AssignEmptyHandlerType,
DEFAULT_BUTTON_SETTING,
FieldPermissionType,
OPERATION_BUTTON_NAME,
REJECT_HANDLER_TYPES,
RejectHandlerType,
} from '#/components/simple-process-design/consts';
import { useFormFieldsPermission } from '#/components/simple-process-design/helpers';
defineOptions({ name: 'ElementCustomConfig4UserTask' });
const props = defineProps({
id: {
type: String,
required: false,
default: '',
},
type: {
type: String,
required: false,
default: '',
},
});
const prefix = inject('prefix');
// 审批人与提交人为同一人时
const assignStartUserHandlerTypeEl = ref<any>();
const assignStartUserHandlerType = ref<any>();
// 审批人拒绝时
const rejectHandlerTypeEl = ref<any>();
const rejectHandlerType = ref<any>();
const returnNodeIdEl = ref<any>();
const returnNodeId = ref<any>();
const returnTaskList = ref<any[]>([]);
// 审批人为空时
const assignEmptyHandlerTypeEl = ref<any>();
const assignEmptyHandlerType = ref<any>();
const assignEmptyUserIdsEl = ref<any>();
const assignEmptyUserIds = ref<any>();
// 操作按钮
const buttonsSettingEl = ref<any>();
const { btnDisplayNameEdit, changeBtnDisplayName, btnDisplayNameBlurEvent } =
useButtonsSetting();
// 字段权限
const fieldsPermissionEl = ref<any[]>([]);
const { formType, fieldsPermissionConfig, getNodeConfigFormFields } =
useFormFieldsPermission(FieldPermissionType.READ);
// 审批类型
const approveType = ref({ value: ApproveType.USER });
// 是否需要签名
const signEnable = ref({ value: false });
// 审批意见
const reasonRequire = ref({ value: false });
const elExtensionElements = ref<any>();
const otherExtensions = ref<any>();
const bpmnElement = ref<any>();
const bpmnInstances = () => (window as any)?.bpmnInstances;
const resetCustomConfigList = () => {
bpmnElement.value = bpmnInstances().bpmnElement;
// 获取可回退的列表
returnTaskList.value = findAllPredecessorsExcludingStart(
bpmnElement.value.id,
bpmnInstances().modeler,
);
// 获取元素扩展属性 或者 创建扩展属性
elExtensionElements.value =
bpmnElement.value.businessObject?.extensionElements ??
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] });
// 审批类型
approveType.value =
elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:ApproveType`,
)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:ApproveType`, {
value: ApproveType.USER,
});
// 审批人与提交人为同一人时
assignStartUserHandlerTypeEl.value =
elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:AssignStartUserHandlerType`,
)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:AssignStartUserHandlerType`, {
value: 1,
});
assignStartUserHandlerType.value = assignStartUserHandlerTypeEl.value.value;
// 审批人拒绝时
rejectHandlerTypeEl.value =
elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:RejectHandlerType`,
)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:RejectHandlerType`, { value: 1 });
rejectHandlerType.value = rejectHandlerTypeEl.value.value;
returnNodeIdEl.value =
elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:RejectReturnTaskId`,
)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:RejectReturnTaskId`, {
value: '',
});
returnNodeId.value = returnNodeIdEl.value.value;
// 审批人为空时
assignEmptyHandlerTypeEl.value =
elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:AssignEmptyHandlerType`,
)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:AssignEmptyHandlerType`, {
value: 1,
});
assignEmptyHandlerType.value = assignEmptyHandlerTypeEl.value.value;
assignEmptyUserIdsEl.value =
elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:AssignEmptyUserIds`,
)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:AssignEmptyUserIds`, {
value: '',
});
assignEmptyUserIds.value = assignEmptyUserIdsEl.value.value
?.split(',')
.map((item: string) => {
// 如果数字超出了最大安全整数范围,则将其作为字符串处理
const num = Number(item);
return num > Number.MAX_SAFE_INTEGER || num < -Number.MAX_SAFE_INTEGER
? item
: num;
});
// 操作按钮
buttonsSettingEl.value = elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:ButtonsSetting`,
);
if (buttonsSettingEl.value.length === 0) {
DEFAULT_BUTTON_SETTING.forEach((item) => {
buttonsSettingEl.value.push(
bpmnInstances().moddle.create(`${prefix}:ButtonsSetting`, {
'flowable:id': item.id,
'flowable:displayName': item.displayName,
'flowable:enable': item.enable,
}),
);
});
}
// 字段权限
if (formType.value === BpmModelFormType.NORMAL) {
const fieldsPermissionList = elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:FieldsPermission`,
);
fieldsPermissionEl.value = [];
getNodeConfigFormFields();
fieldsPermissionConfig.value.forEach((element: any) => {
element.permission =
fieldsPermissionList?.find((obj: any) => obj.field === element.field)
?.permission ?? '1';
fieldsPermissionEl.value.push(
bpmnInstances().moddle.create(`${prefix}:FieldsPermission`, element),
);
});
}
// 是否需要签名
signEnable.value =
elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:SignEnable`,
)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:SignEnable`, { value: false });
// 审批意见
reasonRequire.value =
elExtensionElements.value.values?.filter(
(ex: any) => ex.$type === `${prefix}:ReasonRequire`,
)?.[0] ||
bpmnInstances().moddle.create(`${prefix}:ReasonRequire`, { value: false });
// 保留剩余扩展元素,便于后面更新该元素对应属性
otherExtensions.value =
elExtensionElements.value.values?.filter(
(ex: any) =>
ex.$type !== `${prefix}:AssignStartUserHandlerType` &&
ex.$type !== `${prefix}:RejectHandlerType` &&
ex.$type !== `${prefix}:RejectReturnTaskId` &&
ex.$type !== `${prefix}:AssignEmptyHandlerType` &&
ex.$type !== `${prefix}:AssignEmptyUserIds` &&
ex.$type !== `${prefix}:ButtonsSetting` &&
ex.$type !== `${prefix}:FieldsPermission` &&
ex.$type !== `${prefix}:ApproveType` &&
ex.$type !== `${prefix}:SignEnable` &&
ex.$type !== `${prefix}:ReasonRequire`,
) ?? [];
// 更新元素扩展属性,避免后续报错
updateElementExtensions();
};
const updateAssignStartUserHandlerType = () => {
assignStartUserHandlerTypeEl.value.value = assignStartUserHandlerType.value;
updateElementExtensions();
};
const updateRejectHandlerType = () => {
rejectHandlerTypeEl.value.value = rejectHandlerType.value;
returnNodeId.value = returnTaskList.value[0]?.id;
returnNodeIdEl.value.value = returnNodeId.value;
updateElementExtensions();
};
const updateReturnNodeId = () => {
returnNodeIdEl.value.value = returnNodeId.value;
updateElementExtensions();
};
const updateAssignEmptyHandlerType = () => {
assignEmptyHandlerTypeEl.value.value = assignEmptyHandlerType.value;
updateElementExtensions();
};
const updateAssignEmptyUserIds = () => {
assignEmptyUserIdsEl.value.value = assignEmptyUserIds.value.toString();
updateElementExtensions();
};
const updateElementExtensions = () => {
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
values: [
...otherExtensions.value,
assignStartUserHandlerTypeEl.value,
rejectHandlerTypeEl.value,
returnNodeIdEl.value,
assignEmptyHandlerTypeEl.value,
assignEmptyUserIdsEl.value,
approveType.value,
...buttonsSettingEl.value,
...fieldsPermissionEl.value,
signEnable.value,
reasonRequire.value,
],
});
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
extensionElements: extensions,
});
};
watch(
() => props.id,
(val) => {
val &&
val.length > 0 &&
nextTick(() => {
resetCustomConfigList();
});
},
{ immediate: true },
);
function findAllPredecessorsExcludingStart(elementId: string, modeler: any) {
const elementRegistry = modeler.get('elementRegistry');
const allConnections = elementRegistry.filter(
(element: any) => element.type === 'bpmn:SequenceFlow',
);
const predecessors = new Set(); // 使用 Set 来避免重复节点
const visited = new Set(); // 用于记录已访问的节点
// 检查是否是开始事件节点
function isStartEvent(element: any) {
return element.type === 'bpmn:StartEvent';
}
function findPredecessorsRecursively(element: any) {
// 如果该节点已经访问过,直接返回,避免循环
if (visited.has(element)) {
return;
}
// 标记当前节点为已访问
visited.add(element);
// 获取与当前节点相连的所有连接
const incomingConnections = allConnections.filter(
(connection: any) => connection.target === element,
);
incomingConnections.forEach((connection: any) => {
const source = connection.source; // 获取前置节点
// 只添加不是开始事件的前置节点
if (!isStartEvent(source)) {
predecessors.add(source.businessObject);
// 递归查找前置节点
findPredecessorsRecursively(source);
}
});
}
const targetElement = elementRegistry.get(elementId);
if (targetElement) {
findPredecessorsRecursively(targetElement);
}
return [...predecessors]; // 返回前置节点数组
}
function useButtonsSetting() {
const buttonsSetting = ref<ButtonSetting[]>();
// 操作按钮显示名称可编辑
const btnDisplayNameEdit = ref<boolean[]>([]);
const changeBtnDisplayName = (index: number) => {
btnDisplayNameEdit.value[index] = true;
};
const btnDisplayNameBlurEvent = (index: number) => {
btnDisplayNameEdit.value[index] = false;
const buttonItem = buttonsSetting.value?.[index];
if (buttonItem) {
buttonItem.displayName =
buttonItem.displayName || OPERATION_BUTTON_NAME.get(buttonItem.id)!;
}
};
return {
buttonsSetting,
btnDisplayNameEdit,
changeBtnDisplayName,
btnDisplayNameBlurEvent,
};
}
/** 批量更新权限 */
// TODO @lesan这个页面有一些 idea 红色报错,咱要不要 fix 下!
const updatePermission = (type: string) => {
fieldsPermissionEl.value.forEach((field: any) => {
if (type === 'READ') {
field.permission = FieldPermissionType.READ;
} else if (type === 'WRITE') {
field.permission = FieldPermissionType.WRITE;
} else {
field.permission = FieldPermissionType.NONE;
}
});
};
const userOptions = ref<SystemUserApi.User[]>([]); // 用户列表
onMounted(async () => {
// 获得用户列表
userOptions.value = await getSimpleUserList();
});
</script>
<template>
<div>
<Divider orientation="left">审批类型</Divider>
<Form.Item name="approveType" label="审批类型">
<RadioGroup v-model:value="approveType.value">
<Radio
v-for="(item, index) in APPROVE_TYPE"
:key="index"
:value="item.value"
>
{{ item.label }}
</Radio>
</RadioGroup>
</Form.Item>
<Divider orientation="left">审批人拒绝时</Divider>
<Form.Item name="rejectHandlerType" label="处理方式">
<RadioGroup
v-model:value="rejectHandlerType"
:disabled="returnTaskList.length === 0"
@change="updateRejectHandlerType"
>
<div class="flex-col">
<div v-for="(item, index) in REJECT_HANDLER_TYPES" :key="index">
<Radio :key="item.value" :value="item.value">
{{ item.label }}
</Radio>
</div>
</div>
</RadioGroup>
</Form.Item>
<Form.Item
v-if="rejectHandlerType === RejectHandlerType.RETURN_USER_TASK"
name="returnNodeId"
label="驳回节点"
>
<Select
v-model:value="returnNodeId"
allow-clear
style="width: 100%"
@change="updateReturnNodeId"
placeholder="请选择驳回节点"
>
<SelectOption
v-for="item in returnTaskList"
:key="item.id"
:value="item.id"
>
{{ item.name }}
</SelectOption>
</Select>
</Form.Item>
<Divider orientation="left">审批人为空时</Divider>
<Form.Item prop="assignEmptyHandlerType">
<RadioGroup
v-model:value="assignEmptyHandlerType"
@change="updateAssignEmptyHandlerType"
>
<div class="flex-col">
<div v-for="(item, index) in ASSIGN_EMPTY_HANDLER_TYPES" :key="index">
<Radio :key="item.value" :value="item.value">
{{ item.label }}
</Radio>
</div>
</div>
</RadioGroup>
</Form.Item>
<Form.Item
v-if="assignEmptyHandlerType === AssignEmptyHandlerType.ASSIGN_USER"
label="指定用户"
prop="assignEmptyHandlerUserIds"
>
<Select
v-model:value="assignEmptyUserIds"
allow-clear
mode="multiple"
style="width: 100%"
@change="updateAssignEmptyUserIds"
>
<SelectOption
v-for="item in userOptions"
:key="item.id"
:value="item.id"
>
{{ item.nickname }}
</SelectOption>
</Select>
</Form.Item>
<Divider orientation="left">审批人与提交人为同一人时</Divider>
<RadioGroup
v-model:value="assignStartUserHandlerType"
@change="updateAssignStartUserHandlerType"
>
<div class="flex-col">
<div
v-for="(item, index) in ASSIGN_START_USER_HANDLER_TYPES"
:key="index"
>
<Radio :key="item.value" :value="item.value">
{{ item.label }}
</Radio>
</div>
</div>
</RadioGroup>
<Divider orientation="left">操作按钮</Divider>
<div class="button-setting-pane">
<div class="button-setting-title">
<div class="button-title-label">操作按钮</div>
<div class="button-title-label pl-4">显示名称</div>
<div class="button-title-label">启用</div>
</div>
<div
class="button-setting-item"
v-for="(item, index) in buttonsSettingEl"
:key="index"
>
<div class="button-setting-item-label">
{{ OPERATION_BUTTON_NAME.get(item.id) }}
</div>
<div class="button-setting-item-label">
<input
type="text"
class="editable-title-input"
@blur="btnDisplayNameBlurEvent(index)"
v-mounted-focus
v-model="item.displayName"
:placeholder="item.displayName"
v-if="btnDisplayNameEdit[index]"
/>
<Button v-else type="text" @click="changeBtnDisplayName(index)">
{{ item.displayName }}
</Button>
</div>
<div class="button-setting-item-label">
<Switch v-model:checked="item.enable" />
</div>
</div>
</div>
<Divider orientation="left">字段权限</Divider>
<div class="field-setting-pane" v-if="formType === BpmModelFormType.NORMAL">
<div class="field-permit-title">
<div class="setting-title-label first-title">字段名称</div>
<div class="other-titles">
<span
class="setting-title-label cursor-pointer"
@click="updatePermission('READ')"
>只读
</span>
<span
class="setting-title-label cursor-pointer"
@click="updatePermission('WRITE')"
>
可编辑
</span>
<span
class="setting-title-label cursor-pointer"
@click="updatePermission('NONE')"
>隐藏
</span>
</div>
</div>
<div
class="field-setting-item"
v-for="(item, index) in fieldsPermissionEl"
:key="index"
>
<div class="field-setting-item-label">{{ item.title }}</div>
<RadioGroup
class="field-setting-item-group"
v-model:value="item.permission"
>
<div class="item-radio-wrap">
<Radio
:value="FieldPermissionType.READ"
size="large"
@change="updateElementExtensions"
>
<span></span>
</Radio>
</div>
<div class="item-radio-wrap">
<Radio
:value="FieldPermissionType.WRITE"
size="large"
@change="updateElementExtensions"
>
<span></span>
</Radio>
</div>
<div class="item-radio-wrap">
<Radio
:value="FieldPermissionType.NONE"
size="large"
@change="updateElementExtensions"
>
<span></span>
</Radio>
</div>
</RadioGroup>
</div>
</div>
<Divider orientation="left">是否需要签名</Divider>
<Form.Item prop="signEnable">
<Switch
v-model:checked="signEnable.value"
checked-children=""
un-checked-children=""
@change="updateElementExtensions"
/>
</Form.Item>
<Divider orientation="left">审批意见</Divider>
<Form.Item prop="reasonRequire">
<Switch
v-model:checked="reasonRequire.value"
checked-children="必填"
un-checked-children="非必填"
@change="updateElementExtensions"
/>
</Form.Item>
</div>
</template>
<style lang="scss" scoped>
.button-setting-pane {
display: flex;
flex-direction: column;
margin-top: 8px;
font-size: 14px;
.button-setting-desc {
padding-right: 8px;
margin-bottom: 16px;
font-size: 16px;
font-weight: 700;
}
.button-setting-title {
display: flex;
align-items: center;
justify-content: space-between;
height: 45px;
padding-left: 12px;
background-color: #f8fafc0a;
border: 1px solid #1f38581a;
& > :first-child {
width: 100px !important;
text-align: left !important;
}
& > :last-child {
text-align: center !important;
}
.button-title-label {
width: 150px;
font-size: 13px;
font-weight: 700;
color: #000;
text-align: left;
}
}
.button-setting-item {
display: flex;
align-items: center;
justify-content: space-between;
height: 38px;
padding-left: 12px;
border: 1px solid #1f38581a;
border-top: 0;
& > :first-child {
width: 100px !important;
}
& > :last-child {
text-align: center !important;
}
.button-setting-item-label {
width: 150px;
overflow: hidden;
text-overflow: ellipsis;
text-align: left;
white-space: nowrap;
}
.editable-title-input {
max-width: 130px;
height: 24px;
margin-left: 4px;
line-height: 24px;
border: 1px solid #d9d9d9;
border-radius: 4px;
transition: all 0.3s;
&:focus {
outline: 0;
border-color: #40a9ff;
box-shadow: 0 0 0 2px rgb(24 144 255 / 20%);
}
}
}
}
.field-setting-pane {
display: flex;
flex-direction: column;
font-size: 14px;
.field-setting-desc {
padding-right: 8px;
margin-bottom: 16px;
font-size: 16px;
font-weight: 700;
}
.field-permit-title {
display: flex;
align-items: center;
justify-content: space-between;
height: 45px;
padding-left: 12px;
line-height: 45px;
background-color: #f8fafc0a;
border: 1px solid #1f38581a;
.first-title {
text-align: left !important;
}
.other-titles {
display: flex;
justify-content: space-between;
}
.setting-title-label {
display: inline-block;
width: 100px;
padding: 5px 0;
font-size: 13px;
font-weight: 700;
color: #000;
text-align: center;
}
}
.field-setting-item {
display: flex;
align-items: center;
justify-content: space-between;
height: 38px;
padding-left: 12px;
border: 1px solid #1f38581a;
border-top: 0;
.field-setting-item-label {
display: inline-block;
width: 100px;
min-height: 16px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
cursor: text;
}
.field-setting-item-group {
display: flex;
justify-content: space-between;
.item-radio-wrap {
display: inline-block;
width: 100px;
text-align: center;
}
}
}
}
</style>

View File

@@ -0,0 +1,13 @@
import BoundaryEventTimer from './components/BoundaryEventTimer.vue';
import UserTaskCustomConfig from './components/UserTaskCustomConfig.vue';
export const CustomConfigMap = {
UserTask: {
name: '用户任务',
component: UserTaskCustomConfig,
},
BoundaryEventTimerEventDefinition: {
name: '定时边界事件(非中断)',
component: BoundaryEventTimer,
},
};