Merge pull request #116 from GoldenZqqq/feature/bpm

流程新增/修改页面bug修复
This commit is contained in:
芋道源码 2024-12-31 09:46:32 +08:00 committed by GitHub
commit f3b28f43f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 522 additions and 198 deletions

View File

@ -54,6 +54,10 @@ const props = defineProps({
modelName: {
type: String,
required: false
},
value: {
type: [String, Object],
required: false
}
})
@ -66,6 +70,10 @@ const userOptions = ref<UserApi.UserVO[]>([]) // 用户列表
const deptOptions = ref<DeptApi.DeptVO[]>([]) //
const deptTreeOptions = ref()
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) //
//
const currentValue = ref<SimpleFlowNode | undefined>()
provide('formFields', formFields)
provide('formType', formType)
provide('roleList', roleOptions)
@ -81,10 +89,10 @@ const errorDialogVisible = ref(false)
let errorNodes: SimpleFlowNode[] = []
//
const updateModel = (key?: string, name?: string) => {
const updateModel = () => {
if (!processNodeTree.value) {
processNodeTree.value = {
name: name || '发起人',
name: '发起人',
type: NodeType.START_USER_NODE,
id: NodeId.START_USER_NODE_ID,
childNode: {
@ -93,45 +101,78 @@ const updateModel = (key?: string, name?: string) => {
type: NodeType.END_EVENT_NODE
}
}
} else if (name) {
//
processNodeTree.value.name = name
//
saveSimpleFlowModel(processNodeTree.value)
}
}
//
const loadProcessData = async (data: any) => {
try {
if (data) {
const parsedData = typeof data === 'string' ? JSON.parse(data) : data
processNodeTree.value = parsedData
currentValue.value = parsedData
//
await nextTick()
if (simpleProcessModelRef.value?.refresh) {
await simpleProcessModelRef.value.refresh()
}
}
} catch (error) {
console.error('加载流程数据失败:', error)
}
}
//
watch([() => props.modelKey, () => props.modelName], ([newKey, newName]) => {
if (!props.modelId && newKey && newName) {
updateModel(newKey, newName)
}
}, { immediate: true, deep: true })
watch(
() => props.value,
async (newValue, oldValue) => {
if (newValue && newValue !== oldValue) {
await loadProcessData(newValue)
}
},
{ immediate: true, deep: true }
)
//
watch(
() => processNodeTree.value,
async (newValue, oldValue) => {
if (newValue && oldValue && JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
await saveSimpleFlowModel(newValue)
}
},
{ deep: true }
)
const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
if (!simpleModelNode) {
message.error('模型数据为空')
return
}
//
errorNodes = []
validateNode(simpleModelNode, errorNodes)
if (errorNodes.length > 0) {
errorDialogVisible.value = true
return
}
try {
loading.value = true
if (props.modelId) {
//
const data = {
id: props.modelId,
simpleModel: simpleModelNode
}
const result = await updateBpmSimpleModel(data)
if (result) {
message.success('修改成功')
emits('success')
} else {
message.alert('修改失败')
}
} else {
//
emits('success', simpleModelNode)
await updateBpmSimpleModel(data)
}
} finally {
loading.value = false
//
currentValue.value = simpleModelNode
emits('success', simpleModelNode)
} catch (error) {
console.error('保存失败:', error)
}
}
@ -196,31 +237,23 @@ onMounted(async () => {
userOptions.value = await UserApi.getSimpleUserList()
//
deptOptions.value = await DeptApi.getSimpleDeptList()
deptTreeOptions.value = handleTree(deptOptions.value as DeptApi.DeptVO[], 'id')
//
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
//
if (props.modelId) {
// SIMPLE
// SIMPLE
const result = await getBpmSimpleModel(props.modelId)
if (result) {
processNodeTree.value = result
}
}
//
if (!processNodeTree.value) {
processNodeTree.value = {
name: props.modelName || '发起人',
type: NodeType.START_USER_NODE,
id: NodeId.START_USER_NODE_ID,
childNode: {
id: NodeId.END_EVENT_NODE_ID,
name: '结束',
type: NodeType.END_EVENT_NODE
}
await loadProcessData(result)
} else {
updateModel()
}
} else if (props.value) {
await loadProcessData(props.value)
} else {
updateModel()
}
} finally {
loading.value = false
@ -231,14 +264,36 @@ const simpleProcessModelRef = ref()
/** 获取当前流程数据 */
const getCurrentFlowData = async () => {
if (simpleProcessModelRef.value) {
return await simpleProcessModelRef.value.getCurrentFlowData()
try {
if (simpleProcessModelRef.value) {
const data = await simpleProcessModelRef.value.getCurrentFlowData()
if (data) {
currentValue.value = data
return data
}
}
return currentValue.value
} catch (error) {
console.error('获取流程数据失败:', error)
return currentValue.value
}
}
//
const refresh = async () => {
try {
if (currentValue.value) {
await loadProcessData(currentValue.value)
}
} catch (error) {
console.error('刷新失败:', error)
}
return undefined
}
defineExpose({
getCurrentFlowData,
updateModel
updateModel,
loadProcessData,
refresh
})
</script>

View File

@ -39,9 +39,10 @@ const props = defineProps<{
modelId?: string
modelKey?: string
modelName?: string
value?: string
}>()
const emit = defineEmits(['success'])
const emit = defineEmits(['success', 'init-finished'])
const message = useMessage() //
//
@ -98,8 +99,35 @@ const initModeler = async (item) => {
// modeler
if (initBpmnInstances()) {
isModelerReady.value = true
if (!props.modelId && props.modelKey && props.modelName) {
await updateModelData(props.modelKey, props.modelName)
emit('init-finished')
//
if (props.modelId) {
//
const data = await ModelApi.getModel(props.modelId)
model.value = {
...data,
bpmnXml: undefined // bpmnXml
}
xmlString.value = data.bpmnXml || getDefaultBpmnXml(data.key, data.name)
} else if (props.modelKey && props.modelName) {
//
xmlString.value = props.value || getDefaultBpmnXml(props.modelKey, props.modelName)
model.value = {
key: props.modelKey,
name: props.modelName
} as ModelApi.ModelVO
}
// XML
await nextTick()
try {
await modeler.value.importXML(xmlString.value)
if (processDesigner.value?.refresh) {
processDesigner.value.refresh()
}
} catch (error) {
console.error('导入XML失败:', error)
}
} else {
console.error('modeler 实例未完全初始化')
@ -123,6 +151,7 @@ const getDefaultBpmnXml = (key: string, name: string) => {
/** 添加/修改模型 */
const save = async (bpmnXml: string) => {
try {
xmlString.value = bpmnXml
if (props.modelId) {
//
const data = {
@ -141,58 +170,44 @@ const save = async (bpmnXml: string) => {
}
}
/** 初始化 */
onMounted(async () => {
try {
if (props.modelId) {
//
//
const data = await ModelApi.getModel(props.modelId)
model.value = {
...data,
bpmnXml: undefined // bpmnXml
}
xmlString.value = data.bpmnXml || getDefaultBpmnXml(data.key, data.name)
} else if (props.modelKey && props.modelName) {
//
xmlString.value = getDefaultBpmnXml(props.modelKey, props.modelName)
model.value = {
key: props.modelKey,
name: props.modelName
} as ModelApi.ModelVO
}
} catch (error) {
console.error('初始化失败:', error)
message.error('初始化失败')
}
})
/** 更新模型数据 */
const updateModelData = async (key?: string, name?: string) => {
if (key && name) {
xmlString.value = getDefaultBpmnXml(key, name)
model.value = {
...model.value,
key: key,
name: name
} as ModelApi.ModelVO
//
await nextTick()
if (processDesigner.value?.refresh) {
processDesigner.value.refresh()
}
}
}
// key name
// keyname value
watch(
[() => props.modelKey, () => props.modelName],
async ([newKey, newName]) => {
if (!props.modelId && newKey && newName && modeler.value) {
await updateModelData(newKey, newName)
[() => props.modelKey, () => props.modelName, () => props.value],
async ([newKey, newName, newValue]) => {
if (!props.modelId && isModelerReady.value) {
let shouldRefresh = false
if (newKey && newName) {
const newXml = newValue || getDefaultBpmnXml(newKey, newName)
if (newXml !== xmlString.value) {
xmlString.value = newXml
shouldRefresh = true
}
model.value = {
...model.value,
key: newKey,
name: newName
} as ModelApi.ModelVO
} else if (newValue && newValue !== xmlString.value) {
xmlString.value = newValue
shouldRefresh = true
}
if (shouldRefresh) {
//
await nextTick()
if (processDesigner.value?.refresh) {
try {
await modeler.value?.importXML(xmlString.value)
processDesigner.value.refresh()
} catch (error) {
console.error('导入XML失败:', error)
}
}
}
}
},
{ immediate: true, deep: true }
{ deep: true }
)
//
@ -209,13 +224,15 @@ onBeforeUnmount(() => {
/** 获取 XML 字符串 */
const saveXML = async () => {
if (!modeler.value) {
return { xml: undefined }
return { xml: xmlString.value }
}
try {
return await modeler.value.saveXML({ format: true })
const result = await modeler.value.saveXML({ format: true })
xmlString.value = result.xml
return result
} catch (error) {
console.error('获取XML失败:', error)
return { xml: undefined }
return { xml: xmlString.value }
}
}
@ -233,9 +250,14 @@ const saveSVG = async () => {
}
/** 刷新视图 */
const refresh = () => {
if (processDesigner.value?.refresh) {
processDesigner.value.refresh()
const refresh = async () => {
if (processDesigner.value?.refresh && modeler.value) {
try {
await modeler.value.importXML(xmlString.value)
processDesigner.value.refresh()
} catch (error) {
console.error('刷新视图失败:', error)
}
}
}

View File

@ -6,9 +6,10 @@
:model-id="modelData.id"
:model-key="modelData.key"
:model-name="modelData.name"
:value="modelData.bpmnXml"
:value="currentBpmnXml"
ref="bpmnEditorRef"
@success="handleDesignSuccess"
@init-finished="handleEditorInit"
/>
</template>
@ -19,9 +20,10 @@
:model-id="modelData.id"
:model-key="modelData.key"
:model-name="modelData.name"
:value="modelData.bpmnXml"
:value="currentSimpleModel"
ref="simpleEditorRef"
@success="handleDesignSuccess"
@init-finished="handleEditorInit"
/>
</template>
</template>
@ -42,6 +44,7 @@ const emit = defineEmits(['update:modelValue', 'success'])
const bpmnEditorRef = ref()
const simpleEditorRef = ref()
const isEditorInitialized = ref(false)
//
const modelData = computed({
@ -49,49 +52,120 @@ const modelData = computed({
set: (val) => emit('update:modelValue', val)
})
// XML
const currentBpmnXml = ref('')
const currentSimpleModel = ref('')
// XML
const initOrUpdateXmlData = () => {
if (modelData.value) {
if (modelData.value.type === BpmModelType.BPMN) {
currentBpmnXml.value = modelData.value.bpmnXml || ''
} else {
currentSimpleModel.value = modelData.value.simpleModel || ''
}
}
}
// modelValue
watch(
() => props.modelValue,
(newVal) => {
if (newVal) {
if (newVal.type === BpmModelType.BPMN) {
if (newVal.bpmnXml && newVal.bpmnXml !== currentBpmnXml.value) {
currentBpmnXml.value = newVal.bpmnXml
//
if (isEditorInitialized.value && bpmnEditorRef.value?.refresh) {
nextTick(() => {
bpmnEditorRef.value.refresh()
})
}
}
} else {
if (newVal.simpleModel && newVal.simpleModel !== currentSimpleModel.value) {
currentSimpleModel.value = newVal.simpleModel
//
if (isEditorInitialized.value && simpleEditorRef.value?.refresh) {
nextTick(() => {
simpleEditorRef.value.refresh()
})
}
}
}
}
},
{ immediate: true, deep: true }
)
/** 编辑器初始化完成的回调 */
const handleEditorInit = async () => {
isEditorInitialized.value = true
// tick
await nextTick()
//
if (modelData.value.type === BpmModelType.BPMN) {
if (modelData.value.bpmnXml) {
currentBpmnXml.value = modelData.value.bpmnXml
if (bpmnEditorRef.value?.refresh) {
await nextTick()
bpmnEditorRef.value.refresh()
}
}
} else {
if (modelData.value.simpleModel) {
currentSimpleModel.value = modelData.value.simpleModel
if (simpleEditorRef.value?.refresh) {
await nextTick()
simpleEditorRef.value.refresh()
}
}
}
}
/** 获取当前流程数据 */
const getProcessData = async () => {
try {
if (modelData.value.type === BpmModelType.BPMN) {
// BPMN
if (bpmnEditorRef.value) {
const { xml } = await bpmnEditorRef.value.saveXML()
if (xml) {
return xml
}
if (!bpmnEditorRef.value || !isEditorInitialized.value) {
return currentBpmnXml.value || undefined
}
const { xml } = await bpmnEditorRef.value.saveXML()
if (xml) {
currentBpmnXml.value = xml
return xml
}
} else {
// Simple
if (simpleEditorRef.value) {
const flowData = await simpleEditorRef.value.getCurrentFlowData()
if (flowData) {
return flowData //
}
if (!simpleEditorRef.value || !isEditorInitialized.value) {
return currentSimpleModel.value || undefined
}
const flowData = await simpleEditorRef.value.getCurrentFlowData()
if (flowData) {
currentSimpleModel.value = flowData
return flowData
}
}
return undefined
return modelData.value.type === BpmModelType.BPMN
? currentBpmnXml.value
: currentSimpleModel.value
} catch (error) {
console.error('获取流程数据失败:', error)
return undefined
return modelData.value.type === BpmModelType.BPMN
? currentBpmnXml.value
: currentSimpleModel.value
}
}
/** 表单校验 */
const validate = async () => {
try {
//
const editorRef =
modelData.value.type === BpmModelType.BPMN ? bpmnEditorRef.value : simpleEditorRef.value
if (!editorRef) {
throw new Error('流程设计器未初始化')
}
//
const processData = await getProcessData()
if (!processData) {
throw new Error('请设计流程')
}
return true
} catch (error) {
throw error
@ -99,21 +173,24 @@ const validate = async () => {
}
/** 处理设计器保存成功 */
const handleDesignSuccess = (data?: any) => {
const handleDesignSuccess = async (data?: any) => {
if (data) {
if (modelData.value.type === BpmModelType.BPMN) {
modelData.value = {
...modelData.value,
bpmnXml: data,
simpleModel: null
}
currentBpmnXml.value = data
} else {
modelData.value = {
...modelData.value,
bpmnXml: null,
simpleModel: data
}
currentSimpleModel.value = data
}
//
const newModelData = {
...modelData.value,
bpmnXml: modelData.value.type === BpmModelType.BPMN ? data : null,
simpleModel: modelData.value.type === BpmModelType.BPMN ? null : data
}
// 使emit
await nextTick()
emit('update:modelValue', newModelData)
emit('success', data)
}
}
@ -123,6 +200,33 @@ const showDesigner = computed(() => {
return Boolean(modelData.value?.key && modelData.value?.name)
})
//
onMounted(() => {
initOrUpdateXmlData()
})
//
onBeforeUnmount(async () => {
try {
//
const data = await getProcessData()
if (data) {
//
const newModelData = {
...modelData.value,
bpmnXml: modelData.value.type === BpmModelType.BPMN ? data : null,
simpleModel: modelData.value.type === BpmModelType.BPMN ? null : data
}
// 使emit
await nextTick()
emit('update:modelValue', newModelData)
}
} catch (error) {
console.error('保存数据失败:', error)
}
})
defineExpose({
validate,
getProcessData

View File

@ -194,11 +194,32 @@ const validateAllSteps = async () => {
}
//
await processDesignRef.value?.validate()
const processData = await processDesignRef.value?.getProcessData()
if (!processData) {
currentStep.value = 2
throw new Error('请设计流程')
//
if (!formData.value.bpmnXml && !formData.value.simpleModel) {
//
if (currentStep.value !== 2) {
await steps[currentStep.value].validator()
//
currentStep.value = 2
//
await nextTick()
}
//
await processDesignRef.value?.validate()
const processData = await processDesignRef.value?.getProcessData()
if (!processData) {
throw new Error('请设计流程')
}
//
if (formData.value.type === BpmModelType.BPMN) {
formData.value.bpmnXml = processData
formData.value.simpleModel = null
} else {
formData.value.bpmnXml = null
formData.value.simpleModel = processData
}
}
return true
@ -213,22 +234,23 @@ const handleSave = async () => {
//
await validateAllSteps()
//
const processData = await processDesignRef.value?.getProcessData()
if (!processData) {
throw new Error('获取流程数据失败')
}
//
const modelData = {
...formData.value
}
if (formData.value.type === BpmModelType.BPMN) {
modelData.bpmnXml = processData
modelData.simpleModel = null
} else {
modelData.bpmnXml = null
modelData.simpleModel = processData // 使
//
if (currentStep.value === 2) {
const processData = await processDesignRef.value?.getProcessData()
if (processData) {
if (formData.value.type === BpmModelType.BPMN) {
modelData.bpmnXml = processData
modelData.simpleModel = null
} else {
modelData.bpmnXml = null
modelData.simpleModel = processData
}
}
}
if (formData.value.id) {
@ -281,22 +303,23 @@ const handleDeploy = async () => {
//
await validateAllSteps()
//
const processData = await processDesignRef.value?.getProcessData()
if (!processData) {
throw new Error('获取流程数据失败')
}
//
const modelData = {
...formData.value
}
if (formData.value.type === BpmModelType.BPMN) {
modelData.bpmnXml = processData
modelData.simpleModel = null
} else {
modelData.bpmnXml = null
modelData.simpleModel = processData // 使
//
if (currentStep.value === 2) {
const processData = await processDesignRef.value?.getProcessData()
if (processData) {
if (formData.value.type === BpmModelType.BPMN) {
modelData.bpmnXml = processData
modelData.simpleModel = null
} else {
modelData.bpmnXml = null
modelData.simpleModel = processData
}
}
}
//
@ -320,27 +343,51 @@ const handleDeploy = async () => {
/** 步骤切换处理 */
const handleStepClick = async (index: number) => {
// keyname
if (index === 2) {
if (!formData.value.key || !formData.value.name) {
message.warning('请先填写流程标识和流程名称')
return
}
}
//
if (index > currentStep.value) {
try {
if (typeof steps[currentStep.value].validator === 'function') {
await steps[currentStep.value].validator()
try {
// keyname
if (index === 2) {
if (!formData.value.key || !formData.value.name) {
message.warning('请先填写流程标识和流程名称')
return
}
currentStep.value = index
} catch (error) {
message.warning('请先完善当前步骤必填信息')
}
} else {
//
//
if (currentStep.value === 2) {
const processData = await processDesignRef.value?.getProcessData()
if (processData) {
if (formData.value.type === BpmModelType.BPMN) {
formData.value.bpmnXml = processData
formData.value.simpleModel = null
} else {
formData.value.bpmnXml = null
formData.value.simpleModel = processData
}
}
} else {
//
if (index > currentStep.value) {
if (typeof steps[currentStep.value].validator === 'function') {
await steps[currentStep.value].validator()
}
}
}
//
currentStep.value = index
//
if (index === 2) {
await nextTick()
//
await new Promise(resolve => setTimeout(resolve, 200))
if (processDesignRef.value?.refresh) {
await processDesignRef.value.refresh()
}
}
} catch (error) {
console.error('步骤切换失败:', error)
message.warning('请先完善当前步骤必填信息')
}
}

View File

@ -1,10 +1,12 @@
<template>
<ContentWrap :bodyStyle="{ padding: '20px 16px' }">
<SimpleProcessDesigner
:model-id="modelId"
<SimpleProcessDesigner
:model-id="modelId"
:model-key="modelKey"
:model-name="modelName"
@success="handleSuccess"
:value="currentValue"
@success="handleSuccess"
@init-finished="handleInit"
ref="designerRef"
/>
</ContentWrap>
@ -20,38 +22,132 @@ const props = defineProps<{
modelId?: string
modelKey?: string
modelName?: string
value?: string
}>()
const emit = defineEmits(['success'])
const emit = defineEmits(['success', 'init-finished'])
const designerRef = ref()
const isInitialized = ref(false)
const currentValue = ref('')
//
const initOrUpdateValue = async () => {
console.log('initOrUpdateValue', props.value)
if (props.value) {
currentValue.value = props.value
//
if (isInitialized.value && designerRef.value) {
try {
await designerRef.value.loadProcessData(props.value)
await nextTick()
if (designerRef.value.refresh) {
await designerRef.value.refresh()
}
} catch (error) {
console.error('加载流程数据失败:', error)
}
}
}
}
//
watch([() => props.modelKey, () => props.modelName], ([newKey, newName]) => {
if (designerRef.value && newKey && newName) {
designerRef.value.updateModel(newKey, newName)
watch(
[() => props.modelKey, () => props.modelName, () => props.value],
async ([newKey, newName, newValue], [oldKey, oldName, oldValue]) => {
if (designerRef.value && isInitialized.value) {
try {
if (newKey && newName && (newKey !== oldKey || newName !== oldName)) {
await designerRef.value.updateModel(newKey, newName)
}
if (newValue && newValue !== oldValue) {
currentValue.value = newValue
await designerRef.value.loadProcessData(newValue)
await nextTick()
if (designerRef.value.refresh) {
await designerRef.value.refresh()
}
}
} catch (error) {
console.error('更新流程数据失败:', error)
}
}
},
{ deep: true, immediate: true }
)
//
const handleInit = async () => {
try {
isInitialized.value = true
emit('init-finished')
// tick
await nextTick()
//
if (props.modelKey && props.modelName) {
await designerRef.value.updateModel(props.modelKey, props.modelName)
}
if (props.value) {
currentValue.value = props.value
await designerRef.value.loadProcessData(props.value)
//
await nextTick()
if (designerRef.value.refresh) {
await designerRef.value.refresh()
}
}
} catch (error) {
console.error('初始化流程数据失败:', error)
}
}, { immediate: true, deep: true })
}
//
const handleSuccess = (data?: any) => {
emit('success', data)
console.warn('handleSuccess', data)
if (data && data !== currentValue.value) {
currentValue.value = data
emit('success', data)
}
}
/** 获取当前流程数据 */
const getCurrentFlowData = async () => {
try {
if (designerRef.value) {
return await designerRef.value.getCurrentFlowData()
const data = await designerRef.value.getCurrentFlowData()
if (data) {
currentValue.value = data
}
return data
}
return undefined
return currentValue.value || undefined
} catch (error) {
console.error('获取流程数据失败:', error)
return undefined
return currentValue.value || undefined
}
}
//
onMounted(() => {
initOrUpdateValue()
})
//
onBeforeUnmount(async () => {
try {
const data = await getCurrentFlowData()
if (data) {
emit('success', data)
}
} catch (error) {
console.error('保存数据失败:', error)
}
})
defineExpose({
getCurrentFlowData
getCurrentFlowData,
refresh: () => designerRef.value?.refresh?.()
})
</script>
<style lang="scss" scoped></style>