Pārlūkot izejas kodu

多节点审批/业务节点权限控制

Renxy 2 gadi atpakaļ
vecāks
revīzija
9fdd096997

+ 3 - 2
src/components/Flow/node/rect/mapServe.tsx

@@ -119,8 +119,9 @@ const Component = (props: any) => {
   useEffect(() => {
     if(!roleList || roleList.length <= 0) return;
     let op = []
-    roleList.forEach(item => {
-      op.push({label:item.Name, value:item.ID})
+    console.log(roleList)
+    roleList.filter(cur=>cur.RoleType == 4).forEach(item => {
+      op.push({label:`${item.Name}(${item.ID})`, value:item.ID})
     })
     setOptions(op);
     console.log(op)

+ 150 - 69
src/pages/PurchaseAdmin/PurchaseList/Detail/CommitAuditModal.js

@@ -1,66 +1,67 @@
 import React, { useEffect, useState, useRef, useMemo } from 'react';
-import { Form } from '@ant-design/compatible';
 import '@ant-design/compatible/assets/index.css';
-import { Modal, Input, Select, message } from 'antd';
+import { Modal, Input, Select, message, Cascader, Form } from 'antd';
 import { connect } from 'dva';
+import { isArray, result } from 'lodash';
+import { useForm } from 'rc-field-form';
+import { async } from '@antv/x6/lib/registry/marker/async';
 
 const { TextArea } = Input;
 const { Option } = Select;
 
 // 提交
 function CommitAuditModal(props) {
-  const { visible, onClose, onOk, form, loading, version, versionList, flowDetail } = props;
+  const { visible, onClose, onOk, loading, version, versionList, flowDetail } = props;
   const [auditId, setAuditId] = useState();
-  const handleOk = () => {
-    form.validateFields((err, fieldsValue) => {
-      if (err) return;
-      let approvalNode = flowDetail.nodes.find?.(item => item.Id == fieldsValue.node_id);
-      let serviceNode = flowDetail.nodes.find?.(
-        item => item.Id == fieldsValue.next_template_node_id
-      );
-      let params = {
-        desc: fieldsValue.desc,
-        // 审核流程id
-        flow_id: approvalNode?.flow_id || 0,
-        node_level_id: approvalNode?.flow_id ? 1 : 0,
-
-        id: version.id,
-        project_id: version.project_id,
-        cur_template_node_id: version.template_node_id * 1, // 当前节点
-        template_node_id: approvalNode?.Id, // 将要流转的节点
-        next_template_node_id: serviceNode.Id * 1, // 审核完成后的业务节点
+  const [data, setData] = useState([])
+  const [length, setLength] = useState(1)
+  const [formData, setFromData] = useState({})
+  const [form] = Form.useForm();
+  useEffect(() => {
+    const { edges, nodes } = flowDetail;
+    let Id = version.template_node_id;
+    const currentId = flowDetail.nodes.find?.(item => item.Id == Id)?.node_id
+    const data = treeData(currentId)
+    if(data.length <= 0) setAuditId(currentId)
+    setData(data)
+  }, [auditId, version.template_node_id])
 
-        // 模板id.一致就行
-        template_id: version.template_id,
-        cur_template_id: version.template_id,
-        next_template_id: version.template_id,
-      };
-      if (approvalNode?.Id) {
-        if (!approvalNode?.flow_id) {
-          message.error('审批节点未绑定审批流程!请联系管理员。');
-          return;
-        }
-      }
+  const treeData = (currentId) => {
+    const list = getNextNodes(currentId,'custom-circle');
+    const fun = (nodes) => {
+      const re = nodes?.forEach((item, idx)=>{
+        const data = getNextNodes(item.Id,'custom-circle');
+        if(data || data.length > 0) list.push(...data);
+        fun(nodes[idx].children)
+      })
+    }
+    fun(list)
+    
+    const fun2 = (list) => {
+      const parents = list.filter(item=>list.findIndex(node=>node.Id == item.parentId) == -1)
+      let translator = (parents, children) => {
+        setLength(length+1)
+        parents.forEach((parent) => {
+            children.forEach((current, index) => {
+                if (current.parentId === parent.Id) {
+                    let temp = JSON.parse(JSON.stringify(children))
+                    temp.splice(index, 1)
+                    translator([current], temp)
+                    if(!parent.children.find(item=>item.Id == current.Id))parent.children.push(current)
+                }
+            })
+        })
+      }   
+      translator(parents, list)   
+      return parents
+    } 
+    return fun2(list)
+  }
 
-      // // 判断业务节点是否允许多清单
-      // if (!serviceNode.muti_version) {
-      //   //audit_status=4 表示为清单推进后留存的副本.不计入多清单计算
-      //   let serviceVersion = versionList.find(
-      //     item => item.audit_status != 4 && item.template_node_id == serviceNode.Id
-      //   );
-      //   if (serviceVersion) {
-      //     message.error(
-      //       `流转失败!业务节点【${serviceNode.label}】为单清单节点。已存在清单【${serviceVersion.version_name}】。`
-      //     );
-      //     return;
-      //   }
-      // }
-      setAuditId();
-      onOk(params);
-    });
-  };
   const currentNodeId = useMemo(() => {
     let Id = version.template_node_id;
+    console.log("+++++++++++++++++++++", version)
+    setAuditId(currentNodeId)
     return flowDetail.nodes.find?.(item => item.Id == Id)?.node_id;
   }, [flowDetail, version]);
   /**
@@ -75,13 +76,15 @@ function CommitAuditModal(props) {
     let targetIds = edges
       .filter(edge => edge.source.cell == currentId)
       .map(item => item.target.cell);
+    edges.filter(edge =>  edge.source.cell == currentId)
     let auditNodes = nodes.filter(node => {
       if (type && node.name != type) {
         return false;
       }
       return targetIds.indexOf(node.id) != -1;
     });
-    return auditNodes || [];
+    const result = auditNodes.map(item=>{return { label:item.label, value:item.Id, Id:item.node_id,parentId:currentId, children:[]}})
+    return result || [];
   };
 
   const changeAudit = id => {
@@ -89,6 +92,92 @@ function CommitAuditModal(props) {
     setAuditId(node.node_id);
   };
 
+  const onChange = (value) => {
+    changeAudit(value[value.length-1])
+  };
+
+  const onFinish = async() => {
+    var fieldsValue = await form.validateFields();
+
+    const getFlowPath = (node) => { //[134, 135]
+      let itemData = {}
+      const Function = (curId,index) => {
+        if(!curId) return;
+        let data = {}
+        let approvalNode = flowDetail.nodes.find?.(item => item.Id == curId);
+        console.log("approvalNode==========================",approvalNode)
+        data.template_id = version.template_id;
+        data.flow_id = approvalNode?.flow_id || 0;
+        data.node_level_id = approvalNode?.flow_id ? 1 : 0;
+        data.template_node_id = approvalNode?.Id;
+        index++;
+        const res = Function(node[index], index);
+        if(res) {
+          data.flow_path = [res]
+        }
+        return data
+      }
+      itemData = Function(node[0], 0)
+      return itemData
+    }
+    let  result = Object.values(fieldsValue).map(item=> {if(item && Array.isArray(item)) return item}).filter(item=>item)
+    // console.log(result)
+    let serviceNode = flowDetail.nodes.find?.(
+      item => item.Id == fieldsValue.next_template_node_id
+    );
+    let params = {
+      desc: fieldsValue.desc,
+      // 审核流程id
+      // flow_id: approvalNode?.flow_id || 0,
+      // node_level_id: approvalNode?.flow_id ? 1 : 0,
+
+      id: version.id,
+      project_id: version.project_id,
+      cur_template_node_id: version.template_node_id * 1, // 当前节点
+      next_template_node_id: serviceNode.Id * 1, // 审核完成后的业务节点
+      // template_node_id: result[0][0], // 将要流转的节点审批节点
+      // flow_path:flow_path, //审批节点数组
+      // 模板id.一致就行
+      template_id: version.template_id,
+      cur_template_id: version.template_id,
+      next_template_id: version.template_id,
+    };
+    if(result.length <= 0){
+      //直接走业务节点
+    }else if(result.length <= 1 && result[0]?.length <= 1){
+      //单个审批节点
+      let approvalNode = flowDetail.nodes.find?.(item => item.Id == result[0][0]);
+      params.flow_id = approvalNode?.flow_id || 0;
+      params.node_level_id = approvalNode?.flow_id ? 1 : 0;
+      params.template_node_id = result[0][0]; // 将要流转的节点审批节点
+    }else{
+      //多节点审批
+      params.template_node_id = result[0][0]; // 将要流转的节点审批节点
+      params.flow_path = result.map(item=>getFlowPath(item));
+    }
+    console.log("flow_path    ",params)
+
+    
+    //   if (approvalNode?.Id) {
+    //     if (!approvalNode?.flow_id) {
+    //       message.error('审批节点未绑定审批流程!请联系管理员。');
+    //       return;
+    //     }
+    //   }
+    
+    // setAuditId();
+    form.resetFields()
+    onOk(params);
+  }
+
+  const CascaderNode = (index) => {
+    return(
+      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label={`审批节点${index}`} name={`circle${index}`} key={`circle${index}`}>
+        <Cascader style={{ width: '100%' }}  options={data} onChange={onChange} />
+      </Form.Item>
+    )
+  }
+
   return (
     <Modal
       confirmLoading={loading}
@@ -99,29 +188,21 @@ function CommitAuditModal(props) {
         setAuditId();
         onClose();
       }}
-      onOk={handleOk}
-    >
-      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="审批节点">
-        {form.getFieldDecorator('node_id')(
-          <Select style={{ width: '100%' }} onChange={changeAudit}>
-            {getNextNodes(currentNodeId, 'custom-circle').map(item => (
-              <Option key={item.Id}>{item.label}</Option>
-            ))}
-          </Select>
-        )}
-      </Form.Item>
-      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="业务节点">
-        {form.getFieldDecorator('next_template_node_id')(
+      onOk={onFinish}
+    > 
+    <Form form={form}>
+      {data.map((item, idx)=>  data.length == 1? CascaderNode(''): CascaderNode(idx) )}
+      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="业务节点" name='next_template_node_id' >
           <Select style={{ width: '100%' }}>
-            {getNextNodes(auditId || currentNodeId, auditId ? 'custom-rect' : '').map(item => (
-              <Option key={item.Id}>{item.label}</Option>
+            {getNextNodes(data.length < 0 ?currentNodeId : auditId,  'custom-rect' ).map(item => (
+              <Option key={item.value}>{item.label}</Option>
             ))}
           </Select>
-        )}
       </Form.Item>
-      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="备注信息">
-        {form.getFieldDecorator('desc')(<Input.TextArea />)}
+      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="备注信息" name="desc">
+        <Input.TextArea />
       </Form.Item>
+    </Form>
     </Modal>
   );
 }
@@ -129,4 +210,4 @@ function CommitAuditModal(props) {
 export default connect(({ xflow, detail }) => ({
   flowDetail: xflow.flowDetail,
   versionList: detail.versionList,
-}))(Form.create()(CommitAuditModal));
+}))(CommitAuditModal);

+ 143 - 0
src/pages/PurchaseAdmin/PurchaseList/Detail/CommitAuditModalNew.js

@@ -0,0 +1,143 @@
+import React, { useEffect, useState, useRef, useMemo } from 'react';
+import { Form } from '@ant-design/compatible';
+import '@ant-design/compatible/assets/index.css';
+import { Modal, Input, Select, message } from 'antd';
+import { connect } from 'dva';
+
+const { TextArea } = Input;
+const { Option } = Select;
+
+// 提交
+function CommitAuditModal(props) {
+  const { visible, onClose, onOk, form, loading, version, versionList, flowDetail } = props;
+  const [auditId, setAuditId] = useState();
+  const handleOk = () => {
+    form.validateFields((err, fieldsValue) => {
+      console.log("-------",fieldsValue )
+      if (err) return;
+      let approvalNode = flowDetail.nodes.find?.(item => item.Id == fieldsValue.node_id);
+      let serviceNode = flowDetail.nodes.find?.(
+        item => item.Id == fieldsValue.next_template_node_id
+      );
+      let params = {
+        desc: fieldsValue.desc,
+        // 审核流程id
+        flow_id: approvalNode?.flow_id || 0,
+        node_level_id: approvalNode?.flow_id ? 1 : 0,
+
+        id: version.id,
+        project_id: version.project_id,
+        cur_template_node_id: version.template_node_id * 1, // 当前节点
+        template_node_id: approvalNode?.Id, // 将要流转的节点审批节点
+        next_template_node_id: serviceNode.Id * 1, // 审核完成后的业务节点
+
+        // 模板id.一致就行
+        template_id: version.template_id,
+        cur_template_id: version.template_id,
+        next_template_id: version.template_id,
+      };
+      if (approvalNode?.Id) {
+        if (!approvalNode?.flow_id) {
+          message.error('审批节点未绑定审批流程!请联系管理员。');
+          return;
+        }
+      }
+      // // 判断业务节点是否允许多清单
+      // if (!serviceNode.muti_version) {
+      //   //audit_status=4 表示为清单推进后留存的副本.不计入多清单计算
+      //   let serviceVersion = versionList.find(
+      //     item => item.audit_status != 4 && item.template_node_id == serviceNode.Id
+      //   );
+      //   if (serviceVersion) {
+      //     message.error(
+      //       `流转失败!业务节点【${serviceNode.label}】为单清单节点。已存在清单【${serviceVersion.version_name}】。`
+      //     );
+      //     return;
+      //   }
+      // }
+
+
+      setAuditId();
+      // onOk(params);
+    });
+  };
+  const currentNodeId = useMemo(() => {
+    let Id = version.template_node_id;
+    return flowDetail.nodes.find?.(item => item.Id == Id)?.node_id;
+  }, [flowDetail, version]);
+  /**
+   *
+   * @param {*} currentId 当前节点
+   * @param {*} type 下一个节点的类型  custom-circle: 审批节点   custom-rect: 业务节点
+   * @returns
+   */
+  const getNextNodes = (currentId, type) => {
+    const { edges, nodes } = flowDetail;
+    if (!currentId) return [];
+    let targetIds = edges
+      .filter(edge => edge.source.cell == currentId)
+      .map(item => item.target.cell);
+    let auditNodes = nodes.filter(node => {
+      if (type && node.name != type) {
+        return false;
+      }
+      return targetIds.indexOf(node.id) != -1;
+    });
+    return auditNodes || [];
+  };
+
+  const changeAudit = id => {
+    let node = flowDetail.nodes.find?.(item => item.Id == id);
+    setAuditId(node.node_id);
+  };
+
+  return (
+    <Modal
+      confirmLoading={loading}
+      destroyOnClose
+      title="提交流转目标"
+      visible={visible}
+      onCancel={() => {
+        setAuditId();
+        onClose();
+      }}
+      onOk={handleOk}
+    >
+      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="审批节点">
+        {form.getFieldDecorator('node_id')(
+          <Select style={{ width: '100%' }} onChange={changeAudit}>
+            {getNextNodes(currentNodeId, 'custom-circle').map(item => (
+              <Option key={item.Id}>{item.label}</Option>
+            ))}
+          </Select>
+        )}
+      </Form.Item>
+      {/* <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="审批节点2">
+        {form.getFieldDecorator('nod_id')(
+          <Select style={{ width: '100%' }} onChange={changeAudit}>
+            {getNextNodes(auditId || currentNodeId, 'custom-circle').map(item => (
+              <Option key={item.Id}>{item.label}</Option>
+            ))}
+          </Select>
+        )}
+      </Form.Item> */}
+      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="业务节点">
+        {form.getFieldDecorator('next_template_node_id')(
+          <Select style={{ width: '100%' }}>
+            {getNextNodes(auditId || currentNodeId, auditId ? 'custom-rect' : '').map(item => (
+              <Option key={item.Id}>{item.label}</Option>
+            ))}
+          </Select>
+        )}
+      </Form.Item>
+      <Form.Item labelCol={{ span: 7 }} wrapperCol={{ span: 15 }} label="备注信息">
+        {form.getFieldDecorator('desc')(<Input.TextArea />)}
+      </Form.Item>
+    </Modal>
+  );
+}
+
+export default connect(({ xflow, detail }) => ({
+  flowDetail: xflow.flowDetail,
+  versionList: detail.versionList,
+}))(Form.create()(CommitAuditModal));

+ 6 - 2
src/pages/PurchaseAdmin/PurchaseList/Detail/FlowModal.js

@@ -1,5 +1,5 @@
 import React, { useEffect, useState, useRef, useMemo } from 'react';
-import { Modal, Input, Select, List, Row, Col, Table } from 'antd';
+import { Modal, Input, Select, List, Row, Col, Table, message } from 'antd';
 import Flow from '@/components/Flow/index';
 import { connect } from 'dva';
 import { GetTokenFromUrl, getToken } from '@/utils/utils';
@@ -54,6 +54,10 @@ function FlowModal(props) {
     }
     setNodeLoading(false);
   };
+  const handleChangeClick = (audit_status) => {
+    if(audit_status != 1) return
+    message.success('哈哈哈哈')
+  }
   const columns = useMemo(() => {
     return [
       {
@@ -89,7 +93,7 @@ function FlowModal(props) {
               txt = '已提交';
               break;
           }
-          return <span style={style}>{txt}</span>;
+          return <span style={style} onClick={()=>handleChangeClick(item.audit_status)}>{txt}</span>;
         },
       },
       {

+ 1 - 0
src/pages/PurchaseAdmin/PurchaseList/Detail/Index.js

@@ -538,6 +538,7 @@ function Detail(props) {
         const nodeId = version.template_node_id;
         if (!flowDetail?.nodes || !nodeId) return;
         const node = flowDetail.nodes.find(item => item.Id == nodeId);
+        if(node.name=='custom-circle') return ;
         if( node?.role_list && node?.role_list.length > 0){
           return node.role_list.split(",").some(id=> currentUser.roleList?.find(role=>role.ID == id))
         } 

+ 470 - 110
src/pages/PurchaseAdmin/PurchaseList/Flow/FlowDetail.json

@@ -10,10 +10,22 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "293a90b5" },
-          { "group": "right", "id": "92334433" },
-          { "group": "bottom", "id": "c2ab5849" },
-          { "group": "left", "id": "6079a903" }
+          {
+            "group": "top",
+            "id": "293a90b5"
+          },
+          {
+            "group": "right",
+            "id": "92334433"
+          },
+          {
+            "group": "bottom",
+            "id": "c2ab5849"
+          },
+          {
+            "group": "left",
+            "id": "6079a903"
+          }
         ]
       },
       "isCustom": true,
@@ -31,15 +43,27 @@
       "height": 90,
       "ports": {
         "items": [
-          { "group": "top", "id": "a61170c3" },
-          { "group": "right", "id": "821f59c0" },
-          { "group": "bottom", "id": "17360bc4" },
-          { "group": "left", "id": "15d1b217" }
+          {
+            "group": "top",
+            "id": "a61170c3"
+          },
+          {
+            "group": "right",
+            "id": "821f59c0"
+          },
+          {
+            "group": "bottom",
+            "id": "17360bc4"
+          },
+          {
+            "group": "left",
+            "id": "15d1b217"
+          }
         ]
       },
       "isCustom": true,
       "parentKey": "1",
-      "x": -369,
+      "x": -290,
       "y": -170,
       "zIndex": 10
     },
@@ -52,15 +76,27 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "4b4d9fa6" },
-          { "group": "right", "id": "ce88d7e2" },
-          { "group": "bottom", "id": "e69d8709" },
-          { "group": "left", "id": "c29d7b43" }
+          {
+            "group": "top",
+            "id": "4b4d9fa6"
+          },
+          {
+            "group": "right",
+            "id": "ce88d7e2"
+          },
+          {
+            "group": "bottom",
+            "id": "e69d8709"
+          },
+          {
+            "group": "left",
+            "id": "c29d7b43"
+          }
         ]
       },
       "isCustom": true,
       "parentKey": "1",
-      "x": -162,
+      "x": -138,
       "y": -150,
       "zIndex": 10
     },
@@ -73,10 +109,22 @@
       "height": 90,
       "ports": {
         "items": [
-          { "group": "top", "id": "331cd291" },
-          { "group": "right", "id": "ff6724ee" },
-          { "group": "bottom", "id": "16b4df46" },
-          { "group": "left", "id": "34c9dbc6" }
+          {
+            "group": "top",
+            "id": "331cd291"
+          },
+          {
+            "group": "right",
+            "id": "ff6724ee"
+          },
+          {
+            "group": "bottom",
+            "id": "16b4df46"
+          },
+          {
+            "group": "left",
+            "id": "34c9dbc6"
+          }
         ]
       },
       "isCustom": true,
@@ -94,10 +142,22 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "f71ce55b" },
-          { "group": "right", "id": "e67dc19c" },
-          { "group": "bottom", "id": "a06aba2c" },
-          { "group": "left", "id": "b578cc26" }
+          {
+            "group": "top",
+            "id": "f71ce55b"
+          },
+          {
+            "group": "right",
+            "id": "e67dc19c"
+          },
+          {
+            "group": "bottom",
+            "id": "a06aba2c"
+          },
+          {
+            "group": "left",
+            "id": "b578cc26"
+          }
         ]
       },
       "isCustom": true,
@@ -115,10 +175,22 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "0fc44196" },
-          { "group": "right", "id": "d2030f1b" },
-          { "group": "bottom", "id": "188c9b68" },
-          { "group": "left", "id": "4e9ce7ad" }
+          {
+            "group": "top",
+            "id": "0fc44196"
+          },
+          {
+            "group": "right",
+            "id": "d2030f1b"
+          },
+          {
+            "group": "bottom",
+            "id": "188c9b68"
+          },
+          {
+            "group": "left",
+            "id": "4e9ce7ad"
+          }
         ]
       },
       "isCustom": true,
@@ -136,15 +208,27 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "b58731c5" },
-          { "group": "right", "id": "b3dfbc16" },
-          { "group": "bottom", "id": "89c0bc16" },
-          { "group": "left", "id": "a3adcac9" }
+          {
+            "group": "top",
+            "id": "b58731c5"
+          },
+          {
+            "group": "right",
+            "id": "b3dfbc16"
+          },
+          {
+            "group": "bottom",
+            "id": "89c0bc16"
+          },
+          {
+            "group": "left",
+            "id": "a3adcac9"
+          }
         ]
       },
       "isCustom": true,
       "parentKey": "1",
-      "x": -384,
+      "x": -305,
       "y": -304,
       "zIndex": 10
     },
@@ -157,15 +241,27 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "7f8dc65b" },
-          { "group": "right", "id": "9a699e3f" },
-          { "group": "bottom", "id": "d348b56a" },
-          { "group": "left", "id": "47317157" }
+          {
+            "group": "top",
+            "id": "7f8dc65b"
+          },
+          {
+            "group": "right",
+            "id": "9a699e3f"
+          },
+          {
+            "group": "bottom",
+            "id": "d348b56a"
+          },
+          {
+            "group": "left",
+            "id": "47317157"
+          }
         ]
       },
       "isCustom": true,
       "parentKey": "1",
-      "x": -162,
+      "x": -138,
       "y": -304,
       "zIndex": 10
     },
@@ -178,10 +274,22 @@
       "height": 90,
       "ports": {
         "items": [
-          { "group": "top", "id": "cf1c4df0" },
-          { "group": "right", "id": "1eb352b0" },
-          { "group": "bottom", "id": "83b59198" },
-          { "group": "left", "id": "94f485b5" }
+          {
+            "group": "top",
+            "id": "cf1c4df0"
+          },
+          {
+            "group": "right",
+            "id": "1eb352b0"
+          },
+          {
+            "group": "bottom",
+            "id": "83b59198"
+          },
+          {
+            "group": "left",
+            "id": "94f485b5"
+          }
         ]
       },
       "isCustom": true,
@@ -199,10 +307,22 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "3ca1320c" },
-          { "group": "right", "id": "4dee75d9" },
-          { "group": "bottom", "id": "0f72f2ba" },
-          { "group": "left", "id": "bac7962b" }
+          {
+            "group": "top",
+            "id": "3ca1320c"
+          },
+          {
+            "group": "right",
+            "id": "4dee75d9"
+          },
+          {
+            "group": "bottom",
+            "id": "0f72f2ba"
+          },
+          {
+            "group": "left",
+            "id": "bac7962b"
+          }
         ]
       },
       "isCustom": true,
@@ -220,10 +340,22 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "04c81e99" },
-          { "group": "right", "id": "0d594eef" },
-          { "group": "bottom", "id": "17ff5fe6" },
-          { "group": "left", "id": "73307680" }
+          {
+            "group": "top",
+            "id": "04c81e99"
+          },
+          {
+            "group": "right",
+            "id": "0d594eef"
+          },
+          {
+            "group": "bottom",
+            "id": "17ff5fe6"
+          },
+          {
+            "group": "left",
+            "id": "73307680"
+          }
         ]
       },
       "isCustom": true,
@@ -241,10 +373,22 @@
       "height": 50,
       "ports": {
         "items": [
-          { "group": "top", "id": "2aae2a71" },
-          { "group": "right", "id": "e3a42bda" },
-          { "group": "bottom", "id": "0f06668a" },
-          { "group": "left", "id": "8e0bff55" }
+          {
+            "group": "top",
+            "id": "2aae2a71"
+          },
+          {
+            "group": "right",
+            "id": "e3a42bda"
+          },
+          {
+            "group": "bottom",
+            "id": "0f06668a"
+          },
+          {
+            "group": "left",
+            "id": "8e0bff55"
+          }
         ]
       },
       "isCustom": true,
@@ -262,10 +406,22 @@
       "height": 90,
       "ports": {
         "items": [
-          { "group": "top", "id": "8ab6c3f6" },
-          { "group": "right", "id": "205f1437" },
-          { "group": "bottom", "id": "761ad2b5" },
-          { "group": "left", "id": "22d16375" }
+          {
+            "group": "top",
+            "id": "8ab6c3f6"
+          },
+          {
+            "group": "right",
+            "id": "205f1437"
+          },
+          {
+            "group": "bottom",
+            "id": "761ad2b5"
+          },
+          {
+            "group": "left",
+            "id": "22d16375"
+          }
         ]
       },
       "isCustom": true,
@@ -283,10 +439,22 @@
       "height": 90,
       "ports": {
         "items": [
-          { "group": "top", "id": "99f69f24" },
-          { "group": "right", "id": "0bccd839" },
-          { "group": "bottom", "id": "42c0d925" },
-          { "group": "left", "id": "58f52f2c" }
+          {
+            "group": "top",
+            "id": "99f69f24"
+          },
+          {
+            "group": "right",
+            "id": "0bccd839"
+          },
+          {
+            "group": "bottom",
+            "id": "42c0d925"
+          },
+          {
+            "group": "left",
+            "id": "58f52f2c"
+          }
         ]
       },
       "isCustom": true,
@@ -294,98 +462,290 @@
       "x": 42,
       "y": 6,
       "zIndex": 10
-    }
-  ],
-  "edges": [
-    {
-      "id": "41561012:92334433-975bf288:15d1b217",
-      "source": { "cell": "41561012", "port": "92334433" },
-      "target": { "cell": "975bf288", "port": "15d1b217" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
     },
     {
-      "id": "975bf288:821f59c0-5764f3ce:c29d7b43",
-      "source": { "cell": "975bf288", "port": "821f59c0" },
-      "target": { "cell": "5764f3ce", "port": "c29d7b43" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "id": "2427bf29",
+      "renderKey": "custom-circle",
+      "name": "custom-circle",
+      "label": "三级审批1",
+      "width": 90,
+      "height": 90,
+      "ports": {
+        "items": [
+          {
+            "group": "top",
+            "id": "e5a149c4"
+          },
+          {
+            "group": "right",
+            "id": "e1a1ecea"
+          },
+          {
+            "group": "bottom",
+            "id": "6e131e6a"
+          },
+          {
+            "group": "left",
+            "id": "6bbf9ae4"
+          }
+        ]
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -425,
+      "y": -224,
+      "zIndex": 10
     },
+    {
+      "id": "be25fe75",
+      "renderKey": "custom-circle",
+      "name": "custom-circle",
+      "label": "三级审批2",
+      "width": 90,
+      "height": 90,
+      "ports": {
+        "items": [
+          {
+            "group": "top",
+            "id": "13e4b9ea"
+          },
+          {
+            "group": "right",
+            "id": "ce651308"
+          },
+          {
+            "group": "bottom",
+            "id": "9a0b8942"
+          },
+          {
+            "group": "left",
+            "id": "a705e7ed"
+          }
+        ]
+      },
+      "isCustom": true,
+      "parentKey": "1",
+      "x": -425,
+      "y": -115,
+      "zIndex": 10
+    }
+  ],
+  "edges": [
     {
       "id": "975bf288:a61170c3-8c1f18d0:89c0bc16",
-      "source": { "cell": "975bf288", "port": "a61170c3" },
-      "target": { "cell": "8c1f18d0", "port": "89c0bc16" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "975bf288",
+        "port": "a61170c3"
+      },
+      "target": {
+        "cell": "8c1f18d0",
+        "port": "89c0bc16"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "8c1f18d0:a3adcac9-41561012:293a90b5",
-      "source": { "cell": "8c1f18d0", "port": "a3adcac9" },
-      "target": { "cell": "41561012", "port": "293a90b5" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "8c1f18d0",
+        "port": "a3adcac9"
+      },
+      "target": {
+        "cell": "41561012",
+        "port": "293a90b5"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "5764f3ce:4b4d9fa6-1aed14d1:d348b56a",
-      "source": { "cell": "5764f3ce", "port": "4b4d9fa6" },
-      "target": { "cell": "1aed14d1", "port": "d348b56a" },
+      "source": {
+        "cell": "5764f3ce",
+        "port": "4b4d9fa6"
+      },
+      "target": {
+        "cell": "1aed14d1",
+        "port": "d348b56a"
+      },
       "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "1aed14d1:9a699e3f-4651130e:94f485b5",
-      "source": { "cell": "1aed14d1", "port": "9a699e3f" },
-      "target": { "cell": "4651130e", "port": "94f485b5" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "1aed14d1",
+        "port": "9a699e3f"
+      },
+      "target": {
+        "cell": "4651130e",
+        "port": "94f485b5"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "5764f3ce:ce88d7e2-3fb8d302:22d16375",
-      "source": { "cell": "5764f3ce", "port": "ce88d7e2" },
-      "target": { "cell": "3fb8d302", "port": "22d16375" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "5764f3ce",
+        "port": "ce88d7e2"
+      },
+      "target": {
+        "cell": "3fb8d302",
+        "port": "22d16375"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "3fb8d302:205f1437-a48131e0:bac7962b",
-      "source": { "cell": "3fb8d302", "port": "205f1437" },
-      "target": { "cell": "a48131e0", "port": "bac7962b" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "3fb8d302",
+        "port": "205f1437"
+      },
+      "target": {
+        "cell": "a48131e0",
+        "port": "bac7962b"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "a48131e0:4dee75d9-b57b57c8:73307680",
-      "source": { "cell": "a48131e0", "port": "4dee75d9" },
-      "target": { "cell": "b57b57c8", "port": "73307680" },
+      "source": {
+        "cell": "a48131e0",
+        "port": "4dee75d9"
+      },
+      "target": {
+        "cell": "b57b57c8",
+        "port": "73307680"
+      },
       "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "a48131e0:0f72f2ba-3631eae9:2aae2a71",
-      "source": { "cell": "a48131e0", "port": "0f72f2ba" },
-      "target": { "cell": "3631eae9", "port": "2aae2a71" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "a48131e0",
+        "port": "0f72f2ba"
+      },
+      "target": {
+        "cell": "3631eae9",
+        "port": "2aae2a71"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "3631eae9:8e0bff55-399bddb7:0bccd839",
-      "source": { "cell": "3631eae9", "port": "8e0bff55" },
-      "target": { "cell": "399bddb7", "port": "0bccd839" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "3631eae9",
+        "port": "8e0bff55"
+      },
+      "target": {
+        "cell": "399bddb7",
+        "port": "0bccd839"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "399bddb7:58f52f2c-c28a18d3:e67dc19c",
-      "source": { "cell": "399bddb7", "port": "58f52f2c" },
-      "target": { "cell": "c28a18d3", "port": "e67dc19c" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "399bddb7",
+        "port": "58f52f2c"
+      },
+      "target": {
+        "cell": "c28a18d3",
+        "port": "e67dc19c"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "c28a18d3:b578cc26-5359e23c:ff6724ee",
-      "source": { "cell": "c28a18d3", "port": "b578cc26" },
-      "target": { "cell": "5359e23c", "port": "ff6724ee" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "c28a18d3",
+        "port": "b578cc26"
+      },
+      "target": {
+        "cell": "5359e23c",
+        "port": "ff6724ee"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "5359e23c:34c9dbc6-5216c5dc:d2030f1b",
-      "source": { "cell": "5359e23c", "port": "34c9dbc6" },
-      "target": { "cell": "5216c5dc", "port": "d2030f1b" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "5359e23c",
+        "port": "34c9dbc6"
+      },
+      "target": {
+        "cell": "5216c5dc",
+        "port": "d2030f1b"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     },
     {
       "id": "b57b57c8:04c81e99-4651130e:1eb352b0",
-      "source": { "cell": "b57b57c8", "port": "04c81e99" },
-      "target": { "cell": "4651130e", "port": "1eb352b0" },
-      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":[0,0],\"strokeWidth\":1}}"
+      "source": {
+        "cell": "b57b57c8",
+        "port": "04c81e99"
+      },
+      "target": {
+        "cell": "4651130e",
+        "port": "1eb352b0"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
+    },
+    {
+      "id": "41561012:92334433-2427bf29:6bbf9ae4",
+      "source": {
+        "cell": "41561012",
+        "port": "92334433"
+      },
+      "target": {
+        "cell": "2427bf29",
+        "port": "6bbf9ae4"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
+    },
+    {
+      "id": "41561012:92334433-be25fe75:a705e7ed",
+      "source": {
+        "cell": "41561012",
+        "port": "92334433"
+      },
+      "target": {
+        "cell": "be25fe75",
+        "port": "a705e7ed"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
+    },
+    {
+      "id": "2427bf29:e1a1ecea-975bf288:15d1b217",
+      "source": {
+        "cell": "2427bf29",
+        "port": "e1a1ecea"
+      },
+      "target": {
+        "cell": "975bf288",
+        "port": "15d1b217"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
+    },
+    {
+      "id": "be25fe75:ce651308-975bf288:15d1b217",
+      "source": {
+        "cell": "be25fe75",
+        "port": "ce651308"
+      },
+      "target": {
+        "cell": "975bf288",
+        "port": "15d1b217"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
+    },
+    {
+      "id": "975bf288:821f59c0-5764f3ce:c29d7b43",
+      "source": {
+        "cell": "975bf288",
+        "port": "821f59c0"
+      },
+      "target": {
+        "cell": "5764f3ce",
+        "port": "c29d7b43"
+      },
+      "attr": "{\"line\":{\"stroke\":\"#A2B1C3\",\"targetMarker\":{\"name\":\"block\",\"width\":12,\"height\":8},\"strokeDasharray\":\"5 5\",\"strokeWidth\":1}}"
     }
   ]
-}
+}