Quellcode durchsuchen

feat: 流程模型新增/修改/设计合为一个页面基本切换tab逻辑校验与页面样式优化

GoldenZqqqq vor 8 Monaten
Ursprung
Commit
9459a7296f

+ 46 - 23
src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue

@@ -38,12 +38,21 @@ import * as UserGroupApi from '@/api/bpm/userGroup'
 defineOptions({
   name: 'SimpleProcessDesigner'
 })
+
 const emits = defineEmits(['success']) // 保存成功事件
 
 const props = defineProps({
   modelId: {
     type: String,
-    required: true
+    required: false
+  },
+  modelKey: {
+    type: String,
+    required: false
+  },
+  modelName: {
+    type: String,
+    required: false
   }
 })
 
@@ -69,6 +78,7 @@ const message = useMessage() // 国际化
 const processNodeTree = ref<SimpleFlowNode | undefined>()
 const errorDialogVisible = ref(false)
 let errorNodes: SimpleFlowNode[] = []
+
 const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
   if (!simpleModelNode) {
     message.error('模型数据为空')
@@ -76,21 +86,28 @@ const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
   }
   try {
     loading.value = true
-    const data = {
-      id: props.modelId,
-      simpleModel: simpleModelNode
-    }
-    const result = await updateBpmSimpleModel(data)
-    if (result) {
-      message.success('修改成功')
-      emits('success')
+    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 {
-      message.alert('修改失败')
+      // 新建模式,直接返回数据
+      emits('success', simpleModelNode)
     }
   } finally {
     loading.value = false
   }
 }
+
 // 校验节点设置。 暂时以 showText 为空 未节点错误配置
 const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
   if (node) {
@@ -134,12 +151,14 @@ onMounted(async () => {
   try {
     loading.value = true
     // 获取表单字段
-    const bpmnModel = await getModel(props.modelId)
-    if (bpmnModel) {
-      formType.value = bpmnModel.formType
-      if (formType.value === 10) {
-        const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
-        formFields.value = bpmnForm?.fields
+    if (props.modelId) {
+      const bpmnModel = await getModel(props.modelId)
+      if (bpmnModel) {
+        formType.value = bpmnModel.formType
+        if (formType.value === 10) {
+          const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
+          formFields.value = bpmnForm?.fields
+        }
       }
     }
     // 获得角色列表
@@ -155,14 +174,18 @@ onMounted(async () => {
     // 获取用户组列表
     userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
 
-    //获取 SIMPLE 设计器模型
-    const result = await getBpmSimpleModel(props.modelId)
-    if (result) {
-      processNodeTree.value = result
-    } else {
-      // 初始值
+    if (props.modelId) {
+      //获取 SIMPLE 设计器模型
+      const result = await getBpmSimpleModel(props.modelId)
+      if (result) {
+        processNodeTree.value = result
+      }
+    }
+    
+    // 如果没有现有模型,创建初始模型
+    if (!processNodeTree.value) {
       processNodeTree.value = {
-        name: '发起人',
+        name: props.modelName || '发起人',
         type: NodeType.START_USER_NODE,
         id: NodeId.START_USER_NODE_ID,
         childNode: {

+ 1 - 1
src/views/bpm/model/CategoryDraggableModel.vue

@@ -249,7 +249,7 @@ import { formatDate } from '@/utils/formatTime'
 import * as ModelApi from '@/api/bpm/model'
 import * as FormApi from '@/api/bpm/form'
 import { setConfAndFields2 } from '@/utils/formCreate'
-import { BpmModelFormType, BpmModelType } from '@/utils/constants'
+import { BpmModelFormType } from '@/utils/constants'
 import { checkPermi } from '@/utils/permission'
 import { useUserStoreWithOut } from '@/store/modules/user'
 import { useAppStore } from '@/store/modules/app'

+ 60 - 26
src/views/bpm/model/CreateUpdate.vue

@@ -1,42 +1,47 @@
 <template>
   <!-- 头部导航栏 -->
   <div
-    class="absolute top-0 left-0 right-0 h-50px bg-white border-bottom z-10 flex items-center justify-between px-20px"
+    class="absolute top-0 left-0 right-0 h-50px bg-white border-bottom z-10 flex items-center px-20px"
   >
-    <div class="flex items-center">
-      <Icon icon="ep:arrow-left" class="cursor-pointer" @click="router.back()" />
-      <span class="ml-10px text-16px">{{ formData.name || '创建流程' }}</span>
+    <!-- 左侧标题,固定宽度 -->
+    <div class="w-200px flex items-center overflow-hidden">
+      <Icon icon="ep:arrow-left" class="cursor-pointer flex-shrink-0" @click="router.back()" />
+      <span class="ml-10px text-16px truncate" :title="formData.name || '创建流程'">
+        {{ formData.name || '创建流程' }}
+      </span>
     </div>
 
-    <!-- 步骤条 -->
+    <!-- 步骤条,固定在中间 -->
     <div class="flex-1 flex items-center justify-center h-full">
-      <div
-        v-for="(step, index) in steps"
-        :key="index"
-        class="flex items-center cursor-pointer mx-15px relative h-full"
-        :class="[
-          currentStep === index
-            ? 'text-[#3473ff] border-[#3473ff] border-b-2 border-b-solid'
-            : 'text-gray-500'
-        ]"
-        @click="currentStep = index"
-      >
+      <div class="w-400px flex items-center justify-between h-full">
         <div
-          class="w-28px h-28px rounded-full flex items-center justify-center mr-8px border-2 border-solid text-15px"
+          v-for="(step, index) in steps"
+          :key="index"
+          class="flex items-center cursor-pointer mx-15px relative h-full"
           :class="[
             currentStep === index
-              ? ' bg-[#3473ff] text-white'
-              : 'border-gray-300 bg-white text-gray-500'
+              ? 'text-[#3473ff] border-[#3473ff] border-b-2 border-b-solid'
+              : 'text-gray-500'
           ]"
+          @click="handleStepClick(index)"
         >
-          {{ index + 1 }}
+          <div
+            class="w-28px h-28px rounded-full flex items-center justify-center mr-8px border-2 border-solid text-15px"
+            :class="[
+              currentStep === index
+                ? 'bg-[#3473ff] text-white border-[#3473ff]'
+                : 'border-gray-300 bg-white text-gray-500'
+            ]"
+          >
+            {{ index + 1 }}
+          </div>
+          <span class="text-16px font-bold whitespace-nowrap">{{ step.title }}</span>
         </div>
-        <span class="text-16px font-bold">{{ step.title }}</span>
       </div>
     </div>
 
-    <!-- 操作按钮 -->
-    <div class="flex items-center gap-1">
+    <!-- 右侧按钮,固定宽度 -->
+    <div class="w-200px flex items-center justify-end gap-2">
       <el-button @click="handleSave">保 存</el-button>
       <el-button type="primary" @click="handleDeploy">发 布</el-button>
     </div>
@@ -264,15 +269,26 @@
       </el-form>
     </div>
 
+    <!-- 第三步:流程设计 -->
     <div v-show="currentStep === 2">
       <!-- BPMN设计器 -->
       <template v-if="formData.type === BpmModelType.BPMN">
-        <BpmModelEditor :model-id="formData.id" @success="handleDesignSuccess" />
+        <BpmModelEditor
+          :model-id="formData.id"
+          :model-key="formData.key"
+          :model-name="formData.name"
+          @success="handleDesignSuccess"
+        />
       </template>
 
       <!-- Simple设计器 -->
       <template v-else>
-        <SimpleModelDesign :model-id="formData.id" @success="handleDesignSuccess" />
+        <SimpleModelDesign
+          :model-id="formData.id"
+          :model-key="formData.key"
+          :model-name="formData.name"
+          @success="handleDesignSuccess"
+        />
       </template>
     </div>
   </ContentWrap>
@@ -520,9 +536,27 @@ const steps = [
 ]
 
 // 处理设计器保存成功
-const handleDesignSuccess = () => {
+const handleDesignSuccess = (bpmnXml?: string) => {
+  if (bpmnXml) {
+    // 新建时,保存设计器生成的XML
+    formData.value.bpmnXml = bpmnXml
+  }
   message.success('保存成功')
 }
+
+// 步骤切换处理
+const handleStepClick = async (index: number) => {
+  // 如果是切换到第三步(流程设计),需要校验key和name
+  if (index === 2 && !formData.value.id) {
+    // 新增时才校验
+    if (!formData.value.key || !formData.value.name) {
+      message.warning('请先填写流程标识和流程名称')
+      return
+    }
+  }
+
+  currentStep.value = index
+}
 </script>
 
 <style lang="scss" scoped>

+ 61 - 31
src/views/bpm/model/editor/index.vue

@@ -11,6 +11,7 @@
       ref="processDesigner"
       @init-finished="initModeler"
       :additionalModel="controlForm.additionalModel"
+      :model="model"
       @save="save"
     />
     <!-- 流程属性器,负责编辑每个流程节点的属性 -->
@@ -35,14 +36,15 @@ import * as ModelApi from '@/api/bpm/model'
 defineOptions({ name: 'BpmModelEditor' })
 
 const props = defineProps<{
-  modelId: string
+  modelId?: string
+  modelKey?: string
+  modelName?: string
 }>()
+
 const emit = defineEmits(['success'])
-const router = useRouter() // 路由
-const { query } = useRoute() // 路由的查询
 const message = useMessage() // 国际化
 
-const xmlString = ref(undefined) // BPMN XML
+const xmlString = ref<string>() // BPMN XML
 const modeler = ref(null) // BPMN Modeler
 const controlForm = ref({
   simulation: true,
@@ -61,16 +63,32 @@ const initModeler = (item) => {
   }, 10)
 }
 
+/** 获取默认的BPMN XML */
+const getDefaultBpmnXml = (key: string, name: string) => {
+  return `<?xml version="1.0" encoding="UTF-8"?>
+<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
+  <process id="${key}" name="${name}" isExecutable="true" />
+  <bpmndi:BPMNDiagram id="BPMNDiagram">
+    <bpmndi:BPMNPlane id="${key}_di" bpmnElement="${key}" />
+  </bpmndi:BPMNDiagram>
+</definitions>`
+}
+
 /** 添加/修改模型 */
 const save = async (bpmnXml: string) => {
   try {
-    const data = {
-      ...model.value,
-      bpmnXml: bpmnXml
-    } as unknown as ModelApi.ModelVO
-    // 提交
-    await ModelApi.updateModelBpmn(data)
-    emit('success')
+    if (props.modelId) {
+      // 编辑模式
+      const data = {
+        ...model.value,
+        bpmnXml: bpmnXml
+      } as unknown as ModelApi.ModelVO
+      await ModelApi.updateModelBpmn(data)
+      emit('success')
+    } else {
+      // 新建模式,直接返回XML
+      emit('success', bpmnXml)
+    }
   } catch (error) {
     console.error('保存失败:', error)
     message.error('保存失败')
@@ -79,28 +97,40 @@ const save = async (bpmnXml: string) => {
 
 /** 初始化 */
 onMounted(async () => {
-  if (!props.modelId) {
-    message.error('缺少模型 modelId 编号')
-    return
+  if (props.modelId) {
+    // 编辑模式
+    try {
+      // 查询模型
+      const data = await ModelApi.getModel(props.modelId)
+      model.value = {
+        ...data,
+        bpmnXml: undefined // 清空 bpmnXml 属性
+      }
+      xmlString.value = data.bpmnXml || getDefaultBpmnXml(data.key, data.name)
+    } catch (error) {
+      console.error('获取模型失败:', error)
+      message.error('获取模型失败')
+    }
+  } else if (props.modelKey && props.modelName) {
+    // 新建模式,使用传入的key和name创建默认XML
+    xmlString.value = getDefaultBpmnXml(props.modelKey, props.modelName)
+    model.value = {
+      key: props.modelKey,
+      name: props.modelName
+    } as ModelApi.ModelVO
   }
-  // 查询模型
-  const data = await ModelApi.getModel(String(props.modelId))
-  if (!data.bpmnXml) {
-    // 首次创建的 Model 模型,它是没有 bpmnXml,此时需要给它一个默认的
-    data.bpmnXml = ` <?xml version="1.0" encoding="UTF-8"?>
-<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
-  <process id="${data.key}" name="${data.name}" isExecutable="true" />
-  <bpmndi:BPMNDiagram id="BPMNDiagram">
-    <bpmndi:BPMNPlane id="${data.key}_di" bpmnElement="${data.key}" />
-  </bpmndi:BPMNDiagram>
-</definitions>`
-  }
-  model.value = {
-    ...data,
-    bpmnXml: undefined // 清空 bpmnXml 属性
-  }
-  xmlString.value = data.bpmnXml
 })
+
+// 监听key和name的变化
+watch([() => props.modelKey, () => props.modelName], ([newKey, newName]) => {
+  if (!props.modelId && newKey && newName) {
+    xmlString.value = getDefaultBpmnXml(newKey, newName)
+    model.value = {
+      key: newKey,
+      name: newName
+    } as ModelApi.ModelVO
+  }
+}, { immediate: true })
 </script>
 <style lang="scss">
 .process-panel__container {

+ 11 - 4
src/views/bpm/simple/SimpleModelDesign.vue

@@ -1,6 +1,11 @@
 <template>
   <ContentWrap :bodyStyle="{ padding: '20px 16px' }">
-    <SimpleProcessDesigner :model-id="modelId" @success="handleSuccess" />
+    <SimpleProcessDesigner 
+      :model-id="modelId" 
+      :model-key="modelKey"
+      :model-name="modelName"
+      @success="handleSuccess" 
+    />
   </ContentWrap>
 </template>
 <script setup lang="ts">
@@ -11,14 +16,16 @@ defineOptions({
 })
 
 defineProps<{
-  modelId: string
+  modelId?: string
+  modelKey?: string
+  modelName?: string
 }>()
 
 const emit = defineEmits(['success'])
 
 // 修改成功回调
-const handleSuccess = () => {
-  emit('success')
+const handleSuccess = (data?: any) => {
+  emit('success', data)
 }
 </script>
 <style lang="scss" scoped></style>