Commit f87b37ea by 芋道源码 Committed by Gitee

!619 同步最新的 bpm 改动

Merge pull request !619 from 芋道源码/feature/bpm
parents c79f027d 9f0f5b0e
...@@ -96,8 +96,8 @@ ...@@ -96,8 +96,8 @@
"@vitejs/plugin-vue": "^5.0.4", "@vitejs/plugin-vue": "^5.0.4",
"@vitejs/plugin-vue-jsx": "^3.1.0", "@vitejs/plugin-vue-jsx": "^3.1.0",
"autoprefixer": "^10.4.17", "autoprefixer": "^10.4.17",
"bpmn-js": "8.10.0", "bpmn-js": "^17.9.2",
"bpmn-js-properties-panel": "0.46.0", "bpmn-js-properties-panel": "5.23.0",
"consola": "^3.2.3", "consola": "^3.2.3",
"eslint": "^8.57.0", "eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",
......
...@@ -15,7 +15,6 @@ import { ...@@ -15,7 +15,6 @@ import {
AssignStartUserHandlerType, AssignStartUserHandlerType,
AssignEmptyHandlerType, AssignEmptyHandlerType,
FieldPermissionType, FieldPermissionType,
ProcessVariableEnum
} from './consts' } from './consts'
import { parseFormFields } from '@/components/FormCreate/src/utils/index' import { parseFormFields } from '@/components/FormCreate/src/utils/index'
export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlowNode> { export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlowNode> {
...@@ -37,13 +36,6 @@ const parseFormCreateFields = (formFields?: string[]) => { ...@@ -37,13 +36,6 @@ const parseFormCreateFields = (formFields?: string[]) => {
parseFormFields(JSON.parse(fieldStr), result) parseFormFields(JSON.parse(fieldStr), result)
}) })
} }
// 固定添加发起人 ID 字段
result.unshift({
field: ProcessVariableEnum.START_USER_ID,
title: '发起人',
type: 'UserSelect',
required: true
})
return result return result
} }
......
...@@ -26,19 +26,13 @@ ...@@ -26,19 +26,13 @@
</div> </div>
</template> </template>
<div> <div>
<div class="mb-3 font-size-16px" v-if="currentNode.defaultFlow">未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div> <div class="mb-3 font-size-16px" v-if="currentNode.defaultFlow"
>未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div
>
<div v-else> <div v-else>
<el-form <el-form ref="formRef" :model="currentNode" :rules="formRules" label-position="top">
ref="formRef"
:model="currentNode"
:rules="formRules"
label-position="top"
>
<el-form-item label="配置方式" prop="conditionType"> <el-form-item label="配置方式" prop="conditionType">
<el-radio-group <el-radio-group v-model="currentNode.conditionType" @change="changeConditionType">
v-model="currentNode.conditionType"
@change="changeConditionType"
>
<el-radio <el-radio
v-for="(dict, index) in conditionConfigTypes" v-for="(dict, index) in conditionConfigTypes"
:key="index" :key="index"
...@@ -108,10 +102,11 @@ ...@@ -108,10 +102,11 @@
<div class="mr-2"> <div class="mr-2">
<el-select style="width: 160px" v-model="rule.leftSide"> <el-select style="width: 160px" v-model="rule.leftSide">
<el-option <el-option
v-for="(item, index) in fieldsInfo" v-for="(item, index) in fieldOptions"
:key="index" :key="index"
:label="item.title" :label="item.title"
:value="item.field" :value="item.field"
:disabled="!item.required"
/> />
</el-select> </el-select>
</div> </div>
...@@ -165,10 +160,12 @@ import { ...@@ -165,10 +160,12 @@ import {
COMPARISON_OPERATORS, COMPARISON_OPERATORS,
ConditionGroup, ConditionGroup,
Condition, Condition,
ConditionRule ConditionRule,
ProcessVariableEnum
} from '../consts' } from '../consts'
import { getDefaultConditionNodeName } from '../utils' import { getDefaultConditionNodeName } from '../utils'
import { useFormFields } from '../node' import { useFormFields } from '../node'
import { BpmModelFormType } from '@/utils/constants'
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
defineOptions({ defineOptions({
name: 'ConditionNodeConfig' name: 'ConditionNodeConfig'
...@@ -177,8 +174,8 @@ const formType = inject<Ref<number>>('formType') // 表单类型 ...@@ -177,8 +174,8 @@ const formType = inject<Ref<number>>('formType') // 表单类型
const conditionConfigTypes = computed(() => { const conditionConfigTypes = computed(() => {
return CONDITION_CONFIG_TYPES.filter((item) => { return CONDITION_CONFIG_TYPES.filter((item) => {
// 业务表单暂时去掉条件规则选项 // 业务表单暂时去掉条件规则选项
if (formType?.value !== 10) { if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
return item.value === ConditionType.RULE return false
} else { } else {
return true return true
} }
...@@ -368,16 +365,29 @@ const addConditionRule = (condition: Condition, idx: number) => { ...@@ -368,16 +365,29 @@ const addConditionRule = (condition: Condition, idx: number) => {
const deleteConditionRule = (condition: Condition, idx: number) => { const deleteConditionRule = (condition: Condition, idx: number) => {
condition.rules.splice(idx, 1) condition.rules.splice(idx, 1)
} }
const fieldsInfo = useFormFields() const fieldsInfo = useFormFields()
/** 条件规则可选择的表单字段 */
const fieldOptions = computed(() => {
const fieldsCopy = fieldsInfo.slice()
// 固定添加发起人 ID 字段
fieldsCopy.unshift({
field: ProcessVariableEnum.START_USER_ID,
title: '发起人',
required: true
})
return fieldsCopy
})
/** 获取字段名称 */
const getFieldTitle = (field: string) => { const getFieldTitle = (field: string) => {
const item = fieldsInfo.find((item) => item.field === field) const item = fieldsInfo.find((item) => item.field === field)
return item?.title return item?.title
} }
/** 获取操作符名称 */
const getOpName = (opCode: string): string => { const getOpName = (opCode: string): string => {
const opName = COMPARISON_OPERATORS.find((item) => item.value === opCode) const opName = COMPARISON_OPERATORS.find((item: any) => item.value === opCode)
return opName?.label return opName?.label
} }
</script> </script>
......
...@@ -469,7 +469,8 @@ import { ...@@ -469,7 +469,8 @@ import {
TimeoutHandlerType, TimeoutHandlerType,
ASSIGN_EMPTY_HANDLER_TYPES, ASSIGN_EMPTY_HANDLER_TYPES,
AssignEmptyHandlerType, AssignEmptyHandlerType,
FieldPermissionType FieldPermissionType,
ProcessVariableEnum
} from '../consts' } from '../consts'
import { import {
...@@ -519,6 +520,13 @@ const { formType, fieldsPermissionConfig, formFieldOptions, getNodeConfigFormFie ...@@ -519,6 +520,13 @@ const { formType, fieldsPermissionConfig, formFieldOptions, getNodeConfigFormFie
useFormFieldsPermission(FieldPermissionType.READ) useFormFieldsPermission(FieldPermissionType.READ)
// 表单内用户字段选项, 必须是必填和用户选择器 // 表单内用户字段选项, 必须是必填和用户选择器
const userFieldOnFormOptions = computed(() => { const userFieldOnFormOptions = computed(() => {
// 固定添加发起人 ID 字段
formFieldOptions.unshift({
field: ProcessVariableEnum.START_USER_ID,
title: '发起人',
type: 'UserSelect',
required: true
})
return formFieldOptions.filter((item) => item.type === 'UserSelect') return formFieldOptions.filter((item) => item.type === 'UserSelect')
}) })
// 表单内部门字段选项, 必须是必填和部门选择器 // 表单内部门字段选项, 必须是必填和部门选择器
......
...@@ -406,6 +406,31 @@ ...@@ -406,6 +406,31 @@
"name": "variableMappingDelegateExpression", "name": "variableMappingDelegateExpression",
"isAttr": true, "isAttr": true,
"type": "String" "type": "String"
},
{
"name": "calledElementType",
"isAttr": true,
"type": "String"
},
{
"name": "processInstanceName",
"isAttr": true,
"type": "String"
},
{
"name": "inheritBusinessKey",
"isAttr": true,
"type": "Boolean"
},
{
"name": "businessKey",
"isAttr": true,
"type": "String"
},
{
"name": "inheritVariables",
"isAttr": true,
"type": "Boolean"
} }
] ]
}, },
...@@ -1281,6 +1306,138 @@ ...@@ -1281,6 +1306,138 @@
"isBody": true "isBody": true
} }
] ]
},
{
"name": "ButtonsSetting",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:UserTask"]
},
"properties": [
{
"name": "flowable:id",
"type": "Integer",
"isAttr": true
},
{
"name": "flowable:enable",
"type": "Boolean",
"isAttr": true
},
{
"name": "flowable:displayName",
"type": "String",
"isAttr": true
}
]
},
{
"name": "FieldsPermission",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:UserTask"]
},
"properties": [
{
"name": "flowable:field",
"type": "String",
"isAttr": true
},
{
"name": "flowable:title",
"type": "String",
"isAttr": true
},
{
"name": "flowable:permission",
"type": "String",
"isAttr": true
}
]
},
{
"name": "BoundaryEventType",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:BoundaryEvent"]
},
"properties": [
{
"name": "value",
"type": "Integer",
"isBody": true
}
]
},
{
"name": "TimeoutHandlerType",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:BoundaryEvent"]
},
"properties": [
{
"name": "value",
"type": "Integer",
"isBody": true
}
]
},
{
"name": "ApproveType",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:UserTask"]
},
"properties": [
{
"name": "value",
"type": "Integer",
"isBody": true
}
]
},
{
"name": "ApproveMethod",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:UserTask"]
},
"properties": [
{
"name": "value",
"type": "Integer",
"isBody": true
}
]
},
{
"name": "CandidateStrategy",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:UserTask"]
},
"properties": [
{
"name": "value",
"type": "Integer",
"isBody": true
}
]
},
{
"name": "CandidateParam",
"superClass": ["Element"],
"meta": {
"allowedIn": ["bpmn:UserTask"]
},
"properties": [
{
"name": "value",
"type": "String",
"isBody": true
}
]
} }
], ],
"emumerations": [] "emumerations": []
......
...@@ -165,6 +165,18 @@ F.prototype.getPaletteEntries = function () { ...@@ -165,6 +165,18 @@ F.prototype.getPaletteEntries = function () {
'bpmn-icon-user-task', 'bpmn-icon-user-task',
translate('Create User Task') translate('Create User Task')
), ),
'create.call-activity': createAction(
'bpmn:CallActivity',
'activity',
'bpmn-icon-call-activity',
translate('Create Call Activity')
),
'create.service-task': createAction(
'bpmn:ServiceTask',
'activity',
'bpmn-icon-service',
translate('Create Service Task')
),
'create.data-object': createAction( 'create.data-object': createAction(
'bpmn:DataObjectReference', 'bpmn:DataObjectReference',
'data-object', 'data-object',
......
...@@ -171,6 +171,12 @@ PaletteProvider.prototype.getPaletteEntries = function () { ...@@ -171,6 +171,12 @@ PaletteProvider.prototype.getPaletteEntries = function () {
'bpmn-icon-user-task', 'bpmn-icon-user-task',
translate('Create User Task') translate('Create User Task')
), ),
'create.service-task': createAction(
'bpmn:ServiceTask',
'activity',
'bpmn-icon-service',
translate('Create Service Task')
),
'create.data-object': createAction( 'create.data-object': createAction(
'bpmn:DataObjectReference', 'bpmn:DataObjectReference',
'data-object', 'data-object',
......
...@@ -56,6 +56,8 @@ export default { ...@@ -56,6 +56,8 @@ export default {
'Create EndEvent': '创建结束事件', 'Create EndEvent': '创建结束事件',
'Create Task': '创建任务', 'Create Task': '创建任务',
'Create User Task': '创建用户任务', 'Create User Task': '创建用户任务',
'Create Call Activity': '创建调用活动',
'Create Service Task': '创建服务任务',
'Create Gateway': '创建网关', 'Create Gateway': '创建网关',
'Create DataObjectReference': '创建数据对象', 'Create DataObjectReference': '创建数据对象',
'Create DataStoreReference': '创建数据存储', 'Create DataStoreReference': '创建数据存储',
......
<template> <template>
<div class="process-panel__container" :style="{ width: `${width}px`,maxHeight: '700px' }"> <div class="process-panel__container" :style="{ width: `${width}px`, maxHeight: '700px' }">
<el-collapse v-model="activeTab"> <el-collapse v-model="activeTab">
<el-collapse-item name="base"> <el-collapse-item name="base">
<!-- class="panel-tab__title" --> <!-- class="panel-tab__title" -->
...@@ -26,8 +26,10 @@ ...@@ -26,8 +26,10 @@
<template #title><Icon icon="ep:list" />表单</template> <template #title><Icon icon="ep:list" />表单</template>
<element-form :id="elementId" :type="elementType" /> <element-form :id="elementId" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="task" v-if="elementType.indexOf('Task') !== -1" key="task"> <el-collapse-item name="task" v-if="isTaskCollapseItemShow(elementType)" key="task">
<template #title><Icon icon="ep:checked" />任务(审批人)</template> <template #title
><Icon icon="ep:checked" />{{ getTaskCollapseItemName(elementType) }}</template
>
<element-task :id="elementId" :type="elementType" /> <element-task :id="elementId" :type="elementType" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item <el-collapse-item
...@@ -35,8 +37,12 @@ ...@@ -35,8 +37,12 @@
v-if="elementType.indexOf('Task') !== -1" v-if="elementType.indexOf('Task') !== -1"
key="multiInstance" key="multiInstance"
> >
<template #title><Icon icon="ep:help-filled" />多实例(会签配置)</template> <template #title><Icon icon="ep:help-filled" />多人审批方式</template>
<element-multi-instance :business-object="elementBusinessObject" :type="elementType" /> <element-multi-instance
:id="elementId"
:business-object="elementBusinessObject"
:type="elementType"
/>
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="listeners" key="listeners"> <el-collapse-item name="listeners" key="listeners">
<template #title><Icon icon="ep:bell-filled" />执行监听器</template> <template #title><Icon icon="ep:bell-filled" />执行监听器</template>
...@@ -54,9 +60,13 @@ ...@@ -54,9 +60,13 @@
<template #title><Icon icon="ep:promotion" />其他</template> <template #title><Icon icon="ep:promotion" />其他</template>
<element-other-config :id="elementId" /> <element-other-config :id="elementId" />
</el-collapse-item> </el-collapse-item>
<el-collapse-item name="customConfig" v-if="elementType.indexOf('Task') !== -1" key="customConfig"> <el-collapse-item name="customConfig" key="customConfig">
<template #title><Icon icon="ep:circle-plus-filled" />自定义配置</template> <template #title><Icon icon="ep:tools" />自定义配置</template>
<element-custom-config :id="elementId" :type="elementType" /> <element-custom-config
:id="elementId"
:type="elementType"
:business-object="elementBusinessObject"
/>
</el-collapse-item> </el-collapse-item>
</el-collapse> </el-collapse>
</div> </div>
...@@ -72,6 +82,7 @@ import ElementListeners from './listeners/ElementListeners.vue' ...@@ -72,6 +82,7 @@ import ElementListeners from './listeners/ElementListeners.vue'
import ElementProperties from './properties/ElementProperties.vue' import ElementProperties from './properties/ElementProperties.vue'
// import ElementForm from './form/ElementForm.vue' // import ElementForm from './form/ElementForm.vue'
import UserTaskListeners from './listeners/UserTaskListeners.vue' import UserTaskListeners from './listeners/UserTaskListeners.vue'
import { getTaskCollapseItemName, isTaskCollapseItemShow } from './task/data'
defineOptions({ name: 'MyPropertiesPanel' }) defineOptions({ name: 'MyPropertiesPanel' })
......
<template>
<div>
<el-divider content-position="left">审批人超时未处理时</el-divider>
<el-form-item label="启用开关" prop="timeoutHandlerEnable">
<el-switch
v-model="timeoutHandlerEnable"
active-text="开启"
inactive-text="关闭"
@change="timeoutHandlerChange"
/>
</el-form-item>
<el-form-item label="执行动作" prop="timeoutHandlerType" v-if="timeoutHandlerEnable">
<el-radio-group v-model="timeoutHandlerType.value" @change="onTimeoutHandlerTypeChanged">
<el-radio-button
v-for="item in TIMEOUT_HANDLER_TYPES"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-radio-group>
</el-form-item>
<el-form-item label="超时时间设置" v-if="timeoutHandlerEnable">
<span class="mr-2">当超过</span>
<el-form-item prop="timeDuration">
<el-input-number
class="mr-2"
:style="{ width: '100px' }"
v-model="timeDuration"
:min="1"
controls-position="right"
@change="() => updateTimeModdle()"
/>
</el-form-item>
<el-select
v-model="timeUnit"
class="mr-2"
:style="{ width: '100px' }"
@change="onTimeUnitChange"
>
<el-option
v-for="item in TIME_UNIT_TYPES"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
未处理
</el-form-item>
<el-form-item
label="最大提醒次数"
prop="maxRemindCount"
v-if="timeoutHandlerEnable && timeoutHandlerType.value === 1"
>
<el-input-number
v-model="maxRemindCount"
:min="1"
:max="10"
@change="() => updateTimeModdle()"
/>
</el-form-item>
</div>
</template>
<script lang="ts" setup>
import {
TimeUnitType,
TIME_UNIT_TYPES,
TIMEOUT_HANDLER_TYPES,
} from '@/components/SimpleProcessDesignerV2/src/consts'
import { convertTimeUnit } from '@/components/SimpleProcessDesignerV2/src/utils'
defineOptions({ name: 'ElementCustomConfig4BoundaryEventTimer' })
const props = defineProps({
id: String,
type: String
})
const prefix = inject('prefix')
const bpmnElement = ref()
const bpmnInstances = () => (window as any)?.bpmnInstances
const timeoutHandlerEnable = ref(false)
const boundaryEventType = ref()
const timeoutHandlerType = ref({
value: undefined
})
const timeModdle = ref()
const timeDuration = ref(6)
const timeUnit = ref(TimeUnitType.HOUR)
const maxRemindCount = ref(1)
const elExtensionElements = ref()
const otherExtensions = ref()
const configExtensions = ref([])
const eventDefinition = ref()
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) => 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) => 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]
console.log(maxRemindCountStr)
maxRemindCount.value = parseInt(maxRemindCountStr.slice(1))
timeDuration.value = parseInt(timeDurationStr.slice(2, timeDurationStr.length - 1))
timeUnit.value = convertTimeUnit(timeDurationStr.slice(timeDurationStr.length - 1))
timeModdle.value = eventDefinition.value.timeCycle
}
if (eventDefinition.value.timeDuration) {
const timeDurationStr = eventDefinition.value.timeDuration.body
timeDuration.value = parseInt(timeDurationStr.slice(2, timeDurationStr.length - 1))
timeUnit.value = convertTimeUnit(timeDurationStr.slice(timeDurationStr.length - 1))
timeModdle.value = eventDefinition.value.timeDuration
}
}
// 保留剩余扩展元素,便于后面更新该元素对应属性
otherExtensions.value =
elExtensionElements.value.values?.filter(
(ex) =>
ex.$type !== `${prefix}:BoundaryEventType` && ex.$type !== `${prefix}:TimeoutHandlerType`
) ?? []
}
const timeoutHandlerChange = (val) => {
timeoutHandlerEnable.value = val
if (val) {
// 启用自定义用户任务超时处理
// 边界事件类型 --- 超时
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()
}
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 &&
nextTick(() => {
resetElement()
})
},
{ immediate: true }
)
</script>
<style lang="scss" scoped></style>
import UserTaskCustomConfig from './components/UserTaskCustomConfig.vue'
import BoundaryEventTimer from './components/BoundaryEventTimer.vue'
export const CustomConfigMap = {
UserTask: {
name: '用户任务',
componet: UserTaskCustomConfig
},
BoundaryEventTimerEventDefinition: {
name: '定时边界事件(非中断)',
componet: BoundaryEventTimer
}
}
<template> <template>
<div class="panel-tab__content"> <div class="panel-tab__content">
<el-form label-width="90px"> <el-radio-group v-model="approveMethod" @change="onApproveMethodChange">
<div class="flex-col">
<div v-for="(item, index) in APPROVE_METHODS" :key="index">
<el-radio :value="item.value" :label="item.value">
{{ item.label }}
</el-radio>
<el-form-item prop="approveRatio">
<el-input-number
v-model="approveRatio"
:min="10"
:max="100"
:step="10"
size="small"
v-if="
item.value === ApproveMethodType.APPROVE_BY_RATIO &&
approveMethod === ApproveMethodType.APPROVE_BY_RATIO
"
@change="onApproveRatioChange"
/>
</el-form-item>
</div>
</div>
</el-radio-group>
<!-- 与Simple设计器配置合并,保留以前的代码 -->
<el-form label-width="90px" style="display: none">
<el-form-item label="快捷配置"> <el-form-item label="快捷配置">
<el-button size="small" @click="changeConfig('依次审批')">依次审批</el-button> <el-button size="small" @click="changeConfig('依次审批')">依次审批</el-button>
<el-button size="small" @click="changeConfig('会签')">会签</el-button> <el-button size="small" @click="changeConfig('会签')">会签</el-button>
...@@ -76,11 +100,14 @@ ...@@ -76,11 +100,14 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ApproveMethodType, APPROVE_METHODS } from '@/components/SimpleProcessDesignerV2/src/consts'
defineOptions({ name: 'ElementMultiInstance' }) defineOptions({ name: 'ElementMultiInstance' })
const props = defineProps({ const props = defineProps({
businessObject: Object, businessObject: Object,
type: String type: String,
id: String
}) })
const prefix = inject('prefix') const prefix = inject('prefix')
const loopCharacteristics = ref('') const loopCharacteristics = ref('')
...@@ -267,16 +294,118 @@ const changeConfig = (config) => { ...@@ -267,16 +294,118 @@ const changeConfig = (config) => {
} }
} }
/**
* -----新版本多实例-----
*/
const approveMethod = ref()
const approveRatio = ref(100)
const otherExtensions = ref()
const getElementLoopNew = () => {
const extensionElements =
bpmnElement.value.businessObject?.extensionElements ??
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
approveMethod.value = extensionElements.values.filter(
(ex) => ex.$type === `${prefix}:ApproveMethod`
)?.[0]?.value
otherExtensions.value =
extensionElements.values.filter((ex) => ex.$type !== `${prefix}:ApproveMethod`) ?? []
if (!approveMethod.value) {
approveMethod.value = ApproveMethodType.SEQUENTIAL_APPROVE
updateLoopCharacteristics()
}
}
const onApproveMethodChange = () => {
approveRatio.value = 100
updateLoopCharacteristics()
}
const onApproveRatioChange = () => {
updateLoopCharacteristics()
}
const updateLoopCharacteristics = () => {
// 根据ApproveMethod生成multiInstanceLoopCharacteristics节点
if (approveMethod.value === ApproveMethodType.RANDOM_SELECT_ONE_APPROVE) {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: null
})
} else {
if (approveMethod.value === ApproveMethodType.APPROVE_BY_RATIO) {
multiLoopInstance.value = bpmnInstances().moddle.create(
'bpmn:MultiInstanceLoopCharacteristics',
{ isSequential: false, collection: '${coll_userList}' }
)
multiLoopInstance.value.completionCondition = bpmnInstances().moddle.create(
'bpmn:FormalExpression',
{
body: '${ nrOfCompletedInstances/nrOfInstances >= ' + approveRatio.value / 100 + '}'
}
)
}
if (approveMethod.value === ApproveMethodType.ANY_APPROVE) {
multiLoopInstance.value = bpmnInstances().moddle.create(
'bpmn:MultiInstanceLoopCharacteristics',
{ isSequential: false, collection: '${coll_userList}' }
)
multiLoopInstance.value.completionCondition = bpmnInstances().moddle.create(
'bpmn:FormalExpression',
{
body: '${ nrOfCompletedInstances > 0 }'
}
)
}
if (approveMethod.value === ApproveMethodType.SEQUENTIAL_APPROVE) {
multiLoopInstance.value = bpmnInstances().moddle.create(
'bpmn:MultiInstanceLoopCharacteristics',
{ isSequential: true, collection: '${coll_userList}' }
)
multiLoopInstance.value.loopCardinality = bpmnInstances().moddle.create(
'bpmn:FormalExpression',
{
body: '1'
}
)
multiLoopInstance.value.completionCondition = bpmnInstances().moddle.create(
'bpmn:FormalExpression',
{
body: '${ nrOfCompletedInstances >= nrOfInstances }'
}
)
}
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: toRaw(multiLoopInstance.value)
})
}
// 添加ApproveMethod到ExtensionElements
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
values: [
...otherExtensions.value,
bpmnInstances().moddle.create(`${prefix}:ApproveMethod`, {
value: approveMethod.value
})
]
})
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
extensionElements: extensions
})
}
onBeforeUnmount(() => { onBeforeUnmount(() => {
multiLoopInstance.value = null multiLoopInstance.value = null
bpmnElement.value = null bpmnElement.value = null
}) })
watch( watch(
() => props.businessObject, () => props.id,
(val) => { (val) => {
bpmnElement.value = bpmnInstances().bpmnElement if (val) {
getElementLoop(val) nextTick(() => {
bpmnElement.value = bpmnInstances().bpmnElement
// getElementLoop(val)
getElementLoopNew()
})
}
}, },
{ immediate: true } { immediate: true }
) )
......
...@@ -75,7 +75,6 @@ const attributeFormRef = ref() ...@@ -75,7 +75,6 @@ const attributeFormRef = ref()
const bpmnInstances = () => (window as any)?.bpmnInstances const bpmnInstances = () => (window as any)?.bpmnInstances
const resetAttributesList = () => { const resetAttributesList = () => {
console.log(window, 'windowwindowwindowwindowwindowwindowwindow')
bpmnElement.value = bpmnInstances().bpmnElement bpmnElement.value = bpmnInstances().bpmnElement
otherExtensionList.value = [] // 其他扩展配置 otherExtensionList.value = [] // 其他扩展配置
bpmnElementProperties.value = bpmnElementProperties.value =
...@@ -85,7 +84,7 @@ const resetAttributesList = () => { ...@@ -85,7 +84,7 @@ const resetAttributesList = () => {
otherExtensionList.value.push(ex) otherExtensionList.value.push(ex)
} }
return ex.$type === `${prefix}:Properties` return ex.$type === `${prefix}:Properties`
}) ?? [] }) ?? [];
// 保存所有的 扩展属性字段 // 保存所有的 扩展属性字段
bpmnElementPropertyList.value = bpmnElementProperties.value.reduce( bpmnElementPropertyList.value = bpmnElementProperties.value.reduce(
......
...@@ -29,9 +29,7 @@ ...@@ -29,9 +29,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import UserTask from './task-components/UserTask.vue' import { installedComponent } from './data'
import ScriptTask from './task-components/ScriptTask.vue'
import ReceiveTask from './task-components/ReceiveTask.vue'
defineOptions({ name: 'ElementTaskConfig' }) defineOptions({ name: 'ElementTaskConfig' })
...@@ -45,14 +43,7 @@ const taskConfigForm = ref({ ...@@ -45,14 +43,7 @@ const taskConfigForm = ref({
exclusive: false exclusive: false
}) })
const witchTaskComponent = ref() const witchTaskComponent = ref()
const installedComponent = ref({
// 手工任务与普通任务一致,不需要其他配置
// 接收消息任务,需要在全局下插入新的消息实例,并在该节点下的 messageRef 属性绑定该实例
// 发送任务、服务任务、业务规则任务共用一个相同配置
UserTask: 'UserTask', // 用户任务配置
ScriptTask: 'ScriptTask', // 脚本任务配置
ReceiveTask: 'ReceiveTask' // 消息接收任务
})
const bpmnElement = ref() const bpmnElement = ref()
const bpmnInstances = () => (window as any).bpmnInstances const bpmnInstances = () => (window as any).bpmnInstances
...@@ -78,15 +69,8 @@ watch( ...@@ -78,15 +69,8 @@ watch(
watch( watch(
() => props.type, () => props.type,
() => { () => {
// witchTaskComponent.value = installedComponent.value[props.type] if (props.type) {
if (props.type == installedComponent.value.UserTask) { witchTaskComponent.value = installedComponent[props.type].component
witchTaskComponent.value = UserTask
}
if (props.type == installedComponent.value.ScriptTask) {
witchTaskComponent.value = ScriptTask
}
if (props.type == installedComponent.value.ReceiveTask) {
witchTaskComponent.value = ReceiveTask
} }
}, },
{ immediate: true } { immediate: true }
......
import UserTask from './task-components/UserTask.vue'
import ServiceTask from './task-components/ServiceTask.vue'
import ScriptTask from './task-components/ScriptTask.vue'
import ReceiveTask from './task-components/ReceiveTask.vue'
import CallActivity from './task-components/CallActivity.vue'
export const installedComponent = {
UserTask: {
name: '用户任务',
component: UserTask
},
ServiceTask: {
name: '服务任务',
component: ServiceTask
},
ScriptTask: {
name: '脚本任务',
component: ScriptTask
},
ReceiveTask: {
name: '接收任务',
component: ReceiveTask
},
CallActivity: {
name: '调用活动',
component: CallActivity
}
}
export const getTaskCollapseItemName = (elementType) => {
return installedComponent[elementType].name
}
export const isTaskCollapseItemShow = (elementType) => {
return installedComponent[elementType]
}
<template>
<div>
<el-form label-width="100px">
<el-form-item label="实例名称" prop="processInstanceName">
<el-input
v-model="formData.processInstanceName"
clearable
placeholder="请输入实例名称"
@change="updateCallActivityAttr('processInstanceName')"
/>
</el-form-item>
<!-- TODO 需要可选择已存在的流程 -->
<el-form-item label="被调用流程" prop="calledElement">
<el-input
v-model="formData.calledElement"
clearable
placeholder="请输入被调用流程"
@change="updateCallActivityAttr('calledElement')"
/>
</el-form-item>
<el-form-item label="继承变量" prop="inheritVariables">
<el-switch
v-model="formData.inheritVariables"
@change="updateCallActivityAttr('inheritVariables')"
/>
</el-form-item>
<el-form-item label="继承业务键" prop="inheritBusinessKey">
<el-switch
v-model="formData.inheritBusinessKey"
@change="updateCallActivityAttr('inheritBusinessKey')"
/>
</el-form-item>
<el-form-item v-if="!formData.inheritBusinessKey" label="业务键表达式" prop="businessKey">
<el-input
v-model="formData.businessKey"
clearable
placeholder="请输入业务键表达式"
@change="updateCallActivityAttr('businessKey')"
/>
</el-form-item>
<el-divider />
<div>
<div class="flex mb-10px">
<el-text>输入参数</el-text>
<XButton
class="ml-auto"
type="primary"
preIcon="ep:plus"
title="添加参数"
size="small"
@click="openVariableForm('in', null, -1)"
/>
</div>
<el-table :data="inVariableList" max-height="240" fit border>
<el-table-column label="源" prop="source" min-width="100px" show-overflow-tooltip />
<el-table-column label="目标" prop="target" min-width="100px" show-overflow-tooltip />
<el-table-column label="操作" width="110px">
<template #default="scope">
<el-button link @click="openVariableForm('in', scope.row, scope.$index)" size="small">
编辑
</el-button>
<el-divider direction="vertical" />
<el-button
link
size="small"
style="color: #ff4d4f"
@click="removeVariable('in', scope.$index)"
>
移除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-divider />
<div>
<div class="flex mb-10px">
<el-text>输出参数</el-text>
<XButton
class="ml-auto"
type="primary"
preIcon="ep:plus"
title="添加参数"
size="small"
@click="openVariableForm('out', null, -1)"
/>
</div>
<el-table :data="outVariableList" max-height="240" fit border>
<el-table-column label="源" prop="source" min-width="100px" show-overflow-tooltip />
<el-table-column label="目标" prop="target" min-width="100px" show-overflow-tooltip />
<el-table-column label="操作" width="110px">
<template #default="scope">
<el-button
link
@click="openVariableForm('out', scope.row, scope.$index)"
size="small"
>
编辑
</el-button>
<el-divider direction="vertical" />
<el-button
link
size="small"
style="color: #ff4d4f"
@click="removeVariable('out', scope.$index)"
>
移除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-form>
<!-- 添加或修改参数 -->
<el-dialog
v-model="variableDialogVisible"
title="参数配置"
width="600px"
append-to-body
destroy-on-close
>
<el-form :model="varialbeFormData" label-width="80px" ref="varialbeFormRef">
<el-form-item label="源:" prop="source">
<el-input v-model="varialbeFormData.source" clearable />
</el-form-item>
<el-form-item label="目标:" prop="target">
<el-input v-model="varialbeFormData.target" clearable />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="variableDialogVisible = false">取 消</el-button>
<el-button type="primary" @click="saveVariable">确 定</el-button>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'CallActivity' })
const props = defineProps({
id: String,
type: String
})
const prefix = inject('prefix')
const message = useMessage()
const formData = ref({
processInstanceName: '',
calledElement: '',
inheritVariables: false,
businessKey: '',
inheritBusinessKey: false,
calledElementType: 'key'
})
const inVariableList = ref()
const outVariableList = ref()
const variableType = ref() // 参数类型
const editingVariableIndex = ref(-1) // 编辑参数下标
const variableDialogVisible = ref(false)
const varialbeFormRef = ref()
const varialbeFormData = ref({
source: '',
target: ''
})
const bpmnInstances = () => (window as any)?.bpmnInstances
const bpmnElement = ref()
const otherExtensionList = ref()
const initCallActivity = () => {
bpmnElement.value = bpmnInstances().bpmnElement
console.log(bpmnElement.value.businessObject, 'callActivity')
// 初始化所有配置项
Object.keys(formData.value).forEach((key) => {
formData.value[key] = bpmnElement.value.businessObject[key] ?? formData.value[key]
})
otherExtensionList.value = [] // 其他扩展配置
inVariableList.value = []
outVariableList.value = []
// 初始化输入参数
bpmnElement.value.businessObject?.extensionElements?.values?.forEach((ex) => {
if (ex.$type === `${prefix}:In`) {
inVariableList.value.push(ex)
} else if (ex.$type === `${prefix}:Out`) {
outVariableList.value.push(ex)
} else {
otherExtensionList.value.push(ex)
}
})
// 默认添加
// bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
// calledElementType: 'key'
// })
}
const updateCallActivityAttr = (attr) => {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
[attr]: formData.value[attr]
})
}
const openVariableForm = (type, data, index) => {
editingVariableIndex.value = index
variableType.value = type
varialbeFormData.value = index === -1 ? {} : { ...data }
variableDialogVisible.value = true
}
const removeVariable = async (type, index) => {
try {
await message.delConfirm()
if (type === 'in') {
inVariableList.value.splice(index, 1)
}
if (type === 'out') {
outVariableList.value.splice(index, 1)
}
updateElementExtensions()
} catch {}
}
const saveVariable = () => {
if (editingVariableIndex.value === -1) {
if (variableType.value === 'in') {
inVariableList.value.push(
bpmnInstances().moddle.create(`${prefix}:In`, { ...varialbeFormData.value })
)
}
if (variableType.value === 'out') {
outVariableList.value.push(
bpmnInstances().moddle.create(`${prefix}:Out`, { ...varialbeFormData.value })
)
}
updateElementExtensions()
} else {
if (variableType.value === 'in') {
inVariableList.value[editingVariableIndex.value].source = varialbeFormData.value.source
inVariableList.value[editingVariableIndex.value].target = varialbeFormData.value.target
}
if (variableType.value === 'out') {
outVariableList.value[editingVariableIndex.value].source = varialbeFormData.value.source
outVariableList.value[editingVariableIndex.value].target = varialbeFormData.value.target
}
}
variableDialogVisible.value = false
}
const updateElementExtensions = () => {
const extensions = bpmnInstances().moddle.create('bpmn:ExtensionElements', {
values: [...inVariableList.value, ...outVariableList.value, ...otherExtensionList.value]
})
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
extensionElements: extensions
})
}
watch(
() => props.id,
(val) => {
val &&
val.length &&
nextTick(() => {
initCallActivity()
})
},
{ immediate: true }
)
</script>
<style lang="scss" scoped></style>
<template>
<div>
<el-form-item label="执行类型" key="executeType">
<el-select v-model="serviceTaskForm.executeType">
<el-option label="Java类" value="class" />
<el-option label="表达式" value="expression" />
<el-option label="代理表达式" value="delegateExpression" />
</el-select>
</el-form-item>
<el-form-item
v-if="serviceTaskForm.executeType === 'class'"
label="Java类"
prop="class"
key="execute-class"
>
<el-input v-model="serviceTaskForm.class" clearable @change="updateElementTask" />
</el-form-item>
<el-form-item
v-if="serviceTaskForm.executeType === 'expression'"
label="表达式"
prop="expression"
key="execute-expression"
>
<el-input v-model="serviceTaskForm.expression" clearable @change="updateElementTask" />
</el-form-item>
<el-form-item
v-if="serviceTaskForm.executeType === 'delegateExpression'"
label="代理表达式"
prop="delegateExpression"
key="execute-delegate"
>
<el-input v-model="serviceTaskForm.delegateExpression" clearable @change="updateElementTask" />
</el-form-item>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'ServiceTask' })
const props = defineProps({
id: String,
type: String
})
const defaultTaskForm = ref({
executeType: '',
class: '',
expression: '',
delegateExpression: ''
})
const serviceTaskForm = ref<any>({})
const bpmnElement = ref()
const bpmnInstances = () => (window as any)?.bpmnInstances
const resetTaskForm = () => {
for (let key in defaultTaskForm.value) {
let value = bpmnElement.value?.businessObject[key] || defaultTaskForm.value[key]
serviceTaskForm.value[key] = value
if (value) {
serviceTaskForm.value.executeType = key
}
}
}
const updateElementTask = () => {
let taskAttr = Object.create(null);
const type = serviceTaskForm.value.executeType;
for (let key in serviceTaskForm.value) {
if (key !== 'executeType' && key !== type) taskAttr[key] = null;
}
taskAttr[type] = serviceTaskForm.value[type] || "";
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), taskAttr)
}
onBeforeUnmount(() => {
bpmnElement.value = null
})
watch(
() => props.id,
() => {
bpmnElement.value = bpmnInstances().bpmnElement
nextTick(() => {
resetTaskForm()
})
},
{ immediate: true }
)
</script>
...@@ -31,6 +31,7 @@ import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/d ...@@ -31,6 +31,7 @@ import CustomContentPadProvider from '@/components/bpmnProcessDesigner/package/d
// 自定义左侧菜单(修改 默认任务 为 用户任务) // 自定义左侧菜单(修改 默认任务 为 用户任务)
import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette' import CustomPaletteProvider from '@/components/bpmnProcessDesigner/package/designer/plugins/palette'
import * as ModelApi from '@/api/bpm/model' import * as ModelApi from '@/api/bpm/model'
import { getForm, FormVO } from '@/api/bpm/form'
defineOptions({ name: 'BpmModelEditor' }) defineOptions({ name: 'BpmModelEditor' })
...@@ -38,6 +39,12 @@ const router = useRouter() // 路由 ...@@ -38,6 +39,12 @@ const router = useRouter() // 路由
const { query } = useRoute() // 路由的查询 const { query } = useRoute() // 路由的查询
const message = useMessage() // 国际化 const message = useMessage() // 国际化
// 表单信息
const formFields = ref<string[]>([])
const formType = ref(20)
provide('formFields', formFields)
provide('formType', formType)
const xmlString = ref(undefined) // BPMN XML const xmlString = ref(undefined) // BPMN XML
const modeler = ref(null) // BPMN Modeler const modeler = ref(null) // BPMN Modeler
const controlForm = ref({ const controlForm = ref({
...@@ -99,6 +106,13 @@ onMounted(async () => { ...@@ -99,6 +106,13 @@ onMounted(async () => {
</bpmndi:BPMNDiagram> </bpmndi:BPMNDiagram>
</definitions>` </definitions>`
} }
formType.value = data.formType
if (data.formType === 10) {
const bpmnForm = (await getForm(data.formId)) as unknown as FormVO
formFields.value = bpmnForm?.fields
}
model.value = { model.value = {
...data, ...data,
bpmnXml: undefined // 清空 bpmnXml 属性 bpmnXml: undefined // 清空 bpmnXml 属性
......
...@@ -8,8 +8,8 @@ ...@@ -8,8 +8,8 @@
<!-- 中间主要内容 tab 栏 --> <!-- 中间主要内容 tab 栏 -->
<el-tabs v-model="activeTab"> <el-tabs v-model="activeTab">
<!-- 表单信息 --> <!-- 表单信息 -->
<el-tab-pane label="表单填写" name="form"> <el-tab-pane label="表单填写" name="form" >
<div class="form-scroll-area"> <div class="form-scroll-area" v-loading="processInstanceStartLoading">
<el-scrollbar> <el-scrollbar>
<el-row> <el-row>
<el-col :span="17"> <el-col :span="17">
...@@ -90,7 +90,7 @@ const props = defineProps<{ ...@@ -90,7 +90,7 @@ const props = defineProps<{
selectProcessDefinition: any selectProcessDefinition: any
}>() }>()
const emit = defineEmits(['cancel']) const emit = defineEmits(['cancel'])
const processInstanceStartLoading = ref(false) // 流程实例发起中
const { push, currentRoute } = useRouter() // 路由 const { push, currentRoute } = useRouter() // 路由
const message = useMessage() // 消息弹窗 const message = useMessage() // 消息弹窗
const { delView } = useTagsViewStore() // 视图操作 const { delView } = useTagsViewStore() // 视图操作
...@@ -179,6 +179,8 @@ const submitForm = async () => { ...@@ -179,6 +179,8 @@ const submitForm = async () => {
if (!fApi.value || !props.selectProcessDefinition) { if (!fApi.value || !props.selectProcessDefinition) {
return return
} }
// 流程表单校验
await fApi.value.validate()
// 如果有指定审批人,需要校验 // 如果有指定审批人,需要校验
if (startUserSelectTasks.value?.length > 0) { if (startUserSelectTasks.value?.length > 0) {
for (const userTask of startUserSelectTasks.value) { for (const userTask of startUserSelectTasks.value) {
...@@ -191,7 +193,7 @@ const submitForm = async () => { ...@@ -191,7 +193,7 @@ const submitForm = async () => {
} }
// 提交请求 // 提交请求
fApi.value.btn.loading(true) processInstanceStartLoading.value = true
try { try {
await ProcessInstanceApi.createProcessInstance({ await ProcessInstanceApi.createProcessInstance({
processDefinitionId: props.selectProcessDefinition.id, processDefinitionId: props.selectProcessDefinition.id,
...@@ -206,7 +208,7 @@ const submitForm = async () => { ...@@ -206,7 +208,7 @@ const submitForm = async () => {
name: 'BpmProcessInstanceMy' name: 'BpmProcessInstanceMy'
}) })
} finally { } finally {
fApi.value.btn.loading(false) processInstanceStartLoading.value = false
} }
} }
......
...@@ -49,7 +49,7 @@ ...@@ -49,7 +49,7 @@
class="form-box flex flex-col mb-30px flex-1" class="form-box flex flex-col mb-30px flex-1"
> >
<!-- 情况一:流程表单 --> <!-- 情况一:流程表单 -->
<el-col v-if="processDefinition?.formType === 10"> <el-col v-if="processDefinition?.formType === BpmModelFormType.NORMAL">
<form-create <form-create
v-model="detailForm.value" v-model="detailForm.value"
v-model:api="fApi" v-model:api="fApi"
...@@ -58,7 +58,7 @@ ...@@ -58,7 +58,7 @@
/> />
</el-col> </el-col>
<!-- 情况二:业务表单 --> <!-- 情况二:业务表单 -->
<div v-if="processDefinition?.formType === 20"> <div v-if="processDefinition?.formType === BpmModelFormType.CUSTOM">
<BusinessFormComponent :id="processInstance.businessKey" /> <BusinessFormComponent :id="processInstance.businessKey" />
</div> </div>
</div> </div>
...@@ -116,6 +116,9 @@ ...@@ -116,6 +116,9 @@
:process-instance="processInstance" :process-instance="processInstance"
:process-definition="processDefinition" :process-definition="processDefinition"
:userOptions="userOptions" :userOptions="userOptions"
:normal-form="detailForm"
:normal-form-api="fApi"
:writable-fields="writableFields"
@success="refresh" @success="refresh"
/> />
</div> </div>
...@@ -126,7 +129,7 @@ ...@@ -126,7 +129,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE } from '@/utils/dict'
import { BpmModelType } from '@/utils/constants' import { BpmModelType, BpmModelFormType } from '@/utils/constants'
import { setConfAndFields2 } from '@/utils/formCreate' import { setConfAndFields2 } from '@/utils/formCreate'
import { registerComponent } from '@/utils/routerHelper' import { registerComponent } from '@/utils/routerHelper'
import type { ApiAttrs } from '@form-create/element-ui/types/config' import type { ApiAttrs } from '@form-create/element-ui/types/config'
...@@ -171,6 +174,8 @@ const detailForm = ref({ ...@@ -171,6 +174,8 @@ const detailForm = ref({
value: {} value: {}
}) // 流程实例的表单详情 }) // 流程实例的表单详情
const writableFields: Array<string> = [] // 表单可以编辑的字段
/** 获得详情 */ /** 获得详情 */
const getDetail = () => { const getDetail = () => {
getApprovalDetail() getApprovalDetail()
...@@ -202,11 +207,12 @@ const getApprovalDetail = async () => { ...@@ -202,11 +207,12 @@ const getApprovalDetail = async () => {
processDefinition.value = data.processDefinition processDefinition.value = data.processDefinition
// 设置表单信息 // 设置表单信息
if (processDefinition.value.formType === 10) { if (processDefinition.value.formType === BpmModelFormType.NORMAL) {
// 获取表单字段权限 // 获取表单字段权限
const formFieldsPermission = data.formFieldsPermission const formFieldsPermission = data.formFieldsPermission
// 清空可编辑字段为空
if (detailForm.value.rule.length > 0) { writableFields.splice(0)
if (detailForm.value.rule?.length > 0) {
// 避免刷新 form-create 显示不了 // 避免刷新 form-create 显示不了
detailForm.value.value = processInstance.value.formVariables detailForm.value.value = processInstance.value.formVariables
} else { } else {
...@@ -271,6 +277,8 @@ const setFieldPermission = (field: string, permission: string) => { ...@@ -271,6 +277,8 @@ const setFieldPermission = (field: string, permission: string) => {
if (permission === FieldPermissionType.WRITE) { if (permission === FieldPermissionType.WRITE) {
//@ts-ignore //@ts-ignore
fApi.value?.disabled(false, field) fApi.value?.disabled(false, field)
// 加入可以编辑的字段
writableFields.push(field)
} }
if (permission === FieldPermissionType.NONE) { if (permission === FieldPermissionType.NONE) {
//@ts-ignore //@ts-ignore
...@@ -314,6 +322,7 @@ $process-header-height: 194px; ...@@ -314,6 +322,7 @@ $process-header-height: 194px;
overflow: auto; overflow: auto;
.form-scroll-area { .form-scroll-area {
display: flex;
height: calc( height: calc(
100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px - 100vh - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height) - 35px -
$process-header-height - 40px $process-header-height - 40px
...@@ -323,7 +332,6 @@ $process-header-height: 194px; ...@@ -323,7 +332,6 @@ $process-header-height: 194px;
$process-header-height - 40px $process-header-height - 40px
); );
overflow: auto; overflow: auto;
display: flex;
flex-direction: column; flex-direction: column;
:deep(.box-card) { :deep(.box-card) {
......
...@@ -25,13 +25,14 @@ ...@@ -25,13 +25,14 @@
</el-form-item> </el-form-item>
<!-- TODO @ tuituji:style 可以使用 unocss --> <!-- TODO @ tuituji:style 可以使用 unocss -->
<el-form-item label="" prop="category" :style="{ position: 'absolute', right: '130px' }"> <el-form-item label="" prop="category" :style="{ position: 'absolute', right: '300px' }">
<!-- TODO @tuituji:应该选择好分类,就触发搜索啦。 --> <!-- TODO @tuituji:应该选择好分类,就触发搜索啦。 RE:done & to check-->
<el-select <el-select
v-model="queryParams.category" v-model="queryParams.category"
placeholder="请选择流程分类" placeholder="请选择流程分类"
clearable clearable
class="!w-155px" class="!w-155px"
@change="handleQuery"
> >
<el-option <el-option
v-for="category in categoryList" v-for="category in categoryList"
...@@ -42,21 +43,38 @@ ...@@ -42,21 +43,38 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item label="" prop="status" :style="{ position: 'absolute', right: '130px' }">
<el-select
v-model="queryParams.status"
placeholder="请选择流程状态"
clearable
class="!w-155px"
@change="handleQuery"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<!-- 高级筛选 --> <!-- 高级筛选 -->
<!-- TODO @ tuituji:style 可以使用 unocss --> <!-- TODO @ tuituji:style 可以使用 unocss -->
<el-form-item :style="{ position: 'absolute', right: '0px' }"> <el-form-item :style="{ position: 'absolute', right: '0px' }">
<el-button v-popover="popoverRef" v-click-outside="onClickOutside" :icon="List">
高级筛选
</el-button>
<el-popover <el-popover
ref="popoverRef" :visible="showPopover"
trigger="click"
virtual-triggering
persistent persistent
:width="400" :width="400"
:show-arrow="false" :show-arrow="false"
placement="bottom-end" placement="bottom-end"
> >
<template #reference>
<el-button @click="showPopover = !showPopover">
<Icon icon="ep:plus" class="mr-5px" />高级筛选
</el-button>
</template>
<el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category"> <el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
<el-select <el-select
v-model="queryParams.category" v-model="queryParams.category"
...@@ -86,21 +104,6 @@ ...@@ -86,21 +104,6 @@
class="!w-390px" class="!w-390px"
/> />
</el-form-item> </el-form-item>
<el-form-item label="流程状态" class="bold-label" label-position="top" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择流程状态"
clearable
class="!w-390px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime"> <el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
<el-date-picker <el-date-picker
v-model="queryParams.createTime" v-model="queryParams.createTime"
...@@ -112,8 +115,13 @@ ...@@ -112,8 +115,13 @@
class="!w-240px" class="!w-240px"
/> />
</el-form-item> </el-form-item>
<!-- TODO tuituiji:参考钉钉,1)按照清空、取消、确认排序。2)右对齐。3)确认增加 primary -->
<el-form-item class="bold-label" label-position="top">
<el-button @click="handleQuery"> 确认</el-button>
<el-button @click="showPopover = false"> 取消</el-button>
<el-button @click="resetQuery"> 清空</el-button>
</el-form-item>
</el-popover> </el-popover>
<!-- TODO @tuituji:这里应该有确认,和取消、清空搜索条件,三个按钮。 -->
</el-form-item> </el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
...@@ -130,7 +138,7 @@ ...@@ -130,7 +138,7 @@
fixed="left" fixed="left"
/> />
<!-- TODO @芋艿:摘要 --> <!-- TODO @芋艿:摘要 -->
<!-- TODO @tuituji:流程状态。可见需求文档里 --> <!-- TODO tuituiji:参考钉钉;1)审批中时,展示审批任务;2)非审批中,展示状态 -->
<el-table-column label="流程状态" prop="status" width="120"> <el-table-column label="流程状态" prop="status" width="120">
<template #default="scope"> <template #default="scope">
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" /> <dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
...@@ -198,8 +206,7 @@ ...@@ -198,8 +206,7 @@
</ContentWrap> </ContentWrap>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
// TODO @tuituji:List 改成 <Icon icon="ep:plus" class="mr-5px" /> 类似这种组件哈。 // TODO @tuituji:List 改成 <Icon icon="ep:plus" class="mr-5px" /> 类似这种组件哈。 RE:done & to check
import { List } from '@element-plus/icons-vue'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import { ElMessageBox } from 'element-plus' import { ElMessageBox } from 'element-plus'
...@@ -241,6 +248,8 @@ const getList = async () => { ...@@ -241,6 +248,8 @@ const getList = async () => {
} }
} }
const showPopover = ref(false)
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNo = 1 queryParams.pageNo = 1
...@@ -273,7 +282,7 @@ const handleCreate = async (row?: ProcessInstanceVO) => { ...@@ -273,7 +282,7 @@ const handleCreate = async (row?: ProcessInstanceVO) => {
} }
/** 查看详情 */ /** 查看详情 */
const handleDetail = (row) => { const handleDetail = (row: ProcessInstanceVO) => {
router.push({ router.push({
name: 'BpmProcessInstanceDetail', name: 'BpmProcessInstanceDetail',
query: { query: {
...@@ -283,7 +292,7 @@ const handleDetail = (row) => { ...@@ -283,7 +292,7 @@ const handleDetail = (row) => {
} }
/** 取消按钮操作 */ /** 取消按钮操作 */
const handleCancel = async (row) => { const handleCancel = async (row: ProcessInstanceVO) => {
// 二次确认 // 二次确认
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', { const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', {
confirmButtonText: t('common.ok'), confirmButtonText: t('common.ok'),
...@@ -298,15 +307,6 @@ const handleCancel = async (row) => { ...@@ -298,15 +307,6 @@ const handleCancel = async (row) => {
await getList() await getList()
} }
// TODO @tuituji:这个 import 是不是没用哈?
import { ClickOutside as vClickOutside } from 'element-plus'
// TODO @tuituji:onClickAdvancedSearch。方法名叫这个,会更好一些哇?打开高级搜索。
const popoverRef = ref()
const onClickOutside = () => {
unref(popoverRef).popperRef?.delayHide?.()
}
/** 激活时 **/ /** 激活时 **/
onActivated(() => { onActivated(() => {
getList() getList()
......
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
class="-mb-15px" class="-mb-15px"
label-width="68px" label-width="68px"
> >
<el-form-item label="任务名称" prop="name"> <el-form-item label="" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
class="!w-240px" class="!w-240px"
...@@ -25,27 +25,96 @@ ...@@ -25,27 +25,96 @@
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
end-placeholder="结束日期"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" /> <Icon class="mr-5px" icon="ep:search" />
搜索 搜索
</el-button> </el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
</el-form-item> </el-form-item>
<el-form-item label="" prop="category" :style="{ position: 'absolute', right: '300px' }">
<el-select
v-model="queryParams.category"
placeholder="请选择流程分类"
clearable
class="!w-155px"
@change="handleQuery"
>
<el-option
v-for="category in categoryList"
:key="category.code"
:label="category.name"
:value="category.code"
/>
</el-select>
</el-form-item>
<el-form-item label="" prop="status" :style="{ position: 'absolute', right: '130px' }">
<el-select
v-model="queryParams.status"
placeholder="请选择流程状态"
clearable
class="!w-155px"
@change="handleQuery"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<!-- 高级筛选 -->
<el-form-item :style="{ position: 'absolute', right: '0px' }">
<el-popover
:visible="showPopover"
persistent
:width="400"
:show-arrow="false"
placement="bottom-end"
>
<template #reference>
<el-button @click="showPopover = !showPopover" >
<Icon icon="ep:plus" class="mr-5px" />高级筛选
</el-button>
</template>
<el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
<el-select
v-model="queryParams.category"
placeholder="请选择流程发起人"
clearable
class="!w-390px"
>
<el-option
v-for="category in categoryList"
:key="category.code"
:label="category.name"
:value="category.code"
/>
</el-select>
</el-form-item>
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item class="bold-label" label-position="top">
<el-button @click="handleQuery"> 确认</el-button>
<el-button @click="showPopover = false"> 取消</el-button>
<el-button @click="resetQuery"> 清空</el-button>
</el-form-item>
</el-popover>
</el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
...@@ -110,9 +179,10 @@ ...@@ -110,9 +179,10 @@
</ContentWrap> </ContentWrap>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { DICT_TYPE } from '@/utils/dict' import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { dateFormatter, formatPast2 } from '@/utils/formatTime' import { dateFormatter, formatPast2 } from '@/utils/formatTime'
import * as TaskApi from '@/api/bpm/task' import * as TaskApi from '@/api/bpm/task'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
defineOptions({ name: 'BpmTodoTask' }) defineOptions({ name: 'BpmTodoTask' })
...@@ -125,9 +195,13 @@ const queryParams = reactive({ ...@@ -125,9 +195,13 @@ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
name: '', name: '',
category: undefined,
status: undefined,
createTime: [] createTime: []
}) })
const queryFormRef = ref() // 搜索的表单 const queryFormRef = ref() // 搜索的表单
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
const showPopover = ref(false)
/** 查询任务列表 */ /** 查询任务列表 */
const getList = async () => { const getList = async () => {
...@@ -165,7 +239,8 @@ const handleAudit = (row: any) => { ...@@ -165,7 +239,8 @@ const handleAudit = (row: any) => {
} }
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(async () => {
getList() await getList()
categoryList.value = await CategoryApi.getCategorySimpleList()
}) })
</script> </script>
...@@ -16,7 +16,7 @@ ...@@ -16,7 +16,7 @@
class="-mb-15px" class="-mb-15px"
label-width="68px" label-width="68px"
> >
<el-form-item label="任务名称" prop="name"> <el-form-item label="" prop="name">
<el-input <el-input
v-model="queryParams.name" v-model="queryParams.name"
class="!w-240px" class="!w-240px"
...@@ -25,27 +25,79 @@ ...@@ -25,27 +25,79 @@
@keyup.enter="handleQuery" @keyup.enter="handleQuery"
/> />
</el-form-item> </el-form-item>
<el-form-item label="创建时间" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
end-placeholder="结束日期"
start-placeholder="开始日期"
type="daterange"
value-format="YYYY-MM-DD HH:mm:ss"
/>
</el-form-item>
<el-form-item> <el-form-item>
<el-button @click="handleQuery"> <el-button @click="handleQuery">
<Icon class="mr-5px" icon="ep:search" /> <Icon class="mr-5px" icon="ep:search" />
搜索 搜索
</el-button> </el-button>
<el-button @click="resetQuery">
<Icon class="mr-5px" icon="ep:refresh" />
重置
</el-button>
</el-form-item> </el-form-item>
<el-form-item label="" prop="category" :style="{ position: 'absolute', right: '130px' }">
<el-select
v-model="queryParams.category"
placeholder="请选择流程分类"
clearable
class="!w-155px"
@change="handleQuery"
>
<el-option
v-for="category in categoryList"
:key="category.code"
:label="category.name"
:value="category.code"
/>
</el-select>
</el-form-item>
<!-- 高级筛选 -->
<el-form-item :style="{ position: 'absolute', right: '0px' }">
<el-popover
:visible="showPopover"
persistent
:width="400"
:show-arrow="false"
placement="bottom-end"
>
<template #reference>
<el-button @click="showPopover = !showPopover" >
<Icon icon="ep:plus" class="mr-5px" />高级筛选
</el-button>
</template>
<el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
<el-select
v-model="queryParams.category"
placeholder="请选择流程发起人"
clearable
class="!w-390px"
>
<el-option
v-for="category in categoryList"
:key="category.code"
:label="category.name"
:value="category.code"
/>
</el-select>
</el-form-item>
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
<el-date-picker
v-model="queryParams.createTime"
value-format="YYYY-MM-DD HH:mm:ss"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
class="!w-240px"
/>
</el-form-item>
<el-form-item class="bold-label" label-position="top">
<el-button @click="handleQuery"> 确认</el-button>
<el-button @click="showPopover = false"> 取消</el-button>
<el-button @click="resetQuery"> 清空</el-button>
</el-form-item>
</el-popover>
</el-form-item>
</el-form> </el-form>
</ContentWrap> </ContentWrap>
...@@ -95,6 +147,7 @@ ...@@ -95,6 +147,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { dateFormatter } from '@/utils/formatTime' import { dateFormatter } from '@/utils/formatTime'
import * as TaskApi from '@/api/bpm/task' import * as TaskApi from '@/api/bpm/task'
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
defineOptions({ name: 'BpmTodoTask' }) defineOptions({ name: 'BpmTodoTask' })
...@@ -107,9 +160,11 @@ const queryParams = reactive({ ...@@ -107,9 +160,11 @@ const queryParams = reactive({
pageNo: 1, pageNo: 1,
pageSize: 10, pageSize: 10,
name: '', name: '',
category: undefined,
createTime: [] createTime: []
}) })
const queryFormRef = ref() // 搜索的表单 const queryFormRef = ref() // 搜索的表单
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
/** 查询任务列表 */ /** 查询任务列表 */
const getList = async () => { const getList = async () => {
...@@ -123,6 +178,8 @@ const getList = async () => { ...@@ -123,6 +178,8 @@ const getList = async () => {
} }
} }
const showPopover = ref(false)
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.pageNo = 1 queryParams.pageNo = 1
...@@ -147,7 +204,8 @@ const handleAudit = (row: any) => { ...@@ -147,7 +204,8 @@ const handleAudit = (row: any) => {
} }
/** 初始化 **/ /** 初始化 **/
onMounted(() => { onMounted(async () => {
getList() await getList()
categoryList.value = await CategoryApi.getCategorySimpleList()
}) })
</script> </script>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment