Parcourir la source

仿钉钉流程设计器- 新增发起人节点,去掉开始节点

jason il y a 1 an
Parent
commit
eb79ee1b77

+ 6 - 6
src/components/SimpleProcessDesignerV2/src/NodeHandler.vue

@@ -20,13 +20,13 @@
             </div>
             <div class="handler-item-text">抄送</div>
           </div>
-          <div class="handler-item" @click="addNode(NodeType.EXCLUSIVE_NODE)">
+          <div class="handler-item" @click="addNode(NodeType.CONDITION_BRANCH_NODE)">
             <div class="handler-item-icon condition">
               <span class="iconfont icon-size icon-exclusive"></span>
             </div>
             <div class="handler-item-text">条件分支</div>
           </div>
-          <div class="handler-item" @click="addNode(NodeType.PARALLEL_NODE_FORK)">
+          <div class="handler-item" @click="addNode(NodeType.PARALLEL_BRANCH_NODE)">
             <div class="handler-item-icon condition">
               <span class="iconfont icon-size icon-parallel"></span>
             </div>
@@ -107,10 +107,10 @@ const addNode = (type: number) => {
     }
     emits('update:childNode', data)
   }
-  if (type === NodeType.EXCLUSIVE_NODE) {
+  if (type === NodeType.CONDITION_BRANCH_NODE) {
     const data: SimpleFlowNode = {
       name: '条件分支',
-      type: NodeType.EXCLUSIVE_NODE,
+      type: NodeType.CONDITION_BRANCH_NODE,
       id: 'GateWay_' + generateUUID(),
       childNode: props.childNode,
       conditionNodes: [
@@ -140,10 +140,10 @@ const addNode = (type: number) => {
     }
     emits('update:childNode', data)
   }
-  if (type === NodeType.PARALLEL_NODE_FORK) {
+  if (type === NodeType.PARALLEL_BRANCH_NODE) {
     const data: SimpleFlowNode = {
       name: '并行分支',
-      type: NodeType.PARALLEL_NODE_FORK,
+      type: NodeType.PARALLEL_BRANCH_NODE,
       id: 'GateWay_' + generateUUID(),
       childNode: props.childNode,
       conditionNodes: [

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

@@ -1,7 +1,7 @@
 <template>
-  <!-- 开始节点 -->
-  <StartEventNode
-    v-if="currentNode && currentNode.type === NodeType.START_EVENT_NODE"
+  <!-- 发起人节点 -->
+  <StartUserNode
+    v-if="currentNode && currentNode.type === NodeType.START_USER_NODE"
     :flow-node="currentNode"
   />
   <!-- 审批节点 -->
@@ -19,14 +19,14 @@
   />
   <!-- 条件节点 -->
   <ExclusiveNode
-    v-if="currentNode && currentNode.type === NodeType.EXCLUSIVE_NODE"
+    v-if="currentNode && currentNode.type === NodeType.CONDITION_BRANCH_NODE"
     :flow-node="currentNode"
     @update:model-value="handleModelValueUpdate"
     @find:parent-node="findFromParentNode"
   />
   <!-- 并行节点 -->
   <ParallelNode
-    v-if="currentNode && currentNode.type === NodeType.PARALLEL_NODE_FORK"
+    v-if="currentNode && currentNode.type === NodeType.PARALLEL_BRANCH_NODE"
     :flow-node="currentNode"
     @update:model-value="handleModelValueUpdate"
     @find:parent-node="findFromParentNode"
@@ -43,7 +43,7 @@
   <EndEventNode v-if="currentNode && currentNode.type === NodeType.END_EVENT_NODE" />
 </template>
 <script setup lang="ts">
-import StartEventNode from './nodes/StartEventNode.vue'
+import StartUserNode from './nodes/StartUserNode.vue'
 import EndEventNode from './nodes/EndEventNode.vue'
 import UserTaskNode from './nodes/UserTaskNode.vue'
 import CopyTaskNode from './nodes/CopyTaskNode.vue'
@@ -90,7 +90,11 @@ const recursiveFindParentNode = (
   findNode: SimpleFlowNode,
   nodeType: number
 ) => {
-  if (!findNode || findNode.type === NodeType.START_EVENT_NODE) {
+  if (!findNode) {
+    return
+  }
+  if (findNode.type === NodeType.START_USER_NODE) {
+    nodeList.push(findNode)
     return
   }
 

+ 7 - 8
src/components/SimpleProcessDesignerV2/src/SimpleProcessDesigner.vue

@@ -37,7 +37,7 @@
 <script setup lang="ts">
 import ProcessNodeTree from './ProcessNodeTree.vue'
 import { updateBpmSimpleModel, getBpmSimpleModel } from '@/api/bpm/simple'
-import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts'
+import { SimpleFlowNode, NodeType, NodeId, NODE_DEFAULT_TEXT } from './consts'
 
 defineOptions({
   name: 'SimpleProcessDesigner'
@@ -83,7 +83,7 @@ const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNo
     if (type == NodeType.END_EVENT_NODE) {
       return
     }
-    if (type == NodeType.START_EVENT_NODE) {
+    if (type == NodeType.START_USER_NODE) {
       validateNode(node.childNode, errorNodes)
     }
 
@@ -106,7 +106,7 @@ const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNo
       validateNode(node.childNode, errorNodes)
     }
 
-    if (type == NodeType.EXCLUSIVE_NODE) {
+    if (type == NodeType.CONDITION_BRANCH_NODE) {
       conditionNodes?.forEach((item) => {
         validateNode(item, errorNodes)
       })
@@ -138,17 +138,16 @@ const zoomIn = () => {
 
 onMounted(async () => {
   const result = await getBpmSimpleModel(props.modelId)
-  console.log('the result is :', result)
   if (result) {
     processNodeTree.value = result
   } else {
     // 初始值
     processNodeTree.value = {
-      name: '开始',
-      type: NodeType.START_EVENT_NODE,
-      id: 'StartEvent', 
+      name: '发起人',
+      type: NodeType.START_USER_NODE,
+      id: NodeId.START_USER_NODE_ID,
       childNode: {
-        id: 'EndEvent',
+        id: NodeId.END_EVENT_NODE_ID,
         name: '结束',
         type: NodeType.END_EVENT_NODE
       }

+ 53 - 19
src/components/SimpleProcessDesignerV2/src/consts.ts

@@ -6,49 +6,54 @@ import { DictDataVO } from '@/api/system/dict/types'
  */
 export enum NodeType {
   /**
-   * 发起人节点
+   * 结束节点
    */
-  START_EVENT_NODE = 0,
+  END_EVENT_NODE = 1,
   /**
-   * 结束节点
+   * 发起人节点
    */
-  END_EVENT_NODE = -2,
-
+  START_USER_NODE = 10,
   /**
    * 审批人节点
    */
-  USER_TASK_NODE = 1,
+  USER_TASK_NODE = 11,
 
   /**
    * 抄送人节点
    */
-  COPY_TASK_NODE = 2,
+  COPY_TASK_NODE = 12,
 
   /**
    * 条件节点
    */
-  CONDITION_NODE = 3,
+  CONDITION_NODE = 50,
   /**
-   * 条件分支节点
+   * 条件分支节点 (对应排他网关)
    */
-  EXCLUSIVE_NODE = 4,
+  CONDITION_BRANCH_NODE = 51,
   /**
-   * 并行分支分叉节点
+   * 并行分支节点 (对应并行网关)
    */
-  PARALLEL_NODE_FORK = 5,
+  PARALLEL_BRANCH_NODE = 52,
+
   /**
-   * 并行分支聚合
+   * 包容分支节点 (对应包容网关)
    */
-  PARALLEL_NODE_JOIN = 6,
+  INCLUSIVE_BRANCH_NODE = 53
+}
+
+export enum NodeId {
   /**
-   * 包容分支分叉节点
+   * 发起人节点 Id
    */
-  INCLUSIVE_NODE_FORK = 7,
+  START_USER_NODE_ID = 'StartUserNode',
+
   /**
-   * 包容分支聚合节点
+   * 发起人节点 Id
    */
-  INCLUSIVE_NODE_JOIN = 8
+  END_EVENT_NODE_ID = 'EndEvent'
 }
+
 /**
  *  节点结构定义
  */
@@ -298,7 +303,23 @@ export enum ConditionConfigType {
    */
   RULE = 2
 }
-
+/**
+ * 表单权限的枚举
+ */
+export enum FieldPermissionType {
+  /**
+   * 只读
+   */
+  READ = '1',
+  /**
+   * 编辑
+   */
+  WRITE = '2',
+  /**
+   * 隐藏
+   */
+  NONE = '3'
+}
 /**
  * 操作按钮权限结构定义
  */
@@ -335,6 +356,7 @@ export enum OperationButtonType {
    */
   RETURN = 6
 }
+
 /**
  * 条件规则结构定义
  */
@@ -369,11 +391,13 @@ export const NODE_DEFAULT_TEXT = new Map<number, string>()
 NODE_DEFAULT_TEXT.set(NodeType.USER_TASK_NODE, '请配置审批人')
 NODE_DEFAULT_TEXT.set(NodeType.COPY_TASK_NODE, '请配置抄送人')
 NODE_DEFAULT_TEXT.set(NodeType.CONDITION_NODE, '请设置条件')
+NODE_DEFAULT_TEXT.set(NodeType.START_USER_NODE, '请设置发起人')
 
 export const NODE_DEFAULT_NAME = new Map<number, string>()
 NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
 NODE_DEFAULT_NAME.set(NodeType.COPY_TASK_NODE, '抄送人')
 NODE_DEFAULT_NAME.set(NodeType.CONDITION_NODE, '条件')
+NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人')
 
 // 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
 export const CANDIDATE_STRATEGY: DictDataVO[] = [
@@ -483,6 +507,16 @@ export const DEFAULT_BUTTON_SETTING: ButtonSetting[] = [
   { id: OperationButtonType.RETURN, displayName: '回退', enable: false }
 ]
 
+// 发起人的按钮权限。暂时定死,不可以编辑
+export const START_USER_BUTTON_SETTING: ButtonSetting[] = [
+  { id: OperationButtonType.APPROVE, displayName: '提交', enable: true },
+  { id: OperationButtonType.REJECT, displayName: '拒绝', enable: false },
+  { id: OperationButtonType.TRANSFER, displayName: '转办', enable: false },
+  { id: OperationButtonType.DELEGATE, displayName: '委派', enable: false },
+  { id: OperationButtonType.ADD_SIGN, displayName: '加签', enable: false },
+  { id: OperationButtonType.RETURN, displayName: '回退', enable: false }
+]
+
 export const MULTI_LEVEL_DEPT: DictDataVO = [
   { label: '第 1 级部门', value: 1 },
   { label: '第 2 级部门', value: 2 },

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

@@ -12,7 +12,8 @@ import {
   RejectHandlerType,
   NODE_DEFAULT_NAME,
   AssignStartUserHandlerType,
-  AssignEmptyHandlerType
+  AssignEmptyHandlerType,
+  FieldPermissionType
 } from './consts'
 export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlowNode> {
   const node = ref<SimpleFlowNode>(props.flowNode)
@@ -26,9 +27,9 @@ export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlo
 }
 
 /**
- * @description 表单数据权限配置,用于审批节点、抄送节点
+ * @description 表单数据权限配置,用于发起人节点 、审批节点、抄送节点
  */
-export function useFormFieldsPermission() {
+export function useFormFieldsPermission(defaultPermission: FieldPermissionType) {
   // 字段权限配置. 需要有 field, title,  permissioin 属性
   const fieldsPermissionConfig = ref<Array<Record<string, string>>>([])
 
@@ -66,7 +67,7 @@ export function useFormFieldsPermission() {
       fieldsPermission.push({
         field,
         title,
-        permission: '1' // 只读
+        permission: defaultPermission
       })
       // TODO 子表单 需要处理子表单字段
       // if (type === 'group' && rule.props?.rule && Array.isArray(rule.props.rule)) {
@@ -139,7 +140,6 @@ export function useNodeForm(nodeType: NodeType) {
   const configForm = ref<UserTaskFormType | CopyTaskFormType>()
   if (nodeType === NodeType.USER_TASK_NODE) {
     configForm.value = {
-      //candidateParamArray: [],
       candidateStrategy: CandidateStrategy.USER,
       approveMethod: ApproveMethodType.RRANDOM_SELECT_ONE_APPROVE,
       approveRatio: 100,
@@ -154,7 +154,6 @@ export function useNodeForm(nodeType: NodeType) {
     }
   } else {
     configForm.value = {
-      //candidateParamArray: [],
       candidateStrategy: CandidateStrategy.USER
     }
   }

+ 29 - 5
src/components/SimpleProcessDesignerV2/src/nodes-config/CopyTaskNodeConfig.vue

@@ -157,13 +157,29 @@
             <div class="field-setting-item-label"> {{ item.title }} </div>
             <el-radio-group class="field-setting-item-group" v-model="item.permission">
               <div class="item-radio-wrap">
-                <el-radio value="1" size="large" label="1"><span></span></el-radio>
+                <el-radio
+                  :value="FieldPermissionType.READ"
+                  size="large"
+                  :label="FieldPermissionType.WRITE"
+                  ><span></span
+                ></el-radio>
               </div>
               <div class="item-radio-wrap">
-                <el-radio value="2" size="large" label="2" disabled><span></span></el-radio>
+                <el-radio
+                  :value="FieldPermissionType.WRITE"
+                  size="large"
+                  :label="FieldPermissionType.WRITE"
+                  disabled
+                  ><span></span
+                ></el-radio>
               </div>
               <div class="item-radio-wrap">
-                <el-radio value="3" size="large" label="3"><span></span></el-radio>
+                <el-radio
+                  :value="FieldPermissionType.NONE"
+                  size="large"
+                  :label="FieldPermissionType.NONE"
+                  ><span></span
+                ></el-radio>
               </div>
             </el-radio-group>
           </div>
@@ -180,7 +196,13 @@
   </el-drawer>
 </template>
 <script setup lang="ts">
-import { SimpleFlowNode, CandidateStrategy, NodeType, CANDIDATE_STRATEGY } from '../consts'
+import {
+  SimpleFlowNode,
+  CandidateStrategy,
+  NodeType,
+  CANDIDATE_STRATEGY,
+  FieldPermissionType
+} from '../consts'
 import {
   useWatchNode,
   useDrawer,
@@ -208,7 +230,9 @@ const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.COPY_
 // 激活的 Tab 标签页
 const activeTabName = ref('user')
 // 表单字段权限配置
-const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission()
+const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission(
+  FieldPermissionType.READ
+)
 // 抄送人表单配置
 const formRef = ref() // 表单 Ref
 // 表单校验规则

+ 136 - 0
src/components/SimpleProcessDesignerV2/src/nodes-config/StartUserNodeConfig.vue

@@ -0,0 +1,136 @@
+<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="user">
+        <div> 待实现 </div>
+      </el-tab-pane>
+      <el-tab-pane label="表单字段权限" name="fields" v-if="formType === 10">
+        <div class="field-setting-pane">
+          <div class="field-setting-desc">字段权限</div>
+          <div class="field-permit-title">
+            <div class="setting-title-label first-title"> 字段名称 </div>
+            <div class="other-titles">
+              <span class="setting-title-label">只读</span>
+              <span class="setting-title-label">可编辑</span>
+              <span class="setting-title-label">隐藏</span>
+            </div>
+          </div>
+          <div
+            class="field-setting-item"
+            v-for="(item, index) in fieldsPermissionConfig"
+            :key="index"
+          >
+            <div class="field-setting-item-label"> {{ item.title }} </div>
+            <el-radio-group class="field-setting-item-group" v-model="item.permission">
+              <div class="item-radio-wrap">
+                <el-radio
+                  :value="FieldPermissionType.READ"
+                  size="large"
+                  :label="FieldPermissionType.READ"
+                  ><span></span
+                ></el-radio>
+              </div>
+              <div class="item-radio-wrap">
+                <el-radio
+                  :value="FieldPermissionType.WRITE"
+                  size="large"
+                  :label="FieldPermissionType.WRITE"
+                  ><span></span
+                ></el-radio>
+              </div>
+              <div class="item-radio-wrap">
+                <el-radio
+                  :value="FieldPermissionType.NONE"
+                  size="large"
+                  :label="FieldPermissionType.NONE"
+                  ><span></span
+                ></el-radio>
+              </div>
+            </el-radio-group>
+          </div>
+        </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 { SimpleFlowNode, NodeType, FieldPermissionType, START_USER_BUTTON_SETTING } from '../consts'
+import { useWatchNode, useDrawer, useNodeName, useFormFieldsPermission } from '../node'
+
+defineOptions({
+  name: 'StartUserNodeConfig'
+})
+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.COPY_TASK_NODE)
+// 激活的 Tab 标签页
+const activeTabName = ref('user')
+// 表单字段权限配置
+const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission(
+  FieldPermissionType.WRITE
+)
+
+// 保存配置
+const saveConfig = async () => {
+  activeTabName.value = 'user'
+  currentNode.value.name = nodeName.value!
+  // TODO 暂时写死。后续可以显示谁有权限可以发起
+  currentNode.value.showText = '已设置'
+  // 设置表单权限
+  currentNode.value.fieldsPermission = fieldsPermissionConfig.value
+  // 设置发起人的按钮权限
+  currentNode.value.buttonsSetting = START_USER_BUTTON_SETTING
+  console.log('currentNode.value.buttonsSetting==>', currentNode.value.buttonsSetting)
+  settingVisible.value = false
+  return true
+}
+// 显示发起人节点配置, 由父组件传过来
+const showStartUserNodeConfig = (node: SimpleFlowNode) => {
+  nodeName.value = node.name
+  // 表单字段权限
+  getNodeConfigFormFields(node.fieldsPermission)
+}
+
+defineExpose({ openDrawer, showStartUserNodeConfig }) // 暴露方法给父组件
+</script>
+
+<style lang="scss" scoped></style>

+ 24 - 6
src/components/SimpleProcessDesignerV2/src/nodes-config/UserTaskNodeConfig.vue

@@ -25,7 +25,7 @@
       </div>
     </template>
     <div class="flex flex-items-center mb-3">
-      <span class="font-size-4 mr-3">审批类型 :</span>
+      <span class="font-size-16px mr-3">审批类型 :</span>
       <el-radio-group v-model="approveType">
         <el-radio
           v-for="(item, index) in APPROVE_TYPE"
@@ -390,13 +390,28 @@
             <div class="field-setting-item-label"> {{ item.title }} </div>
             <el-radio-group class="field-setting-item-group" v-model="item.permission">
               <div class="item-radio-wrap">
-                <el-radio value="1" size="large" label="1"><span></span></el-radio>
+                <el-radio
+                  :value="FieldPermissionType.READ"
+                  size="large"
+                  :label="FieldPermissionType.READ"
+                  ><span></span
+                ></el-radio>
               </div>
               <div class="item-radio-wrap">
-                <el-radio value="2" size="large" label="2"><span></span></el-radio>
+                <el-radio
+                  :value="FieldPermissionType.WRITE"
+                  size="large"
+                  :label="FieldPermissionType.WRITE"
+                  ><span></span
+                ></el-radio>
               </div>
               <div class="item-radio-wrap">
-                <el-radio value="3" size="large" label="3"><span></span></el-radio>
+                <el-radio
+                  :value="FieldPermissionType.NONE"
+                  size="large"
+                  :label="FieldPermissionType.NONE"
+                  ><span></span
+                ></el-radio>
               </div>
             </el-radio-group>
           </div>
@@ -435,7 +450,8 @@ import {
   ASSIGN_START_USER_HANDLER_TYPES,
   TimeoutHandlerType,
   ASSIGN_EMPTY_HANDLER_TYPES,
-  AssignEmptyHandlerType
+  AssignEmptyHandlerType,
+  FieldPermissionType
 } from '../consts'
 
 import {
@@ -479,7 +495,9 @@ const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.USER_
 // 激活的 Tab 标签页
 const activeTabName = ref('user')
 // 表单字段权限设置
-const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission()
+const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFieldsPermission(
+  FieldPermissionType.READ
+)
 // 操作按钮设置
 const { buttonsSetting, btnDisplayNameEdit, changeBtnDisplayName, btnDisplayNameBlurEvent } =
   useButtonsSetting()

+ 0 - 43
src/components/SimpleProcessDesignerV2/src/nodes/StartEventNode.vue

@@ -1,43 +0,0 @@
-<template>
-  <div class="start-node-wrapper">
-    <div class="start-node-container">
-      <div class="start-node-box">
-        <Icon icon="ep:avatar" />
-        <span class="node-fixed-name" :title="currentNode.name">{{currentNode.name}}</span>
-        <Icon icon="ep:arrow-right-bold" />
-      </div>
-      <!-- 传递子节点给添加节点组件。会在子节点后面添加节点 -->
-      <NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" />
-    </div>
-  </div>
-</template>
-<script setup lang="ts">
-import NodeHandler from '../NodeHandler.vue'
-import { SimpleFlowNode }  from '../consts';
-defineOptions({
-  name: 'StartEventNode'
-})
-const props = defineProps({
-  flowNode : {
-    type: Object as () => SimpleFlowNode,
-    default: () => null
-  }
-});
-// 定义事件,更新父组件。
-const emits = defineEmits<{
-  'update:modelValue': [node: SimpleFlowNode | undefined]
-}>()
-
-const currentNode = ref<SimpleFlowNode>(props.flowNode);
-
-watch(
-  () => props.flowNode,
-  (newValue) => {
-    currentNode.value = newValue
-  }
-)
-
-</script>
-<style lang="scss" scoped>
-
-</style>

+ 79 - 0
src/components/SimpleProcessDesignerV2/src/nodes/StartUserNode.vue

@@ -0,0 +1,79 @@
+<template>
+  <div class="node-wrapper">
+    <div class="node-container">
+      <div class="node-box" :class="{ 'node-config-error': !currentNode.showText }">
+        <div class="node-title-container">
+          <div class="node-title-icon start-user"
+            ><span class="iconfont icon-start-user"></span
+          ></div>
+          <input
+            v-if="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="clickEvent">
+            {{ 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.START_USER_NODE) }}
+          </div>
+          <Icon icon="ep:arrow-right-bold" />
+        </div>
+      </div>
+      <!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
+      <NodeHandler v-if="currentNode" v-model:child-node="currentNode.childNode" />
+    </div>
+  </div>
+  <StartUserNodeConfig v-if="currentNode" ref="nodeSetting" :flow-node="currentNode" />
+</template>
+<script setup lang="ts">
+import NodeHandler from '../NodeHandler.vue'
+import { useWatchNode } from '../node'
+import { SimpleFlowNode, NODE_DEFAULT_NAME, NODE_DEFAULT_TEXT, NodeType } from '../consts'
+import StartUserNodeConfig from '../nodes-config/StartUserNodeConfig.vue'
+defineOptions({
+  name: 'StartEventNode'
+})
+const props = defineProps({
+  flowNode: {
+    type: Object as () => SimpleFlowNode,
+    default: () => null
+  }
+})
+// 定义事件,更新父组件。
+const emits = defineEmits<{
+  'update:modelValue': [node: SimpleFlowNode | undefined]
+}>()
+
+const currentNode = useWatchNode(props)
+
+// 显示节点名称输入框
+const showInput = ref(false)
+// 节点名称输入框失去焦点
+const blurEvent = () => {
+  showInput.value = false
+  currentNode.value.name =
+    currentNode.value.name || (NODE_DEFAULT_NAME.get(NodeType.START_USER_NODE) as string)
+}
+// 点击节点标题进行输入
+const clickEvent = () => {
+  showInput.value = true
+}
+const nodeSetting = ref()
+// 打开节点配置
+const openNodeConfig = () => {
+  // 把当前节点传递给配置组件
+  nodeSetting.value.showStartUserNodeConfig(currentNode.value)
+  nodeSetting.value.openDrawer()
+}
+</script>
+<style lang="scss" scoped></style>