Forráskód Böngészése

【功能新增】Simple 设计器新增触发器节点

jason 6 hónapja
szülő
commit
b7329b0c44

+ 7 - 9
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue

@@ -51,15 +51,13 @@
             </div>
             <div class="handler-item-text">路由分支</div>
           </div>
-          <!--  TODO 触发器
-            <div class="handler-item" @click="addNode(NodeType.TRIGGER_NODE)">
-              <div class="handler-item-icon trigger">
-                <span class="iconfont icon-size icon-trigger"></span>
-              </div>
-              <div class="handler-item-text">触发器</div>
+          <div class="handler-item" @click="addNode(NodeType.TRIGGER_NODE)">
+            <div class="handler-item-icon trigger">
+              <span class="iconfont icon-size icon-trigger"></span>
             </div>
-           -->
-          </div> 
+            <div class="handler-item-text">触发器</div>
+          </div>
+        </div> 
         <template #reference>
           <div class="add-icon"><Icon icon="ep:plus" /></div>
         </template>
@@ -272,7 +270,7 @@ const addNode = (type: number) => {
   if (type === NodeType.TRIGGER_NODE) {
     const data: SimpleFlowNode = {
       id: 'Activity_' + generateUUID(),
-      name: NODE_DEFAULT_NAME.get(NodeType.ROUTER_BRANCH_NODE) as string,
+      name: NODE_DEFAULT_NAME.get(NodeType.TRIGGER_NODE) as string,
       showText: '',
       type: NodeType.TRIGGER_NODE,
       childNode: props.childNode

+ 7 - 0
src/components/SimpleProcessDesignerV2/src/ProcessNodeTree.vue

@@ -49,6 +49,12 @@
     v-if="currentNode && currentNode.type === NodeType.ROUTER_BRANCH_NODE"
     :flow-node="currentNode"
     @update:flow-node="handleModelValueUpdate"
+  />
+   <!-- 触发器节点 -->
+   <TriggerNode
+    v-if="currentNode && currentNode.type === NodeType.TRIGGER_NODE"
+    :flow-node="currentNode"
+    @update:flow-node="handleModelValueUpdate"
   />
   <!-- 递归显示孩子节点  -->
   <ProcessNodeTree
@@ -74,6 +80,7 @@ import ParallelNode from './nodes/ParallelNode.vue'
 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 { SimpleFlowNode, NodeType } from './consts'
 import { useWatchNode } from './node'
 defineOptions({

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

@@ -121,6 +121,8 @@ export interface SimpleFlowNode {
   signEnable?: boolean
   // 审批意见
   reasonRequire?: boolean
+  // 触发器设置
+  triggerSetting?: TriggerSetting
 }
 // 候选人策略枚举 ( 用于审批节点。抄送节点 )
 export enum CandidateStrategy {
@@ -707,3 +709,38 @@ export type RouterSetting = {
   conditionExpression: string
   conditionGroups: ConditionGroup
 }
+
+// ==================== 触发器相关定义 ==================== 
+/**
+ * 触发器节点结构定义
+ */
+export type TriggerSetting = {
+  type: TriggerTypeEnum
+  httpRequestSetting: HttpRequestTriggerSetting
+}
+
+/**
+ * 触发器类型枚举
+ */
+export enum TriggerTypeEnum {
+  /**
+   * 发送 HTTP 请求触发器
+   */
+  HTTP_REQUEST = 1,
+}
+
+/**
+ * HTTP 请求触发器结构定义
+ */
+export type HttpRequestTriggerSetting = {
+  // 请求 URL
+  url: string
+  // 请求头参数设置
+  header?: ListenerParam[] // TODO 需要重命名一下
+  // 请求体参数设置
+  body?: ListenerParam[]
+}
+
+export const TRIGGER_TYPES: DictDataVO[] = [
+  { label: 'HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST }
+]

+ 12 - 6
src/components/SimpleProcessDesignerV2/src/node.ts

@@ -137,16 +137,22 @@ export type UserTaskFormType = {
   buttonsSetting: any[]
   taskCreateListenerEnable?: boolean
   taskCreateListenerPath?: string
-  taskCreateListenerHeader?: ListenerParam[]
-  taskCreateListenerBody?: ListenerParam[]
+  taskCreateListener?: {
+    header: ListenerParam[],
+    body: ListenerParam[]
+  }
   taskAssignListenerEnable?: boolean
   taskAssignListenerPath?: string
-  taskAssignListenerHeader?: ListenerParam[]
-  taskAssignListenerBody?: ListenerParam[]
+  taskAssignListener?: {
+    header: ListenerParam[],
+    body: ListenerParam[]
+  }
   taskCompleteListenerEnable?: boolean
   taskCompleteListenerPath?: string
-  taskCompleteListenerHeader?: ListenerParam[]
-  taskCompleteListenerBody?: ListenerParam[]
+  taskCompleteListener?:{
+    header: ListenerParam[],
+    body: ListenerParam[]
+  }
   signEnable: boolean
   reasonRequire: boolean
 }

+ 140 - 0
src/components/SimpleProcessDesignerV2/src/nodes-config/TriggerNodeConfig.vue

@@ -0,0 +1,140 @@
+<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>
+    <div>
+      <el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
+        <el-form-item label="触发器类型" prop="type">
+          <el-select v-model="configForm.type">
+            <el-option
+              v-for="(item, index) in TRIGGER_TYPES"
+              :key="index"
+              :value="item.value"
+              :label="item.label"
+            />
+          </el-select>
+        </el-form-item>
+        <div
+          v-if="configForm.type === TriggerTypeEnum.HTTP_REQUEST && configForm.httpRequestSetting"
+        >
+          <el-form-item>
+            <el-alert
+              title="仅支持 POST 请求,以请求体方式接收参数"
+              type="warning"
+              show-icon
+              :closable="false"
+            />
+          </el-form-item>
+          <el-form-item label="请求地址" prop="httpRequestSetting.url">
+            <el-input v-model="configForm.httpRequestSetting.url" />
+          </el-form-item>
+          <HttpRequestParamSetting
+            :header="configForm.httpRequestSetting.header"
+            :body="configForm.httpRequestSetting.body"
+            :bind="'httpRequestSetting'"
+          />
+        </div>
+      </el-form>
+    </div>
+    <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 { SimpleFlowNode, NodeType, TriggerSetting, TRIGGER_TYPES, TriggerTypeEnum } from '../consts'
+import { useWatchNode, useDrawer, useNodeName } from '../node'
+import HttpRequestParamSetting from './components/HttpRequestParamSetting.vue'
+defineOptions({
+  name: 'TriggerNodeConfig'
+})
+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.TRIGGER_NODE)
+// 触发器表单配置
+const formRef = ref() // 表单 Ref
+// 表单校验规则
+const formRules = reactive({
+  type: [{ required: true, message: '触发器类型不能为空', trigger: 'change' }],
+  httpRequestSetting: {
+    url: [{ required: true, message: '请求地址不能为空', trigger: 'blur' }]
+  }
+})
+// 触发器配置表单数据
+const configForm = ref<TriggerSetting>({
+  type: TriggerTypeEnum.HTTP_REQUEST,
+  httpRequestSetting: {
+    url: '',
+    header: [],
+    body: []
+  }
+})
+
+// 保存配置
+const saveConfig = async () => {
+  if (!formRef) return false
+  const valid = await formRef.value.validate()
+  if (!valid) return false
+  const showText = getShowText()
+  if (!showText) return false
+  currentNode.value.showText = showText
+  currentNode.value.triggerSetting = configForm.value
+  settingVisible.value = false
+  return true
+}
+// 获取节点展示内容
+const getShowText = (): string => {
+  let showText = ''
+  if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
+    showText = `${configForm.value.httpRequestSetting.url}`
+  }
+  return showText
+}
+
+// 显示触发器节点配置, 由父组件传过来
+const showTriggerNodeConfig = (node: SimpleFlowNode) => {
+  nodeName.value = node.name
+  if (node.triggerSetting) {
+    configForm.value.type = node.triggerSetting.type
+    configForm.value.httpRequestSetting = node.triggerSetting.httpRequestSetting
+  }
+}
+
+defineExpose({ openDrawer, showTriggerNodeConfig }) // 暴露方法给父组件
+</script>
+
+<style lang="scss" scoped></style>

+ 20 - 14
src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue

@@ -627,7 +627,7 @@ const userTaskListenerRef = ref()
 
 // 保存配置
 const saveConfig = async () => {
-  activeTabName.value = 'user'
+  // activeTabName.value = 'user'
   // 设置审批节点名称
   currentNode.value.name = nodeName.value!
   // 设置审批类型
@@ -684,22 +684,22 @@ const saveConfig = async () => {
   currentNode.value.taskCreateListener = {
     enable: configForm.value.taskCreateListenerEnable ?? false,
     path: configForm.value.taskCreateListenerPath,
-    header: configForm.value.taskCreateListenerHeader,
-    body: configForm.value.taskCreateListenerBody
+    header: configForm.value.taskCreateListener?.header,
+    body: configForm.value.taskCreateListener?.body
   }
   // 指派任务监听器
   currentNode.value.taskAssignListener = {
     enable: configForm.value.taskAssignListenerEnable ?? false,
     path: configForm.value.taskAssignListenerPath,
-    header: configForm.value.taskAssignListenerHeader,
-    body: configForm.value.taskAssignListenerBody
+    header: configForm.value.taskAssignListener?.header,
+    body: configForm.value.taskAssignListener?.body
   }
   // 完成任务监听器
   currentNode.value.taskCompleteListener = {
     enable: configForm.value.taskCompleteListenerEnable ?? false,
     path: configForm.value.taskCompleteListenerPath,
-    header: configForm.value.taskCompleteListenerHeader,
-    body: configForm.value.taskCompleteListenerBody
+    header: configForm.value.taskCompleteListener?.header,
+    body: configForm.value.taskCompleteListener?.body
   }
   // 签名
   currentNode.value.signEnable = configForm.value.signEnable
@@ -760,18 +760,24 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
   // 5.1 创建任务
   configForm.value.taskCreateListenerEnable = node.taskCreateListener!.enable
   configForm.value.taskCreateListenerPath = node.taskCreateListener!.path
-  configForm.value.taskCreateListenerHeader = node.taskCreateListener?.header ?? []
-  configForm.value.taskCreateListenerBody = node.taskCreateListener?.body ?? []
+  configForm.value.taskCreateListener = {
+    header: node.taskCreateListener?.header ?? [],
+    body: node.taskCreateListener?.body ?? []
+  }
   // 5.2 指派任务
   configForm.value.taskAssignListenerEnable = node.taskAssignListener!.enable
   configForm.value.taskAssignListenerPath = node.taskAssignListener!.path
-  configForm.value.taskAssignListenerHeader = node.taskAssignListener?.header ?? []
-  configForm.value.taskAssignListenerBody = node.taskAssignListener?.body ?? []
-  // 5.3 完成任务
+  configForm.value.taskAssignListener = {
+    header: node.taskAssignListener?.header ?? [],
+    body: node.taskAssignListener?.body ?? []
+  }
+ // 5.3 完成任务
   configForm.value.taskCompleteListenerEnable = node.taskCompleteListener!.enable
   configForm.value.taskCompleteListenerPath = node.taskCompleteListener!.path
-  configForm.value.taskCompleteListenerHeader = node.taskCompleteListener?.header ?? []
-  configForm.value.taskCompleteListenerBody = node.taskCompleteListener?.body ?? []
+  configForm.value.taskCompleteListener = {
+    header: node.taskCompleteListener?.header ?? [],
+    body: node.taskCompleteListener?.body ?? []
+  }
   // 6. 签名
   configForm.value.signEnable = node?.signEnable ?? false
   // 7. 审批意见

+ 181 - 0
src/components/SimpleProcessDesignerV2/src/nodes-config/components/HttpRequestParamSetting.vue

@@ -0,0 +1,181 @@
+<template>
+  <el-form-item label="请求头">
+    <div class="flex pt-2" v-for="(item, index) in props.header" :key="index">
+      <div class="mr-2">
+        <el-form-item
+          :prop="`${bind}.header.${index}.key`"
+          :rules="{
+            required: true,
+            message: '参数名不能为空',
+            trigger: 'blur'
+          }"
+        >
+          <el-input class="w-160px" v-model="item.key" />
+        </el-form-item>
+      </div>
+      <div class="mr-2">
+        <el-select class="w-100px!" v-model="item.type">
+          <el-option
+            v-for="types in LISTENER_MAP_TYPES"
+            :key="types.value"
+            :label="types.label"
+            :value="types.value"
+          />
+        </el-select>
+      </div>
+      <div class="mr-2">
+        <el-form-item
+          :prop="`${bind}.header.${index}.value`"
+          :rules="{
+            required: true,
+            message: '参数值不能为空',
+            trigger: 'blur'
+          }"
+        >
+          <el-input
+            v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
+            class="w-160px"
+            v-model="item.value"
+          />
+        </el-form-item>
+        <el-form-item
+          :prop="`${bind}.header.${index}.value`"
+          :rules="{
+            required: true,
+            message: '参数值不能为空',
+            trigger: 'change'
+          }"
+        >
+          <el-select
+            v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
+            class="w-160px!"
+            v-model="item.value"
+          >
+            <el-option
+              v-for="(field, fIdx) in formFieldOptions"
+              :key="fIdx"
+              :label="field.title"
+              :value="field.field"
+              :disabled="!field.required"
+            />
+          </el-select>
+        </el-form-item>
+      </div>
+      <div class="mr-1 flex items-center">
+        <Icon icon="ep:delete" :size="18" @click="deleteHttpRequestParam(props.header, index)" />
+      </div>
+    </div>
+    <el-button type="primary" text @click="addHttpRequestParam(props.header)">
+      <Icon icon="ep:plus" class="mr-5px" />添加一行
+    </el-button>
+  </el-form-item>
+  <el-form-item label="请求体">
+    <div class="flex pt-2" v-for="(item, index) in props.body" :key="index">
+      <div class="mr-2">
+        <el-form-item
+          :prop="`${bind}.body.${index}.key`"
+          :rules="{
+            required: true,
+            message: '参数名不能为空',
+            trigger: 'blur'
+          }"
+        >
+          <el-input class="w-160px" v-model="item.key" />
+        </el-form-item>
+      </div>
+      <div class="mr-2">
+        <el-select class="w-100px!" v-model="item.type">
+          <el-option
+            v-for="types in LISTENER_MAP_TYPES"
+            :key="types.value"
+            :label="types.label"
+            :value="types.value"
+          />
+        </el-select>
+      </div>
+      <div class="mr-2">
+        <el-form-item
+          :prop="`${bind}.body.${index}.value`"
+          :rules="{
+            required: true,
+            message: '参数值不能为空',
+            trigger: 'blur'
+          }"
+        >
+          <el-input
+            v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
+            class="w-160px"
+            v-model="item.value"
+          />
+        </el-form-item>
+        <el-form-item
+          :prop="`${bind}.body.${index}.value`"
+          :rules="{
+            required: true,
+            message: '参数值不能为空',
+            trigger: 'change'
+          }"
+        >
+          <el-select
+            v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
+            class="w-160px!"
+            v-model="item.value"
+          >
+            <el-option
+              v-for="(field, fIdx) in formFieldOptions"
+              :key="fIdx"
+              :label="field.title"
+              :value="field.field"
+              :disabled="!field.required"
+            />
+          </el-select>
+        </el-form-item>
+      </div>
+      <div class="mr-1 flex items-center">
+        <Icon icon="ep:delete" :size="18" @click="deleteHttpRequestParam(props.body, index)" />
+      </div>
+    </div>
+    <el-button type="primary" text @click="addHttpRequestParam(props.body)">
+      <Icon icon="ep:plus" class="mr-5px" />添加一行
+    </el-button>
+  </el-form-item>
+</template>
+<script setup lang="ts">
+import { ListenerParam, LISTENER_MAP_TYPES, ListenerParamTypeEnum } from '../../consts'
+import { useFormFields } from '../../node'
+defineOptions({
+  name: 'HttpRequestParamSetting'
+})
+
+const props = defineProps({
+  header: {
+    type: Array as () => ListenerParam[],
+    required: false,
+    default: () => []
+  },
+  body: {
+    type: Array as () => ListenerParam[],
+    required: false,
+    default: () => []
+  },
+  bind: {
+    type: String,
+    required: true
+  }
+})
+
+const formFieldOptions = useFormFields()
+
+const addHttpRequestParam = (arr) => {
+  arr.push({
+    key: '',
+    type: 1,
+    value: ''
+  })
+}
+const deleteHttpRequestParam = (arr, index) => {
+  arr.splice(index, 1)
+}
+</script>
+
+<style lang="scss" scoped></style>

+ 7 - 180
src/components/SimpleProcessDesignerV2/src/nodes-config/components/UserTaskListener.vue

@@ -31,181 +31,19 @@
         >
           <el-input v-model="configForm[`task${listener.type}ListenerPath`]" />
         </el-form-item>
-        <el-form-item label="请求头">
-          <div
-            class="flex pt-2"
-            v-for="(item, index) in configForm[`task${listener.type}ListenerHeader`]"
-            :key="index"
-          >
-            <div class="mr-2">
-              <el-form-item
-                :prop="`task${listener.type}ListenerHeader.${index}.key`"
-                :rules="{
-                  required: true,
-                  message: '参数名不能为空',
-                  trigger: 'blur'
-                }"
-              >
-                <el-input class="w-160px" v-model="item.key" />
-              </el-form-item>
-            </div>
-            <div class="mr-2">
-              <el-select class="w-100px!" v-model="item.type">
-                <el-option
-                  v-for="types in LISTENER_MAP_TYPES"
-                  :key="types.value"
-                  :label="types.label"
-                  :value="types.value"
-                />
-              </el-select>
-            </div>
-            <div class="mr-2">
-              <el-form-item
-                :prop="`task${listener.type}ListenerHeader.${index}.value`"
-                :rules="{
-                  required: true,
-                  message: '参数值不能为空',
-                  trigger: 'blur'
-                }"
-              >
-                <el-input
-                  v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
-                  class="w-160px"
-                  v-model="item.value"
-                />
-              </el-form-item>
-              <el-form-item
-                :prop="`task${listener.type}ListenerHeader.${index}.value`"
-                :rules="{
-                  required: true,
-                  message: '参数值不能为空',
-                  trigger: 'change'
-                }"
-              >
-                <el-select
-                  v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
-                  class="w-160px!"
-                  v-model="item.value"
-                >
-                  <el-option
-                    v-for="(field, fIdx) in formFieldOptions"
-                    :key="fIdx"
-                    :label="field.title"
-                    :value="field.field"
-                    :disabled="!field.required"
-                  />
-                </el-select>
-              </el-form-item>
-            </div>
-            <div class="mr-1 flex items-center">
-              <Icon
-                icon="ep:delete"
-                :size="18"
-                @click="
-                  deleteTaskListenerParam(configForm[`task${listener.type}ListenerHeader`], index)
-                "
-              />
-            </div>
-          </div>
-          <el-button
-            type="primary"
-            text
-            @click="addTaskListenerParam(configForm[`task${listener.type}ListenerHeader`])"
-          >
-            <Icon icon="ep:plus" class="mr-5px" />添加一行
-          </el-button>
-        </el-form-item>
-        <el-form-item label="请求体">
-          <div
-            class="flex pt-2"
-            v-for="(item, index) in configForm[`task${listener.type}ListenerBody`]"
-            :key="index"
-          >
-            <div class="mr-2">
-              <el-form-item
-                :prop="`task${listener.type}ListenerBody.${index}.key`"
-                :rules="{
-                  required: true,
-                  message: '参数名不能为空',
-                  trigger: 'blur'
-                }"
-              >
-                <el-input class="w-160px" v-model="item.key" />
-              </el-form-item>
-            </div>
-            <div class="mr-2">
-              <el-select class="w-100px!" v-model="item.type">
-                <el-option
-                  v-for="types in LISTENER_MAP_TYPES"
-                  :key="types.value"
-                  :label="types.label"
-                  :value="types.value"
-                />
-              </el-select>
-            </div>
-            <div class="mr-2">
-              <el-form-item
-                :prop="`task${listener.type}ListenerBody.${index}.value`"
-                :rules="{
-                  required: true,
-                  message: '参数值不能为空',
-                  trigger: 'blur'
-                }"
-              >
-                <el-input
-                  v-if="item.type === ListenerParamTypeEnum.FIXED_VALUE"
-                  class="w-160px"
-                  v-model="item.value"
-                />
-              </el-form-item>
-              <el-form-item
-                :prop="`task${listener.type}ListenerBody.${index}.value`"
-                :rules="{
-                  required: true,
-                  message: '参数值不能为空',
-                  trigger: 'change'
-                }"
-              >
-                <el-select
-                  v-if="item.type === ListenerParamTypeEnum.FROM_FORM"
-                  class="w-160px!"
-                  v-model="item.value"
-                >
-                  <el-option
-                    v-for="(field, fIdx) in formFieldOptions"
-                    :key="fIdx"
-                    :label="field.title"
-                    :value="field.field"
-                    :disabled="!field.required"
-                  />
-                </el-select>
-              </el-form-item>
-            </div>
-            <div class="mr-1 flex items-center">
-              <Icon
-                icon="ep:delete"
-                :size="18"
-                @click="
-                  deleteTaskListenerParam(configForm[`task${listener.type}ListenerBody`], index)
-                "
-              />
-            </div>
-          </div>
-          <el-button
-            type="primary"
-            text
-            @click="addTaskListenerParam(configForm[`task${listener.type}ListenerBody`])"
-          >
-            <Icon icon="ep:plus" class="mr-5px" />添加一行
-          </el-button>
-        </el-form-item>
+        <HttpRequestParamSetting 
+          :header="configForm[`task${listener.type}Listener`].header"
+          :body="configForm[`task${listener.type}Listener`].body"
+          :bind="`task${listener.type}Listener`"
+        />
       </div>
     </div>
   </el-form>
 </template>
 
 <script setup lang="ts">
-import { LISTENER_MAP_TYPES, ListenerParamTypeEnum } from '../../consts'
+// import { LISTENER_MAP_TYPES, ListenerParamTypeEnum } from '../../consts'
+import HttpRequestParamSetting from './HttpRequestParamSetting.vue'
 const props = defineProps({
   modelValue: {
     type: Object,
@@ -241,17 +79,6 @@ const taskListener = ref([
   }
 ])
 
-const addTaskListenerParam = (arr) => {
-  arr.push({
-    key: '',
-    type: 1,
-    value: ''
-  })
-}
-const deleteTaskListenerParam = (arr, index) => {
-  arr.splice(index, 1)
-}
-
 const validate = async () => {
   if (!listenerFormRef) return false
   return await listenerFormRef.value.validate()

+ 97 - 0
src/components/SimpleProcessDesignerV2/src/nodes/TriggerNode.vue

@@ -0,0 +1,97 @@
+<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 trigger-node">
+            <span class="iconfont icon-trigger"></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.TRIGGER_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>
+    <TriggerNodeConfig 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 TriggerNodeConfig from '../nodes-config/TriggerNodeConfig.vue'
+
+defineOptions({
+  name: 'TriggerNode'
+})
+
+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.TRIGGER_NODE)
+
+const nodeSetting = ref()
+// 打开节点配置
+const openNodeConfig = () => {
+  if (readonly) {
+    return
+  }
+  nodeSetting.value.showTriggerNodeConfig(currentNode.value)
+  nodeSetting.value.openDrawer()
+}
+
+// 删除节点。更新当前节点为孩子节点
+const deleteNode = () => {
+  emits('update:flowNode', currentNode.value.childNode)
+}
+</script>
+
+<style lang="scss" scoped></style>