Bläddra i källkod

refactor(EasyProcess): 重构流程设计器组件结构

将节点配置和验证逻辑抽离到helper类
统一节点操作接口并优化类型定义
删除冗余代码并添加类型注解
zhanghaijun 11 månader sedan
förälder
incheckning
3765b8597f

+ 1 - 1
src/components/business/Drawer/Drawer.vue

@@ -25,7 +25,7 @@
   </Teleport>
   </Teleport>
 </template>
 </template>
 
 
-<script setup name="Drawer">
+<script setup lang="ts" name="Drawer">
   import { onMounted, onUnmounted } from 'vue'
   import { onMounted, onUnmounted } from 'vue'
   import { Close } from '@element-plus/icons-vue'
   import { Close } from '@element-plus/icons-vue'
 
 

+ 1 - 14
src/components/business/EasyProcess/common/process.data.ts

@@ -3,6 +3,7 @@ import { EasyProcess } from './process.type'
 
 
 export const ProcessConstant = {
 export const ProcessConstant = {
   KEY_VALIDATOR: 'validator',
   KEY_VALIDATOR: 'validator',
+  KEY_PROCESS_HELP: 'process_helper',
   KEY_PROCESS_DATA: 'processData'
   KEY_PROCESS_DATA: 'processData'
 }
 }
 
 
@@ -71,17 +72,3 @@ export const nodeConfigData: Record<EasyProcess.NodeType, EasyProcess.NodeMeta>
 }
 }
 
 
 export const blankProcessData = () => nodeConfigData['start'].defaultNode()
 export const blankProcessData = () => nodeConfigData['start'].defaultNode()
-
-export const getNodeConfig = (type: EasyProcess.NodeType) => {
-  return (
-    nodeConfigData[type] || {
-      title: '未知节点',
-      bgColor: '#3CB371',
-      canAdd: false,
-      canRemoved: true,
-      hasDrawer: false,
-      icon: { name: 'condition' },
-      defaultNode: () => ({ nodeId: getUUID(), nodeName: '未知节点', nodeType: 'other', nodeData: {} })
-    }
-  )
-}

+ 13 - 9
src/components/business/EasyProcess/common/process.helper.ts

@@ -1,14 +1,18 @@
 import { EasyProcess } from './process.type'
 import { EasyProcess } from './process.type'
+import { ref } from 'vue'
 
 
 export class ProcessHelper {
 export class ProcessHelper {
-  processData: EasyProcess.NodeConfig
+  processData: ReturnType<typeof ref<EasyProcess.NodeConfig>>
 
 
   constructor(processData: EasyProcess.NodeConfig) {
   constructor(processData: EasyProcess.NodeConfig) {
-    this.processData = processData
+    this.processData = ref(processData)
   }
   }
 
 
+  public setProcessData(processData: EasyProcess.NodeConfig) {
+    this.processData.value = processData
+  }
   public getProcessData(): EasyProcess.NodeConfig {
   public getProcessData(): EasyProcess.NodeConfig {
-    return this.processData
+    return this.processData.value!
   }
   }
   /**
   /**
    * 添加子节点
    * 添加子节点
@@ -16,7 +20,7 @@ export class ProcessHelper {
    * @param node 要添加的节点
    * @param node 要添加的节点
    */
    */
   public addNode(nodeId: string, node: EasyProcess.NodeConfig): boolean {
   public addNode(nodeId: string, node: EasyProcess.NodeConfig): boolean {
-    const target = this.findNode(nodeId, this.processData)
+    const target = this.findNode(nodeId, this.getProcessData())
     if (!target) return false
     if (!target) return false
 
 
     // 根据类型处理不同结构
     // 根据类型处理不同结构
@@ -38,9 +42,9 @@ export class ProcessHelper {
    * 删除节点(递归查找)
    * 删除节点(递归查找)
    * @param nodeId 要删除的节点ID
    * @param nodeId 要删除的节点ID
    */
    */
-  public deleteNode(nodeId: string, current: EasyProcess.NodeConfig = this.processData): boolean {
+  public deleteNode(nodeId: string, current: EasyProcess.NodeConfig = this.getProcessData()): boolean {
     // 禁止删除根节点
     // 禁止删除根节点
-    if (this.processData.nodeId === nodeId) return false
+    if (this.processData.value?.nodeId === nodeId) return false
     // 删除子节点
     // 删除子节点
     if (current.childNode?.nodeId === nodeId) {
     if (current.childNode?.nodeId === nodeId) {
       current.childNode = current.childNode?.childNode ?? undefined
       current.childNode = current.childNode?.childNode ?? undefined
@@ -55,7 +59,7 @@ export class ProcessHelper {
         if (router.conditionNodes.length < before) {
         if (router.conditionNodes.length < before) {
           if (router.conditionNodes.length > 1) return true
           if (router.conditionNodes.length > 1) return true
           // 如果只剩一个条件节点,将路由节点删除
           // 如果只剩一个条件节点,将路由节点删除
-          return this.deleteNode(router.nodeId!, this.processData)
+          return this.deleteNode(router.nodeId!, this.getProcessData())
         }
         }
         for (const c of router.conditionNodes) {
         for (const c of router.conditionNodes) {
           if (this.deleteNode(nodeId, c)) return true
           if (this.deleteNode(nodeId, c)) return true
@@ -74,7 +78,7 @@ export class ProcessHelper {
    * @param nodeData 更新的字段
    * @param nodeData 更新的字段
    */
    */
   public updateNode(nodeId: string, nodeData: Record<string, unknown>) {
   public updateNode(nodeId: string, nodeData: Record<string, unknown>) {
-    const target = this.findNode(nodeId, this.processData)
+    const target = this.findNode(nodeId, this.getProcessData())
     if (!target) return false
     if (!target) return false
     target.nodeData = { ...target.nodeData, ...nodeData }
     target.nodeData = { ...target.nodeData, ...nodeData }
     return true
     return true
@@ -103,7 +107,7 @@ export class ProcessHelper {
   /**
   /**
    * 打印流程结构(调试用)
    * 打印流程结构(调试用)
    */
    */
-  public printTree(node: EasyProcess.NodeConfig = this.processData, indent = 0): void {
+  public printTree(node: EasyProcess.NodeConfig = this.getProcessData(), indent = 0): void {
     if (indent == 0) console.log('====== 流程结构 ======')
     if (indent == 0) console.log('====== 流程结构 ======')
     console.log(`${' '.repeat(indent)}- ${node.nodeType} (${node.nodeName} | ${node.nodeId})`)
     console.log(`${' '.repeat(indent)}- ${node.nodeType} (${node.nodeName} | ${node.nodeId})`)
     if (node.nodeType === 'router') {
     if (node.nodeType === 'router') {

src/components/business/EasyProcess/utils/validator.js → src/components/business/EasyProcess/common/validator.js


+ 0 - 5
src/components/business/EasyProcess/config/keys.js

@@ -1,5 +0,0 @@
-// 节点验证器KEY
-export const KEY_VALIDATOR = 'validator'
-
-// 流程数据KEY
-export const KEY_PROCESS_DATA = 'processData'

+ 9 - 17
src/components/business/EasyProcess/index.vue

@@ -22,29 +22,21 @@
 
 
 <script setup lang="ts" name="ProcessDesigner">
 <script setup lang="ts" name="ProcessDesigner">
   import NodeWrap from './node/NodeWrap.vue'
   import NodeWrap from './node/NodeWrap.vue'
-  import EndNode from './node/end/node_end.vue'
-  import { ref, watchEffect, provide, nextTick } from 'vue'
-  import { copy } from './common/common.util'
+  import EndNode from './node/base/node_end.vue'
+  import { ref, provide } from 'vue'
   import { blankProcessData, ProcessConstant } from './common/process.data'
   import { blankProcessData, ProcessConstant } from './common/process.data'
   import { Validator } from './common/validator.util'
   import { Validator } from './common/validator.util'
-  import { EasyProcess } from './common/process.type'
-
-  defineProps<{ data?: EasyProcess.NodeConfig }>()
+  import { ProcessHelper } from './common/process.helper'
 
 
   // 流程数据
   // 流程数据
-  let processData = ref({})
-  // 依赖注入: 流程数据
-  provide(ProcessConstant.KEY_PROCESS_DATA, processData)
-
-  watchEffect(() => {
-    processData.value = copy(blankProcessData())
-    nextTick(() => validate())
-  })
+  const processHelper = new ProcessHelper(blankProcessData())
+  provide<ProcessHelper>(ProcessConstant.KEY_PROCESS_HELP, processHelper)
+  const processData = computed(() => processHelper.getProcessData())
 
 
   /* ====== 节点数据验证 ====== start */
   /* ====== 节点数据验证 ====== start */
   // 注册节点验证器
   // 注册节点验证器
-  let validator = new Validator()
-  provide(ProcessConstant.KEY_VALIDATOR, validator)
+  const validator = new Validator()
+  provide<Validator>(ProcessConstant.KEY_VALIDATOR, validator)
   // 验证流程节点数据
   // 验证流程节点数据
   const validate = (callback?: (valid: boolean, messages: string[]) => void) => {
   const validate = (callback?: (valid: boolean, messages: string[]) => void) => {
     let result = validator.validate()
     let result = validator.validate()
@@ -60,7 +52,7 @@
   /** ====== 缩放 ====== end */
   /** ====== 缩放 ====== end */
 
 
   // 暴露方法给父组件使用
   // 暴露方法给父组件使用
-  defineExpose({ validate, getResult: () => copy(processData.value) })
+  defineExpose({ validate, getProcess: () => processHelper.getProcessData() })
 </script>
 </script>
 <style lang="scss" scoped>
 <style lang="scss" scoped>
   .ep-container {
   .ep-container {

+ 12 - 13
src/components/business/EasyProcess/node/base/BaseDrawer.vue

@@ -11,13 +11,23 @@
     </Drawer>
     </Drawer>
   </div>
   </div>
 </template>
 </template>
-<script setup name="BaseDrawer">
+<script setup lang="ts" name="BaseDrawer">
   import Drawer from '@/components/business/Drawer/Drawer.vue'
   import Drawer from '@/components/business/Drawer/Drawer.vue'
   import Button from '@/components/business/Button/Button.vue'
   import Button from '@/components/business/Button/Button.vue'
-  import { ref, shallowRef, defineAsyncComponent } from 'vue'
+  import { ref, shallowRef } from 'vue'
   import { nodeConfigData } from '../../common/process.data'
   import { nodeConfigData } from '../../common/process.data'
   import { copy } from '../../common/common.util.ts'
   import { copy } from '../../common/common.util.ts'
 
 
+  import DrawerOperation from '../operation/operationDrawer.vue'
+  import DrawerFault from '../fault/faultDrawer.vue'
+  import DrawerVoice from '../voice/voiceDrawer.vue'
+
+  const drawerComponents = shallowRef({
+    operation: DrawerOperation,
+    fault: DrawerFault,
+    voice: DrawerVoice
+  })
+
   // 节点数据的副本
   // 节点数据的副本
   let node = ref(null)
   let node = ref(null)
   // 节点配置数据
   // 节点配置数据
@@ -25,17 +35,6 @@
   // 是否显示配置界面
   // 是否显示配置界面
   let isShow = ref(false)
   let isShow = ref(false)
 
 
-  // 加载节点抽屉组件
-  const modules = import.meta.glob('../*/*Drawer.vue')
-  const drawerComponents = shallowRef({})
-  Object.keys(nodeConfigData).forEach((key) => {
-    let item = nodeConfigData[key]
-    if (item.hasDrawer) {
-      let component = defineAsyncComponent(modules[`../${key}/${key}Drawer.vue`])
-      drawerComponents.value[key] = component
-    }
-  })
-
   // 显示节点配置组件
   // 显示节点配置组件
   const show = (data) => {
   const show = (data) => {
     // 复制数据
     // 复制数据

+ 8 - 20
src/components/business/EasyProcess/node/base/BaseNode.vue

@@ -27,21 +27,24 @@
 </template>
 </template>
 
 
 <script setup lang="ts" name="BaseNode">
 <script setup lang="ts" name="BaseNode">
-  import { ref, shallowRef, onMounted, getCurrentInstance, defineAsyncComponent, watch, computed, onUnmounted, inject } from 'vue'
+  import { ref, shallowRef, getCurrentInstance, watch, computed, onUnmounted, inject } from 'vue'
   import BaseDrawer from './BaseDrawer.vue'
   import BaseDrawer from './BaseDrawer.vue'
   import NodeAdd from './node_add.vue'
   import NodeAdd from './node_add.vue'
   import { nodeConfigData, ProcessConstant } from '../../common/process.data'
   import { nodeConfigData, ProcessConstant } from '../../common/process.data'
   import { getUUID } from '../../common/common.util'
   import { getUUID } from '../../common/common.util'
   import { EasyProcess } from '../../common/process.type'
   import { EasyProcess } from '../../common/process.type'
   import { Validator } from '../../common/validator.util'
   import { Validator } from '../../common/validator.util'
+  import { ProcessHelper } from '../../common/process.helper'
 
 
   import NodeStart from '../start/startNode.vue'
   import NodeStart from '../start/startNode.vue'
   import NodeOperation from '../operation/operationNode.vue'
   import NodeOperation from '../operation/operationNode.vue'
   import NodeFault from '../fault/faultNode.vue'
   import NodeFault from '../fault/faultNode.vue'
   import NodeVoice from '../voice/voiceNode.vue'
   import NodeVoice from '../voice/voiceNode.vue'
+  import NodeCondition from '../condition/conditionNode.vue'
 
 
   const nodeComponents = shallowRef({
   const nodeComponents = shallowRef({
     start: NodeStart,
     start: NodeStart,
+    condition: NodeCondition,
     operation: NodeOperation,
     operation: NodeOperation,
     fault: NodeFault,
     fault: NodeFault,
     voice: NodeVoice
     voice: NodeVoice
@@ -73,16 +76,10 @@
     return result ? !result.valid : false
     return result ? !result.valid : false
   })
   })
   // ---------- 节点数据校验 --------- end
   // ---------- 节点数据校验 --------- end
-
+  const processHelper = inject<ProcessHelper>(ProcessConstant.KEY_PROCESS_HELP)
+  const removeNode = () => processHelper?.deleteNode(props.node.nodeId)
   // 节点是否可以被移除
   // 节点是否可以被移除
-  const canRemoved = computed(() => {
-    if (isLastCondition.value) return false
-    return config.value.canRemoved
-  })
-  // 是否是最后一个条件节点
-  const isLastCondition = computed<boolean>(() => {
-    return props.node.nodeType == 'condition' && props.node.isLastCondition
-  })
+  const canRemoved = computed(() => config.value.canRemoved)
 
 
   const { proxy } = getCurrentInstance()
   const { proxy } = getCurrentInstance()
 
 
@@ -90,26 +87,17 @@
   const tempNodeId = getUUID()
   const tempNodeId = getUUID()
 
 
   onUnmounted(async () => {
   onUnmounted(async () => {
-    validator?.remove(tempNodeId)
+    validator?.remove(props.node.nodeId)
     validator?.validate()
     validator?.validate()
   })
   })
 
 
   // 显示节点配置组件
   // 显示节点配置组件
   const showNodeDrawer = () => {
   const showNodeDrawer = () => {
     if (config.value.hasDrawer) {
     if (config.value.hasDrawer) {
-      if (isLastCondition()) {
-        return false
-      }
       proxy.$refs.nodeDrawer.show(props.node)
       proxy.$refs.nodeDrawer.show(props.node)
     }
     }
   }
   }
 
 
-  // 移除当前节点
-  const emit = defineEmits(['removeNode'])
-  const removeNode = () => {
-    emit('removeNode')
-  }
-
   // 更新节点配置属性
   // 更新节点配置属性
   const updateConfig = (data: EasyProcess.NodeConfig) => {
   const updateConfig = (data: EasyProcess.NodeConfig) => {
     props.node = data
     props.node = data

+ 9 - 8
src/components/business/EasyProcess/node/base/node_add.vue

@@ -14,8 +14,9 @@
 
 
 <script setup lang="ts" name="AddNode">
 <script setup lang="ts" name="AddNode">
   import { ref, computed } from 'vue'
   import { ref, computed } from 'vue'
-  import { nodeConfigData } from '../../common/process.data'
+  import { nodeConfigData, ProcessConstant } from '../../common/process.data'
   import { EasyProcess } from '../../common/process.type'
   import { EasyProcess } from '../../common/process.type'
+  import { ProcessHelper } from '../../common/process.helper'
 
 
   const props = withDefaults(defineProps<{ node: EasyProcess.NodeConfig }>(), {})
   const props = withDefaults(defineProps<{ node: EasyProcess.NodeConfig }>(), {})
 
 
@@ -43,17 +44,17 @@
   const toggleVisibility = (flag: boolean) => (showSelect.value = flag)
   const toggleVisibility = (flag: boolean) => (showSelect.value = flag)
   /** ---------- 选择添加节点 ---------- end */
   /** ---------- 选择添加节点 ---------- end */
 
 
-  // 添加节点
+  const processHelper = inject<ProcessHelper>(ProcessConstant.KEY_PROCESS_HELP)
+
   const insertNodeIntoParent = (nodeType: EasyProcess.NodeType) => {
   const insertNodeIntoParent = (nodeType: EasyProcess.NodeType) => {
     let typeConfig = nodeConfigData[nodeType]
     let typeConfig = nodeConfigData[nodeType]
-    if (typeConfig) {
-      const addNode = { ...typeConfig.defaultNode() }
-      const childNode = { ...props.node.childNode } as EasyProcess.NodeConfig
-      addNode.childNode = childNode
-      // eslint-disable-next-line vue/no-mutating-props
-      props.node.childNode = addNode
+    if (!typeConfig) {
+      ElMessage.error(`节点类型${nodeType}不存在`)
+      return
     }
     }
+    processHelper?.addNode(props.node.nodeId, typeConfig.defaultNode())
     toggleVisibility(false)
     toggleVisibility(false)
+    ElMessage.primary(`添加节点【${typeConfig.title}】成功`)
   }
   }
 </script>
 </script>
 
 

+ 1 - 1
src/components/business/EasyProcess/node/end/node_end.vue

@@ -6,7 +6,7 @@
   </div>
   </div>
 </template>
 </template>
 
 
-<script setup name="EndNode"></script>
+<script setup lang="ts" name="EndNode"></script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>
   .ep-node-end {
   .ep-node-end {

+ 9 - 32
src/components/business/EasyProcess/node/router/routerNode.vue

@@ -3,9 +3,9 @@
   <div class="ep-node-router">
   <div class="ep-node-router">
     <div class="ep-condition-node-add" @click="addCondition">添加条件</div>
     <div class="ep-condition-node-add" @click="addCondition">添加条件</div>
     <div class="ep-node-router-box">
     <div class="ep-node-router-box">
-      <div v-for="(condition, index) in props.node.conditionNodes" :key="condition.id || index" class="ep-node-router-col">
+      <div v-for="(condition, index) in props.node.conditionNodes" :key="condition.nodeId || index" class="ep-node-router-col">
         <!-- 生成节点 -->
         <!-- 生成节点 -->
-        <NodeWrap :node="condition" :conditionNodes="props.node.conditionNodes" :conditionIndex="index" @removeNode="removeCondition(index)" />
+        <NodeWrap :node="condition" />
         <!-- 用来遮挡最左列的线 -->
         <!-- 用来遮挡最左列的线 -->
         <template v-if="index == 0">
         <template v-if="index == 0">
           <div class="cover-line top-left-cover-line"></div>
           <div class="cover-line top-left-cover-line"></div>
@@ -23,39 +23,16 @@
   </div>
   </div>
 </template>
 </template>
 
 
-<script setup name="RouterNode">
+<script setup lang="ts" name="RouterNode">
   import NodeWrap from '../NodeWrap.vue'
   import NodeWrap from '../NodeWrap.vue'
   import AddNode from '../base/node_add.vue'
   import AddNode from '../base/node_add.vue'
-  import { onMounted } from 'vue'
-  import { copy } from '../../common/common.util.ts'
-  import { nodeConfigData } from '../../common/process.data'
+  import { nodeConfigData, ProcessConstant } from '../../common/process.data'
+  import { ProcessHelper } from '../../common/process.helper'
+  import { EasyProcess } from '../../common/process.type'
 
 
-  const props = defineProps({
-    node: {
-      // 传入的流程配置数据
-      type: Object,
-      default: () => {}
-    }
-  })
-  onMounted(async () => {})
-
-  // 移除条件
-  const emit = defineEmits(['removeNode'])
-  const removeCondition = (index) => {
-    let conditionNum = props.node.conditionNodes.length
-    if (conditionNum > 2) {
-      props.node.conditionNodes.splice(index, 1)
-    } else {
-      emit('removeNode')
-    }
-  }
-
-  // 添加条件分支
-  const addCondition = () => {
-    let defaultNode = copy(nodeConfigData['condition'].defaultNode)
-    let length = props.node.conditionNodes.length
-    props.node.conditionNodes.splice(length - 1, 0, defaultNode)
-  }
+  const props = withDefaults(defineProps<{ node: EasyProcess.RouterNode }>(), {})
+  const processHelper = inject<ProcessHelper>(ProcessConstant.KEY_PROCESS_HELP)
+  const addCondition = () => processHelper?.addNode(props.node.nodeId, nodeConfigData['condition'].defaultNode())
 </script>
 </script>
 
 
 <style lang="scss" scoped>
 <style lang="scss" scoped>