瀏覽代碼

!712 BPM-子流程
Merge pull request !712 from Lesan/feature/bpm-子流程

芋道源码 6 月之前
父節點
當前提交
cd0f1322fb

+ 1 - 0
src/assets/svgs/bpm/child-process.svg

@@ -0,0 +1 @@
+<svg t="1740116949537" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1153" width="200" height="200"><path d="M440.32 296.96h283.30496v145.92h66.56V230.4H440.32V17.92H17.92v424.96H440.32V296.96zM373.76 376.32H84.48v-291.84H373.76v291.84zM586.24 588.8v143.36512H298.66496V586.24h-66.56v212.48512H586.24V1013.76H1008.64v-424.96h-422.4z m355.84 358.4h-289.28v-291.84H942.08v291.84z" p-id="1154" fill="#ffffff"></path></svg>

+ 25 - 0
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue

@@ -63,6 +63,12 @@
             </div>
             <div class="handler-item-text">触发器</div>
           </div>
+          <div class="handler-item" @click="addNode(NodeType.CHILD_PROCESS_NODE)">
+            <div class="handler-item-icon child-process">
+              <span class="iconfont icon-size icon-child-process"></span>
+            </div>
+            <div class="handler-item-text">子流程</div>
+          </div>
         </div>
         <template #reference>
           <div class="add-icon"><Icon icon="ep:plus" /></div>
@@ -283,6 +289,25 @@ const addNode = (type: number) => {
     }
     emits('update:childNode', data)
   }
+  if (type === NodeType.CHILD_PROCESS_NODE) {
+    const data: SimpleFlowNode = {
+      id: 'Activity_' + generateUUID(),
+      name: NODE_DEFAULT_NAME.get(NodeType.CHILD_PROCESS_NODE) as string,
+      showText: '',
+      type: NodeType.CHILD_PROCESS_NODE,
+      childNode: props.childNode,
+      childProcessSetting: {
+        calledProcessDefinitionKey: '',
+        calledProcessDefinitionName: '',
+        async: false,
+        skipStartUserNode: false,
+        startUserSetting: {
+          type: 1
+        }
+      }
+    }
+    emits('update:childNode', data)
+  }
 }
 </script>
 

+ 9 - 2
src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue

@@ -54,12 +54,18 @@
     :flow-node="currentNode"
     @update:flow-node="handleModelValueUpdate"
   />
-   <!-- 触发器节点 -->
-   <TriggerNode
+  <!-- 触发器节点 -->
+  <TriggerNode
     v-if="currentNode && currentNode.type === NodeType.TRIGGER_NODE"
     :flow-node="currentNode"
     @update:flow-node="handleModelValueUpdate"
   />
+  <!-- 子流程节点 -->
+  <ChildProcessNode
+    v-if="currentNode && currentNode.type === NodeType.CHILD_PROCESS_NODE"
+    :flow-node="currentNode"
+    @update:flow-node="handleModelValueUpdate"
+  />
   <!-- 递归显示孩子节点  -->
   <ProcessNodeTree
     v-if="currentNode && currentNode.childNode"
@@ -85,6 +91,7 @@ import InclusiveNode from './nodes/InclusiveNode.vue'
 import DelayTimerNode from './nodes/DelayTimerNode.vue'
 import RouterNode from './nodes/RouterNode.vue'
 import TriggerNode from './nodes/TriggerNode.vue'
+import ChildProcessNode from './nodes/ChildProcessNode.vue'
 import { SimpleFlowNode, NodeType } from './consts'
 import { useWatchNode } from './node'
 defineOptions({

+ 37 - 2
src/components/SimpleProcessDesignerV2/src/consts.ts

@@ -38,6 +38,11 @@ export enum NodeType {
    */
   TRIGGER_NODE = 15,
 
+  /**
+   * 子流程节点
+   */
+  CHILD_PROCESS_NODE = 20,
+
   /**
    * 条件节点
    */
@@ -128,6 +133,8 @@ export interface SimpleFlowNode {
   reasonRequire?: boolean
   // 触发器设置
   triggerSetting?: TriggerSetting
+  // 子流程
+  childProcessSetting?: ChildProcessSetting
 }
 // 候选人策略枚举 ( 用于审批节点。抄送节点 )
 export enum CandidateStrategy {
@@ -512,6 +519,7 @@ NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器')
 NODE_DEFAULT_TEXT.set(NodeType.ROUTER_BRANCH_NODE, '请设置路由节点')
 NODE_DEFAULT_TEXT.set(NodeType.TRIGGER_NODE, '请设置触发器')
 NODE_DEFAULT_TEXT.set(NodeType.TRANSACTOR_NODE, '请设置办理人')
+NODE_DEFAULT_TEXT.set(NodeType.CHILD_PROCESS_NODE, '请设置子流程')
 
 export const NODE_DEFAULT_NAME = new Map<number, string>()
 NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
@@ -522,6 +530,7 @@ NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器')
 NODE_DEFAULT_NAME.set(NodeType.ROUTER_BRANCH_NODE, '路由分支')
 NODE_DEFAULT_NAME.set(NodeType.TRIGGER_NODE, '触发器')
 NODE_DEFAULT_NAME.set(NodeType.TRANSACTOR_NODE, '办理人')
+NODE_DEFAULT_NAME.set(NodeType.CHILD_PROCESS_NODE, '子流程')
 
 // 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
 export const CANDIDATE_STRATEGY: DictDataVO[] = [
@@ -788,12 +797,38 @@ export type FormTriggerSetting = {
   // 更新表单字段配置
   updateFormFields?: Record<string, any>,
   // 删除表单字段配置
-  deleteFields?: string[] 
+  deleteFields?: string[]
 }
 
 export const TRIGGER_TYPES: DictDataVO[] = [
   { label: 'HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST },
   { label: '异步 HTTP 请求', value: TriggerTypeEnum.ASYNC_HTTP_REQUEST },
   { label: '修改表单数据', value: TriggerTypeEnum.FORM_UPDATE },
-  { label: '删除表单数据', value: TriggerTypeEnum.FORM_DELETE } 
+  { label: '删除表单数据', value: TriggerTypeEnum.FORM_DELETE }
 ]
+
+/**
+ * 子流程节点结构定义
+ */
+export type ChildProcessSetting = {
+  calledProcessDefinitionKey: string
+  calledProcessDefinitionName: string
+  async: boolean,
+  inVariables?: IOParameter[],
+  outVariables?: IOParameter[],
+  skipStartUserNode: boolean,
+  startUserSetting: StartUserSetting,
+}
+
+export type IOParameter = {
+  source: string
+  sourceExpression: string
+  target: string
+  targetExpression: string
+}
+
+export type StartUserSetting = {
+  type: number
+  formField?: string
+  emptyType?: number
+}

+ 341 - 0
src/components/SimpleProcessDesignerV2/src/nodes-config/ChildProcessNodeConfig.vue

@@ -0,0 +1,341 @@
+<template>
+  <el-drawer
+    :append-to-body="true"
+    v-model="settingVisible"
+    :show-close="false"
+    :size="550"
+    :before-close="saveConfig"
+  >
+    <template #header>
+      <div class="config-header">
+        <input
+          v-if="showInput"
+          type="text"
+          class="config-editable-input"
+          @blur="blurEvent()"
+          v-mountedFocus
+          v-model="nodeName"
+          :placeholder="nodeName"
+        />
+        <div v-else class="node-name">
+          {{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
+        </div>
+        <div class="divide-line"></div>
+      </div>
+    </template>
+    <el-tabs type="border-card" v-model="activeTabName">
+      <el-tab-pane label="子流程" name="child">
+        <div>
+          <el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
+            <el-form-item label="是否异步" prop="async">
+              <el-switch
+                v-model="configForm.async"
+                active-text="异步"
+                inactive-text="不异步"
+              />
+            </el-form-item>
+            <el-form-item label="选择子流程" prop="calledProcessDefinitionKey">
+              <el-select
+                v-model="configForm.calledProcessDefinitionKey"
+                clearable
+                @change="handleCalledElementChange"
+              >
+                <el-option
+                  v-for="(item, index) in childProcessOptions"
+                  :key="index"
+                  :label="item.name"
+                  :value="item.key"
+                />
+              </el-select>
+            </el-form-item>
+            <el-form-item label="是否自动跳过子流程发起节点" prop="skipStartUserNode">
+              <el-switch
+                v-model="configForm.skipStartUserNode"
+                active-text="跳过"
+                inactive-text="不跳过"
+              />
+            </el-form-item>
+            <el-form-item label="主→子变量传递" prop="inVariables">
+              <div class="flex pt-2" v-for="(item, index) in configForm.inVariables" :key="index">
+                <div class="mr-2">
+                  <el-form-item
+                    :prop="`inVariables.${index}.source`"
+                    :rules="{
+                      required: true,
+                      message: '变量不能为空',
+                      trigger: 'blur'
+                    }"
+                  >
+                    <el-select class="w-200px!" v-model="item.source">
+                      <el-option
+                        v-for="(field, fIdx) in formFieldOptions"
+                        :key="fIdx"
+                        :label="field.title"
+                        :value="field.field"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </div>
+                <div class="mr-2">
+                  <el-form-item
+                    :prop="`inVariables.${index}.target`"
+                    :rules="{
+                      required: true,
+                      message: '变量不能为空',
+                      trigger: 'blur'
+                    }"
+                  >
+                    <el-select class="w-200px!" v-model="item.target">
+                      <el-option
+                        v-for="(field, fIdx) in childFormFieldOptions"
+                        :key="fIdx"
+                        :label="field.title"
+                        :value="field.field"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </div>
+                <div class="mr-1 flex items-center">
+                  <Icon
+                    icon="ep:delete"
+                    :size="18"
+                    @click="deleteVariable(configForm.inVariables, index)"
+                  />
+                </div>
+              </div>
+              <el-button type="primary" text @click="addVariable(configForm.inVariables)">
+                <Icon icon="ep:plus" class="mr-5px" />添加一行
+              </el-button>
+            </el-form-item>
+            <el-form-item
+              v-if="configForm.async === false"
+              label="子→主变量传递"
+              prop="outVariables"
+            >
+              <div class="flex pt-2" v-for="(item, index) in configForm.outVariables" :key="index">
+                <div class="mr-2">
+                  <el-form-item
+                    :prop="`outVariables.${index}.source`"
+                    :rules="{
+                      required: true,
+                      message: '变量不能为空',
+                      trigger: 'blur'
+                    }"
+                  >
+                    <el-select class="w-200px!" v-model="item.source">
+                      <el-option
+                        v-for="(field, fIdx) in childFormFieldOptions"
+                        :key="fIdx"
+                        :label="field.title"
+                        :value="field.field"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </div>
+                <div class="mr-2">
+                  <el-form-item
+                    :prop="`outVariables.${index}.target`"
+                    :rules="{
+                      required: true,
+                      message: '变量不能为空',
+                      trigger: 'blur'
+                    }"
+                  >
+                    <el-select class="w-200px!" v-model="item.target">
+                      <el-option
+                        v-for="(field, fIdx) in formFieldOptions"
+                        :key="fIdx"
+                        :label="field.title"
+                        :value="field.field"
+                      />
+                    </el-select>
+                  </el-form-item>
+                </div>
+                <div class="mr-1 flex items-center">
+                  <Icon
+                    icon="ep:delete"
+                    :size="18"
+                    @click="deleteVariable(configForm.outVariables, index)"
+                  />
+                </div>
+              </div>
+              <el-button type="primary" text @click="addVariable(configForm.outVariables)">
+                <Icon icon="ep:plus" class="mr-5px" />添加一行
+              </el-button>
+            </el-form-item>
+            <el-form-item label="子流程发起人" prop="startUserType">
+              <el-radio-group v-model="configForm.startUserType">
+                <el-radio :value="1">同主流程发起人</el-radio>
+                <el-radio :value="2">表单</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item
+              v-if="configForm.startUserType === 2"
+              label="当子流程发起人为空时"
+              prop="startUserType"
+            >
+              <el-radio-group v-model="configForm.startUserEmptyType">
+                <el-radio :value="1">同主流程发起人</el-radio>
+                <el-radio :value="2">子流程管理员</el-radio>
+                <el-radio :value="3">主流程管理员</el-radio>
+              </el-radio-group>
+            </el-form-item>
+            <el-form-item
+              v-if="configForm.startUserType === 2"
+              label="发起人表单"
+              prop="startUserFormField"
+            >
+              <el-select class="w-200px!" v-model="configForm.startUserFormField">
+                <el-option
+                  v-for="(field, fIdx) in formFieldOptions"
+                  :key="fIdx"
+                  :label="field.title"
+                  :value="field.field"
+                />
+              </el-select>
+            </el-form-item>
+          </el-form>
+        </div>
+      </el-tab-pane>
+    </el-tabs>
+    <template #footer>
+      <el-divider />
+      <div>
+        <el-button type="primary" @click="saveConfig">确 定</el-button>
+        <el-button @click="closeDrawer">取 消</el-button>
+      </div>
+    </template>
+  </el-drawer>
+</template>
+<script setup lang="ts">
+import { getModelList } from '@/api/bpm/model'
+import { getForm } from '@/api/bpm/form'
+import { SimpleFlowNode, NodeType } from '../consts'
+import { useWatchNode, useDrawer, useNodeName, useFormFieldsAndStartUser } from '../node'
+import { parseFormFields } from '@/components/FormCreate/src/utils'
+defineOptions({
+  name: 'ChildProcessNodeConfig'
+})
+const props = defineProps({
+  flowNode: {
+    type: Object as () => SimpleFlowNode,
+    required: true
+  }
+})
+// 抽屉配置
+const { settingVisible, closeDrawer, openDrawer } = useDrawer()
+// 当前节点
+const currentNode = useWatchNode(props)
+// 节点名称
+const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.CHILD_PROCESS_NODE)
+// 激活的 Tab 标签页
+const activeTabName = ref('child')
+// 子流程表单配置
+const formRef = ref() // 表单 Ref
+// 表单校验规则
+const formRules = reactive({
+  async: [{ required: true, message: '是否异步不能为空', trigger: 'change' }],
+  calledProcessDefinitionKey: [{ required: true, message: '子流程不能为空', trigger: 'change' }],
+  skipStartUserNode: [
+    { required: true, message: '是否自动跳过子流程发起节点不能为空', trigger: 'change' }
+  ],
+  startUserType: [{ required: true, message: '子流程发起人不能为空', trigger: 'change' }],
+  startUserEmptyType: [
+    { required: true, message: '当子流程发起人为空时不能为空', trigger: 'change' }
+  ],
+  startUserFormField: [{ required: true, message: '发起人表单不能为空', trigger: 'change' }]
+})
+const configForm = ref({
+  calledProcessDefinitionKey: '',
+  skipStartUserNode: false,
+  inVariables: [],
+  outVariables: [],
+  startUserType: 1,
+  startUserEmptyType: 1,
+  startUserFormField: ''
+})
+const childProcessOptions = ref()
+const formFieldOptions = useFormFieldsAndStartUser()
+const childFormFieldOptions = ref()
+
+// 保存配置
+const saveConfig = async () => {
+  activeTabName.value = 'child'
+  if (!formRef) return false
+  const valid = await formRef.value.validate()
+  if (!valid) return false
+  const childInfo = childProcessOptions.value.find(
+    (option) => option.key === configForm.value.calledProcessDefinitionKey
+  )
+  currentNode.value.name = nodeName.value!
+  if (currentNode.value.childProcessSetting) {
+    currentNode.value.childProcessSetting.async = configForm.value.async
+    currentNode.value.childProcessSetting.calledProcessDefinitionKey = childInfo.key
+    currentNode.value.childProcessSetting.calledProcessDefinitionName = childInfo.name
+    currentNode.value.childProcessSetting.skipStartUserNode = configForm.value.skipStartUserNode
+    currentNode.value.childProcessSetting.inVariables = configForm.value.inVariables
+    currentNode.value.childProcessSetting.outVariables = configForm.value.outVariables
+    currentNode.value.childProcessSetting.startUserSetting.type = configForm.value.startUserType
+    currentNode.value.childProcessSetting.startUserSetting.emptyType =
+      configForm.value.startUserEmptyType
+    currentNode.value.childProcessSetting.startUserSetting.formField =
+      configForm.value.startUserFormField
+  }
+  currentNode.value.showText = `调用子流程:${childInfo.name}`
+  settingVisible.value = false
+  return true
+}
+// 显示子流程节点配置, 由父组件传过来
+const showChildProcessNodeConfig = (node: SimpleFlowNode) => {
+  nodeName.value = node.name
+  if (node.childProcessSetting) {
+    configForm.value.async =
+      node.childProcessSetting.async
+    configForm.value.calledProcessDefinitionKey =
+      node.childProcessSetting.calledProcessDefinitionKey
+    configForm.value.skipStartUserNode = node.childProcessSetting.skipStartUserNode
+    configForm.value.inVariables = node.childProcessSetting.inVariables
+    configForm.value.outVariables = node.childProcessSetting.outVariables
+    configForm.value.startUserType = node.childProcessSetting.startUserSetting.type
+    configForm.value.startUserEmptyType = node.childProcessSetting.startUserSetting.emptyType ?? 1
+    configForm.value.startUserFormField = node.childProcessSetting.startUserSetting.formField ?? ''
+  }
+  loadFormInfo()
+}
+
+defineExpose({ openDrawer, showChildProcessNodeConfig }) // 暴露方法给父组件
+
+const addVariable = (arr) => {
+  arr.push({
+    source: '',
+    target: ''
+  })
+}
+const deleteVariable = (arr, index: number) => {
+  arr.splice(index, 1)
+}
+const handleCalledElementChange = () => {
+  configForm.value.inVariables = []
+  configForm.value.outVariables = []
+  loadFormInfo()
+}
+const loadFormInfo = async () => {
+  const childInfo = childProcessOptions.value.find(
+    (option) => option.key === configForm.value.calledProcessDefinitionKey
+  )
+  const formInfo = await getForm(childInfo.formId)
+  childFormFieldOptions.value = []
+  if (formInfo.fields) {
+    formInfo.fields.forEach((fieldStr: string) => {
+      parseFormFields(JSON.parse(fieldStr), childFormFieldOptions.value)
+    })
+  }
+  console.log(childFormFieldOptions.value)
+}
+
+onMounted(async () => {
+  childProcessOptions.value = await getModelList(undefined)
+})
+</script>
+
+<style lang="scss" scoped></style>

+ 104 - 0
src/components/SimpleProcessDesignerV2/src/nodes/ChildProcessNode.vue

@@ -0,0 +1,104 @@
+<template>
+  <div class="node-wrapper">
+    <div class="node-container">
+      <div
+        class="node-box"
+        :class="[
+          { 'node-config-error': !currentNode.showText },
+          `${useTaskStatusClass(currentNode?.activityStatus)}`
+        ]"
+      >
+        <div class="node-title-container">
+          <div
+            :class="`node-title-icon ${currentNode.childProcessSetting?.async === true ? 'async-child-process' : 'child-process'}`"
+          >
+            <span
+              :class="`iconfont ${currentNode.childProcessSetting?.async === true ? 'icon-async-child-process' : 'icon-child-process'}`"
+            >
+            </span>
+          </div>
+          <input
+            v-if="!readonly && showInput"
+            type="text"
+            class="editable-title-input"
+            @blur="blurEvent()"
+            v-mountedFocus
+            v-model="currentNode.name"
+            :placeholder="currentNode.name"
+          />
+          <div v-else class="node-title" @click="clickTitle">
+            {{ currentNode.name }}
+          </div>
+        </div>
+        <div class="node-content" @click="openNodeConfig">
+          <div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
+            {{ currentNode.showText }}
+          </div>
+          <div class="node-text" v-else>
+            {{ NODE_DEFAULT_TEXT.get(NodeType.CHILD_PROCESS_NODE) }}
+          </div>
+          <Icon v-if="!readonly" icon="ep:arrow-right-bold" />
+        </div>
+        <div v-if="!readonly" class="node-toolbar">
+          <div class="toolbar-icon"
+            ><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
+          /></div>
+        </div>
+      </div>
+
+      <!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
+      <NodeHandler
+        v-if="currentNode"
+        v-model:child-node="currentNode.childNode"
+        :current-node="currentNode"
+      />
+    </div>
+    <ChildProcessNodeConfig
+      v-if="!readonly && currentNode"
+      ref="nodeSetting"
+      :flow-node="currentNode"
+    />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
+import NodeHandler from '../NodeHandler.vue'
+import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
+import ChildProcessNodeConfig from '../nodes-config/ChildProcessNodeConfig.vue'
+defineOptions({
+  name: 'ChildProcessNode'
+})
+const props = defineProps({
+  flowNode: {
+    type: Object as () => SimpleFlowNode,
+    required: true
+  }
+})
+// 定义事件,更新父组件。
+const emits = defineEmits<{
+  'update:flowNode': [node: SimpleFlowNode | undefined]
+}>()
+// 是否只读
+const readonly = inject<Boolean>('readonly')
+// 监控节点的变化
+const currentNode = useWatchNode(props)
+// 节点名称编辑
+const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.CHILD_PROCESS_NODE)
+const nodeSetting = ref()
+// 打开节点配置
+const openNodeConfig = () => {
+  if (readonly) {
+    return
+  }
+  nodeSetting.value.showChildProcessNodeConfig(currentNode.value)
+  nodeSetting.value.openDrawer()
+}
+
+// 删除节点。更新当前节点为孩子节点
+const deleteNode = () => {
+  emits('update:flowNode', currentNode.value.childNode)
+}
+</script>
+
+<style scoped></style>

二進制
src/components/SimpleProcessDesignerV2/theme/iconfont.ttf


二進制
src/components/SimpleProcessDesignerV2/theme/iconfont.woff


二進制
src/components/SimpleProcessDesignerV2/theme/iconfont.woff2


+ 24 - 0
src/components/SimpleProcessDesignerV2/theme/simple-process-designer.scss

@@ -181,6 +181,14 @@
     color: #330099;
   }
 
+  .child-process {
+    color: #996633;
+  }
+
+  .async-child-process {
+    color: #006666;
+  }
+
   .handler-item-text {
     margin-top: 4px;
     width: 80px;
@@ -302,6 +310,14 @@
           &.transactor-task {
             color: #330099;
           }
+
+          &.child-process {
+            color: #996633;
+          }
+
+          &.async-child-process {
+            color: #006666;
+          }
         }
 
         .node-title {
@@ -800,3 +816,11 @@
 .icon-parallel:before {
   content: "\e688";
 }
+
+.icon-async-child-process:before {
+  content: "\e6f2";
+}
+
+.icon-child-process:before {
+  content: "\e6c1";
+}

+ 19 - 18
src/views/bpm/processInstance/create/ProcessDefinitionDetail.vue

@@ -157,24 +157,25 @@ const initProcessInfo = async (row: any, formVariables?: any) => {
 }
 
 /** 预测流程节点会因为输入的参数值而产生新的预测结果值,所以需重新预测一次 */
-watch(
-  detailForm.value,
-  (newValue) => {
-    if (newValue && Object.keys(newValue.value).length > 0) {
-      // 记录之前的节点审批人
-      tempStartUserSelectAssignees.value = startUserSelectAssignees.value
-      startUserSelectAssignees.value = {}
-      // 加载最新的审批详情
-      getApprovalDetail({
-        id: props.selectProcessDefinition.id,
-        processVariablesStr: JSON.stringify(newValue.value) // 解决 GET 无法传递对象的问题,后端 String 再转 JSON
-      })
-    }
-  },
-  {
-    immediate: true
-  }
-)
+// TODO @芋艿:这里我执行填写表单的时候不知道为啥一直报错,先注释了
+// watch(
+//   detailForm.value,
+//   (newValue) => {
+//     if (newValue && Object.keys(newValue.value).length > 0) {
+//       // 记录之前的节点审批人
+//       tempStartUserSelectAssignees.value = startUserSelectAssignees.value
+//       startUserSelectAssignees.value = {}
+//       // 加载最新的审批详情
+//       getApprovalDetail({
+//         id: props.selectProcessDefinition.id,
+//         processVariablesStr: JSON.stringify(newValue.value) // 解决 GET 无法传递对象的问题,后端 String 再转 JSON
+//       })
+//     }
+//   },
+//   {
+//     immediate: true
+//   }
+// )
 
 /** 获取审批详情 */
 const getApprovalDetail = async (row: any) => {

+ 2 - 1
src/views/bpm/processInstance/detail/ProcessInstanceSimpleViewer.vue

@@ -85,7 +85,8 @@ const setSimpleModelNodeTaskStatus = (
   if (
     simpleModel.type === NodeType.START_USER_NODE ||
     simpleModel.type === NodeType.USER_TASK_NODE ||
-    simpleModel.type === NodeType.TRANSACTOR_NODE
+    simpleModel.type === NodeType.TRANSACTOR_NODE ||
+    simpleModel.type === NodeType.CHILD_PROCESS_NODE
   ) {
     simpleModel.activityStatus = TaskStatusEnum.NOT_START
     if (rejectedTaskActivityIds.includes(simpleModel.id)) {

+ 5 - 1
src/views/bpm/processInstance/detail/ProcessInstanceTimeline.vue

@@ -181,6 +181,7 @@ import conditionSvg from '@/assets/svgs/bpm/condition.svg'
 import parallelSvg from '@/assets/svgs/bpm/parallel.svg'
 import finishSvg from '@/assets/svgs/bpm/finish.svg'
 import transactorSvg from '@/assets/svgs/bpm/transactor.svg'
+import childProcessSvg from '@/assets/svgs/bpm/child-process.svg'
 
 defineOptions({ name: 'BpmProcessInstanceTimeline' })
 withDefaults(
@@ -249,7 +250,9 @@ const nodeTypeSvgMap = {
   // 条件分支节点
   [NodeType.CONDITION_NODE]: { color: '#14bb83', svg: conditionSvg },
   // 并行分支节点
-  [NodeType.PARALLEL_BRANCH_NODE]: { color: '#14bb83', svg: parallelSvg }
+  [NodeType.PARALLEL_BRANCH_NODE]: { color: '#14bb83', svg: parallelSvg },
+  // 子流程节点
+  [NodeType.CHILD_PROCESS_NODE]: { color: '#14bb83', svg: childProcessSvg },
 }
 
 // 只有只有状态是 -1、0、1 才展示头像右小角状态小icon
@@ -269,6 +272,7 @@ const getApprovalNodeIcon = (taskStatus: number, nodeType: NodeType) => {
     nodeType === NodeType.START_USER_NODE ||
     nodeType === NodeType.USER_TASK_NODE ||
     nodeType === NodeType.TRANSACTOR_NODE ||
+    nodeType === NodeType.CHILD_PROCESS_NODE ||
     nodeType === NodeType.END_EVENT_NODE
   ) {
     return statusIconMap[taskStatus]?.icon