فهرست منبع

feat: 审批流支持“由发起方”进行审批

wuwenyi 4 روز پیش
والد
کامیت
7af11a18b5
3فایلهای تغییر یافته به همراه44 افزوده شده و 6 حذف شده
  1. 3 1
      src/types/flow.ts
  2. 19 1
      src/views/flow/designer/DesignerPropertyPanel.vue
  3. 22 4
      src/views/flow/designer/useFlowDesigner.ts

+ 3 - 1
src/types/flow.ts

@@ -95,8 +95,10 @@ export interface FlowSubNodeConfig {
 }
 
 export interface FlowNodeProperties {
-  assigneeType?: 'ROLE'
+  assigneeType?: 'ROLE' | 'SELF' | 'EXTERNAL'
   assigneeValue?: string
+  /** 外部指定审批人时,与 SCRM 审批人配置匹配的节点编码 */
+  nodeKey?: string
   approveMode?: 'or' | 'and'
   timeoutHours?: number
   timeoutAction?: 'remind' | 'pass' | 'reject'

+ 19 - 1
src/views/flow/designer/DesignerPropertyPanel.vue

@@ -15,10 +15,11 @@
               v-model="nodeProps.assigneeType"
               placeholder="请选择审批对象类型"
               style="width: 100%"
-              @change="nodeProps.assigneeValue = ''"
+              @change="onAssigneeTypeChange"
             >
               <el-option label="审批角色" value="ROLE" />
               <el-option label="发起人自己" value="SELF" />
+              <el-option label="外部指定(SCRM)" value="EXTERNAL" />
             </el-select>
           </el-form-item>
           <el-form-item v-if="nodeProps.assigneeType === 'ROLE'" label="审批角色">
@@ -37,6 +38,10 @@
               />
             </el-select>
           </el-form-item>
+          <el-form-item v-if="nodeProps.assigneeType === 'EXTERNAL'" label="节点编码">
+            <el-input v-model="nodeProps.nodeKey" placeholder="如 STORE_MANAGER" />
+            <div class="assignee-tip">需与 SCRM「审批人配置」中的节点编码一致</div>
+          </el-form-item>
           <el-form-item label="审批方式">
             <el-select v-model="nodeProps.approveMode" placeholder="请选择" style="width: 100%">
               <el-option label="或签(一人通过即可)" value="or" />
@@ -356,6 +361,13 @@ function getSubNodeAssigneeDisplay(sub: any): string {
   return getRoleName(sub.assigneeValue) || '未设置审批角色'
 }
 
+function onAssigneeTypeChange() {
+  // 切换审批对象类型时清空另一种类型的配置,避免残留
+  nodeProps.value.assigneeValue = ''
+  nodeProps.value.nodeKey = ''
+  saveCurrentNodeProps?.()
+}
+
 function onSubNodesEnabledChange(enabled: boolean | string | number) {
   if (enabled && !Array.isArray(nodeProps.value.subNodes)) {
     nodeProps.value.subNodes = []
@@ -456,6 +468,12 @@ function handleDragEnd() {
 </script>
 
 <style scoped>
+.assignee-tip {
+  margin-top: 4px;
+  font-size: 12px;
+  color: #909399;
+  line-height: 1.4;
+}
 .property-panel {
   width: 300px;
   border-left: 1px solid #dcdfe6;

+ 22 - 4
src/views/flow/designer/useFlowDesigner.ts

@@ -39,6 +39,7 @@ export function useFlowDesigner(lfContainer: Ref<HTMLDivElement | undefined>) {
   const nodeProps = reactive<Record<string, any>>({
     assigneeType: 'ROLE',
     assigneeValue: '',
+    nodeKey: '',
     approveMode: 'or',
     timeoutHours: undefined as number | undefined,
     timeoutAction: 'remind',
@@ -131,6 +132,7 @@ export function useFlowDesigner(lfContainer: Ref<HTMLDivElement | undefined>) {
       return {
         assigneeType: 'ROLE',
         assigneeValue: '',
+        nodeKey: '',
         approveMode: 'or',
         timeoutHours: undefined,
         timeoutAction: 'remind',
@@ -152,6 +154,7 @@ export function useFlowDesigner(lfContainer: Ref<HTMLDivElement | undefined>) {
       if (type === 'approval-node') {
         props.assigneeType = nodeProps.assigneeType ?? 'ROLE'
         props.assigneeValue = nodeProps.assigneeValue ?? ''
+        props.nodeKey = nodeProps.nodeKey ?? ''
         props.approveMode = nodeProps.approveMode ?? 'or'
         props.timeoutHours = nodeProps.timeoutHours
         props.timeoutAction = nodeProps.timeoutAction ?? 'remind'
@@ -198,7 +201,7 @@ export function useFlowDesigner(lfContainer: Ref<HTMLDivElement | undefined>) {
     Object.assign(nodeProps, getDefaultProps(data.type))
     // 兼容旧格式:approver + approveType -> 新格式
     if (data.type === 'approval-node') {
-      const supportedTypes = ['ROLE', 'SELF']
+      const supportedTypes = ['ROLE', 'SELF', 'EXTERNAL']
       let assigneeType = cloned.assigneeType ?? 'ROLE'
       let assigneeValue = cloned.assigneeValue ?? ''
       const approveMode = cloned.approveMode ?? cloned.approveType ?? 'or'
@@ -206,17 +209,18 @@ export function useFlowDesigner(lfContainer: Ref<HTMLDivElement | undefined>) {
         assigneeType = 'ROLE'
         assigneeValue = cloned.approver
       }
-      // 当前设计器只支持 ROLE / SELF,其它旧类型降级为 ROLE 并清空值
+      // 当前设计器支持 ROLE / SELF / EXTERNAL,其它旧类型降级为 ROLE 并清空值
       if (!supportedTypes.includes(assigneeType)) {
         assigneeType = 'ROLE'
         assigneeValue = ''
       }
-      // SELF 不需要审批角色值
-      if (assigneeType === 'SELF') {
+      // SELF / EXTERNAL 不需要审批角色值
+      if (assigneeType === 'SELF' || assigneeType === 'EXTERNAL') {
         assigneeValue = ''
       }
       nodeProps.assigneeType = assigneeType
       nodeProps.assigneeValue = assigneeValue
+      nodeProps.nodeKey = cloned.nodeKey ?? ''
       nodeProps.approveMode = approveMode
       nodeProps.timeoutHours = cloned.timeoutHours ?? undefined
       nodeProps.timeoutAction = cloned.timeoutAction ?? 'remind'
@@ -665,6 +669,7 @@ export function useFlowDesigner(lfContainer: Ref<HTMLDivElement | undefined>) {
     if (endNodes.length === 0) return '流程图缺少结束节点'
     if (startNodes.length > 1) return '流程图只能有一个开始节点'
     // 检查审批节点是否配置了审批对象
+    const externalNodeKeys = new Set<string>()
     for (const node of nodes) {
       if (node.type === 'approval-node' || node.type === 'approval') {
         const props = node.properties || {}
@@ -673,6 +678,19 @@ export function useFlowDesigner(lfContainer: Ref<HTMLDivElement | undefined>) {
         if (assigneeType === 'ROLE' && !assigneeValue && props.subNodesEnabled !== true) {
           return `审批节点 "${node.text || node.name}" 未选择审批角色`
         }
+        if (assigneeType === 'EXTERNAL') {
+          if (props.subNodesEnabled === true) {
+            return `审批节点 "${node.text || node.name}" 使用外部指定审批人时不支持子节点`
+          }
+          const nodeKey = String(props.nodeKey || '').trim()
+          if (!nodeKey) {
+            return `审批节点 "${node.text || node.name}" 未填写节点编码(nodeKey)`
+          }
+          if (externalNodeKeys.has(nodeKey)) {
+            return `节点编码 "${nodeKey}" 重复,请保证每个外部指定节点编码唯一`
+          }
+          externalNodeKeys.add(nodeKey)
+        }
         if (props.subNodesEnabled === true) {
           const subNodes = props.subNodes || []
           if (subNodes.length === 0) {