Переглянути джерело

Merge branch 'feature/bpm' of https://gitee.com/yudaocode/yudao-ui-admin-vue3

YunaiV 7 місяців тому
батько
коміт
42f5223941

+ 106 - 45
src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue

@@ -54,6 +54,15 @@ const props = defineProps({
   modelName: {
     type: String,
     required: false
+  },
+  // 可发起流程的人员编号
+  startUserIds : {
+    type: Array,
+    required: false
+  },
+  value: {
+    type: [String, Object],
+    required: false
   }
 })
 
@@ -66,6 +75,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)
@@ -74,6 +87,7 @@ provide('userList', userOptions)
 provide('deptList', deptOptions)
 provide('userGroupList', userGroupOptions)
 provide('deptTree', deptTreeOptions)
+provide('startUserIds', props.startUserIds)
 
 const message = useMessage() // 国际化
 const processNodeTree = ref<SimpleFlowNode | undefined>()
@@ -81,10 +95,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 +107,78 @@ const updateModel = (key?: string, name?: string) => {
         type: NodeType.END_EVENT_NODE
       }
     }
-  } else if (name) {
-    // 更新现有模型的名称
-    processNodeTree.value.name = name
+    // 初始化时也触发一次保存
+    saveSimpleFlowModel(processNodeTree.value)
   }
 }
 
-// 监听属性变化
-watch([() => props.modelKey, () => props.modelName], ([newKey, newName]) => {
-  if (!props.modelId && newKey && newName) {
-    updateModel(newKey, newName)
+// 加载流程数据
+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)
   }
-}, { 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 +243,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 +270,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>

+ 27 - 3
src/components/SimpleProcessDesignerV2/src/node.ts

@@ -14,7 +14,7 @@ import {
   NODE_DEFAULT_NAME,
   AssignStartUserHandlerType,
   AssignEmptyHandlerType,
-  FieldPermissionType,
+  FieldPermissionType
 } from './consts'
 import { parseFormFields } from '@/components/FormCreate/src/utils/index'
 export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlowNode> {
@@ -52,9 +52,33 @@ export function useFormFieldsPermission(defaultPermission: FieldPermissionType)
 
   const getNodeConfigFormFields = (nodeFormFields?: Array<Record<string, string>>) => {
     nodeFormFields = toRaw(nodeFormFields)
-    fieldsPermissionConfig.value =
-      cloneDeep(nodeFormFields) || getDefaultFieldsPermission(unref(formFields))
+    if (!nodeFormFields || nodeFormFields.length === 0) {
+      fieldsPermissionConfig.value = getDefaultFieldsPermission(unref(formFields))
+    } else {
+      fieldsPermissionConfig.value = mergeFieldsPermission(nodeFormFields, unref(formFields))
+    }
   }
+  // 合并已经设置的表单字段权限,当前流程表单字段 (可能新增,或删除了字段)
+  const mergeFieldsPermission = (
+    formFieldsPermisson: Array<Record<string, string>>,
+    formFields?: string[]
+  ) => {
+    let mergedFieldsPermission: Array<Record<string, any>> = []
+    if (formFields) {
+      mergedFieldsPermission = parseFormCreateFields(formFields).map((item) => {
+        const found = formFieldsPermisson.find(
+          (fieldPermission) => fieldPermission.field == item.field
+        )
+        return {
+          field: item.field,
+          title: item.title,
+          permission: found ? found.permission : defaultPermission
+        }
+      })
+    }
+    return mergedFieldsPermission
+  }
+
   // 默认的表单权限: 获取表单的所有字段,设置字段默认权限为只读
   const getDefaultFieldsPermission = (formFields?: string[]) => {
     let defaultFieldsPermission: Array<Record<string, any>> = []

+ 32 - 4
src/components/SimpleProcessDesignerV2/src/nodes-config/StartUserNodeConfig.vue

@@ -25,7 +25,20 @@
     </template>
     <el-tabs type="border-card" v-model="activeTabName">
       <el-tab-pane label="权限" name="user">
-        <div> 待实现 </div>
+        <el-text v-if="!startUserIds || startUserIds.length === 0"> 全部成员可以发起流程 </el-text>
+        <el-text v-else-if="startUserIds.length == 1">
+          {{ getUserNicknames(startUserIds) }} 可发起流程
+        </el-text>
+        <el-text v-else>
+          <el-tooltip
+            class="box-item"
+            effect="dark"
+            placement="top"
+            :content="getUserNicknames(startUserIds)"
+          >
+            {{ getUserNicknames(startUserIds.slice(0,2)) }} 等 {{ startUserIds.length }} 人可发起流程
+          </el-tooltip>
+        </el-text>
       </el-tab-pane>
       <el-tab-pane label="表单字段权限" name="fields" v-if="formType === 10">
         <div class="field-setting-pane">
@@ -86,7 +99,7 @@
 <script setup lang="ts">
 import { SimpleFlowNode, NodeType, FieldPermissionType, START_USER_BUTTON_SETTING } from '../consts'
 import { useWatchNode, useDrawer, useNodeName, useFormFieldsPermission } from '../node'
-
+import * as UserApi from '@/api/system/user'
 defineOptions({
   name: 'StartUserNodeConfig'
 })
@@ -96,6 +109,10 @@ const props = defineProps({
     required: true
   }
 })
+// 可发起流程的用户编号
+const startUserIds = inject<Ref<any[]>>('startUserIds')
+// 用户列表
+const userOptions = inject<Ref<UserApi.UserVO[]>>('userList')
 // 抽屉配置
 const { settingVisible, closeDrawer, openDrawer } = useDrawer()
 // 当前节点
@@ -108,12 +125,23 @@ const activeTabName = ref('user')
 const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission(
   FieldPermissionType.WRITE
 )
-
+const getUserNicknames = (userIds: number[]): string => {
+  if (!userIds || userIds.length === 0) {
+    return ''
+  }
+  const nicknames: string[] = []
+  userIds.forEach((userId) => {
+    const found = userOptions?.value.find((item) => item.id === userId)
+    if (found && found.nickname) {
+      nicknames.push(found.nickname)
+    }
+  })
+  return nicknames.join(',')
+}
 // 保存配置
 const saveConfig = async () => {
   activeTabName.value = 'user'
   currentNode.value.name = nodeName.value!
-  // TODO 暂时写死。后续可以显示谁有权限可以发起
   currentNode.value.showText = '已设置'
   // 设置表单权限
   currentNode.value.fieldsPermission = fieldsPermissionConfig.value

+ 79 - 57
src/views/bpm/model/editor/index.vue

@@ -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('初始化失败')
-  }
-})
+// 监听 key、name 和 value 的变化
+watch(
+  [() => props.modelKey, () => props.modelName, () => props.value],
+  async ([newKey, newName, newValue]) => {
+    if (!props.modelId && isModelerReady.value) {
+      let shouldRefresh = false
 
-/** 更新模型数据 */
-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()
-    }
-  }
-}
+      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
+      }
 
-// 监听 key 和 name 的变化
-watch(
-  [() => props.modelKey, () => props.modelName],
-  async ([newKey, newName]) => {
-    if (!props.modelId && newKey && newName && modeler.value) {
-      await updateModelData(newKey, newName)
+      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)
+    }
   }
 }
 

+ 140 - 35
src/views/bpm/model/form/ProcessDesign.vue

@@ -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,11 @@
       :model-id="modelData.id"
       :model-key="modelData.key"
       :model-name="modelData.name"
-      :value="modelData.bpmnXml"
+      :start-user-ids="modelData.startUserIds"
+      :value="currentSimpleModel"
       ref="simpleEditorRef"
       @success="handleDesignSuccess"
+      @init-finished="handleEditorInit"
     />
   </template>
 </template>
@@ -42,6 +45,7 @@ const emit = defineEmits(['update:modelValue', 'success'])
 
 const bpmnEditorRef = ref()
 const simpleEditorRef = ref()
+const isEditorInitialized = ref(false)
 
 // 创建本地数据副本
 const modelData = computed({
@@ -49,49 +53,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 +174,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 +201,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

+ 92 - 45
src/views/bpm/model/form/index.vue

@@ -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) => {
-  // 如果是切换到第三步(流程设计),需要校验key和name
-  if (index === 2) {
-    if (!formData.value.key || !formData.value.name) {
-      message.warning('请先填写流程标识和流程名称')
-      return
+  try {
+    // 如果是切换到第三步(流程设计),需要校验key和name
+    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()
+    // 保存当前步骤的数据
+    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
-    } catch (error) {
-      message.warning('请先完善当前步骤必填信息')
     }
-  } else {
-    // 向前切换时直接切换
+
+    // 切换步骤
     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('请先完善当前步骤必填信息')
   }
 }
 

+ 111 - 13
src/views/bpm/simple/SimpleModelDesign.vue

@@ -1,10 +1,13 @@
 <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"
+      :start-user-ids="startUserIds"
       ref="designerRef"
     />
   </ContentWrap>
@@ -20,38 +23,133 @@ const props = defineProps<{
   modelId?: string
   modelKey?: string
   modelName?: string
+  value?: string
+  startUserIds?: number[]
 }>()
 
-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>