fanfan 2 năm trước cách đây
mục cha
commit
11aa84bd1c

+ 2 - 0
package.json

@@ -43,6 +43,8 @@
     "animate.css": "^4.1.1",
     "ant-design-vue": "^3.2.15",
     "axios": "^1.2.2",
+    "bpmn-js": "^11.5.0",
+    "bpmn-js-properties-panel": "^1.20.0",
     "codemirror": "^6.0.1",
     "crypto-js": "^4.1.1",
     "dayjs": "^1.11.6",

+ 202 - 1
pnpm-lock.yaml

@@ -32,6 +32,8 @@ specifiers:
   ant-design-vue: ^3.2.15
   autoprefixer: ^10.4.13
   axios: ^1.2.2
+  bpmn-js: ^11.5.0
+  bpmn-js-properties-panel: ^1.20.0
   cloc: ^2.10.0
   codemirror: ^6.0.1
   color: ^4.2.3
@@ -113,6 +115,8 @@ dependencies:
   animate.css: 4.1.1
   ant-design-vue: 3.2.15_vue@3.2.47
   axios: 1.3.4
+  bpmn-js: 11.5.0
+  bpmn-js-properties-panel: 1.20.0_bpmn-js@11.5.0
   codemirror: 6.0.1
   crypto-js: 4.1.1
   dayjs: 1.11.7
@@ -1418,6 +1422,36 @@ packages:
       '@babel/helper-validator-identifier': 7.19.1
       to-fast-properties: 2.0.0
 
+  /@bpmn-io/diagram-js-ui/0.2.2:
+    resolution: {integrity: sha512-IgOIxOwoqsFB2mMPdXtcbPVPjdYkZ3huW7ipowYLhg5jdRGHlBronQ+LER+lfWro6sPtzEsw7qX8D8Yq9M2S5g==}
+    dependencies:
+      htm: 3.1.1
+      preact: 10.13.1
+    dev: false
+
+  /@bpmn-io/element-templates-validator/0.13.0:
+    resolution: {integrity: sha512-eu2S2lXRxfbGpZk0JiB7Q+TzKm1+1hTsDJJEw7+AAvW8woY+URIz8qOaRB/j/q/V9SrijP7n78dzW41Eurrf6w==}
+    dependencies:
+      '@camunda/element-templates-json-schema': 0.12.0
+      '@camunda/zeebe-element-templates-json-schema': 0.8.0
+      json-source-map: 0.6.1
+      min-dash: 4.0.0
+    dev: false
+
+  /@bpmn-io/extract-process-variables/0.8.0:
+    resolution: {integrity: sha512-yAS7ZYX+D56K+luC36u96eRMLb4VHcPUwTUqMZ/Z/Je2gou2DJLRbuBTHAB4jjKt4wFCHSG4B8Y+TrBciEYf4w==}
+    dependencies:
+      min-dash: 4.0.0
+    dev: false
+
+  /@camunda/element-templates-json-schema/0.12.0:
+    resolution: {integrity: sha512-f5r/Xe0KgtSl+dG7TQVEATP70pGNMEn3Od8DVBpLXDgMiJWbQ9XR2XNlsao0XEoCu0AW0veLUD5/ItAEt0/a1A==}
+    dev: false
+
+  /@camunda/zeebe-element-templates-json-schema/0.8.0:
+    resolution: {integrity: sha512-KwGFOQrgROmqip+yyxgvT727b2JB1WQh72Y81AKdX+jEt417eE92mNP2FjhmDcbHmqLJDxzh9Ic9turZQGiE7A==}
+    dev: false
+
   /@claviska/jquery-minicolors/2.3.6_jquery@3.6.4:
     resolution: {integrity: sha512-8Ro6D4GCrmOl41+6w4NFhEOpx8vjxwVRI69bulXsFDt49uVRKhLU5TnzEV7AmOJrylkVq+ugnYNMiGHBieeKUQ==}
     peerDependencies:
@@ -3007,6 +3041,11 @@ packages:
     resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==}
     dev: true
 
+  /array-move/3.0.1:
+    resolution: {integrity: sha512-H3Of6NIn2nNU1gsVDqDnYKY/LCdWvCMMOWifNGhKcVQgiZ6nOek39aESOvro6zmueP07exSl93YLvkN4fZOkSg==}
+    engines: {node: '>=10'}
+    dev: false
+
   /array-tree-filter/2.1.0:
     resolution: {integrity: sha512-4ROwICNlNw/Hqa9v+rk5h22KjmzB1JGTMVKP2AKJBOCgb0yL0ASf0+YvCcLNNwquOHNX48jkeZIJ3a+oOQqKcw==}
     dev: false
@@ -3197,6 +3236,50 @@ packages:
     resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
     dev: true
 
+  /bpmn-js-properties-panel/1.20.0_bpmn-js@11.5.0:
+    resolution: {integrity: sha512-Ns2BmuDoYob+rLp6GE9YKatfif6+QqxoSiuq5Zvm/MVOZCmUwWe++v6nZGqUCUK5+cNhDMcycmx3swF/6yvvQw==}
+    peerDependencies:
+      '@bpmn-io/properties-panel': ^1.0.1
+      bpmn-js: '>= 8'
+      camunda-bpmn-js-behaviors: '>= 0.4'
+      diagram-js: '>= 7'
+    dependencies:
+      '@bpmn-io/element-templates-validator': 0.13.0
+      '@bpmn-io/extract-process-variables': 0.8.0
+      array-move: 3.0.1
+      bpmn-js: 11.5.0
+      classnames: 2.3.2
+      ids: 1.0.0
+      min-dash: 4.0.0
+      min-dom: 4.1.0
+      preact-markup: 2.1.1
+      semver-compare: 1.0.0
+    transitivePeerDependencies:
+      - preact
+    dev: false
+
+  /bpmn-js/11.5.0:
+    resolution: {integrity: sha512-Bdj53UvfiDtGE1wmiBmpgjl5RMLhCGV/C841dyC+t4kBHj7vApAeeHs2Qiycj390HO4B2U8UDROLT7yjdXEEUA==}
+    dependencies:
+      bpmn-moddle: 8.0.1
+      diagram-js: 11.11.0
+      diagram-js-direct-editing: 2.0.0_diagram-js@11.11.0
+      ids: 1.0.0
+      inherits-browser: 0.1.0
+      min-dash: 4.0.0
+      min-dom: 4.1.0
+      object-refs: 0.3.0
+      tiny-svg: 3.0.0
+    dev: false
+
+  /bpmn-moddle/8.0.1:
+    resolution: {integrity: sha512-mwZcrWhi52+JH5Oq58WwKYcUxQ1ZMiDQuzt1bpqiqEEFFnQLqCgtLwEXQuDXFmAuQPdMAghyPzqdOZQqIQVesw==}
+    dependencies:
+      min-dash: 4.0.0
+      moddle: 6.2.1
+      moddle-xml: 10.1.0
+    dev: false
+
   /brace-expansion/1.1.11:
     resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
     dependencies:
@@ -3447,6 +3530,10 @@ packages:
       static-extend: 0.1.2
     dev: true
 
+  /classnames/2.3.2:
+    resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==}
+    dev: false
+
   /clean-css/5.3.2:
     resolution: {integrity: sha512-JVJbM+f3d3Q704rF4bqQ5UUyTtuJ0JRKNbTKVEeujCCBoMdkEi+V+e8oktO9qGQNSvHrFTM6JZRXrUvGR1czww==}
     engines: {node: '>= 10.0'}
@@ -3503,6 +3590,11 @@ packages:
     engines: {node: '>=0.8'}
     dev: true
 
+  /clsx/1.2.1:
+    resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
+    engines: {node: '>=6'}
+    dev: false
+
   /codemirror/6.0.1:
     resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
     dependencies:
@@ -3608,6 +3700,10 @@ packages:
     resolution: {integrity: sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==}
     dev: true
 
+  /component-event/0.2.1:
+    resolution: {integrity: sha512-wGA++isMqiDq1jPYeyv2as/Bt/u+3iLW0rEa+8NQ82jAv3TgqMiCM+B2SaBdn2DfLilLjjq736YcezihRYhfxw==}
+    dev: false
+
   /compute-scroll-into-view/1.0.20:
     resolution: {integrity: sha512-UCB0ioiyj8CRjtrvaceBLqqhZCVP+1B8+NWQhmdsm0VXOJtobBCf1dBQmebCCo34qZmUwZfIH2MZLqNHazrfjg==}
     dev: false
@@ -4064,6 +4160,35 @@ packages:
       minimist: 1.2.8
     dev: true
 
+  /diagram-js-direct-editing/2.0.0_diagram-js@11.11.0:
+    resolution: {integrity: sha512-/12OWL0B0RMCfaT1w3723c729MD42r5fay4wtm2DvxNFNBMdPaEvOHCTA/khLKjFzOzMVKxSzbAp7IEwBGonVw==}
+    peerDependencies:
+      diagram-js: '*'
+    dependencies:
+      diagram-js: 11.11.0
+      min-dash: 4.0.0
+      min-dom: 4.1.0
+    dev: false
+
+  /diagram-js/11.11.0:
+    resolution: {integrity: sha512-+GJ6NPCihQBOqLKAjrXE+bQNYIhFjrCQgYHfPb22OcrYJ9k6vkTfJfG5PsyJ9P/AEIrXwTnrJAff/iKU1RgfAA==}
+    dependencies:
+      '@bpmn-io/diagram-js-ui': 0.2.2
+      clsx: 1.2.1
+      didi: 9.0.2
+      hammerjs: 2.0.8
+      inherits-browser: 0.1.0
+      min-dash: 4.0.0
+      min-dom: 4.1.0
+      object-refs: 0.3.0
+      path-intersection: 2.2.1
+      tiny-svg: 3.0.0
+    dev: false
+
+  /didi/9.0.2:
+    resolution: {integrity: sha512-q2+aj+lnJcUweV7A9pdUrwFr4LHVmRPwTmQLtHPFz4aT7IBoryN6Iy+jmFku+oIzr5ebBkvtBCOb87+dJhb7bg==}
+    dev: false
+
   /didyoumean/1.2.2:
     resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==}
     dev: true
@@ -4150,6 +4275,10 @@ packages:
       domelementtype: 2.3.0
     dev: true
 
+  /domify/1.4.1:
+    resolution: {integrity: sha512-x18nuiDHMCZGXr4KJSRMf/TWYtiaRo6RX8KN9fEbW54mvbQ6pieUuerC2ahBg+kEp1wycFj8MPUI0WkIOw5E9w==}
+    dev: false
+
   /dompurify/2.4.5:
     resolution: {integrity: sha512-jggCCd+8Iqp4Tsz0nIvpcb22InKEBrGz5dw3EQJMs8HPJDsKbFIO3STYtAvCfDx26Muevn1MHVI0XxjgFfmiSA==}
     requiresBuild: true
@@ -5091,6 +5220,11 @@ packages:
     resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
     dev: true
 
+  /hammerjs/2.0.8:
+    resolution: {integrity: sha512-tSQXBXS/MWQOn/RKckawJ61vvsDpCom87JgxiYdGwHdOa0ht0vzUWDlfioofFCRU0L+6NGDt6XzbgoJvZkMeRQ==}
+    engines: {node: '>=0.8.0'}
+    dev: false
+
   /hard-rejection/2.1.0:
     resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
     engines: {node: '>=6'}
@@ -5208,6 +5342,10 @@ packages:
       lru-cache: 6.0.0
     dev: true
 
+  /htm/3.1.1:
+    resolution: {integrity: sha512-983Vyg8NwUE7JkZ6NmOqpCZ+sh1bKv2iYTlUkzlWmA5JD2acKoxd4KVxbMmxX/85mtfdnDmTFoNKcg5DGAvxNQ==}
+    dev: false
+
   /html-minifier-terser/6.1.0:
     resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==}
     engines: {node: '>=12'}
@@ -5283,6 +5421,10 @@ packages:
     resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
     dev: true
 
+  /ids/1.0.0:
+    resolution: {integrity: sha512-Zvtq1xUto4LttpstyOlFum8lKx+i1OmRfg+6A9drFS9iSZsDPMHG4Sof/qwNR4kCU7jBeWFPrY2ocHxiz7cCRw==}
+    dev: false
+
   /ieee754/1.2.1:
     resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
     dev: true
@@ -5328,6 +5470,10 @@ packages:
       wrappy: 1.0.2
     dev: true
 
+  /inherits-browser/0.1.0:
+    resolution: {integrity: sha512-CJHHvW3jQ6q7lzsXPpapLdMx5hDpSF3FSh45pwsj6bKxJJ8Nl8v43i5yXnr3BdfOimGHKyniewQtnAIp3vyJJw==}
+    dev: false
+
   /inherits/2.0.3:
     resolution: {integrity: sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw==}
     dev: false
@@ -5757,6 +5903,10 @@ packages:
     resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==}
     dev: true
 
+  /json-source-map/0.6.1:
+    resolution: {integrity: sha512-1QoztHPsMQqhDq0hlXY5ZqcEdUzxQEIxgFkKl4WUp2pgShObl+9ovi4kRh2TfvAfxAoHOJ9vIMEqk3k4iex7tg==}
+    dev: false
+
   /json-stable-stringify-without-jsonify/1.0.1:
     resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
     dev: true
@@ -6206,6 +6356,18 @@ packages:
     engines: {node: '>=10'}
     dev: true
 
+  /min-dash/4.0.0:
+    resolution: {integrity: sha512-piIvVJ/nxuA4+LpnYIzF6oCtRvdtDvQJteSC+H768H2UvPKFKIt5oiJnUVtr0ZdchneXTcvUZ91vIrvWVIN0AA==}
+    dev: false
+
+  /min-dom/4.1.0:
+    resolution: {integrity: sha512-1lj1EyoSwY/UmTeT/hhPiZTsq+vK9D+8FAJ/53iK5jT1otkG9rJTixSKdjmTieEvdfES+sKbbTptzaQJhnacjA==}
+    dependencies:
+      component-event: 0.2.1
+      domify: 1.4.1
+      min-dash: 4.0.0
+    dev: false
+
   /min-indent/1.0.1:
     resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==}
     engines: {node: '>=4'}
@@ -6266,6 +6428,20 @@ packages:
     dependencies:
       commander: 10.0.0
 
+  /moddle-xml/10.1.0:
+    resolution: {integrity: sha512-erWckwLt+dYskewKXJso9u+aAZ5172lOiYxSOqKCPTy7L/xmqH1PoeoA7eVC7oJTt3PqF5TkZzUmbjGH6soQBg==}
+    dependencies:
+      min-dash: 4.0.0
+      moddle: 6.2.1
+      saxen: 8.1.2
+    dev: false
+
+  /moddle/6.2.1:
+    resolution: {integrity: sha512-rBT4P19k9wKOerFHNJQugw25CK6DK5m4lVZGac7godbWNPsbJgr1K4GJ+pqM1ErbRYxljXCTDgPhJLoDWE4wwQ==}
+    dependencies:
+      min-dash: 4.0.0
+    dev: false
+
   /mri/1.2.0:
     resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
     engines: {node: '>=4'}
@@ -6473,6 +6649,10 @@ packages:
     engines: {node: '>= 0.4'}
     dev: true
 
+  /object-refs/0.3.0:
+    resolution: {integrity: sha512-eP0ywuoWOaDoiake/6kTJlPJhs+k0qNm4nYRzXLNHj6vh+5M3i9R1epJTdxIPGlhWc4fNRQ7a6XJNCX+/L4FOQ==}
+    dev: false
+
   /object-visit/1.0.1:
     resolution: {integrity: sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==}
     engines: {node: '>=0.10.0'}
@@ -6650,6 +6830,10 @@ packages:
     engines: {node: '>=8'}
     dev: true
 
+  /path-intersection/2.2.1:
+    resolution: {integrity: sha512-9u8xvMcSfuOiStv9bPdnRJQhGQXLKurew94n4GPQCdH1nj9QKC9ObbNoIpiRq8skiOBxKkt277PgOoFgAt3/rA==}
+    dev: false
+
   /path-is-absolute/1.0.1:
     resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
     engines: {node: '>=0.10.0'}
@@ -7205,6 +7389,16 @@ packages:
       posthtml-render: 1.4.0
     dev: true
 
+  /preact-markup/2.1.1:
+    resolution: {integrity: sha512-8JL2p36mzK8XkspOyhBxUSPjYwMxDM0L5BWBZWxsZMVW8WsGQrYQDgVuDKkRspt2hwrle+Cxr/053hpc9BJwfw==}
+    peerDependencies:
+      preact: '>=10'
+    dev: false
+
+  /preact/10.13.1:
+    resolution: {integrity: sha512-KyoXVDU5OqTpG9LXlB3+y639JAGzl8JSBXLn1J9HTSB3gbKcuInga7bZnXLlxmK94ntTs1EFeZp0lrja2AuBYQ==}
+    dev: false
+
   /prebuild-install/7.1.1:
     resolution: {integrity: sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==}
     engines: {node: '>=10'}
@@ -7655,6 +7849,10 @@ packages:
     dev: true
     optional: true
 
+  /saxen/8.1.2:
+    resolution: {integrity: sha512-xUOiiFbc3Ow7p8KMxwsGICPx46ZQvy3+qfNVhrkwfz3Vvq45eGt98Ft5IQaA1R/7Tb5B5MKh9fUR9x3c3nDTxw==}
+    dev: false
+
   /scroll-into-view-if-needed/2.2.31:
     resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
     dependencies:
@@ -7663,7 +7861,6 @@ packages:
 
   /semver-compare/1.0.0:
     resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==}
-    dev: true
 
   /semver/5.7.1:
     resolution: {integrity: sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==}
@@ -8428,6 +8625,10 @@ packages:
       readable-stream: 3.6.2
     dev: true
 
+  /tiny-svg/3.0.0:
+    resolution: {integrity: sha512-+u6VomQO7MbI7CQe5q1IwNePpbVKG/HVdUQBmaEpSCdP/QmeyjhrS6WKFsNetXlvf9LWu/f5woRqjMdxBMe/0w==}
+    dev: false
+
   /to-fast-properties/2.0.0:
     resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
     engines: {node: '>=4'}

+ 33 - 0
src/components/Bpmn/tmpl/code.vue

@@ -0,0 +1,33 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    @ok="handleSubmit"
+    width="900px"
+  >
+    <CodeEditor v-model:value="codeData.value" :mode="codeData.mode" />
+  </BasicModal>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { CodeEditor } from '/@/components/CodeEditor';
+  const getTitle = '代码预览';
+  const codeData = ref({
+    value: '',
+    mode: 'javascript',
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    setModalProps({ confirmLoading: false });
+    codeData.value.value = data.record;
+  });
+  // 提交按钮事件
+  async function handleSubmit() {
+    closeModal();
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 294 - 0
src/components/Bpmn/tmpl/customModeler/custom/CustomPalette.js

@@ -0,0 +1,294 @@
+/**
+ * A palette that allows you to create BPMN _and_ custom elements.
+ */
+import { assign } from 'min-dash';
+import { getNodeNum } from './store';
+let numFlag = 'x';
+
+export default function PaletteProvider(
+  palette,
+  create,
+  elementFactory,
+  handTool,
+  lassoTool,
+  spaceTool,
+  globalConnect,
+  bpmnFactory,
+) {
+  this.create = create;
+  this.elementFactory = elementFactory;
+  this.handTool = handTool;
+  this.lassoTool = lassoTool;
+  this.spaceTool = spaceTool;
+  this.globalConnect = globalConnect;
+  this.bpmnFactory = bpmnFactory;
+  palette.registerProvider(this);
+  if (window.location.hash.indexOf('uuid=') > 0) {
+    numFlag = window.location.hash.split('uuid=')[1];
+  } else if (window.location.hash.indexOf('?id=') > 0) {
+    numFlag = window.location.hash.split('?id=')[1];
+  }
+  console.log('numFlag1:' + numFlag);
+}
+
+PaletteProvider.$inject = [
+  'palette',
+  'create',
+  'elementFactory',
+  'handTool',
+  'lassoTool',
+  'spaceTool',
+  'globalConnect',
+  'bpmnFactory',
+];
+
+PaletteProvider.prototype.getPaletteEntries = function (element) {
+  const {
+    create,
+    elementFactory,
+    bpmnFactory,
+    handTool,
+    lassoTool,
+    spaceTool,
+    globalConnect,
+    translate,
+  } = this;
+
+  // function createTask() {
+  //   return function(event) {
+  //     const id ="N"+(getNodeNum(numFlag)-0+1);
+  //     const businessObject = bpmnFactory.create('bpmn:Task', { id,custom: 2 });
+  //     // businessObject['custom'] = 1 // 这样不行
+  //     const shape = elementFactory.createShape({
+  //       type: 'bpmn:Task',
+  //       businessObject
+  //     });
+  //     const label = elementFactory.createLabel();
+  //     console.log(shape) // 只在拖动或者点击时触发
+  //     console.log(label) // 只在拖动或者点击时触发
+  //     create.start(event, shape);
+  //     // create.start(event, label);
+  //   }
+  // }
+
+  function createUserNode() {
+    return function (event) {
+      console.log(numFlag);
+      const id = 'N' + (getNodeNum(numFlag) - 0 + 2);
+      const businessObject = bpmnFactory.create('bpmn:UserTask', {
+        id,
+        custom: 2,
+        name: '审批节点',
+      });
+      // businessObject['custom'] = 1 // 这样不行
+      const shape = elementFactory.createShape({
+        type: 'bpmn:UserTask',
+        businessObject,
+      });
+      console.log('🚀 ~ file: CustomPalette.js:88 ~ elementFactory:', elementFactory);
+      debugger;
+      const label = elementFactory.createLabel({ di: shape?.di });
+      console.log(shape); // 只在拖动或者点击时触发
+      console.log(label); // 只在拖动或者点击时触发
+      create.start(event, shape);
+      // create.start(event, label);
+    };
+  }
+
+  // function createStratEvent() {
+  //   return function(event) {
+  //     const shape = elementFactory.createShape({
+  //       type: 'bpmn:StartEvent'
+  //     });
+  //     create.start(event, shape);
+  //   }
+  // }
+
+  // function createGateway() {
+  //   return function(event) {
+  //     const shape = elementFactory.createShape({
+  //       type: 'bpmn:ExclusiveGateway'
+  //     });
+  //     create.start(event, shape);
+  //   }
+  // }
+
+  function createAction(type, group, className, title, options) {
+    function createListener(event) {
+      const id = 'N' + (getNodeNum(numFlag) - 0 + 2);
+      let name = '';
+      if (type === 'bpmn:ReceiveTask') {
+        name = '抄送节点';
+      } else if (type === 'bpmn:ScriptTask') {
+        name = '脚本节点';
+      } else if (type === 'bpmn:ServiceTask') {
+        name = '服务节点';
+      } else if (type === 'bpmn:ExclusiveGateway') {
+        name = '互斥网关';
+      } else if (type === 'bpmn:ParallelGateway') {
+        name = '并行网关';
+      }
+      console.log(type);
+      const taskBusinessObject = bpmnFactory.create(type, { id, name, custom: 2 });
+      const shape = elementFactory.createShape(
+        assign({ type: type }, options, { businessObject: taskBusinessObject }),
+      );
+      // var shape = elementFactory.createShape(assign({ type: type }, options));
+      if (options) {
+        shape.businessObject.di.isExpanded = options.isExpanded;
+      }
+      create.start(event, shape);
+    }
+
+    const shortType = type.replace(/^bpmn:/, '');
+
+    return {
+      group: group,
+      className: className,
+      title: title || translate('Create {type}', { type: shortType }),
+      action: {
+        dragstart: createListener,
+        click: createListener,
+      },
+    };
+  }
+
+  return {
+    'hand-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-hand-tool',
+      title: '激活抓手工具',
+      action: {
+        click: function (event) {
+          handTool.activateHand(event);
+        },
+      },
+    },
+    'lasso-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-lasso-tool',
+      title: '激活套索工具',
+      action: {
+        click: function (event) {
+          lassoTool.activateSelection(event);
+        },
+      },
+    },
+    'space-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-space-tool',
+      title: '激活空间工具',
+      action: {
+        click: function (event) {
+          spaceTool.activateSelection(event);
+        },
+      },
+    },
+    'global-connect-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-connection-multi',
+      title: '激活连接工具',
+      action: {
+        click: function (event) {
+          globalConnect.toggle(event);
+        },
+      },
+    },
+    'tool-separator': {
+      //工具分割线
+      group: 'tools',
+      separator: true,
+    },
+    'create.user-task': {
+      group: 'model',
+      className: 'bpmn-icon-user-task',
+      title: '添加审批节点',
+      action: {
+        dragstart: createUserNode(),
+        click: createUserNode(),
+      },
+    },
+    'create.receive-task': createAction(
+      'bpmn:ReceiveTask',
+      'activity',
+      'bpmn-icon-receive-task',
+      '添加抄送节点',
+    ),
+    'create.script-task': createAction(
+      'bpmn:ScriptTask',
+      'activity',
+      'bpmn-icon-script-task',
+      '添加脚本节点',
+    ),
+    'create.service-task': createAction(
+      'bpmn:ServiceTask',
+      'activity',
+      'bpmn-icon-service-task',
+      '添加服务节点',
+    ),
+    'create.exclusive-gateway': createAction(
+      'bpmn:ExclusiveGateway',
+      'activity',
+      'bpmn-icon-gateway-xor',
+      '添加互斥网关',
+    ),
+    'create.parallel-gateway': createAction(
+      'bpmn:ParallelGateway',
+      'activity',
+      'bpmn-icon-gateway-parallel',
+      '添加并行网关',
+    ),
+    // "create.data-object": createAction(
+    //   "bpmn:DataObject",
+    //   "activity",
+    //   "bpmn-icon-data-object",
+    //   "添加数据对象"
+    // ),
+    // "create.data-store": createAction(
+    //   "bpmn:DataStore",
+    //   "activity",
+    //   "bpmn-icon-data-store",
+    //   "添加数据存储"
+    // ),
+    'create.participant': createAction(
+      'bpmn:Participant',
+      'activity',
+      'bpmn-icon-participant',
+      '添加泳道',
+    ),
+    'create.group': createAction('bpmn:Group', 'activity', 'bpmn-icon-group', '添加分组'),
+    // "create.data-store": createAction(
+    //   "bpmn:DataStore",
+    //   "activity",
+    //   "bpmn-icon-data-store",
+    //   "添加数据存储"
+    // ),
+    // 'create.user-node': {
+    //   group: 'model',
+    //   className: 'bpmn-icon-user-task',
+    //   title: '添加审批节点',
+    //   action: {
+    //     dragstart: createUserNode(),
+    //     click: createUserNode()
+    //   }
+    // },
+    // 'create.lindaidai-task': {
+    //   group: 'model',
+    //   className: 'icon-custom lindaidai-task',
+    //   title: '创建一个类型为lindaidai-task的任务节点',
+    //   action: {
+    //     dragstart: createTask(),
+    //     click: createTask()
+    //   }
+    // },
+    // 'create.exclusive-gateway': {
+    //   group: 'gateway',
+    //   className: 'bpmn-icon-gateway-none',
+    //   title: '创建一个网关',
+    //   action: {
+    //     dragstart: createGateway(),
+    //     click: createGateway()
+    //   }
+    // }
+  };
+};

+ 104 - 0
src/components/Bpmn/tmpl/customModeler/custom/CustomPalette3.js

@@ -0,0 +1,104 @@
+import { assign } from 'min-dash';
+
+export default function PaletteProvider(
+  palette,
+  create,
+  elementFactory,
+  handTool,
+  lassoTool,
+  spaceTool,
+  globalConnect,
+  translate,
+) {
+  this.create = create;
+  this.elementFactory = elementFactory;
+  this.handTool = handTool;
+  this.lassoTool = lassoTool;
+  this.spaceTool = spaceTool;
+  this.globalConnect = globalConnect;
+  this.translate = translate;
+
+  palette.registerProvider(this);
+}
+
+PaletteProvider.$inject = [
+  'palette',
+  'create',
+  'elementFactory',
+  'handTool',
+  'lassoTool',
+  'spaceTool',
+  'globalConnect',
+  'translate',
+];
+
+PaletteProvider.prototype.getPaletteEntries = function (element) {
+  const { create, elementFactory, handTool, lassoTool, spaceTool, globalConnect, translate } = this;
+
+  function createAction(type, group, className, title, options) {
+    function createListener(event) {
+      const shape = elementFactory.createShape(assign({ type: type }, options));
+
+      if (options) {
+        shape.businessObject.di.isExpanded = options.isExpanded;
+      }
+
+      create.start(event, shape);
+    }
+
+    const shortType = type.replace(/^bpmn:/, '');
+
+    return {
+      group: group,
+      className: className,
+      title: title || translate('Create {type}', { type: shortType }),
+      action: {
+        dragstart: createListener,
+        click: createListener,
+      },
+    };
+  }
+
+  return {
+    'lasso-tool': {
+      group: 'tools',
+      className: 'bpmn-icon-lasso-tool',
+      title: 'Activate the lasso tool',
+      action: {
+        click: function (event) {
+          lassoTool.activateSelection(event);
+        },
+      },
+    },
+
+    'tool-separator': {
+      group: 'tools',
+      separator: true,
+    },
+
+    'create.start-event': createAction(
+      'bpmn:StartEvent',
+      'event',
+      'bpmn-icon-start-event-none',
+      '创建开始节点',
+    ),
+    'create.end-event': createAction(
+      'bpmn:EndEvent',
+      'event',
+      'bpmn-icon-end-event-none',
+      '创建结束节点',
+    ),
+    'create.user-task': createAction(
+      'bpmn:UserTask',
+      'activity',
+      'bpmn-icon-user-task',
+      '创建用户任务',
+    ),
+    'create.exclusive-gateway': createAction(
+      'bpmn:ExclusiveGateway',
+      'gateway',
+      'bpmn-icon-gateway-xor',
+      '创建排他网关',
+    ),
+  };
+};

+ 200 - 0
src/components/Bpmn/tmpl/customModeler/custom/CustomRenderer.js

@@ -0,0 +1,200 @@
+/* eslint-disable no-unused-vars */
+import inherits from 'inherits';
+
+import BaseRenderer from 'diagram-js/lib/draw/BaseRenderer';
+import { assign } from 'min-dash';
+import { append as svgAppend, create as svgCreate, classes as svgClasses } from 'tiny-svg';
+
+import { customElements, customConfig } from '../../utils/util';
+/**
+ * A renderer that knows how to render custom elements.
+ */
+export default function CustomRenderer(eventBus, styles, textRenderer) {
+  BaseRenderer.call(this, eventBus, 2000);
+
+  const computeStyle = styles.computeStyle;
+
+  function renderLabel(parentGfx, label, options) {
+    options = assign(
+      {
+        size: {
+          width: 100,
+        },
+      },
+      options,
+    );
+
+    const text = textRenderer.createText(label || '', options);
+
+    svgClasses(text).add('djs-label');
+
+    svgAppend(parentGfx, text);
+
+    return text;
+  }
+
+  this.drawCustomElements = function (parentNode, element) {
+    // console.log(678)
+    // console.log(parentNode)
+    console.log('自定义渲染');
+    console.log(element);
+    const type = element.type; // 获取到类型
+    if (type !== 'label') {
+      console.log('type=' + type);
+      if (type === 'bpmn:Task') {
+        // or customConfig[type]
+        console.log('重构');
+        const data = {};
+        return;
+      } else if (type === 'bpmn:UserTask') {
+        const { url, attr } = customConfig[type];
+        const myShape = svgCreate('rect', {
+          width: attr.width,
+          height: attr.height,
+          fill: 'transparent',
+          stroke: 'black',
+          'stroke-width': '2',
+          rx: '10',
+        });
+        svgAppend(parentNode, myShape);
+
+        const titleDiv = svgCreate('text', {
+          'font-size': '14',
+          'font-family': 'Courier New',
+          fill: '#000',
+          'text-anchor': 'middle',
+        });
+        titleDiv.innerHTML = "<tspan x='50' dy='20' fill='black'>" + element.id + '</tspan>';
+        svgAppend(parentNode, titleDiv);
+
+        const contDiv = svgCreate('text', {
+          'font-size': '12',
+          'font-family': 'Courier New',
+          fill: '#000',
+          'text-anchor': 'middle',
+        });
+        contDiv.innerHTML = '';
+        if (element.businessObject.name) {
+          const size = Math.ceil(element.businessObject.name.length / 7);
+          if (size === 1) {
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='48'>" + element.businessObject.name + '</tspan>';
+          } else if (size === 2) {
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='45'>" + element.businessObject.name.substring(0, 7) + '</tspan>';
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='16'>" + element.businessObject.name.substring(7) + '</tspan>';
+          } else if (size === 3) {
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='42'>" + element.businessObject.name.substring(0, 7) + '</tspan>';
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='16'>" + element.businessObject.name.substring(7, 14) + '</tspan>';
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='16'>" + element.businessObject.name.substring(14) + '</tspan>';
+          } else if (size > 3) {
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='42'>" + element.businessObject.name.substring(0, 7) + '</tspan>';
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='16'>" + element.businessObject.name.substring(7, 14) + '</tspan>';
+            contDiv.innerHTML +=
+              "<tspan x='50' dy='16'>" +
+              element.businessObject.name.substring(14, 19) +
+              '...' +
+              '</tspan>';
+          }
+          // else{
+          //     for(let i=0;i<size;i++){
+          //         if(i!==size-1){
+          //             if(i===0){
+          //                 contDiv.innerHTML+="<tspan x='50' dy='36'>"+element.businessObject.name.substring(i*7,7*(i+1))+"</tspan>";
+          //             }else{
+          //                 contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(i*7,7*(i+1))+"</tspan>";
+          //             }
+          //         }else{
+          //             if(i===0){
+          //                 contDiv.innerHTML+="<tspan x='50' dy='38'>"+element.businessObject.name.substring(i*7)+"</tspan>";
+          //             }else{
+          //                 contDiv.innerHTML+="<tspan x='50' dy='16'>"+element.businessObject.name.substring(i*7)+"</tspan>";
+          //             }
+          //         }
+          //     }
+          // }
+        }
+        // contDiv.innerHTML="<tspan x='8' dy='38'>"+element.businessObject.name+"</tspan>";
+        svgAppend(parentNode, contDiv);
+
+        if (element.id === 'N1') {
+          const imgPath = svgCreate('path', {
+            d: 'm 17,15 c 0.234,-0.01 5.604,0.008 8.029,0.004 0.808,0 1.271,-0.172 1.417,-0.752 0.227,-0.898 -0.334,-1.314 -1.338,-1.316 -2.467,-0.01 -7.886,-0.004 -8.108,-0.004 -0.014,-0.079 0.016,-0.533 0,-0.61 0.195,-0.042 8.507,0.006 9.616,0.002 0.877,-0.007 1.35,-0.438 1.353,-1.208 0.003,-0.768 -0.479,-1.09 -1.35,-1.091 -2.968,-0.002 -9.619,-0.013 -9.619,-0.013 v -0.591 c 0,0 5.052,-0.016 7.225,-0.016 0.888,-0.002 1.354,-0.416 1.351,-1.193 -0.006,-0.761 -0.492,-1.196 -1.361,-1.196 -3.473,-0.005 -10.86,-0.003 -11.0829995,-0.003 -0.022,-0.047 -0.045,-0.094 -0.069,-0.139 0.3939995,-0.319 2.0409995,-1.626 2.4149995,-2.017 0.469,-0.4870005 0.519,-1.1650005 0.162,-1.6040005 -0.414,-0.511 -0.973,-0.5 -1.48,-0.236 -1.4609995,0.764 -6.5999995,3.6430005 -7.7329995,4.2710005 -0.9,0.499 -1.516,1.253 -1.882,2.19 -0.37000002,0.95 -0.17,2.01 -0.166,2.979 0.004,0.718 -0.27300002,1.345 -0.055,2.063 0.629,2.087 2.425,3.312 4.859,3.318 4.6179995,0.014 9.2379995,-0.139 13.8569995,-0.158 0.755,-0.004 1.171,-0.301 1.182,-1.033 0.012,-0.754 -0.423,-0.969 -1.183,-0.973 -1.778,-0.01 -5.824,-0.004 -6.04,-0.004 10e-4,-0.084 0.003,-0.586 10e-4,-0.67 z',
+            fill: 'white',
+            'stroke-width': '0.5px',
+            stroke: 'black',
+          });
+          svgAppend(parentNode, imgPath);
+        } else {
+          const imgPath1 = svgCreate('path', {
+            d: 'm 15,12 c 0.909,-0.845 1.594,-2.049 1.594,-3.385 0,-2.554 -1.805,-4.62199999 -4.357,-4.62199999 -2.55199998,0 -4.28799998,2.06799999 -4.28799998,4.62199999 0,1.348 0.974,2.562 1.89599998,3.405 -0.52899998,0.187 -5.669,2.097 -5.794,4.7560005 v 6.718 h 17 v -6.718 c 0,-2.2980005 -5.5279996,-4.5950005 -6.0509996,-4.7760005 zm -8,6 l 0,5.5 m 11,0 l 0,-5',
+            fill: 'white',
+            'stroke-width': '0.5px',
+            stroke: 'black',
+          });
+
+          const imgPath2 = svgCreate('path', {
+            d: 'm 15,12 m 2.162,1.009 c 0,2.4470005 -2.158,4.4310005 -4.821,4.4310005 -2.66499998,0 -4.822,-1.981 -4.822,-4.4310005 ',
+            fill: 'white',
+            'stroke-width': '0.5px',
+            stroke: 'black',
+          });
+
+          const imgPath3 = svgCreate('path', {
+            d: 'm 15,12 m -6.9,-3.80 c 0,0 2.25099998,-2.358 4.27399998,-1.177 2.024,1.181 4.221,1.537 4.124,0.965 -0.098,-0.57 -0.117,-3.79099999 -4.191,-4.13599999 -3.57499998,0.001 -4.20799998,3.36699999 -4.20699998,4.34799999 z',
+            fill: 'black',
+            'stroke-width': '0.5px',
+            stroke: 'black',
+          });
+
+          svgAppend(parentNode, imgPath1);
+          svgAppend(parentNode, imgPath2);
+          svgAppend(parentNode, imgPath3);
+        }
+
+        return myShape;
+      }
+      // else if (true) {
+      //     console.log('我是绿色的')
+      //     // let color = element.businessObject.color
+      //     // element.businessObject.di.set('bioc:stroke', color)
+      //     const shape = this.bpmnRenderer.drawShape(parentNode, element)
+      //     return shape
+      // }
+      const shape = this.bpmnRenderer.drawShape(parentNode, element);
+      return shape;
+    } else {
+      // element
+      const shape = this.bpmnRenderer.drawShape(parentNode, element);
+      return shape;
+    }
+  };
+}
+
+inherits(CustomRenderer, BaseRenderer);
+
+CustomRenderer.$inject = ['eventBus', 'styles', 'textRenderer'];
+
+CustomRenderer.prototype.canRender = function (element) {
+  // ignore labels
+  return true;
+  // return !element.labelTarget;
+};
+
+CustomRenderer.prototype.drawShape = function (p, element) {
+  console.log(element);
+  console.log(element.type);
+  if (customElements.includes(element.type)) {
+    return this.drawCustomElements(p, element);
+  }
+};
+
+CustomRenderer.prototype.getShapePath = function (shape) {
+  console.log(shape);
+};

+ 7 - 0
src/components/Bpmn/tmpl/customModeler/custom/index.js

@@ -0,0 +1,7 @@
+import CustomPalette from './CustomPalette';
+import CustomRenderer from './CustomRenderer';
+export default {
+  __init__: ['paletteProvider', 'customRenderer'],
+  paletteProvider: ['type', CustomPalette],
+  customRenderer: ['type', CustomRenderer],
+};

+ 52 - 0
src/components/Bpmn/tmpl/customModeler/custom/store.ts

@@ -0,0 +1,52 @@
+const nodeNumMap = {} as any;
+const lineNumMap = {} as any;
+
+export const putNodeNum = (id: string, num: Number) => {
+  let flag = false;
+  for (const key in nodeNumMap) {
+    if (key == id) {
+      flag = true;
+      if (num > nodeNumMap[key]) {
+        nodeNumMap[key] = num;
+      }
+      break;
+    }
+  }
+  if (!flag) {
+    nodeNumMap[id] = num;
+  }
+};
+
+export const getNodeNum = (id: string) => {
+  for (const key in nodeNumMap) {
+    if (key == id) {
+      return nodeNumMap[id];
+    }
+  }
+  return null;
+};
+
+export const putLineNum = (id: string, num: Number) => {
+  let flag = false;
+  for (const key in lineNumMap) {
+    if (key == id) {
+      flag = true;
+      if (num > lineNumMap[key]) {
+        lineNumMap[key] = num;
+      }
+      break;
+    }
+  }
+  if (!flag) {
+    lineNumMap[id] = num;
+  }
+};
+
+export const getLineNum = (id: string) => {
+  for (const key in lineNumMap) {
+    if (key == id) {
+      return lineNumMap[id];
+    }
+  }
+  return null;
+};

+ 15 - 0
src/components/Bpmn/tmpl/customModeler/index.js

@@ -0,0 +1,15 @@
+import Modeler from 'bpmn-js/lib/Modeler';
+
+import inherits from 'inherits';
+
+import CustomModule from './custom';
+
+export default function CustomModeler(options) {
+  Modeler.call(this, options);
+
+  this._customElements = [];
+}
+
+inherits(CustomModeler, Modeler);
+
+CustomModeler.prototype._modules = [].concat(CustomModeler.prototype._modules, [CustomModule]);

+ 106 - 0
src/components/Bpmn/tmpl/data.ts

@@ -0,0 +1,106 @@
+import { FormSchema } from '/@/components/Form';
+
+export const dataFormSchema: FormSchema[] = [
+  {
+    field: 'type',
+    label: '节点类型',
+    component: 'Input',
+    required: true,
+    componentProps: {
+      placeholder: '请输入节点类型',
+    },
+    ifShow: false,
+  },
+  {
+    field: 'name',
+    label: '节点名称',
+    component: 'Input',
+    required: true,
+    componentProps: {
+      placeholder: '请输入节点名称',
+    },
+  },
+  {
+    field: 'hatyp',
+    label: '审批人类型',
+    component: 'RadioGroup',
+    required: true,
+    componentProps: {
+      options: [
+        { label: '从组织架构选择', value: 1 },
+        { label: '使用公式定义器', value: 2 },
+      ],
+    },
+    defaultValue: 1,
+    ifShow: ({ values }) => values.type != 'bpmn:EndEvent' && values.type != 'bpmn:StartEvent',
+  },
+  {
+    field: 'hamen',
+    label: '审批人',
+    component: 'Input',
+    required: true,
+    componentProps: {
+      placeholder: '请输入审批人',
+    },
+    defaultValue: '1,2,3,4',
+    ifShow: ({ values }) => values.type != 'bpmn:EndEvent' && values.type != 'bpmn:StartEvent',
+  },
+  {
+    field: 'flway',
+    label: '流转方式',
+    component: 'RadioGroup',
+    required: true,
+    componentProps: {
+      options: [
+        { label: '串行', value: 1 },
+        { label: '并行', value: 2 },
+        { label: '会审', value: 3 },
+      ],
+    },
+    defaultValue: 1,
+    ifShow: ({ values }) => values.type == 'bpmn:UserTask',
+  },
+  {
+    field: 'remod',
+    label: '身份重复配置',
+    component: 'RadioGroup',
+    required: true,
+    componentProps: {
+      options: [
+        { label: '仅相邻处理人身份重复跳过', value: 1 },
+        { label: '处理人身份重复跳过', value: 2 },
+        { label: '处理人身份重复不跳过', value: 3 },
+      ],
+    },
+    defaultValue: 1,
+    ifShow: ({ values }) => values.type == 'bpmn:UserTask',
+  },
+  {
+    field: 'edtag',
+    label: '权限',
+    component: 'RadioGroup',
+    componentProps: {
+      options: [{ label: '编辑主文档', value: 1 }],
+    },
+    defaultValue: 1,
+    ifShow: ({ values }) => values.type == 'bpmn:UserTask',
+  },
+  {
+    label: '流转条件',
+    field: 'conds',
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder:
+        '编写JS代码,返回true,则往下流。(注1:$代表form对象;注2:如果是表达式,则表达式为返回结果;注3:如果是复杂的多条语句,则z代表返回结果;)',
+    },
+    ifShow: ({ values }) => values.type == 'bpmn:UserTask',
+  },
+  {
+    label: '节点备注',
+    field: 'notes',
+    component: 'InputTextArea',
+    componentProps: {
+      placeholder: '节点备注',
+    },
+  },
+];

+ 340 - 0
src/components/Bpmn/tmpl/edit.vue

@@ -0,0 +1,340 @@
+<template>
+  <div>
+    <div style="margin-bottom: 10px">
+      <!--      <svg class="icon" aria-hidden="true" style="height: 25px;width: 40px">-->
+      <!--        <use xlink:href="#icon-shangchuan"></use>-->
+      <!--      </svg>-->
+      <!--      <el-button type='primary' @click='impXml' plain :icon='Upload'>导入</el-button>-->
+      <!--      <el-button type='primary' @click='expXML' plain :icon='Download'>导出XML</el-button>-->
+      <a-button type="primary" @click="expSVG">导出SVG</a-button>
+      <a-button type="primary" @click="codeShow">代码预览</a-button>
+      <span style="color: red; margin-left: 10px">注:双击节点或连线编辑信息</span>
+    </div>
+    <div class="containers">
+      <div class="canvas" ref="canvasRef" style="height: 1000px" />
+      <NodeModal @register="registerNodeModal" @close="closeNodeModal" />
+    </div>
+    <CodeModal @register="registerCodeModal" />
+  </div>
+</template>
+
+<script lang="ts" setup>
+  // import BpmnModeler from 'bpmn-js/lib/Modeler'
+  import { defineExpose, onMounted, reactive, ref, toRefs } from 'vue';
+  import 'bpmn-js/dist/assets/diagram-js.css'; // 左边工具栏以及编辑节点的样式
+  import 'bpmn-js/dist/assets/bpmn-font/css/bpmn.css';
+  import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-codes.css';
+  import 'bpmn-js/dist/assets/bpmn-font/css/bpmn-embedded.css';
+  // import customModule from './custom';
+  import CustomModeler from './customModeler';
+  import NodeModal from './modal.vue';
+  import CodeModal from './code.vue';
+  import { putNodeNum, putLineNum, getLineNum, getNodeNum } from './customModeler/custom/store';
+  import { useRoute } from 'vue-router';
+  import { useModal } from '/@/components/Modal';
+  import { nanoid } from 'nanoid';
+
+  const route = useRoute();
+
+  const canvasRef = ref();
+  const [registerCodeModal, { openModal: openCodeModal }] = useModal();
+  const [registerNodeModal, { openModal: openNodeModal }] = useModal();
+
+  const state = reactive({
+    data: [] as any,
+    bpmnModeler: {} as any,
+    numFlag: 'x' as any,
+  });
+
+  const props = defineProps({
+    prxml: String,
+  });
+
+  const { bpmnModeler } = toRefs(state);
+
+  onMounted(async () => {
+    if (route.query?.uuid) {
+      state.numFlag = route.query?.uuid;
+    } else if (route.query?.id) {
+      state.numFlag = route.query?.id;
+    }
+    console.log('numFlag2:' + state.numFlag);
+    bpmnModeler.value = new CustomModeler({
+      container: canvasRef.value,
+      additionalModules: [
+        {
+          __init__: ['labelEditingProvider'],
+          labelEditingProvider: ['value', null],
+        },
+      ],
+    });
+
+    bpmnModeler.value.get('keyboard').bind(document);
+
+    if (props.prxml) {
+      await bpmnModeler.value.importXML(props.prxml, (err: any) => {
+        if (err) {
+          console.error(err);
+        } else {
+          console.log('ok');
+        }
+      });
+    } else {
+      await bpmnModeler.value.importXML(defxml, (err: any) => {
+        if (err) {
+          console.error(err);
+        } else {
+          console.log('ok');
+        }
+      });
+    }
+    addModelerListener();
+    addEventBusListener();
+  });
+
+  const addModelerListener = () => {
+    // const bpmnjs = this.bpmnModeler
+    // const that = this
+    // 这里我是用了一个forEach给modeler上添加要绑定的事件
+    const events = [
+      'shape.added',
+      'shape.move.end',
+      'shape.removed',
+      'connection.added',
+      'connect.end',
+      'connect.move',
+    ];
+    events.forEach(function (event) {
+      bpmnModeler.value.on(event, e => {
+        const elementRegistry = bpmnModeler.value.get('elementRegistry');
+        const shape = e.element ? elementRegistry.get(e.element.id) : e.shape;
+        if (event == 'shape.added') {
+          debugger;
+          if (
+            e.element.id.substring(0, 1) == 'N' &&
+            e.element.id.substring(0, 2) !== 'NS' &&
+            e.element.id.substring(0, 2) !== 'NE'
+          ) {
+            // shape.name="审批节点"
+            putNodeNum(state.numFlag, e.element.id.substring(1) - 0);
+          }
+        } else if (event == 'connection.added') {
+          // console.log("创建了线段");
+          // console.log(e.element);
+          if (e.element.id.substring(0, 1) == 'L') {
+            putLineNum(state.numFlag, e.element.id.substring(1) - 0);
+          } else {
+            //如果更新Line的ID?
+            // bpmnModeler.value.get('modeling').updateProperties(e.element, {
+            // 	id: "L"+(getLineNum(state.numFlag)-0+1),
+            // 	// data:JSON.stringify(data),
+            // });
+            // putLineNum(state.numFlag, (getLineNum(state.numFlag)-0+1));
+          }
+        }
+      });
+    });
+  };
+
+  let currNode = {};
+  // let lastNum=10;
+
+  const addEventBusListener = () => {
+    const eventBus = bpmnModeler.value.get('eventBus'); // 需要使用eventBus
+    const eventTypes = ['element.dblclick']; // 需要监听的事件集合
+    eventTypes.forEach(function (eventType) {
+      eventBus.on(eventType, function (e) {
+        if (!e || e.element.type == 'bpmn:Process') return;
+
+        const elementRegistry = bpmnModeler.value.get('elementRegistry');
+        const shape = elementRegistry.get(e.element.id); // 传递id进去
+
+        // if(e.element.id.substring(0,1)!="N"){
+        //
+        // 	// bpmnModeler.value.get('modeling').updateProperties(shape,{
+        // 	// 	name: '我哎你'
+        // 	// })
+        //
+        // 	// // e.element.id="N"+(++lastNum);
+        // 	// shape.id="N"+(++lastNum);
+        // 	++lastNum;
+        // 	shape.id="N"+lastNum;
+        // 	shape.businessObject.id="N"+lastNum;
+        //
+        // 	console.log('新增了shape');
+        // 	// console.log(e.element.id);
+        // 	// 展示新增图形的属性
+        // }
+
+        console.log('双击了shape');
+        console.log(shape);
+        openNodeModal(true, {
+          record: shape,
+        });
+        currNode = shape;
+
+        // openNodeModal(shape);
+
+        // console.log(shape.businessObject.id);
+        // shape.businessObject.name='李四';
+        // bpmnModeler.value.get('modeling').updateProperties(shape,{
+        // 	name: '我是修改后的Task名称'
+        // })
+
+        // this.getModeling().updateProperties(this.getShape(), {
+        // 	[modelName]: multiple ? [newElement] : newElement,
+        // });
+        console.log(shape); // {Shape}
+        // console.log(e.element) // {Shape}
+        // console.log(JSON.stringify(shape)===JSON.stringify(e.element)) // true
+      });
+    });
+  };
+
+  const closeNodeModal = (data: any) => {
+    if (data.conds) {
+      bpmnModeler.value.get('modeling').updateProperties(currNode, {
+        name: data.name,
+        conds: data.conds,
+      });
+    } else {
+      bpmnModeler.value.get('modeling').updateProperties(currNode, {
+        name: data.name,
+        hamen: data.hamen,
+        flway: data.flway,
+        // data:JSON.stringify(data),
+      });
+    }
+  };
+
+  const getXml = async () => {
+    let backXml = '';
+    await bpmnModeler.value.saveXML({ format: true }).then(res => {
+      backXml = res.xml;
+    });
+    return backXml;
+  };
+
+  defineExpose({ getXml });
+
+  const defxml =
+    '<?xml version="1.0" encoding="UTF-8"?>\n' +
+    '<bpmn2:definitions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmn2="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:activiti="http://activiti.org/bpmn" id="sample-diagram" targetNamespace="http://activiti.org/bpmn" xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd">\n' +
+    '  <bpmn2:process id="Process_1" name="1" isExecutable="true">\n' +
+    '    <bpmn2:startEvent id="NS" name="开始节点">\n' +
+    '      <bpmn2:outgoing>L1</bpmn2:outgoing>\n' +
+    '    </bpmn2:startEvent>\n' +
+    '    <bpmn2:sequenceFlow id="L1" sourceRef="NS" targetRef="N1" />\n' +
+    '    <bpmn2:endEvent id="NE" name="结束节点">\n' +
+    '      <bpmn2:incoming>L2</bpmn2:incoming>\n' +
+    '    </bpmn2:endEvent>\n' +
+    '    <bpmn2:userTask id="N1" name="起草节点" activiti:assignee="l4" activiti:candidateUsers="">\n' +
+    '      <bpmn2:documentation>起草节点,表单数据一般从绑定的表单提取</bpmn2:documentation>\n' +
+    '      <bpmn2:extensionElements>\n' +
+    '        <activiti:formProperty id="userid" type="string" />\n' +
+    '        <activiti:formProperty id="money" type="int" />\n' +
+    '        <activiti:properties>\n' +
+    '          <activiti:property name="编辑" value="edit" />\n' +
+    '          <activiti:property name="撤回" value="back" />\n' +
+    '          <activiti:property name="提交" value="commit" />\n' +
+    '        </activiti:properties>\n' +
+    '      </bpmn2:extensionElements>\n' +
+    '      <bpmn2:incoming>L1</bpmn2:incoming>\n' +
+    '      <bpmn2:outgoing>L2</bpmn2:outgoing>\n' +
+    '    </bpmn2:userTask>\n' +
+    '    <bpmn2:sequenceFlow id="L2" sourceRef="N1" targetRef="NE" />\n' +
+    '  </bpmn2:process>\n' +
+    '  <bpmndi:BPMNDiagram id="BPMNDiagram_1">\n' +
+    '    <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="Process_1">\n' +
+    '      <bpmndi:BPMNEdge id="Flow_1u6pmzo_di" bpmnElement="L1">\n' +
+    '        <di:waypoint x="360" y="78" />\n' +
+    '        <di:waypoint x="360" y="160" />\n' +
+    '      </bpmndi:BPMNEdge>\n' +
+    '      <bpmndi:BPMNEdge id="Flow_0rj5mf6_di" bpmnElement="L2">\n' +
+    '        <di:waypoint x="360" y="240" />\n' +
+    '        <di:waypoint x="360" y="452" />\n' +
+    '      </bpmndi:BPMNEdge>\n' +
+    '      <bpmndi:BPMNShape id="Event_0byql27_di" bpmnElement="NS">\n' +
+    '        <dc:Bounds x="342" y="42" width="36" height="36" />\n' +
+    '        <bpmndi:BPMNLabel>\n' +
+    '          <dc:Bounds x="339" y="12" width="43" height="14" />\n' +
+    '        </bpmndi:BPMNLabel>\n' +
+    '      </bpmndi:BPMNShape>\n' +
+    '      <bpmndi:BPMNShape id="Activity_0g48n8q_di" bpmnElement="N1">\n' +
+    '        <dc:Bounds x="310" y="160" width="100" height="80" />\n' +
+    '      </bpmndi:BPMNShape>\n' +
+    '      <bpmndi:BPMNShape id="Event_1h4oob7_di" bpmnElement="NE">\n' +
+    '        <dc:Bounds x="342" y="452" width="36" height="36" />\n' +
+    '        <bpmndi:BPMNLabel>\n' +
+    '          <dc:Bounds x="339" y="495" width="43" height="14" />\n' +
+    '        </bpmndi:BPMNLabel>\n' +
+    '      </bpmndi:BPMNShape>\n' +
+    '    </bpmndi:BPMNPlane>\n' +
+    '  </bpmndi:BPMNDiagram>\n' +
+    '</bpmn2:definitions>\n';
+
+  const impXml = () => {};
+
+  const expXML = async () => {
+    await bpmnModeler.value.saveXML({ format: true }).then(res => {
+      download(res.xml, 'process', 'bpmn');
+    });
+  };
+
+  const expSVG = async () => {
+    await bpmnModeler.value.saveSVG({ format: true }).then(res => {
+      download(res.svg, 'process', 'svg');
+    });
+  };
+
+  //文本下载
+  const download = (data: string, filename: string, type: string): void => {
+    const blob = new Blob([data]);
+    const tempLink = document.createElement('a'); // 创建a标签
+    const href = window.URL.createObjectURL(blob); // 创建下载的链接
+    //filename
+    const fileName = `${filename}.${type}`;
+    tempLink.href = href;
+    tempLink.target = '_blank';
+    tempLink.download = fileName;
+    document.body.appendChild(tempLink);
+    tempLink.click(); // 点击下载
+    document.body.removeChild(tempLink); // 下载完成移除元素
+    window.URL.revokeObjectURL(href); // 释放掉blob对象
+  };
+
+  const codeShow = async () => {
+    await bpmnModeler.value.saveXML({ format: true }).then(res => {
+      openCodeModal(true, {
+        record: res.xml,
+      });
+    });
+  };
+</script>
+
+<style scoped>
+  .containers {
+    position: absolute;
+    background-color: #ffffff;
+    width: 100%;
+    height: 100%;
+    background: url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNDAiIGhlaWdodD0iNDAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PHBhdHRlcm4gaWQ9ImEiIHdpZHRoPSI0MCIgaGVpZ2h0PSI0MCIgcGF0dGVyblVuaXRzPSJ1c2VyU3BhY2VPblVzZSI+PHBhdGggZD0iTTAgMTBoNDBNMTAgMHY0ME0wIDIwaDQwTTIwIDB2NDBNMCAzMGg0ME0zMCAwdjQwIiBmaWxsPSJub25lIiBzdHJva2U9IiNlMGUwZTAiIG9wYWNpdHk9Ii4yIi8+PHBhdGggZD0iTTQwIDBIMHY0MCIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjZTBlMGUwIi8+PC9wYXR0ZXJuPjwvZGVmcz48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSJ1cmwoI2EpIi8+PC9zdmc+')
+      repeat !important;
+  }
+
+  .dark .containers {
+    background-color: #666 !important;
+  }
+
+  .canvas {
+    width: 100%;
+    height: 100%;
+  }
+
+  .panel {
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 300px;
+  }
+</style>

+ 118 - 0
src/components/Bpmn/tmpl/modal.vue

@@ -0,0 +1,118 @@
+<template>
+  <BasicModal
+    v-bind="$attrs"
+    destroyOnClose
+    @register="registerModal"
+    :title="getTitle"
+    @ok="handleSubmit"
+    width="900px"
+  >
+    <a-tabs v-model:activeKey="activeKey">
+      <a-tab-pane key="1" tab="基本">
+        <BasicForm @register="registerForm" />
+      </a-tab-pane>
+      <a-tab-pane key="2" tab="事件"> 暂未设置 </a-tab-pane>
+    </a-tabs>
+  </BasicModal>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form';
+  import { dataFormSchema } from './data';
+
+  const emit = defineEmits(['close']);
+  const getTitle = ref('节点操作');
+  const activeKey = ref('1');
+  const [registerForm, { setFieldsValue, getFieldsValue, validate }] = useForm({
+    labelWidth: 160,
+    schemas: dataFormSchema,
+    showActionButtonGroup: false,
+    baseColProps: { lg: 24, md: 24 },
+    actionColOptions: {
+      span: 23,
+    },
+  });
+  const [registerModal, { setModalProps, closeModal }] = useModalInner(async data => {
+    console.log('🚀 ~ file: code.vue:21 ~ data:', data);
+    setModalProps({ confirmLoading: false });
+    const record = data.record;
+    getTitle.value = record.businessObject.name + record.id;
+    await setFieldsValue(setFormInfo(record));
+  });
+
+  function setFormInfo(record: any) {
+    const data = {} as any;
+    switch (record.type) {
+      case 'bpmn:UserTask':
+        data.title = '审批节点 ' + record.id;
+        if (record.id == 'N1') {
+          data.title = '起草节点 ' + record.id;
+        }
+        break;
+      case 'bpmn:ReceiveTask':
+        data.title = '抄送节点 ' + record.id;
+        break;
+      case 'bpmn:StartEvent':
+        data.title = '开始节点 ' + record.id;
+        break;
+      case 'bpmn:EndEvent':
+        data.title = '结束节点 ' + record.id;
+        break;
+      case 'bpmn:ScriptTask':
+        data.title = '脚本节点 ' + record.id;
+        break;
+      case 'bpmn:ServiceTask':
+        data.title = '服务节点 ' + record.id;
+        break;
+      case 'bpmn:ExclusiveGateway':
+        data.title = '互斥网关 ' + record.id;
+        break;
+      case 'bpmn:ParallelGateway':
+        data.title = '并行网关 ' + record.id;
+        break;
+      case 'bpmn:SequenceFlow':
+        data.title = '连线 ' + record.id;
+        data.name = '连线名称:';
+        data.notes = '连线备注:';
+        if (record?.source.type == 'bpmn:ExclusiveGateway') {
+          data.cotag = true;
+        }
+        break;
+      default:
+        data.title = '未知节点 ' + record.id;
+        break;
+    }
+    data.type = record.type;
+    data.name = record.businessObject.name || data.title;
+    data.flway = record.businessObject.$attrs.flway;
+    if (!data.flway) {
+      data.flway = 1;
+    }
+    data.hatyp = record.businessObject.$attrs.hatyp;
+    if (!data.hatyp) {
+      data.hatyp = 1;
+    }
+    data.conds = record.businessObject.$attrs.conds;
+    if (record.businessObject.$attrs.hamen) {
+      console.log('record.businessObject.$attrs.hamen', record.businessObject.$attrs.hamen);
+      data.hamen = '1,2,3,4';
+    } else {
+      data.hamen = [];
+    }
+    data.isShow = true;
+    return data;
+  }
+
+  // 提交按钮事件
+  async function handleSubmit() {
+    const data = getFieldsValue();
+    data.hamen = data.hamen.split(',');
+    console.log('🚀 ~ file: modal.vue:110 ~ handleSubmit ~ data:', data);
+    emit('close', data);
+    closeModal();
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 96 - 0
src/components/Bpmn/tmpl/modal/TaskModal.vue

@@ -0,0 +1,96 @@
+<template>
+	<el-dialog v-model='state.isShow' title='表配置' width='1000px' draggable>
+		<div style='height: 600px'>
+			<el-row gutter="8">
+				<el-col :span='8'>
+					<div style='height: 600px'>
+						<TableTree @node-click='nodeClick'/>
+					</div>
+				</el-col>
+				<el-col :span='16'>
+					<el-table ref="tableRef" height='600' :cell-style="{padding:'2px'}" :row-style="{height: '36px'}"
+										v-loading='state.loading' :data='state.list'
+										border stripe>
+						<el-table-column type='selection' width='55' align='center'/>
+						<el-table-column label='序号' type='index' width='55' align='center'/>
+						<el-table-column label='字段名称' width='120'>
+							<template #default='scope'>
+								{{ scope.row.name }}
+							</template>
+						</el-table-column>
+						<el-table-column label='字段注释' prop='comet'/>
+						<el-table-column label='字段类型' prop='type' width='140'/>
+						<el-table-column label='KEY' prop='cokey' width='60'/>
+					</el-table>
+				</el-col>
+			</el-row>
+		</div>
+		<template #footer>
+      <span class='dialog-footer'>
+        <el-button type='primary' @click='closeModal'>确认</el-button>
+        <el-button @click='state.isShow = false'>取消</el-button>
+      </span>
+		</template>
+	</el-dialog>
+</template>
+
+<script lang='ts' setup>
+import {defineExpose, reactive, ref} from 'vue';
+import TableTree from './tableTree.vue';
+import request from "/@/utils/request";
+import {vListQuery} from "/@/comps/vxe";
+
+const state = reactive({
+	isShow: false,basid:'',
+	list:[] as any,
+	tabna:'',
+});
+
+
+const openModal = async (data: any) => {
+	state.isShow = true;
+};
+
+defineExpose({ openModal });
+
+const tableRef=ref();
+
+const emits = defineEmits(['close']);
+const closeModal = () => {
+	const backData={tabna:state.tabna,fields:[] as any};
+	const selectedData= tableRef.value.getSelectionRows();
+	for (const row of selectedData) {
+		const myRow={} as any;
+		myRow.name=row.name;
+		myRow.comet=row.comet;
+		myRow.type=row.type;
+		backData.fields.push(myRow);
+	}
+	console.log(backData)
+	emits('close', backData);
+	state.isShow = false;
+};
+
+//region a 左侧部门树点击
+const nodeClick = async (node: any) => {
+	if(node){
+		state.tabna = node.name;
+		state.basid = node.basid;
+		await listQuery();
+	}
+};
+//endregion
+
+const listQuery=async ()=>{
+	state.list = await request({
+		url: '/bi/model/field/list',
+		params:{tabna:state.tabna,basid:state.basid},
+		method: 'get',
+	});
+}
+
+</script>
+
+<style scoped>
+
+</style>

+ 28 - 0
src/components/Bpmn/tmpl/utils/util.js

@@ -0,0 +1,28 @@
+const customElements = ['bpmn:Task','bpmn:UserTask'] // 自定义元素的类型
+const customConfig = { // 自定义元素的配置
+    'bpmn:Task': {
+        // 'url': require('@assets/rules.png'),
+        // 'url': require('../../assets/rules.png'),
+        // 'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png',
+        'attr': { x: 0, y: 0, width: 220, height: 80}
+    },
+    'bpmn:UserTask': {
+        // 'url': require('@assets/rules.png'),
+        // 'url': require('../../assets/rules.png'),
+        'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png',
+        'attr': { x: 0, y: 0, width: 100, height: 80}
+    },
+    'bpmn:UserNode': {
+        // 'url': require('@assets/rules.png'),
+        // 'url': require('../../assets/rules.png'),
+        'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/rules.png',
+        'attr': { x: 0, y: 0, width: 220, height: 80}
+    },
+    // 'bpmn:StartEvent': {
+    //     'url': 'https://hexo-blog-1256114407.cos.ap-shenzhen-fsi.myqcloud.com/start.png',
+    //     'attr': { x: 0, y: 0, width: 40, height: 40 }
+    // }
+}
+const hasLabelElements = ['bpmn:StartEvent', 'bpmn:EndEvent'] // 一开始就有label标签的元素类型
+
+export { customElements, customConfig, hasLabelElements }

+ 9 - 3
src/layouts/default/feature/index.vue

@@ -16,11 +16,14 @@
     components: {
       BackTop,
       // LayoutLockPage: createAsyncComponent(() => import('/@/views/base/lock/index.vue')),
-      SettingDrawer: createAsyncComponent(() => import('/@/layouts/default/header/components/setting/index.vue')),
+      SettingDrawer: createAsyncComponent(
+        () => import('/@/layouts/default/header/components/setting/index.vue'),
+      ),
       SessionTimeoutLogin,
     },
     setup() {
-      const { getUseOpenBackTop, getShowSettingButton, getSettingButtonPosition, getFullContent } = useRootSetting();
+      const { getUseOpenBackTop, getShowSettingButton, getSettingButtonPosition, getFullContent } =
+        useRootSetting();
       const userStore = useUserStoreWithOut();
       const { prefixCls } = useDesign('setting-drawer-feature');
       const { getShowHeader } = useHeaderSetting();
@@ -59,7 +62,10 @@
 
 <style lang="less">
   @prefix-cls: ~'@{namespace}-setting-drawer-feature';
-
+  .ant-back-top-icon {
+    font-size: 24px;
+    line-height: 2.2;
+  }
   .@{prefix-cls} {
     position: absolute;
     top: 45%;

+ 0 - 2
src/main.ts

@@ -22,8 +22,6 @@ import 'ant-design-vue/dist/antd.variable.min.css';
 import './assets/iconfont/iconfont.js';
 import './assets/iconfont/iconfont.css';
 
-import useWebSocketFn from './hooks/web/useWebsocket';
-
 async function bootstrap() {
   const app = createApp(App);
   // Configure store

+ 1 - 0
src/store/modules/user.ts

@@ -149,6 +149,7 @@ export const useUserStore = defineStore({
       // this.setUserInfo(userInfo);
       // getWebsocket();
       const sessionTimeout = this.sessionTimeout;
+      console.log('🚀 ~ file: user.ts:152 ~ afterLoginAction ~ sessionTimeout:', sessionTimeout);
       if (sessionTimeout) {
         this.setSessionTimeout(false);
       } else {

+ 97 - 0
src/views/monitor/monitorAbout/index.vue

@@ -0,0 +1,97 @@
+<template>
+  <PageWrapper title="关于前端信息">
+    <template #headerContent>
+      <div class="flex justify-between items-center">
+        <span class="flex-1">
+          <a :href="GITHUB_URL" target="_blank">{{ name }}</a>
+          是一个基于Vue3.0、Vite、 Ant-Design-Vue 、TypeScript 的后台解决方案。
+        </span>
+      </div>
+    </template>
+    <Description @register="infoRegister" class="enter-y" />
+    <Description @register="register" class="my-4 enter-y" />
+    <Description @register="registerDev" class="enter-y" />
+  </PageWrapper>
+</template>
+<script lang="ts" setup>
+  import { h } from 'vue';
+  import { Tag } from 'ant-design-vue';
+  import { PageWrapper } from '/@/components/Page';
+  import { Description, DescItem, useDescription } from '/@/components/Description/index';
+  import { GITHUB_URL, SITE_URL, DOC_URL } from '/@/settings/siteSetting';
+
+  const { pkg, lastBuildTime } = __APP_INFO__;
+
+  const { dependencies, devDependencies, name, version } = pkg;
+
+  const schema: DescItem[] = [];
+  const devSchema: DescItem[] = [];
+
+  const commonTagRender = (color: string) => curVal => h(Tag, { color }, () => curVal);
+  const commonLinkRender = (text: string) => href => h('a', { href, target: '_blank' }, text);
+
+  const infoSchema: DescItem[] = [
+    {
+      label: '版本',
+      field: 'version',
+      render: commonTagRender('blue'),
+    },
+    {
+      label: '最后编译时间',
+      field: 'lastBuildTime',
+      render: commonTagRender('blue'),
+    },
+    {
+      label: '文档地址',
+      field: 'doc',
+      render: commonLinkRender('文档地址'),
+    },
+    {
+      label: '预览地址',
+      field: 'preview',
+      render: commonLinkRender('预览地址'),
+    },
+    {
+      label: 'Github',
+      field: 'github',
+      render: commonLinkRender('Github'),
+    },
+  ];
+
+  const infoData = {
+    version,
+    lastBuildTime,
+    doc: DOC_URL,
+    preview: SITE_URL,
+    github: GITHUB_URL,
+  };
+
+  Object.keys(dependencies).forEach(key => {
+    schema.push({ field: key, label: key });
+  });
+
+  Object.keys(devDependencies).forEach(key => {
+    devSchema.push({ field: key, label: key });
+  });
+
+  const [register] = useDescription({
+    title: '生产环境依赖',
+    data: dependencies,
+    schema: schema,
+    column: 3,
+  });
+
+  const [registerDev] = useDescription({
+    title: '开发环境依赖',
+    data: devDependencies,
+    schema: devSchema,
+    column: 3,
+  });
+
+  const [infoRegister] = useDescription({
+    title: '项目信息',
+    data: infoData,
+    schema: infoSchema,
+    column: 2,
+  });
+</script>

+ 0 - 0
src/views/sys/sysLog/DescDrawer.vue → src/views/monitor/monitorLog/DescDrawer.vue


+ 0 - 0
src/views/sys/sysLog/data.ts → src/views/monitor/monitorLog/data.ts


+ 0 - 0
src/views/sys/sysLog/index.vue → src/views/monitor/monitorLog/index.vue


+ 0 - 0
src/views/sys/onlineUser/data.ts → src/views/monitor/onlineUser/data.ts


+ 0 - 0
src/views/sys/onlineUser/index.vue → src/views/monitor/onlineUser/index.vue


+ 31 - 0
src/views/sys/sysBpmn/index.vue

@@ -0,0 +1,31 @@
+<template>
+  <div>
+    <div>
+      <div>
+        <a-button type="success" @click="save" plain>保 存</a-button>
+        <a-button type="info" plain>复 制</a-button>
+        <a-button type="info" plain>关 闭</a-button>
+      </div>
+    </div>
+    <BpmTmplEdit ref="bpmTmplEditRef" :prxml="form.prxml" />
+  </div>
+</template>
+
+<script setup lang="ts">
+  import { ref } from 'vue';
+  import BpmTmplEdit from '/@/components/Bpmn/tmpl/edit.vue';
+
+  const form = ref({
+    avtag: true,
+    protd: '',
+    prxml: '',
+  });
+
+  const bpmTmplEditRef = ref();
+  async function save() {
+    // form.value.vform = JSON.stringify(vFormRef.value.getFormJson());
+    form.value.prxml = await bpmTmplEditRef.value.getXml();
+  }
+</script>
+
+<style lang="less" scoped></style>

+ 0 - 7
src/views/sys/sysFlow/index.vue

@@ -1,7 +0,0 @@
-<template>
-  <div> bpmn logicFlow </div>
-</template>
-
-<script setup lang="ts"></script>
-
-<style lang="less" scoped></style>

+ 12 - 33
src/views/sys/sysMenu/sysMenuTable/FormModal.vue

@@ -16,25 +16,18 @@
   import { BasicForm, useForm } from '/@/components/Form';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { dataFormSchema } from './data';
-
-  // import {
-  //   // addObj,
-  //   // editObj,
-  //   // viewObj,
-  //   selectMenuTreeAndExcludeNode,
-  // } from '/@/api/modules/system/sysMenuApi';
-
   import {
     rbacMenuAdd,
     rbacMenuEdit,
     rbacMenuDetailId,
     rbacMenuQueryTreeAbbr,
   } from '/@/api/sys/rbacMenuApi';
-  import { listDictModel } from '/@/api/common';
-  import { transformDict } from '/@/utils';
+  import { usePermissionStore } from '/@/store/modules/permission';
 
   const emit = defineEmits(['success', 'register']);
 
+  const permissionStore = usePermissionStore();
+
   const getTitle = computed(() => (!unref(isUpdate) ? '新增菜单' : '编辑菜单'));
   const isUpdate = ref(false);
   const rowId = ref();
@@ -74,37 +67,23 @@
       });
     }
 
-    const sysMenuTypeOptions = transformDict(await listDictModel({ dictCode: 'sys_menu_type' }));
     const treeData = await rbacMenuQueryTreeAbbr({ excludeNodeIds: [data.record.id] });
-    await updateSchema([
-      {
-        field: 'menuType',
-        componentProps: { options: sysMenuTypeOptions },
-      },
-      // {
-      //   field: 'parentId',
-      //   componentProps: {
-      //     treeData: treeData,
-      //     fieldNames: {
-      //       label: 'name',
-      //       key: 'id',
-      //       value: 'id',
-      //     },
-      //   },
-      // },
-    ]);
     await updateSchema({ field: 'parentId', componentProps: { treeData } });
   });
-
   // 提交按钮事件
   async function handleSubmit() {
     try {
       const values = await validate();
       setModalProps({ confirmLoading: true });
-      !unref(isUpdate)
-        ? await rbacMenuAdd({ ...values })
-        : await rbacMenuEdit({ ...values, id: rowId.value });
-      !unref(isUpdate) ? createMessage.success('新增成功!') : createMessage.success('编辑成功!');
+      if (!unref(isUpdate)) {
+        await rbacMenuAdd({ ...values });
+        // 重新构建路由
+        await permissionStore.buildRoutesAction();
+        createMessage.success('新增成功!');
+      } else {
+        await rbacMenuEdit({ ...values, id: rowId.value });
+        createMessage.success('编辑成功!');
+      }
       closeModal();
       emit('success', { isUpdate: unref(isUpdate), values: { ...values, id: rowId.value } });
     } finally {

+ 11 - 2
src/views/sys/sysMenu/sysMenuTable/data.ts

@@ -101,9 +101,18 @@ export const dataFormSchema: FormSchema[] = [
   {
     field: 'menuType',
     label: '菜单类型',
-    component: 'RadioButtonGroup',
-    defaultValue: 'dir',
+    component: 'ApiRadioGroup',
     required: true,
+    componentProps: {
+      api: listDictModel,
+      isBtn: true,
+      params: {
+        dictCode: 'sys_menu_type',
+      },
+      labelField: 'dictItemName',
+      valueField: 'dictItemCode',
+    },
+    defaultValue: 'dir',
   },
   {
     field: 'menuName',