fix(ts): 修复 BPMN 设计器类型错误

- 设计器/预览补 DOM 空值保护与局部 service 类型(canvas/elementRegistry)
- UserTaskCustomConfig/ElementForm/ElementMultiInstance 等补 ref/数组/element 类型
- ProcessListener/Expression Dialog 补声明实际已 emit 的 select 事件
- UserTask 删除 return 后不可达旧代码
- ElementMultiInstance:loopCharacteristics 按需写入,不影响 ApproveMethod 扩展保存
- 运行时:keyboard 配置 { bindTo: document } → { bind: true },适配 diagram-js 15
master
YunaiV 2026-06-20 19:43:55 -07:00
parent cb9400e93d
commit 3f779091be
9 changed files with 85 additions and 46 deletions

View File

@ -405,7 +405,10 @@ console.log(additionalModules, 'additionalModules()')
console.log(moddleExtensions, 'moddleExtensions()')
const initBpmnModeler = () => {
if (bpmnModeler) return
let data = document.getElementById('bpmnCanvas')
const data = document.getElementById('bpmnCanvas')
if (!data) {
return
}
console.log(data, 'data')
console.log(props.keyboard, 'props.keyboard')
console.log(additionalModules, 'additionalModules()')
@ -422,9 +425,9 @@ const initBpmnModeler = () => {
// propertiesPanel: {
// parent: '#js-properties-panel'
// },
keyboard: props.keyboard ? { bindTo: document } : null,
keyboard: props.keyboard ? { bind: true } : null,
// additionalModules: additionalModules.value,
additionalModules: additionalModules.value,
additionalModules: additionalModules.value as any[],
moddleExtensions: moddleExtensions.value
// additionalModules: [

View File

@ -140,6 +140,8 @@
import '../theme/index.scss'
import BpmnViewer from 'bpmn-js/lib/Viewer'
import MoveCanvasModule from 'diagram-js/lib/navigation/movecanvas'
import type Canvas from 'diagram-js/lib/core/Canvas'
import type ElementRegistry from 'diagram-js/lib/core/ElementRegistry'
import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue'
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter, formatPast2 } from '@/utils/formatTime'
@ -170,10 +172,18 @@ const dialogTitle = ref<string | undefined>(undefined) // 弹窗标题
const selectActivityType = ref<string | undefined>(undefined) // Task
const selectTasks = ref<any[]>([]) //
type BpmnCanvas = Omit<Canvas, 'zoom'> & {
_svg?: SVGSVGElement
zoom: (newScale?: number | 'fit-viewport', center?: 'auto' | { x: number; y: number }) => number
}
const getCanvas = () => bpmnViewer.value?.get<BpmnCanvas>('canvas')
const getElementRegistry = () => bpmnViewer.value?.get<ElementRegistry>('elementRegistry')
/** Zoom恢复 */
const processReZoom = () => {
defaultZoom.value = 1
bpmnViewer.value?.get('canvas').zoom('fit-viewport', 'auto')
getCanvas()?.zoom('fit-viewport', 'auto')
}
let resizeObserver: ResizeObserver | null = null
@ -218,7 +228,7 @@ const processZoomIn = (zoomStep = 0.1) => {
throw new Error('[Process Designer Warn ]: The zoom ratio cannot be greater than 4')
}
defaultZoom.value = newZoom
bpmnViewer.value?.get('canvas').zoom(defaultZoom.value)
getCanvas()?.zoom(defaultZoom.value)
}
/** Zoom缩小 */
@ -228,7 +238,7 @@ const processZoomOut = (zoomStep = 0.1) => {
throw new Error('[Process Designer Warn ]: The zoom ratio cannot be less than 0.2')
}
defaultZoom.value = newZoom
bpmnViewer.value?.get('canvas').zoom(defaultZoom.value)
getCanvas()?.zoom(defaultZoom.value)
}
/** 流程图预览清空 */
@ -249,9 +259,9 @@ const addCustomDefs = () => {
if (!bpmnViewer.value) {
return
}
const canvas = bpmnViewer.value?.get('canvas')
const canvas = getCanvas()
const svg = canvas?._svg
svg.appendChild(customDefs.value)
svg?.appendChild(customDefs.value)
}
/** 节点选中 */
@ -340,8 +350,11 @@ const setProcessStatus = (view: any) => {
finishedSequenceFlowActivityIds,
rejectedTaskActivityIds
} = view
const canvas = bpmnViewer.value.get('canvas')
const elementRegistry = bpmnViewer.value.get('elementRegistry')
const canvas = getCanvas()
const elementRegistry = getElementRegistry()
if (!canvas || !elementRegistry) {
return
}
//
if (Array.isArray(finishedSequenceFlowActivityIds)) {
@ -349,7 +362,7 @@ const setProcessStatus = (view: any) => {
if (item != null) {
canvas.addMarker(item, 'success')
const element = elementRegistry.get(item)
const conditionExpression = element.businessObject.conditionExpression
const conditionExpression = element?.businessObject.conditionExpression
if (conditionExpression) {
canvas.addMarker(item, 'condition-expression')
}

View File

@ -101,7 +101,7 @@ const maxRemindCount = ref(1)
const elExtensionElements = ref()
const otherExtensions = ref()
const configExtensions = ref([])
const configExtensions = ref<any[]>([])
const eventDefinition = ref()
const resetElement = () => {

View File

@ -215,6 +215,18 @@ import * as UserApi from '@/api/system/user'
import { useFormFieldsPermission } from '@/components/SimpleProcessDesignerV2/src/node'
import { BpmModelFormType } from '@/utils/constants'
type BpmnElement = {
id: string
type: string
businessObject: Record<string, any>
source?: BpmnElement
target?: BpmnElement
}
type ReturnTask = Record<string, any> & {
id: string
name?: string
}
defineOptions({ name: 'ElementCustomConfig4UserTask' })
const props = defineProps({
id: String,
@ -231,16 +243,16 @@ const rejectHandlerTypeEl = ref()
const rejectHandlerType = ref()
const returnNodeIdEl = ref()
const returnNodeId = ref()
const returnTaskList = ref([])
const returnTaskList = ref<ReturnTask[]>([])
//
const assignEmptyHandlerTypeEl = ref()
const assignEmptyHandlerType = ref()
const assignEmptyUserIdsEl = ref()
const assignEmptyUserIds = ref()
const assignEmptyUserIds = ref<Array<string | number>>([])
//
const buttonsSettingEl = ref()
const buttonsSettingEl = ref<any[]>([])
const { btnDisplayNameEdit, changeBtnDisplayName } = useButtonsSetting()
const btnDisplayNameBlurEvent = (index: number) => {
btnDisplayNameEdit.value[index] = false
@ -250,7 +262,7 @@ const btnDisplayNameBlurEvent = (index: number) => {
}
//
const fieldsPermissionEl = ref([])
const fieldsPermissionEl = ref<any[]>([])
const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission(
FieldPermissionType.READ
)
@ -451,18 +463,20 @@ watch(
{ immediate: true }
)
function findAllPredecessorsExcludingStart(elementId, modeler) {
function findAllPredecessorsExcludingStart(elementId: string, modeler: any) {
const elementRegistry = modeler.get('elementRegistry')
const allConnections = elementRegistry.filter((element) => element.type === 'bpmn:SequenceFlow')
const predecessors = new Set() // 使 Set
const visited = new Set() // 访
const allConnections = elementRegistry.filter(
(element: BpmnElement) => element.type === 'bpmn:SequenceFlow'
)
const predecessors = new Set<Record<string, any>>() // 使 Set
const visited = new Set<BpmnElement>() // 访
//
function isStartEvent(element) {
function isStartEvent(element: BpmnElement) {
return element.type === 'bpmn:StartEvent'
}
function findPredecessorsRecursively(element) {
function findPredecessorsRecursively(element: BpmnElement) {
// 访
if (visited.has(element)) {
return
@ -472,10 +486,15 @@ function findAllPredecessorsExcludingStart(elementId, modeler) {
visited.add(element)
//
const incomingConnections = allConnections.filter((connection) => connection.target === element)
const incomingConnections = allConnections.filter(
(connection: BpmnElement) => connection.target === element
)
incomingConnections.forEach((connection) => {
incomingConnections.forEach((connection: BpmnElement) => {
const source = connection.source //
if (!source) {
return
}
//
if (!isStartEvent(source)) {
@ -491,7 +510,7 @@ function findAllPredecessorsExcludingStart(elementId, modeler) {
findPredecessorsRecursively(targetElement)
}
return Array.from(predecessors) //
return Array.from(predecessors) as ReturnTask[] //
}
function useButtonsSetting() {

View File

@ -236,7 +236,7 @@ const props = defineProps({
})
const prefix = inject('prefix')
const formKey = ref(undefined)
const formKey = ref<number | undefined>()
const bpmnELement = ref()
const elExtensionElements = ref()
const formData = ref()
@ -282,10 +282,10 @@ const updateElementExtensions = () => {
})
}
const formList = ref([]) //
const formList = ref<Array<{ id: number; name: string }>>([]) //
onMounted(async () => {
formList.value = await FormApi.getFormSimpleList()
formKey.value = parseInt(formKey.value)
formKey.value = formKey.value != null ? Number(formKey.value) : undefined
})
watch(

View File

@ -76,8 +76,8 @@ const getList = async () => {
}
/** 提交表单 */
const emit = defineEmits(['success']) // success
const select = async (row) => {
const emit = defineEmits(['success', 'select']) // success/select
const select = async (row: ProcessListenerVO) => {
dialogVisible.value = false
//
emit('select', row)

View File

@ -117,8 +117,8 @@ const props = defineProps({
const prefix = inject('prefix')
const loopCharacteristics = ref('')
const loopInstanceForm = ref<any>({})
const bpmnElement = ref(null)
const multiLoopInstance = ref(null)
const bpmnElement = ref<any | null>(null)
const multiLoopInstance = ref<any | null>(null)
const bpmnInstances = () => (window as any)?.bpmnInstances
const changeLoopCharacteristicsType = (type) => {
@ -261,7 +261,7 @@ const approveMethod = ref()
const approveRatio = ref(100)
const otherExtensions = ref()
const getElementLoopNew = () => {
if (props.type === 'UserTask') {
if (props.type === 'UserTask' && bpmnElement.value) {
const extensionElements =
bpmnElement.value.businessObject?.extensionElements ??
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
@ -286,6 +286,9 @@ const onApproveRatioChange = () => {
updateLoopCharacteristics()
}
const updateLoopCharacteristics = () => {
if (!bpmnElement.value) {
return
}
// ApproveMethodmultiInstanceLoopCharacteristics
if (approveMethod.value === ApproveMethodType.RANDOM_SELECT_ONE_APPROVE) {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
@ -334,9 +337,11 @@ const updateLoopCharacteristics = () => {
}
)
}
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: toRaw(multiLoopInstance.value)
})
if (multiLoopInstance.value) {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
loopCharacteristics: toRaw(multiLoopInstance.value)
})
}
}
// ApproveMethodExtensionElements

View File

@ -61,8 +61,8 @@ const getList = async () => {
}
/** 提交表单 */
const emit = defineEmits(['success']) // success
const select = async (row) => {
const emit = defineEmits(['success', 'select']) // success/select
const select = async (row: ProcessExpressionVO) => {
dialogVisible.value = false
//
emit('select', row)

View File

@ -233,7 +233,13 @@ const props = defineProps({
type: String
})
const prefix = inject('prefix')
const userTaskForm = ref({
type CandidateParam = Array<string | number> | string | number
type UserTaskForm = {
candidateStrategy?: CandidateStrategy
candidateParam: CandidateParam
skipExpression: string
}
const userTaskForm = ref<UserTaskForm>({
candidateStrategy: undefined, //
candidateParam: [], //
skipExpression: '' //
@ -405,13 +411,6 @@ const updateElementTask = () => {
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
extensionElements: extensions
})
// extensionElements
return
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
candidateStrategy: userTaskForm.value.candidateStrategy,
candidateParam: userTaskForm.value.candidateParam.join(',')
})
}
const updateSkipExpression = () => {