mirror of
https://gitee.com/myxzgzs/boyue-ui-admin-vue3
synced 2025-08-08 08:22:41 +08:00
Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into dev
# Conflicts: # pnpm-lock.yaml # src/views/ai/model/model/index.vue # src/views/bpm/model/form/index.vue
This commit is contained in:
commit
be6ae75ce5
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "yudao-ui-admin-vue3",
|
||||
"version": "2.4.0-snapshot",
|
||||
"version": "2.4.1-snapshot",
|
||||
"description": "基于vue3、vite4、element-plus、typesScript",
|
||||
"author": "xingyu",
|
||||
"private": false,
|
||||
@ -73,6 +73,7 @@
|
||||
"vue-i18n": "9.10.2",
|
||||
"vue-router": "4.4.5",
|
||||
"vue-types": "^5.1.1",
|
||||
"vue3-signature": "^0.2.4",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"web-storage-cache": "^1.1.1",
|
||||
"xml-js": "^1.6.11"
|
||||
|
251
pnpm-lock.yaml
generated
251
pnpm-lock.yaml
generated
@ -49,7 +49,7 @@ importers:
|
||||
version: 1.1.5
|
||||
bpmn-js-token-simulation:
|
||||
specifier: ^0.36.0
|
||||
version: 0.36.3
|
||||
version: 0.36.2
|
||||
camunda-bpmn-moddle:
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1
|
||||
@ -152,6 +152,9 @@ importers:
|
||||
vue-types:
|
||||
specifier: ^5.1.1
|
||||
version: 5.1.3(vue@3.5.12(typescript@5.3.3))
|
||||
vue3-signature:
|
||||
specifier: ^0.2.4
|
||||
version: 0.2.4(vue@3.5.12(typescript@5.3.3))
|
||||
vuedraggable:
|
||||
specifier: ^4.1.0
|
||||
version: 4.1.0(vue@3.5.12(typescript@5.3.3))
|
||||
@ -865,7 +868,7 @@ packages:
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@bpmn-io/cm-theme@0.1.0-alpha.2':
|
||||
resolution: {integrity: sha512-ZILgiYzxk3KMvxplUXmdRFQo45/JehDPg5k9tWfehmzUOSE13ssyLPil8uCloMQnb3yyzyOWTjb/wzKXTHlFQw==, tarball: https://registry.npmmirror.com/@bpmn-io/cm-theme/-/cm-theme-0.1.0-alpha.2.tgz}
|
||||
resolution: {integrity: sha512-ZILgiYzxk3KMvxplUXmdRFQo45/JehDPg5k9tWfehmzUOSE13ssyLPil8uCloMQnb3yyzyOWTjb/wzKXTHlFQw==}
|
||||
|
||||
'@bpmn-io/diagram-js-ui@0.2.3':
|
||||
resolution: {integrity: sha512-OGyjZKvGK8tHSZ0l7RfeKhilGoOGtFDcoqSGYkX0uhFlo99OVZ9Jn1K7TJGzcE9BdKwvA5Y5kGqHEhdTxHvFfw==, tarball: https://registry.npmmirror.com/@bpmn-io/diagram-js-ui/-/diagram-js-ui-0.2.3.tgz}
|
||||
@ -874,17 +877,17 @@ packages:
|
||||
resolution: {integrity: sha512-yAS7ZYX+D56K+luC36u96eRMLb4VHcPUwTUqMZ/Z/Je2gou2DJLRbuBTHAB4jjKt4wFCHSG4B8Y+TrBciEYf4w==, tarball: https://registry.npmmirror.com/@bpmn-io/extract-process-variables/-/extract-process-variables-0.8.0.tgz}
|
||||
|
||||
'@bpmn-io/feel-editor@1.9.1':
|
||||
resolution: {integrity: sha512-UxSORdh5cwKM4fib4f9ov6J1/BHGpQVNtA+wPyEdKQyCyz3wqwE2/xe5wneVR1j5QFC5m2Na8nTy4a1TDFvZTw==, tarball: https://registry.npmmirror.com/@bpmn-io/feel-editor/-/feel-editor-1.9.1.tgz}
|
||||
resolution: {integrity: sha512-UxSORdh5cwKM4fib4f9ov6J1/BHGpQVNtA+wPyEdKQyCyz3wqwE2/xe5wneVR1j5QFC5m2Na8nTy4a1TDFvZTw==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
'@bpmn-io/feel-lint@1.3.1':
|
||||
resolution: {integrity: sha512-wcFkJKhOm/iqCt5bzkKvxL5Dr9wKwUD+t164bQYbJsTYouAqmkkxiGsoqck42hXwdIhMSguZ+vqQ3hj5QdiYCA==, tarball: https://registry.npmmirror.com/@bpmn-io/feel-lint/-/feel-lint-1.3.1.tgz}
|
||||
resolution: {integrity: sha512-wcFkJKhOm/iqCt5bzkKvxL5Dr9wKwUD+t164bQYbJsTYouAqmkkxiGsoqck42hXwdIhMSguZ+vqQ3hj5QdiYCA==}
|
||||
|
||||
'@bpmn-io/properties-panel@3.25.0':
|
||||
resolution: {integrity: sha512-SRGgj8uJc1Yyjcht2g36Q+xKR7sTx5VZXvcwDrdmQKlx5Y3nRmvmMjDGzeGDJDb7pNU1DSlaBJic84uISDBMWg==, tarball: https://registry.npmmirror.com/@bpmn-io/properties-panel/-/properties-panel-3.25.0.tgz}
|
||||
resolution: {integrity: sha512-SRGgj8uJc1Yyjcht2g36Q+xKR7sTx5VZXvcwDrdmQKlx5Y3nRmvmMjDGzeGDJDb7pNU1DSlaBJic84uISDBMWg==}
|
||||
|
||||
'@codemirror/autocomplete@6.18.3':
|
||||
resolution: {integrity: sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==, tarball: https://registry.npmmirror.com/@codemirror/autocomplete/-/autocomplete-6.18.3.tgz}
|
||||
resolution: {integrity: sha512-1dNIOmiM0z4BIBwxmxEfA1yoxh1MF/6KPBbh20a5vphGV0ictKlgQsbJs6D6SkR6iJpGbpwRsa6PFMNlg9T9pQ==}
|
||||
peerDependencies:
|
||||
'@codemirror/language': ^6.0.0
|
||||
'@codemirror/state': ^6.0.0
|
||||
@ -892,19 +895,19 @@ packages:
|
||||
'@lezer/common': ^1.0.0
|
||||
|
||||
'@codemirror/commands@6.7.1':
|
||||
resolution: {integrity: sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==, tarball: https://registry.npmmirror.com/@codemirror/commands/-/commands-6.7.1.tgz}
|
||||
resolution: {integrity: sha512-llTrboQYw5H4THfhN4U3qCnSZ1SOJ60ohhz+SzU0ADGtwlc533DtklQP0vSFaQuCPDn3BPpOd1GbbnUtwNjsrw==}
|
||||
|
||||
'@codemirror/language@6.10.6':
|
||||
resolution: {integrity: sha512-KrsbdCnxEztLVbB5PycWXFxas4EOyk/fPAfruSOnDDppevQgid2XZ+KbJ9u+fDikP/e7MW7HPBTvTb8JlZK9vA==, tarball: https://registry.npmmirror.com/@codemirror/language/-/language-6.10.6.tgz}
|
||||
resolution: {integrity: sha512-KrsbdCnxEztLVbB5PycWXFxas4EOyk/fPAfruSOnDDppevQgid2XZ+KbJ9u+fDikP/e7MW7HPBTvTb8JlZK9vA==}
|
||||
|
||||
'@codemirror/lint@6.8.4':
|
||||
resolution: {integrity: sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==, tarball: https://registry.npmmirror.com/@codemirror/lint/-/lint-6.8.4.tgz}
|
||||
resolution: {integrity: sha512-u4q7PnZlJUojeRe8FJa/njJcMctISGgPQ4PnWsd9268R4ZTtU+tfFYmwkBvgcrK2+QQ8tYFVALVb5fVJykKc5A==}
|
||||
|
||||
'@codemirror/state@6.4.1':
|
||||
resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==, tarball: https://registry.npmmirror.com/@codemirror/state/-/state-6.4.1.tgz}
|
||||
resolution: {integrity: sha512-QkEyUiLhsJoZkbumGZlswmAhA7CBU02Wrz7zvH4SrcifbsqwlXShVXg65f3v/ts57W3dqyamEriMhij1Z3Zz4A==}
|
||||
|
||||
'@codemirror/view@6.35.0':
|
||||
resolution: {integrity: sha512-I0tYy63q5XkaWsJ8QRv5h6ves7kvtrBWjBcnf/bzohFJQc5c14a1AQRdE8QpPF9eMp5Mq2FMm59TCj1gDfE7kw==, tarball: https://registry.npmmirror.com/@codemirror/view/-/view-6.35.0.tgz}
|
||||
resolution: {integrity: sha512-I0tYy63q5XkaWsJ8QRv5h6ves7kvtrBWjBcnf/bzohFJQc5c14a1AQRdE8QpPF9eMp5Mq2FMm59TCj1gDfE7kw==}
|
||||
|
||||
'@commitlint/cli@19.6.0':
|
||||
resolution: {integrity: sha512-v17BgGD9w5KnthaKxXnEg6KLq6DYiAxyiN44TpiRtqyW8NSq+Kx99mkEG8Qo6uu6cI5eMzMojW2muJxjmPnF8w==, tarball: https://registry.npmmirror.com/@commitlint/cli/-/cli-19.6.0.tgz}
|
||||
@ -1011,139 +1014,139 @@ packages:
|
||||
vue: ^3.2.0
|
||||
|
||||
'@esbuild/aix-ppc64@0.19.12':
|
||||
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==, tarball: https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==, tarball: https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.19.12':
|
||||
resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==, tarball: https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.19.12':
|
||||
resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==, tarball: https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==, tarball: https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.19.12':
|
||||
resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==, tarball: https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==, tarball: https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.19.12':
|
||||
resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==, tarball: https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==, tarball: https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.19.12':
|
||||
resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==, tarball: https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.19.12':
|
||||
resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==, tarball: https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.19.12':
|
||||
resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==, tarball: https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.19.12':
|
||||
resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==, tarball: https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.19.12':
|
||||
resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==, tarball: https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.19.12':
|
||||
resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==, tarball: https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.19.12':
|
||||
resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==, tarball: https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.19.12':
|
||||
resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==, tarball: https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-x64@0.19.12':
|
||||
resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==, tarball: https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.19.12':
|
||||
resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==, tarball: https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/sunos-x64@0.19.12':
|
||||
resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==, tarball: https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.19.12':
|
||||
resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==, tarball: https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.19.12':
|
||||
resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==, tarball: https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.19.12':
|
||||
resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==, tarball: https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz}
|
||||
resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
|
||||
engines: {node: '>=12'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@ -1330,16 +1333,16 @@ packages:
|
||||
resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==, tarball: https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz}
|
||||
|
||||
'@lezer/common@1.2.3':
|
||||
resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==, tarball: https://registry.npmmirror.com/@lezer/common/-/common-1.2.3.tgz}
|
||||
resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==}
|
||||
|
||||
'@lezer/highlight@1.2.1':
|
||||
resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==, tarball: https://registry.npmmirror.com/@lezer/highlight/-/highlight-1.2.1.tgz}
|
||||
resolution: {integrity: sha512-Z5duk4RN/3zuVO7Jq0pGLJ3qynpxUVsh7IbUbGj88+uV2ApSAn6kWg2au3iJb+0Zi7kKtqffIESgNcRXWZWmSA==}
|
||||
|
||||
'@lezer/lr@1.4.2':
|
||||
resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==, tarball: https://registry.npmmirror.com/@lezer/lr/-/lr-1.4.2.tgz}
|
||||
resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==}
|
||||
|
||||
'@lezer/markdown@1.3.2':
|
||||
resolution: {integrity: sha512-Wu7B6VnrKTbBEohqa63h5vxXjiC4pO5ZQJ/TDbhJxPQaaIoRD/6UVDhSDtVsCwVZV12vvN9KxuLL3ATMnlG0oQ==, tarball: https://registry.npmmirror.com/@lezer/markdown/-/markdown-1.3.2.tgz}
|
||||
resolution: {integrity: sha512-Wu7B6VnrKTbBEohqa63h5vxXjiC4pO5ZQJ/TDbhJxPQaaIoRD/6UVDhSDtVsCwVZV12vvN9KxuLL3ATMnlG0oQ==}
|
||||
|
||||
'@microsoft/fetch-event-source@2.0.1':
|
||||
resolution: {integrity: sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==, tarball: https://registry.npmmirror.com/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz}
|
||||
@ -1357,95 +1360,89 @@ packages:
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
'@parcel/watcher-android-arm64@2.5.0':
|
||||
resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==, tarball: https://registry.npmmirror.com/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-qlX4eS28bUcQCdribHkg/herLe+0A9RyYC+mm2PXpncit8z5b3nSqGVzMNR3CmtAOgRutiZ02eIJJgP/b1iEFQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@parcel/watcher-darwin-arm64@2.5.0':
|
||||
resolution: {integrity: sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==, tarball: https://registry.npmmirror.com/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-hyZ3TANnzGfLpRA2s/4U1kbw2ZI4qGxaRJbBH2DCSREFfubMswheh8TeiC1sGZ3z2jUf3s37P0BBlrD3sjVTUw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@parcel/watcher-darwin-x64@2.5.0':
|
||||
resolution: {integrity: sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==, tarball: https://registry.npmmirror.com/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-9rhlwd78saKf18fT869/poydQK8YqlU26TMiNg7AIu7eBp9adqbJZqmdFOsbZ5cnLp5XvRo9wcFmNHgHdWaGYA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@parcel/watcher-freebsd-x64@2.5.0':
|
||||
resolution: {integrity: sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==, tarball: https://registry.npmmirror.com/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-syvfhZzyM8kErg3VF0xpV8dixJ+RzbUaaGaeb7uDuz0D3FK97/mZ5AJQ3XNnDsXX7KkFNtyQyFrXZzQIcN49Tw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@parcel/watcher-linux-arm-glibc@2.5.0':
|
||||
resolution: {integrity: sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-0VQY1K35DQET3dVYWpOaPFecqOT9dbuCfzjxoQyif1Wc574t3kOSkKevULddcR9znz1TcklCE7Ht6NIxjvTqLA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm-musl@2.5.0':
|
||||
resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-6uHywSIzz8+vi2lAzFeltnYbdHsDm3iIB57d4g5oaB9vKwjb6N6dRIgZMujw4nm5r6v9/BQH0noq6DzHrqr2pA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-arm64-glibc@2.5.0':
|
||||
resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-BfNjXwZKxBy4WibDb/LDCriWSKLz+jJRL3cM/DllnHH5QUyoiUNEp3GmL80ZqxeumoADfCCP19+qiYiC8gUBjA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-arm64-musl@2.5.0':
|
||||
resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-S1qARKOphxfiBEkwLUbHjCY9BWPdWnW9j7f7Hb2jPplu8UZ3nes7zpPOW9bkLbHRvWM0WDTsjdOTUgW0xLBN1Q==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-linux-x64-glibc@2.5.0':
|
||||
resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-d9AOkusyXARkFD66S6zlGXyzx5RvY+chTP9Jp0ypSTC9d4lzyRs9ovGf/80VCxjKddcUvnsGwCHWuF2EoPgWjw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@parcel/watcher-linux-x64-musl@2.5.0':
|
||||
resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==, tarball: https://registry.npmmirror.com/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-iqOC+GoTDoFyk/VYSFHwjHhYrk8bljW6zOhPuhi5t9ulqiYq1togGJB5e3PwYVFFfeVgc6pbz3JdQyDoBszVaA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@parcel/watcher-win32-arm64@2.5.0':
|
||||
resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==, tarball: https://registry.npmmirror.com/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-twtft1d+JRNkM5YbmexfcH/N4znDtjgysFaV9zvZmmJezQsKpkfLYJ+JFV3uygugK6AtIM2oADPkB2AdhBrNig==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher-win32-ia32@2.5.0':
|
||||
resolution: {integrity: sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==, tarball: https://registry.npmmirror.com/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-+rgpsNRKwo8A53elqbbHXdOMtY/tAtTzManTWShB5Kk54N8Q9mzNWV7tV+IbGueCbcj826MfWGU3mprWtuf1TA==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher-win32-x64@2.5.0':
|
||||
resolution: {integrity: sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==, tarball: https://registry.npmmirror.com/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-lPrxve92zEHdgeff3aiu4gDOIt4u7sJYha6wbdEZDCDUhtjTsOMiaJzG5lMY4GkWH8p0fMmO2Ppq5G5XXG+DQw==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@parcel/watcher@2.5.0':
|
||||
resolution: {integrity: sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==, tarball: https://registry.npmmirror.com/@parcel/watcher/-/watcher-2.5.0.tgz}
|
||||
resolution: {integrity: sha512-i0GV1yJnm2n3Yq1qw6QrUrd/LI9bE8WEBOTtOkpCXHHdyN3TAGgqAK/DAT05z4fq2x04cARXt2pDmjWjL92iTQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@pkgjs/parseargs@0.11.0':
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==, tarball: https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz}
|
||||
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
'@pkgr/core@0.1.1':
|
||||
@ -1487,101 +1484,92 @@ packages:
|
||||
optional: true
|
||||
|
||||
'@rollup/rollup-android-arm-eabi@4.27.4':
|
||||
resolution: {integrity: sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==, tarball: https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-2Y3JT6f5MrQkICUyRVCw4oa0sutfAsgaSsb0Lmmy1Wi2y7X5vT9Euqw4gOsCyy0YfKURBg35nhUKZS4mDcfULw==}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-android-arm64@4.27.4':
|
||||
resolution: {integrity: sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==, tarball: https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-wzKRQXISyi9UdCVRqEd0H4cMpzvHYt1f/C3CoIjES6cG++RHKhrBj2+29nPF0IB5kpy9MS71vs07fvrNGAl/iA==}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@rollup/rollup-darwin-arm64@4.27.4':
|
||||
resolution: {integrity: sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==, tarball: https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-PlNiRQapift4LNS8DPUHuDX/IdXiLjf8mc5vdEmUR0fF/pyy2qWwzdLjB+iZquGr8LuN4LnUoSEvKRwjSVYz3Q==}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-darwin-x64@4.27.4':
|
||||
resolution: {integrity: sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-o9bH2dbdgBDJaXWJCDTNDYa171ACUdzpxSZt+u/AAeQ20Nk5x+IhA+zsGmrQtpkLiumRJEYef68gcpn2ooXhSQ==}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@rollup/rollup-freebsd-arm64@4.27.4':
|
||||
resolution: {integrity: sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==, tarball: https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-NBI2/i2hT9Q+HySSHTBh52da7isru4aAAo6qC3I7QFVsuhxi2gM8t/EI9EVcILiHLj1vfi+VGGPaLOUENn7pmw==}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-freebsd-x64@4.27.4':
|
||||
resolution: {integrity: sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==, tarball: https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-wYcC5ycW2zvqtDYrE7deary2P2UFmSh85PUpAx+dwTCO9uw3sgzD6Gv9n5X4vLaQKsrfTSZZ7Z7uynQozPVvWA==}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@rollup/rollup-linux-arm-gnueabihf@4.27.4':
|
||||
resolution: {integrity: sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-9OwUnK/xKw6DyRlgx8UizeqRFOfi9mf5TYCw1uolDaJSbUmBxP85DE6T4ouCMoN6pXw8ZoTeZCSEfSaYo+/s1w==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.27.4':
|
||||
resolution: {integrity: sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-Vgdo4fpuphS9V24WOV+KwkCVJ72u7idTgQaBoLRD0UxBAWTF9GWurJO9YD9yh00BzbkhpeXtm6na+MvJU7Z73A==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.27.4':
|
||||
resolution: {integrity: sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-pleyNgyd1kkBkw2kOqlBx+0atfIIkkExOTiifoODo6qKDSpnc6WzUY5RhHdmTdIJXBdSnh6JknnYTtmQyobrVg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.27.4':
|
||||
resolution: {integrity: sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-caluiUXvUuVyCHr5DxL8ohaaFFzPGmgmMvwmqAITMpV/Q+tPoaHZ/PWa3t8B2WyoRcIIuu1hkaW5KkeTDNSnMA==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-powerpc64le-gnu@4.27.4':
|
||||
resolution: {integrity: sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-FScrpHrO60hARyHh7s1zHE97u0KlT/RECzCKAdmI+LEoC1eDh/RDji9JgFqyO+wPDb86Oa/sXkily1+oi4FzJQ==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.27.4':
|
||||
resolution: {integrity: sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-qyyprhyGb7+RBfMPeww9FlHwKkCXdKHeGgSqmIXw9VSUtvyFZ6WZRtnxgbuz76FK7LyoN8t/eINRbPUcvXB5fw==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.27.4':
|
||||
resolution: {integrity: sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-PFz+y2kb6tbh7m3A7nA9++eInGcDVZUACulf/KzDtovvdTizHpZaJty7Gp0lFwSQcrnebHOqxF1MaKZd7psVRg==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.27.4':
|
||||
resolution: {integrity: sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-Ni8mMtfo+o/G7DVtweXXV/Ol2TFf63KYjTtoZ5f078AUgJTmaIJnj4JFU7TK/9SVWTaSJGxPi5zMDgK4w+Ez7Q==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.27.4':
|
||||
resolution: {integrity: sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-5AeeAF1PB9TUzD+3cROzFTnAJAcVUGLuR8ng0E0WXGkYhp6RD6L+6szYVX+64Rs0r72019KHZS1ka1q+zU/wUw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-win32-arm64-msvc@4.27.4':
|
||||
resolution: {integrity: sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-yOpVsA4K5qVwu2CaS3hHxluWIK5HQTjNV4tWjQXluMiiiu4pJj4BN98CvxohNCpcjMeTXk/ZMJBRbgRg8HBB6A==}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-ia32-msvc@4.27.4':
|
||||
resolution: {integrity: sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-KtwEJOaHAVJlxV92rNYiG9JQwQAdhBlrjNRp7P9L8Cb4Rer3in+0A+IPhJC9y68WAi9H0sX4AiG2NTsVlmqJeQ==}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@rollup/rollup-win32-x64-msvc@4.27.4':
|
||||
resolution: {integrity: sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.27.4.tgz}
|
||||
resolution: {integrity: sha512-3j4jx1TppORdTAoBJRd+/wJRGCPC0ETWkXOecJ6PPZLj6SptXkrXcNqdj0oclbKML6FkQltdz7bBA3rUSirZug==}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
@ -1589,65 +1577,61 @@ packages:
|
||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==, tarball: https://registry.npmmirror.com/@sinclair/typebox/-/typebox-0.27.8.tgz}
|
||||
|
||||
'@swc/core-darwin-arm64@1.9.3':
|
||||
resolution: {integrity: sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w==, tarball: https://registry.npmmirror.com/@swc/core-darwin-arm64/-/core-darwin-arm64-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-hGfl/KTic/QY4tB9DkTbNuxy5cV4IeejpPD4zo+Lzt4iLlDWIeANL4Fkg67FiVceNJboqg48CUX+APhDHO5G1w==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@swc/core-darwin-x64@1.9.3':
|
||||
resolution: {integrity: sha512-IaRq05ZLdtgF5h9CzlcgaNHyg4VXuiStnOFpfNEMuI5fm5afP2S0FHq8WdakUz5WppsbddTdplL+vpeApt/WCQ==, tarball: https://registry.npmmirror.com/@swc/core-darwin-x64/-/core-darwin-x64-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-IaRq05ZLdtgF5h9CzlcgaNHyg4VXuiStnOFpfNEMuI5fm5afP2S0FHq8WdakUz5WppsbddTdplL+vpeApt/WCQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@swc/core-linux-arm-gnueabihf@1.9.3':
|
||||
resolution: {integrity: sha512-Pbwe7xYprj/nEnZrNBvZfjnTxlBIcfApAGdz2EROhjpPj+FBqBa3wOogqbsuGGBdCphf8S+KPprL1z+oDWkmSQ==, tarball: https://registry.npmmirror.com/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-Pbwe7xYprj/nEnZrNBvZfjnTxlBIcfApAGdz2EROhjpPj+FBqBa3wOogqbsuGGBdCphf8S+KPprL1z+oDWkmSQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@swc/core-linux-arm64-gnu@1.9.3':
|
||||
resolution: {integrity: sha512-AQ5JZiwNGVV/2K2TVulg0mw/3LYfqpjZO6jDPtR2evNbk9Yt57YsVzS+3vHSlUBQDRV9/jqMuZYVU3P13xrk+g==, tarball: https://registry.npmmirror.com/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-AQ5JZiwNGVV/2K2TVulg0mw/3LYfqpjZO6jDPtR2evNbk9Yt57YsVzS+3vHSlUBQDRV9/jqMuZYVU3P13xrk+g==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@swc/core-linux-arm64-musl@1.9.3':
|
||||
resolution: {integrity: sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg==, tarball: https://registry.npmmirror.com/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-tzVH480RY6RbMl/QRgh5HK3zn1ZTFsThuxDGo6Iuk1MdwIbdFYUY034heWUTI4u3Db97ArKh0hNL0xhO3+PZdg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@swc/core-linux-x64-gnu@1.9.3':
|
||||
resolution: {integrity: sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w==, tarball: https://registry.npmmirror.com/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-ivXXBRDXDc9k4cdv10R21ccBmGebVOwKXT/UdH1PhxUn9m/h8erAWjz5pcELwjiMf27WokqPgaWVfaclDbgE+w==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@swc/core-linux-x64-musl@1.9.3':
|
||||
resolution: {integrity: sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg==, tarball: https://registry.npmmirror.com/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-ILsGMgfnOz1HwdDz+ZgEuomIwkP1PHT6maigZxaCIuC6OPEhKE8uYna22uU63XvYcLQvZYDzpR3ms47WQPuNEg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@swc/core-win32-arm64-msvc@1.9.3':
|
||||
resolution: {integrity: sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg==, tarball: https://registry.npmmirror.com/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-e+XmltDVIHieUnNJHtspn6B+PCcFOMYXNJB1GqoCcyinkEIQNwC8KtWgMqUucUbEWJkPc35NHy9k8aCXRmw9Kg==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-win32-ia32-msvc@1.9.3':
|
||||
resolution: {integrity: sha512-rqpzNfpAooSL4UfQnHhkW8aL+oyjqJniDP0qwZfGnjDoJSbtPysHg2LpcOBEdSnEH+uIZq6J96qf0ZFD8AGfXA==, tarball: https://registry.npmmirror.com/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-rqpzNfpAooSL4UfQnHhkW8aL+oyjqJniDP0qwZfGnjDoJSbtPysHg2LpcOBEdSnEH+uIZq6J96qf0ZFD8AGfXA==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@swc/core-win32-x64-msvc@1.9.3':
|
||||
resolution: {integrity: sha512-3YJJLQ5suIEHEKc1GHtqVq475guiyqisKSoUnoaRtxkDaW5g1yvPt9IoSLOe2mRs7+FFhGGU693RsBUSwOXSdQ==, tarball: https://registry.npmmirror.com/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.9.3.tgz}
|
||||
resolution: {integrity: sha512-3YJJLQ5suIEHEKc1GHtqVq475guiyqisKSoUnoaRtxkDaW5g1yvPt9IoSLOe2mRs7+FFhGGU693RsBUSwOXSdQ==}
|
||||
engines: {node: '>=10'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@ -1668,7 +1652,7 @@ packages:
|
||||
resolution: {integrity: sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==, tarball: https://registry.npmmirror.com/@swc/types/-/types-0.1.17.tgz}
|
||||
|
||||
'@sxzz/popperjs-es@2.11.7':
|
||||
resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==, tarball: https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz}
|
||||
resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
|
||||
|
||||
'@transloadit/prettier-bytes@0.0.7':
|
||||
resolution: {integrity: sha512-VeJbUb0wEKbcwaSlj5n+LscBl9IPgLPkHVGBkh00cztv6X4L/TJXK58LzFuBKX7/GAfiGhIwH67YTLTlzvIzBA==, tarball: https://registry.npmmirror.com/@transloadit/prettier-bytes/-/prettier-bytes-0.0.7.tgz}
|
||||
@ -1816,10 +1800,10 @@ packages:
|
||||
resolution: {integrity: sha512-l4cmyPEckf8moNYHdJ+4wkHvFxjyW6ulm9l4YGaOxeyBWPhBOT0gvni1InpFPdzx1dKf/2s62qGITwxNWnPQng==, tarball: https://registry.npmmirror.com/@types/svgo/-/svgo-2.6.4.tgz}
|
||||
|
||||
'@types/trusted-types@2.0.7':
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==, tarball: https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz}
|
||||
resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
|
||||
|
||||
'@types/video.js@7.3.58':
|
||||
resolution: {integrity: sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ==, tarball: https://registry.npmmirror.com/@types/video.js/-/video.js-7.3.58.tgz}
|
||||
resolution: {integrity: sha512-1CQjuSrgbv1/dhmcfQ83eVyYbvGyqhTvb2Opxr0QCV+iJ4J6/J+XWQ3Om59WiwCd1MN3rDUHasx5XRrpUtewYQ==}
|
||||
|
||||
'@types/web-bluetooth@0.0.16':
|
||||
resolution: {integrity: sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==, tarball: https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz}
|
||||
@ -2473,8 +2457,8 @@ packages:
|
||||
camunda-bpmn-js-behaviors: '>= 0.4'
|
||||
diagram-js: '>= 11.9'
|
||||
|
||||
bpmn-js-token-simulation@0.36.3:
|
||||
resolution: {integrity: sha512-HyiExdi+vENiStn284gIUQkQliiWly4dk2kY9PJILwwuTIoKtvg1zw8LGr9ReNUiScibNbpkt45bR25Oqfq9wA==, tarball: https://registry.npmmirror.com/bpmn-js-token-simulation/-/bpmn-js-token-simulation-0.36.3.tgz}
|
||||
bpmn-js-token-simulation@0.36.2:
|
||||
resolution: {integrity: sha512-sN7US4gIA5tGs74gYLnZ2Eay+gPqkKPjEttp/VRTeydSg0RGPuGiGwTo1TaLf8cV8FXFCDD2actkQWn/aeg79Q==}
|
||||
engines: {node: '>= 16'}
|
||||
|
||||
bpmn-js@17.11.1:
|
||||
@ -2533,7 +2517,7 @@ packages:
|
||||
engines: {node: '>=6'}
|
||||
|
||||
camunda-bpmn-js-behaviors@1.7.2:
|
||||
resolution: {integrity: sha512-xjLJHc18T40tcYu4JCeYDo1wR5i9+ZqcVnXVP6c4ooAe2gKISbBvFc07gqGpqiwm7TpEBvUfDj3PrRr+ofaf4w==, tarball: https://registry.npmmirror.com/camunda-bpmn-js-behaviors/-/camunda-bpmn-js-behaviors-1.7.2.tgz}
|
||||
resolution: {integrity: sha512-xjLJHc18T40tcYu4JCeYDo1wR5i9+ZqcVnXVP6c4ooAe2gKISbBvFc07gqGpqiwm7TpEBvUfDj3PrRr+ofaf4w==}
|
||||
peerDependencies:
|
||||
bpmn-js: '>= 9'
|
||||
camunda-bpmn-moddle: '>= 7'
|
||||
@ -2577,7 +2561,7 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
classnames@2.5.1:
|
||||
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==, tarball: https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz}
|
||||
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
|
||||
|
||||
cli-cursor@5.0.0:
|
||||
resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==, tarball: https://registry.npmmirror.com/cli-cursor/-/cli-cursor-5.0.0.tgz}
|
||||
@ -2722,7 +2706,7 @@ packages:
|
||||
optional: true
|
||||
|
||||
crelt@1.0.6:
|
||||
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==, tarball: https://registry.npmmirror.com/crelt/-/crelt-1.0.6.tgz}
|
||||
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
|
||||
|
||||
cropperjs@1.6.2:
|
||||
resolution: {integrity: sha512-nhymn9GdnV3CqiEHJVai54TULFAE3VshJTXSqSJKa8yXAKyBKDWdhHarnlIPrshJ0WMFTGuFvG02YjLXfPiuOA==, tarball: https://registry.npmmirror.com/cropperjs/-/cropperjs-1.6.2.tgz}
|
||||
@ -2962,6 +2946,9 @@ packages:
|
||||
deep-is@0.1.4:
|
||||
resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==, tarball: https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz}
|
||||
|
||||
default-passive-events@2.0.0:
|
||||
resolution: {integrity: sha512-eMtt76GpDVngZQ3ocgvRcNCklUMwID1PaNbCNxfpDXuiOXttSh0HzBbda1HU9SIUsDc02vb7g9+3I5tlqe/qMQ==}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==, tarball: https://registry.npmmirror.com/define-data-property/-/define-data-property-1.1.4.tgz}
|
||||
engines: {node: '>= 0.4'}
|
||||
@ -3068,7 +3055,7 @@ packages:
|
||||
resolution: {integrity: sha512-m4yreHcUWHBncGVV7U+yQzc12vIlq0jMrtHZ5mW6dQMiL/7skSYNVX9wqKwOtyO9SGCgevrAFEgOCAHmamHTUA==, tarball: https://registry.npmmirror.com/domify/-/domify-1.4.2.tgz}
|
||||
|
||||
domify@2.0.0:
|
||||
resolution: {integrity: sha512-rmvrrmWQPD/X1A/nPBfIVg4r05792QdG9Z4Prk6oQG0F9zBMDkr0GKAdds1wjb2dq1rTz/ywc4ZxpZbgz0tttg==, tarball: https://registry.npmmirror.com/domify/-/domify-2.0.0.tgz}
|
||||
resolution: {integrity: sha512-rmvrrmWQPD/X1A/nPBfIVg4r05792QdG9Z4Prk6oQG0F9zBMDkr0GKAdds1wjb2dq1rTz/ywc4ZxpZbgz0tttg==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
dompurify@3.2.1:
|
||||
@ -3369,10 +3356,10 @@ packages:
|
||||
optional: true
|
||||
|
||||
feelers@1.4.0:
|
||||
resolution: {integrity: sha512-CGa/7ILuqoqTaeYeoKsg/4tzu2es9sEEJTmSjdu0lousZBw4V9gcYhHYFNmbrSrKmbAVfOzj6/DsymGJWFIOeg==, tarball: https://registry.npmmirror.com/feelers/-/feelers-1.4.0.tgz}
|
||||
resolution: {integrity: sha512-CGa/7ILuqoqTaeYeoKsg/4tzu2es9sEEJTmSjdu0lousZBw4V9gcYhHYFNmbrSrKmbAVfOzj6/DsymGJWFIOeg==}
|
||||
|
||||
feelin@3.2.0:
|
||||
resolution: {integrity: sha512-GFDbHsTYk7YXO1tyw1dOjb7IODeAZvNIosdGZThUwPx5XcD/XhO0hnPZXsIbAzSsIdrgGlTEEdby9fZ2gixysA==, tarball: https://registry.npmmirror.com/feelin/-/feelin-3.2.0.tgz}
|
||||
resolution: {integrity: sha512-GFDbHsTYk7YXO1tyw1dOjb7IODeAZvNIosdGZThUwPx5XcD/XhO0hnPZXsIbAzSsIdrgGlTEEdby9fZ2gixysA==}
|
||||
|
||||
file-entry-cache@6.0.1:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==, tarball: https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz}
|
||||
@ -3417,7 +3404,7 @@ packages:
|
||||
resolution: {integrity: sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA==, tarball: https://registry.npmmirror.com/flatted/-/flatted-3.3.2.tgz}
|
||||
|
||||
focus-trap@7.6.2:
|
||||
resolution: {integrity: sha512-9FhUxK1hVju2+AiQIDJ5Dd//9R2n2RAfJ0qfhF4IHGHgcoEUTMpbTeG/zbEuwaiYXfuAH6XE0/aCyxDdRM+W5w==, tarball: https://registry.npmmirror.com/focus-trap/-/focus-trap-7.6.2.tgz}
|
||||
resolution: {integrity: sha512-9FhUxK1hVju2+AiQIDJ5Dd//9R2n2RAfJ0qfhF4IHGHgcoEUTMpbTeG/zbEuwaiYXfuAH6XE0/aCyxDdRM+W5w==}
|
||||
|
||||
follow-redirects@1.15.9:
|
||||
resolution: {integrity: sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==, tarball: https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.9.tgz}
|
||||
@ -3458,7 +3445,7 @@ packages:
|
||||
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==, tarball: https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz}
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
@ -4029,14 +4016,14 @@ packages:
|
||||
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==, tarball: https://registry.npmmirror.com/kolorist/-/kolorist-1.8.0.tgz}
|
||||
|
||||
lang-feel@2.2.0:
|
||||
resolution: {integrity: sha512-Ebo5nftYsMfJzB3Ny8Oy4oaDXZXb5x61qtVVmKv6aImvAZUbT76mD60ZbEilizjZQzsR2CcU1iMK5sacIa1NVA==, tarball: https://registry.npmmirror.com/lang-feel/-/lang-feel-2.2.0.tgz}
|
||||
resolution: {integrity: sha512-Ebo5nftYsMfJzB3Ny8Oy4oaDXZXb5x61qtVVmKv6aImvAZUbT76mD60ZbEilizjZQzsR2CcU1iMK5sacIa1NVA==}
|
||||
|
||||
levn@0.4.1:
|
||||
resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==, tarball: https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
lezer-feel@1.4.0:
|
||||
resolution: {integrity: sha512-kNxG7O38gwpuYy+C3JCRxQNTCE2qu9uTuH5dE3EGVnRhIQMe6rPDz0S8t3urLEOsMud6HI795m6zX2ujfUaqTw==, tarball: https://registry.npmmirror.com/lezer-feel/-/lezer-feel-1.4.0.tgz}
|
||||
resolution: {integrity: sha512-kNxG7O38gwpuYy+C3JCRxQNTCE2qu9uTuH5dE3EGVnRhIQMe6rPDz0S8t3urLEOsMud6HI795m6zX2ujfUaqTw==}
|
||||
|
||||
lilconfig@3.1.2:
|
||||
resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==, tarball: https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.2.tgz}
|
||||
@ -4160,7 +4147,7 @@ packages:
|
||||
resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==, tarball: https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz}
|
||||
|
||||
luxon@3.5.0:
|
||||
resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==, tarball: https://registry.npmmirror.com/luxon/-/luxon-3.5.0.tgz}
|
||||
resolution: {integrity: sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
m3u8-parser@4.8.0:
|
||||
@ -4278,7 +4265,7 @@ packages:
|
||||
resolution: {integrity: sha512-TMoL8SEEIhUWYgkj7XMSgxmwSyGI+4fP2KFFGnN3FbHfbGHVdsLYSz8LoIsgPhz4dWRmLvxWWSMgzZMJW5sZuA==, tarball: https://registry.npmmirror.com/min-dom/-/min-dom-4.2.1.tgz}
|
||||
|
||||
min-dom@5.1.1:
|
||||
resolution: {integrity: sha512-GaKUlguMAofd3OJsB0OkP17i5kucKqErgVCJxPawO9l5NwIPnr28SAr99zzlzMCWWljISBYrnZVWdE2Q92YGFQ==, tarball: https://registry.npmmirror.com/min-dom/-/min-dom-5.1.1.tgz}
|
||||
resolution: {integrity: sha512-GaKUlguMAofd3OJsB0OkP17i5kucKqErgVCJxPawO9l5NwIPnr28SAr99zzlzMCWWljISBYrnZVWdE2Q92YGFQ==}
|
||||
|
||||
minimatch@3.1.2:
|
||||
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==, tarball: https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz}
|
||||
@ -4752,7 +4739,7 @@ packages:
|
||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==, tarball: https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz}
|
||||
|
||||
randomcolor@0.6.2:
|
||||
resolution: {integrity: sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A==, tarball: https://registry.npmmirror.com/randomcolor/-/randomcolor-0.6.2.tgz}
|
||||
resolution: {integrity: sha512-Mn6TbyYpFgwFuQ8KJKqf3bqqY9O1y37/0jgSK/61PUxV4QfIMv0+K2ioq8DfOjkBslcjwSzRfIDEXfzA9aCx7A==}
|
||||
|
||||
rd@2.0.1:
|
||||
resolution: {integrity: sha512-/XdKU4UazUZTXFmI0dpABt8jSXPWcEyaGdk340KdHnsEOdkTctlX23aAK7ChQDn39YGNlAJr1M5uvaKt4QnpNw==, tarball: https://registry.npmmirror.com/rd/-/rd-2.0.1.tgz}
|
||||
@ -4981,6 +4968,9 @@ packages:
|
||||
resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==, tarball: https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz}
|
||||
engines: {node: '>=14'}
|
||||
|
||||
signature_pad@3.0.0-beta.4:
|
||||
resolution: {integrity: sha512-cOf2NhVuTiuNqe2X/ycEmizvCDXk0DoemhsEpnkcGnA4kS5iJYTCqZ9As7tFBbsch45Q1EdX61833+6sjJ8rrw==}
|
||||
|
||||
sirv@2.0.4:
|
||||
resolution: {integrity: sha512-94Bdh3cC2PKrbgSOUqTiGPWVZeSiXfKOVZNJniWoqrWrRkB1CJzBU3NEbiTsPcYy1lDsANA/THzS+9WBiy5nfQ==, tarball: https://registry.npmmirror.com/sirv/-/sirv-2.0.4.tgz}
|
||||
engines: {node: '>= 10'}
|
||||
@ -5141,7 +5131,7 @@ packages:
|
||||
resolution: {integrity: sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==, tarball: https://registry.npmmirror.com/strnum/-/strnum-1.0.5.tgz}
|
||||
|
||||
style-mod@4.1.2:
|
||||
resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==, tarball: https://registry.npmmirror.com/style-mod/-/style-mod-4.1.2.tgz}
|
||||
resolution: {integrity: sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw==}
|
||||
|
||||
stylelint-config-html@1.1.0:
|
||||
resolution: {integrity: sha512-IZv4IVESjKLumUGi+HWeb7skgO6/g4VMuAYrJdlqQFndgbj6WJAXPhaysvBiXefX79upBdQVumgYcdd17gCpjQ==, tarball: https://registry.npmmirror.com/stylelint-config-html/-/stylelint-config-html-1.1.0.tgz}
|
||||
@ -5215,7 +5205,7 @@ packages:
|
||||
resolution: {integrity: sha512-Nk8c4lXvMB98MtbmjX7JwJRgJOL8fluecYCfCeYBznwmpOs8Bf15hLM6z4z71EDAhQVrQrI+wt1aLWSXZq+hXA==, tarball: https://registry.npmmirror.com/systemjs/-/systemjs-6.15.1.tgz}
|
||||
|
||||
tabbable@6.2.0:
|
||||
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==, tarball: https://registry.npmmirror.com/tabbable/-/tabbable-6.2.0.tgz}
|
||||
resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
|
||||
|
||||
table@6.8.2:
|
||||
resolution: {integrity: sha512-w2sfv80nrAh2VCbqR5AK27wswXhqcck2AhfnNW76beQXskGZ1V12GwS//yYVa3d3fcvAip2OUnbDAjW2k3v9fA==, tarball: https://registry.npmmirror.com/table/-/table-6.8.2.tgz}
|
||||
@ -5580,6 +5570,11 @@ packages:
|
||||
vue:
|
||||
optional: true
|
||||
|
||||
vue3-signature@0.2.4:
|
||||
resolution: {integrity: sha512-XFwwFVK9OG3F085pKIq2SlNVqx32WdFH+TXbGEWc5FfEKpx8oMmZuAwZZ50K/pH2FgmJSE8IRwU9DDhrLpd6iA==}
|
||||
peerDependencies:
|
||||
vue: ^3.2.0
|
||||
|
||||
vue@3.5.12:
|
||||
resolution: {integrity: sha512-CLVZtXtn2ItBIi/zHZ0Sg1Xkb7+PU32bJJ8Bmy7ts3jxXTcbfsEfBivFYYWz1Hur+lalqGAh65Coin0r+HRUfg==, tarball: https://registry.npmmirror.com/vue/-/vue-3.5.12.tgz}
|
||||
peerDependencies:
|
||||
@ -5594,7 +5589,7 @@ packages:
|
||||
vue: ^3.0.1
|
||||
|
||||
w3c-keyname@2.2.8:
|
||||
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==, tarball: https://registry.npmmirror.com/w3c-keyname/-/w3c-keyname-2.2.8.tgz}
|
||||
resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
|
||||
|
||||
wangeditor@4.7.15:
|
||||
resolution: {integrity: sha512-aPTdREd8BxXVyJ5MI+LU83FQ7u1EPd341iXIorRNYSOvoimNoZ4nPg+yn3FGbB93/owEa6buLw8wdhYnMCJQLg==, tarball: https://registry.npmmirror.com/wangeditor/-/wangeditor-4.7.15.tgz}
|
||||
@ -5725,7 +5720,7 @@ packages:
|
||||
engines: {node: '>=12.20'}
|
||||
|
||||
zeebe-bpmn-moddle@1.7.0:
|
||||
resolution: {integrity: sha512-eZ6OXSt0c4n9V/oN/46gTlwDIS3GhWQLt9jbM5uS/YryB4yN8wdrrKrtw+TpyNy0SSKWXNDHyC83nCA2blPO3Q==, tarball: https://registry.npmmirror.com/zeebe-bpmn-moddle/-/zeebe-bpmn-moddle-1.7.0.tgz}
|
||||
resolution: {integrity: sha512-eZ6OXSt0c4n9V/oN/46gTlwDIS3GhWQLt9jbM5uS/YryB4yN8wdrrKrtw+TpyNy0SSKWXNDHyC83nCA2blPO3Q==}
|
||||
|
||||
zrender@5.6.0:
|
||||
resolution: {integrity: sha512-uzgraf4njmmHAbEUxMJ8Oxg+P3fT04O+9p7gY+wJRVxo8Ge+KmYv0WJev945EH4wFuc4OY2NLXz46FZrWS9xJg==, tarball: https://registry.npmmirror.com/zrender/-/zrender-5.6.0.tgz}
|
||||
@ -8280,7 +8275,7 @@ snapshots:
|
||||
min-dash: 4.2.2
|
||||
min-dom: 4.2.1
|
||||
|
||||
bpmn-js-token-simulation@0.36.3:
|
||||
bpmn-js-token-simulation@0.36.2:
|
||||
dependencies:
|
||||
inherits-browser: 0.1.0
|
||||
min-dash: 4.2.2
|
||||
@ -8828,6 +8823,8 @@ snapshots:
|
||||
|
||||
deep-is@0.1.4: {}
|
||||
|
||||
default-passive-events@2.0.0: {}
|
||||
|
||||
define-data-property@1.1.4:
|
||||
dependencies:
|
||||
es-define-property: 1.0.0
|
||||
@ -10983,6 +10980,8 @@ snapshots:
|
||||
|
||||
signal-exit@4.1.0: {}
|
||||
|
||||
signature_pad@3.0.0-beta.4: {}
|
||||
|
||||
sirv@2.0.4:
|
||||
dependencies:
|
||||
'@polka/url': 1.0.0-next.28
|
||||
@ -11723,6 +11722,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
vue3-signature@0.2.4(vue@3.5.12(typescript@5.3.3)):
|
||||
dependencies:
|
||||
default-passive-events: 2.0.0
|
||||
signature_pad: 3.0.0-beta.4
|
||||
vue: 3.5.12(typescript@5.3.3)
|
||||
|
||||
vue@3.5.12(typescript@5.3.3):
|
||||
dependencies:
|
||||
'@vue/compiler-dom': 3.5.12
|
||||
|
@ -72,3 +72,7 @@ export const deleteModel = async (id: number) => {
|
||||
export const deployModel = async (id: number) => {
|
||||
return await request.post({ url: '/bpm/model/deploy?id=' + id })
|
||||
}
|
||||
|
||||
export const cleanModel = async (id: number) => {
|
||||
return await request.delete({ url: '/bpm/model/clean?id=' + id })
|
||||
}
|
||||
|
@ -36,6 +36,7 @@ export type ApprovalTaskInfo = {
|
||||
assigneeUser: User
|
||||
status: number
|
||||
reason: string
|
||||
signPicUrl: string
|
||||
}
|
||||
|
||||
// 审批节点信息
|
||||
@ -89,7 +90,7 @@ export const getProcessInstanceCopyPage = async (params: any) => {
|
||||
|
||||
// 获取审批详情
|
||||
export const getApprovalDetail = async (params: any) => {
|
||||
return await request.get({ url: 'bpm/process-instance/get-approval-detail' , params })
|
||||
return await request.get({ url: 'bpm/process-instance/get-approval-detail', params })
|
||||
}
|
||||
|
||||
// 获取表单字段权限
|
||||
|
@ -83,5 +83,5 @@ export const reqCheck = (data: any) => {
|
||||
|
||||
// 通过短信重置密码
|
||||
export const smsResetPassword = (data: any) => {
|
||||
return request.post({ url: '/system/auth/sms-reset-password', data })
|
||||
return request.post({ url: '/system/auth/reset-password', data })
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ const editorConfig = computed((): IEditorConfig => {
|
||||
},
|
||||
|
||||
// 超时时间,默认为 10 秒
|
||||
timeout: 5 * 1000, // 5 秒
|
||||
timeout: 15 * 1000, // 15 秒
|
||||
|
||||
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
|
||||
fieldName: 'file',
|
||||
|
@ -75,7 +75,7 @@ watch(
|
||||
|
||||
<template>
|
||||
<ElIcon :class="prefixCls" :color="color" :size="size">
|
||||
<svg v-if="isLocal" :class="getSvgClass" aria-hidden="true">
|
||||
<svg v-if="isLocal" :class="getSvgClass">
|
||||
<use :xlink:href="symbolId" />
|
||||
</svg>
|
||||
|
||||
|
@ -40,12 +40,23 @@
|
||||
<div class="handler-item-text">包容分支</div>
|
||||
</div>
|
||||
<div class="handler-item" @click="addNode(NodeType.DELAY_TIMER_NODE)">
|
||||
<!-- TODO @芋艿 需要更换一下iconfont的图标 -->
|
||||
<div class="handler-item-icon copy">
|
||||
<span class="iconfont icon-size icon-copy"></span>
|
||||
<div class="handler-item-icon delay">
|
||||
<span class="iconfont icon-size icon-delay"></span>
|
||||
</div>
|
||||
<div class="handler-item-text">延迟器</div>
|
||||
</div>
|
||||
<div class="handler-item" @click="addNode(NodeType.ROUTER_BRANCH_NODE)">
|
||||
<div class="handler-item-icon router">
|
||||
<span class="iconfont icon-size icon-router"></span>
|
||||
</div>
|
||||
<div class="handler-item-text">路由分支</div>
|
||||
</div>
|
||||
<div class="handler-item" @click="addNode(NodeType.TRIGGER_NODE)">
|
||||
<div class="handler-item-icon trigger">
|
||||
<span class="iconfont icon-size icon-trigger"></span>
|
||||
</div>
|
||||
<div class="handler-item-text">触发器</div>
|
||||
</div>
|
||||
</div>
|
||||
<template #reference>
|
||||
<div class="add-icon"><Icon icon="ep:plus" /></div>
|
||||
@ -60,12 +71,14 @@ import {
|
||||
ApproveMethodType,
|
||||
AssignEmptyHandlerType,
|
||||
AssignStartUserHandlerType,
|
||||
ConditionType,
|
||||
NODE_DEFAULT_NAME,
|
||||
NodeType,
|
||||
RejectHandlerType,
|
||||
SimpleFlowNode
|
||||
SimpleFlowNode,
|
||||
DEFAULT_CONDITION_GROUP_VALUE
|
||||
} from './consts'
|
||||
import { generateUUID } from '@/utils'
|
||||
import {generateUUID} from '@/utils'
|
||||
|
||||
defineOptions({
|
||||
name: 'NodeHandler'
|
||||
@ -120,7 +133,16 @@ const addNode = (type: number) => {
|
||||
type: AssignEmptyHandlerType.APPROVE
|
||||
},
|
||||
assignStartUserHandlerType: AssignStartUserHandlerType.START_USER_AUDIT,
|
||||
childNode: props.childNode
|
||||
childNode: props.childNode,
|
||||
taskCreateListener: {
|
||||
enable: false
|
||||
},
|
||||
taskAssignListener: {
|
||||
enable: false
|
||||
},
|
||||
taskCompleteListener: {
|
||||
enable: false
|
||||
}
|
||||
}
|
||||
emits('update:childNode', data)
|
||||
}
|
||||
@ -147,8 +169,11 @@ const addNode = (type: number) => {
|
||||
showText: '',
|
||||
type: NodeType.CONDITION_NODE,
|
||||
childNode: undefined,
|
||||
conditionType: 1,
|
||||
defaultFlow: false
|
||||
conditionSetting: {
|
||||
defaultFlow: false,
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Flow_' + generateUUID(),
|
||||
@ -156,9 +181,10 @@ const addNode = (type: number) => {
|
||||
showText: '未满足其它条件时,将进入此分支',
|
||||
type: NodeType.CONDITION_NODE,
|
||||
childNode: undefined,
|
||||
conditionType: undefined,
|
||||
conditionSetting: {
|
||||
defaultFlow: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
emits('update:childNode', data)
|
||||
@ -201,7 +227,11 @@ const addNode = (type: number) => {
|
||||
showText: '',
|
||||
type: NodeType.CONDITION_NODE,
|
||||
childNode: undefined,
|
||||
defaultFlow: false
|
||||
conditionSetting: {
|
||||
defaultFlow: false,
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'Flow_' + generateUUID(),
|
||||
@ -209,8 +239,10 @@ const addNode = (type: number) => {
|
||||
showText: '未满足其它条件时,将进入此分支',
|
||||
type: NodeType.CONDITION_NODE,
|
||||
childNode: undefined,
|
||||
conditionSetting: {
|
||||
defaultFlow: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
emits('update:childNode', data)
|
||||
@ -225,6 +257,26 @@ const addNode = (type: number) => {
|
||||
}
|
||||
emits('update:childNode', data)
|
||||
}
|
||||
if (type === NodeType.ROUTER_BRANCH_NODE) {
|
||||
const data: SimpleFlowNode = {
|
||||
id: 'GateWay_' + generateUUID(),
|
||||
name: NODE_DEFAULT_NAME.get(NodeType.ROUTER_BRANCH_NODE) as string,
|
||||
showText: '',
|
||||
type: NodeType.ROUTER_BRANCH_NODE,
|
||||
childNode: props.childNode
|
||||
}
|
||||
emits('update:childNode', data)
|
||||
}
|
||||
if (type === NodeType.TRIGGER_NODE) {
|
||||
const data: SimpleFlowNode = {
|
||||
id: 'Activity_' + generateUUID(),
|
||||
name: NODE_DEFAULT_NAME.get(NodeType.TRIGGER_NODE) as string,
|
||||
showText: '',
|
||||
type: NodeType.TRIGGER_NODE,
|
||||
childNode: props.childNode
|
||||
}
|
||||
emits('update:childNode', data)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -44,6 +44,18 @@
|
||||
:flow-node="currentNode"
|
||||
@update:flow-node="handleModelValueUpdate"
|
||||
/>
|
||||
<!-- 路由分支节点 -->
|
||||
<RouterNode
|
||||
v-if="currentNode && currentNode.type === NodeType.ROUTER_BRANCH_NODE"
|
||||
:flow-node="currentNode"
|
||||
@update:flow-node="handleModelValueUpdate"
|
||||
/>
|
||||
<!-- 触发器节点 -->
|
||||
<TriggerNode
|
||||
v-if="currentNode && currentNode.type === NodeType.TRIGGER_NODE"
|
||||
:flow-node="currentNode"
|
||||
@update:flow-node="handleModelValueUpdate"
|
||||
/>
|
||||
<!-- 递归显示孩子节点 -->
|
||||
<ProcessNodeTree
|
||||
v-if="currentNode && currentNode.childNode"
|
||||
@ -67,6 +79,8 @@ import ExclusiveNode from './nodes/ExclusiveNode.vue'
|
||||
import ParallelNode from './nodes/ParallelNode.vue'
|
||||
import InclusiveNode from './nodes/InclusiveNode.vue'
|
||||
import DelayTimerNode from './nodes/DelayTimerNode.vue'
|
||||
import RouterNode from './nodes/RouterNode.vue'
|
||||
import TriggerNode from './nodes/TriggerNode.vue'
|
||||
import { SimpleFlowNode, NodeType } from './consts'
|
||||
import { useWatchNode } from './node'
|
||||
defineOptions({
|
||||
|
@ -25,7 +25,6 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import SimpleProcessModel from './SimpleProcessModel.vue'
|
||||
import { updateBpmSimpleModel, getBpmSimpleModel } from '@/api/bpm/simple'
|
||||
import { SimpleFlowNode, NodeType, NodeId, NODE_DEFAULT_TEXT } from './consts'
|
||||
import { getModel } from '@/api/bpm/model'
|
||||
import { getForm, FormVO } from '@/api/bpm/form'
|
||||
@ -35,12 +34,13 @@ import * as DeptApi from '@/api/system/dept'
|
||||
import * as PostApi from '@/api/system/post'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as UserGroupApi from '@/api/bpm/userGroup'
|
||||
import { BpmModelFormType } from '@/utils/constants'
|
||||
|
||||
defineOptions({
|
||||
name: 'SimpleProcessDesigner'
|
||||
})
|
||||
|
||||
const emits = defineEmits(['success', 'init-finished']) // 保存成功事件
|
||||
const emits = defineEmits(['success']) // 保存成功事件
|
||||
|
||||
const props = defineProps({
|
||||
modelId: {
|
||||
@ -56,16 +56,13 @@ const props = defineProps({
|
||||
required: false
|
||||
},
|
||||
// 可发起流程的人员编号
|
||||
startUserIds : {
|
||||
startUserIds: {
|
||||
type: Array,
|
||||
required: false
|
||||
},
|
||||
value: {
|
||||
type: [String, Object],
|
||||
required: false
|
||||
}
|
||||
})
|
||||
|
||||
const processData = inject('processData') as Ref
|
||||
const loading = ref(false)
|
||||
const formFields = ref<string[]>([])
|
||||
const formType = ref(20)
|
||||
@ -76,9 +73,6 @@ const deptOptions = ref<DeptApi.DeptVO[]>([]) // 部门列表
|
||||
const deptTreeOptions = ref()
|
||||
const userGroupOptions = ref<UserGroupApi.UserGroupVO[]>([]) // 用户组列表
|
||||
|
||||
// 添加当前值的引用
|
||||
const currentValue = ref<SimpleFlowNode | undefined>()
|
||||
|
||||
provide('formFields', formFields)
|
||||
provide('formType', formType)
|
||||
provide('roleList', roleOptions)
|
||||
@ -88,9 +82,11 @@ provide('deptList', deptOptions)
|
||||
provide('userGroupList', userGroupOptions)
|
||||
provide('deptTree', deptTreeOptions)
|
||||
provide('startUserIds', props.startUserIds)
|
||||
|
||||
provide('tasks', [])
|
||||
provide('processInstance', {})
|
||||
const message = useMessage() // 国际化
|
||||
const processNodeTree = ref<SimpleFlowNode | undefined>()
|
||||
provide('processNodeTree', processNodeTree)
|
||||
const errorDialogVisible = ref(false)
|
||||
let errorNodes: SimpleFlowNode[] = []
|
||||
|
||||
@ -112,70 +108,13 @@ const updateModel = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// 加载流程数据
|
||||
const loadProcessData = async (data: any) => {
|
||||
try {
|
||||
if (data) {
|
||||
const parsedData = typeof data === 'string' ? JSON.parse(data) : data
|
||||
processNodeTree.value = parsedData
|
||||
currentValue.value = parsedData
|
||||
// 确保数据加载后刷新视图
|
||||
await nextTick()
|
||||
if (simpleProcessModelRef.value?.refresh) {
|
||||
await simpleProcessModelRef.value.refresh()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载流程数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 监听属性变化
|
||||
watch(
|
||||
() => props.value,
|
||||
async (newValue, oldValue) => {
|
||||
if (newValue && newValue !== oldValue) {
|
||||
await loadProcessData(newValue)
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
// 监听流程节点树变化,自动保存
|
||||
watch(
|
||||
() => processNodeTree.value,
|
||||
async (newValue, oldValue) => {
|
||||
if (newValue && oldValue && JSON.stringify(newValue) !== JSON.stringify(oldValue)) {
|
||||
await saveSimpleFlowModel(newValue)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
const saveSimpleFlowModel = async (simpleModelNode: SimpleFlowNode) => {
|
||||
if (!simpleModelNode) {
|
||||
return
|
||||
}
|
||||
|
||||
// 校验节点
|
||||
errorNodes = []
|
||||
validateNode(simpleModelNode, errorNodes)
|
||||
if (errorNodes.length > 0) {
|
||||
errorDialogVisible.value = true
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (props.modelId) {
|
||||
// 编辑模式
|
||||
const data = {
|
||||
id: props.modelId,
|
||||
simpleModel: simpleModelNode
|
||||
}
|
||||
await updateBpmSimpleModel(data)
|
||||
}
|
||||
// 无论是编辑还是新建模式,都更新当前值并触发事件
|
||||
currentValue.value = simpleModelNode
|
||||
processData.value = simpleModelNode
|
||||
emits('success', simpleModelNode)
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error)
|
||||
@ -229,7 +168,7 @@ onMounted(async () => {
|
||||
const bpmnModel = await getModel(props.modelId)
|
||||
if (bpmnModel) {
|
||||
formType.value = bpmnModel.formType
|
||||
if (formType.value === 10) {
|
||||
if (formType.value === BpmModelFormType.NORMAL && bpmnModel.formId) {
|
||||
const bpmnForm = (await getForm(bpmnModel.formId)) as unknown as FormVO
|
||||
formFields.value = bpmnForm?.fields
|
||||
}
|
||||
@ -246,61 +185,18 @@ onMounted(async () => {
|
||||
deptTreeOptions.value = handleTree(deptOptions.value as DeptApi.DeptVO[], 'id')
|
||||
// 获取用户组列表
|
||||
userGroupOptions.value = await UserGroupApi.getUserGroupSimpleList()
|
||||
|
||||
// 加载流程数据
|
||||
if (props.modelId) {
|
||||
// 获取 SIMPLE 设计器模型
|
||||
const result = await getBpmSimpleModel(props.modelId)
|
||||
if (result) {
|
||||
await loadProcessData(result)
|
||||
} else {
|
||||
updateModel()
|
||||
}
|
||||
} else if (props.value) {
|
||||
await loadProcessData(props.value)
|
||||
if (processData.value) {
|
||||
processNodeTree.value = processData?.value
|
||||
} else {
|
||||
updateModel()
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
emits('init-finished')
|
||||
}
|
||||
})
|
||||
|
||||
const simpleProcessModelRef = ref()
|
||||
|
||||
/** 获取当前流程数据 */
|
||||
const getCurrentFlowData = async () => {
|
||||
try {
|
||||
if (simpleProcessModelRef.value) {
|
||||
const data = await simpleProcessModelRef.value.getCurrentFlowData()
|
||||
if (data) {
|
||||
currentValue.value = data
|
||||
return data
|
||||
}
|
||||
}
|
||||
return currentValue.value
|
||||
} catch (error) {
|
||||
console.error('获取流程数据失败:', error)
|
||||
return currentValue.value
|
||||
}
|
||||
}
|
||||
|
||||
// 刷新方法
|
||||
const refresh = async () => {
|
||||
try {
|
||||
if (currentValue.value) {
|
||||
await loadProcessData(currentValue.value)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('刷新失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getCurrentFlowData,
|
||||
updateModel,
|
||||
loadProcessData,
|
||||
refresh
|
||||
})
|
||||
defineExpose({})
|
||||
</script>
|
||||
|
@ -3,6 +3,22 @@
|
||||
<div class="position-absolute top-0px right-0px bg-#fff">
|
||||
<el-row type="flex" justify="end">
|
||||
<el-button-group key="scale-control" size="default">
|
||||
<el-button v-if="!readonly" size="default" @click="exportJson">
|
||||
<Icon icon="ep:download" /> 导出
|
||||
</el-button>
|
||||
<el-button v-if="!readonly" size="default" @click="importJson">
|
||||
<Icon icon="ep:upload" />导入
|
||||
</el-button>
|
||||
<!-- 用于打开本地文件-->
|
||||
<input
|
||||
v-if="!readonly"
|
||||
type="file"
|
||||
id="files"
|
||||
ref="refFile"
|
||||
style="display: none"
|
||||
accept=".json"
|
||||
@change="importLocalFile"
|
||||
/>
|
||||
<el-button size="default" :icon="ScaleToOriginal" @click="processReZoom()" />
|
||||
<el-button size="default" :plain="true" :icon="ZoomOut" @click="zoomOut()" />
|
||||
<el-button size="default" class="w-80px"> {{ scaleValue }}% </el-button>
|
||||
@ -34,6 +50,8 @@ import ProcessNodeTree from './ProcessNodeTree.vue'
|
||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from './consts'
|
||||
import { useWatchNode } from './node'
|
||||
import { ZoomOut, ZoomIn, ScaleToOriginal } from '@element-plus/icons-vue'
|
||||
import { isString } from '@/utils/is'
|
||||
import download from '@/utils/download'
|
||||
|
||||
defineOptions({
|
||||
name: 'SimpleProcessModel'
|
||||
@ -52,7 +70,7 @@ const props = defineProps({
|
||||
})
|
||||
|
||||
const emits = defineEmits<{
|
||||
'save': [node: SimpleFlowNode | undefined]
|
||||
save: [node: SimpleFlowNode | undefined]
|
||||
}>()
|
||||
|
||||
const processNodeTree = useWatchNode(props)
|
||||
@ -85,6 +103,16 @@ const processReZoom = () => {
|
||||
const errorDialogVisible = ref(false)
|
||||
let errorNodes: SimpleFlowNode[] = []
|
||||
|
||||
const saveSimpleFlowModel = async () => {
|
||||
errorNodes = []
|
||||
validateNode(processNodeTree.value, errorNodes)
|
||||
if (errorNodes.length > 0) {
|
||||
errorDialogVisible.value = true
|
||||
return
|
||||
}
|
||||
emits('save', processNodeTree.value)
|
||||
}
|
||||
|
||||
// 校验节点设置。 暂时以 showText 为空 未节点错误配置
|
||||
const validateNode = (node: SimpleFlowNode | undefined, errorNodes: SimpleFlowNode[]) => {
|
||||
if (node) {
|
||||
@ -143,6 +171,28 @@ const getCurrentFlowData = async () => {
|
||||
defineExpose({
|
||||
getCurrentFlowData
|
||||
})
|
||||
|
||||
/** 导出 JSON */
|
||||
const exportJson = () => {
|
||||
download.json(new Blob([JSON.stringify(processNodeTree.value)]), 'model.json')
|
||||
}
|
||||
|
||||
/** 导入 JSON */
|
||||
const refFile = ref()
|
||||
const importJson = () => {
|
||||
refFile.value.click()
|
||||
}
|
||||
const importLocalFile = () => {
|
||||
const file = refFile.value.files[0]
|
||||
const reader = new FileReader()
|
||||
reader.readAsText(file)
|
||||
reader.onload = function () {
|
||||
if (isString(this.result)) {
|
||||
processNodeTree.value = JSON.parse(this.result)
|
||||
emits('save', processNodeTree.value)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -28,6 +28,11 @@ export enum NodeType {
|
||||
*/
|
||||
DELAY_TIMER_NODE = 14,
|
||||
|
||||
/**
|
||||
* 触发器节点
|
||||
*/
|
||||
TRIGGER_NODE = 15,
|
||||
|
||||
/**
|
||||
* 条件节点
|
||||
*/
|
||||
@ -44,7 +49,11 @@ export enum NodeType {
|
||||
/**
|
||||
* 包容分支节点 (对应包容网关)
|
||||
*/
|
||||
INCLUSIVE_BRANCH_NODE = 53
|
||||
INCLUSIVE_BRANCH_NODE = 53,
|
||||
/**
|
||||
* 路由分支节点
|
||||
*/
|
||||
ROUTER_BRANCH_NODE = 54
|
||||
}
|
||||
|
||||
export enum NodeId {
|
||||
@ -93,18 +102,27 @@ export interface SimpleFlowNode {
|
||||
assignEmptyHandler?: AssignEmptyHandler
|
||||
// 审批节点的审批人与发起人相同时,对应的处理类型
|
||||
assignStartUserHandlerType?: number
|
||||
// 条件类型
|
||||
conditionType?: ConditionType
|
||||
// 条件表达式
|
||||
conditionExpression?: string
|
||||
// 条件组
|
||||
conditionGroups?: ConditionGroup
|
||||
// 是否默认的条件
|
||||
defaultFlow?: boolean
|
||||
// 创建任务监听器
|
||||
taskCreateListener?: ListenerHandler
|
||||
// 创建任务监听器
|
||||
taskAssignListener?: ListenerHandler
|
||||
// 创建任务监听器
|
||||
taskCompleteListener?: ListenerHandler
|
||||
// 条件设置
|
||||
conditionSetting?: ConditionSetting
|
||||
// 活动的状态,用于前端节点状态展示
|
||||
activityStatus?: TaskStatusEnum
|
||||
// 延迟设置
|
||||
delaySetting?: DelaySetting
|
||||
// 路由分支
|
||||
routerGroups?: RouterSetting[]
|
||||
defaultFlowId?: string
|
||||
// 签名
|
||||
signEnable?: boolean
|
||||
// 审批意见
|
||||
reasonRequire?: boolean
|
||||
// 触发器设置
|
||||
triggerSetting?: TriggerSetting
|
||||
}
|
||||
// 候选人策略枚举 ( 用于审批节点。抄送节点 )
|
||||
export enum CandidateStrategy {
|
||||
@ -222,6 +240,41 @@ export type AssignEmptyHandler = {
|
||||
userIds?: number[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 监听器的结构定义
|
||||
*/
|
||||
export type ListenerHandler = {
|
||||
enable: boolean
|
||||
path?: string
|
||||
header?: HttpRequestParam[]
|
||||
body?: HttpRequestParam[]
|
||||
}
|
||||
export type HttpRequestParam = {
|
||||
key: string
|
||||
type: number
|
||||
value: string
|
||||
}
|
||||
export enum BpmHttpRequestParamTypeEnum {
|
||||
/**
|
||||
* 固定值
|
||||
*/
|
||||
FIXED_VALUE = 1,
|
||||
/**
|
||||
* 表单
|
||||
*/
|
||||
FROM_FORM = 2
|
||||
}
|
||||
export const BPM_HTTP_REQUEST_PARAM_TYPES = [
|
||||
{
|
||||
value: 1,
|
||||
label: '固定值'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
label: '表单'
|
||||
}
|
||||
]
|
||||
|
||||
// 审批拒绝类型枚举
|
||||
export enum RejectHandlerType {
|
||||
/**
|
||||
@ -315,6 +368,20 @@ export enum TimeUnitType {
|
||||
DAY = 3
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件节点设置结构定义,用于条件节点
|
||||
*/
|
||||
export type ConditionSetting = {
|
||||
// 条件类型
|
||||
conditionType?: ConditionType
|
||||
// 条件表达式
|
||||
conditionExpression?: string
|
||||
// 条件组
|
||||
conditionGroups?: ConditionGroup
|
||||
// 是否默认的条件
|
||||
defaultFlow?: boolean
|
||||
}
|
||||
|
||||
// 条件配置类型 ( 用于条件节点配置 )
|
||||
export enum ConditionType {
|
||||
/**
|
||||
@ -389,8 +456,6 @@ export enum OperationButtonType {
|
||||
* 条件规则结构定义
|
||||
*/
|
||||
export type ConditionRule = {
|
||||
type: number
|
||||
opName: string
|
||||
opCode: string
|
||||
leftSide: string
|
||||
rightSide: string
|
||||
@ -405,6 +470,24 @@ export type ConditionGroup = {
|
||||
// 条件数组
|
||||
conditions: Condition[]
|
||||
}
|
||||
/**
|
||||
* 条件组默认值
|
||||
*/
|
||||
export const DEFAULT_CONDITION_GROUP_VALUE = {
|
||||
and: true,
|
||||
conditions: [
|
||||
{
|
||||
and: true,
|
||||
rules: [
|
||||
{
|
||||
opCode: '==',
|
||||
leftSide: '',
|
||||
rightSide: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
/**
|
||||
* 条件结构定义
|
||||
@ -421,6 +504,8 @@ NODE_DEFAULT_TEXT.set(NodeType.COPY_TASK_NODE, '请配置抄送人')
|
||||
NODE_DEFAULT_TEXT.set(NodeType.CONDITION_NODE, '请设置条件')
|
||||
NODE_DEFAULT_TEXT.set(NodeType.START_USER_NODE, '请设置发起人')
|
||||
NODE_DEFAULT_TEXT.set(NodeType.DELAY_TIMER_NODE, '请设置延迟器')
|
||||
NODE_DEFAULT_TEXT.set(NodeType.ROUTER_BRANCH_NODE, '请设置路由节点')
|
||||
NODE_DEFAULT_TEXT.set(NodeType.TRIGGER_NODE, '请设置触发器')
|
||||
|
||||
export const NODE_DEFAULT_NAME = new Map<number, string>()
|
||||
NODE_DEFAULT_NAME.set(NodeType.USER_TASK_NODE, '审批人')
|
||||
@ -428,6 +513,8 @@ NODE_DEFAULT_NAME.set(NodeType.COPY_TASK_NODE, '抄送人')
|
||||
NODE_DEFAULT_NAME.set(NodeType.CONDITION_NODE, '条件')
|
||||
NODE_DEFAULT_NAME.set(NodeType.START_USER_NODE, '发起人')
|
||||
NODE_DEFAULT_NAME.set(NodeType.DELAY_TIMER_NODE, '延迟器')
|
||||
NODE_DEFAULT_NAME.set(NodeType.ROUTER_BRANCH_NODE, '路由分支')
|
||||
NODE_DEFAULT_NAME.set(NodeType.TRIGGER_NODE, '触发器')
|
||||
|
||||
// 候选人策略。暂时不从字典中取。 后续可能调整。控制显示顺序
|
||||
export const CANDIDATE_STRATEGY: DictDataVO[] = [
|
||||
@ -460,8 +547,8 @@ export const APPROVE_METHODS: DictDataVO[] = [
|
||||
]
|
||||
|
||||
export const CONDITION_CONFIG_TYPES: DictDataVO[] = [
|
||||
{ label: '条件表达式', value: ConditionType.EXPRESSION },
|
||||
{ label: '条件规则', value: ConditionType.RULE }
|
||||
{ label: '条件规则', value: ConditionType.RULE },
|
||||
{ label: '条件表达式', value: ConditionType.EXPRESSION }
|
||||
]
|
||||
|
||||
// 时间单位类型
|
||||
@ -575,7 +662,15 @@ export enum ProcessVariableEnum {
|
||||
/**
|
||||
* 发起用户 ID
|
||||
*/
|
||||
START_USER_ID = 'PROCESS_START_USER_ID'
|
||||
START_USER_ID = 'PROCESS_START_USER_ID',
|
||||
/**
|
||||
* 发起时间
|
||||
*/
|
||||
START_TIME = 'PROCESS_START_TIME',
|
||||
/**
|
||||
* 流程定义名称
|
||||
*/
|
||||
PROCESS_DEFINITION_NAME = 'PROCESS_DEFINITION_NAME'
|
||||
}
|
||||
|
||||
/**
|
||||
@ -604,3 +699,64 @@ export const DELAY_TYPE = [
|
||||
{ label: '固定时长', value: DelayTypeEnum.FIXED_TIME_DURATION },
|
||||
{ label: '固定日期', value: DelayTypeEnum.FIXED_DATE_TIME }
|
||||
]
|
||||
|
||||
/**
|
||||
* 路由分支结构定义
|
||||
*/
|
||||
export type RouterSetting = {
|
||||
nodeId: string
|
||||
conditionType: ConditionType
|
||||
conditionExpression: string
|
||||
conditionGroups: ConditionGroup
|
||||
}
|
||||
|
||||
// ==================== 触发器相关定义 ====================
|
||||
/**
|
||||
* 触发器节点结构定义
|
||||
*/
|
||||
export type TriggerSetting = {
|
||||
type: TriggerTypeEnum
|
||||
httpRequestSetting?: HttpRequestTriggerSetting
|
||||
normalFormSetting?: NormalFormTriggerSetting
|
||||
}
|
||||
|
||||
/**
|
||||
* 触发器类型枚举
|
||||
*/
|
||||
export enum TriggerTypeEnum {
|
||||
/**
|
||||
* 发送 HTTP 请求触发器
|
||||
*/
|
||||
HTTP_REQUEST = 1,
|
||||
/**
|
||||
* 更新流程表单触发器
|
||||
*/
|
||||
UPDATE_NORMAL_FORM = 2 // TODO @jason:FORM_UPDATE?
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP 请求触发器结构定义
|
||||
*/
|
||||
export type HttpRequestTriggerSetting = {
|
||||
// 请求 URL
|
||||
url: string
|
||||
// 请求头参数设置
|
||||
header?: HttpRequestParam[]
|
||||
// 请求体参数设置
|
||||
body?: HttpRequestParam[]
|
||||
// 请求响应设置
|
||||
response?: Record<string, string>[]
|
||||
}
|
||||
|
||||
/**
|
||||
* 流程表单触发器配置结构定义
|
||||
*/
|
||||
export type NormalFormTriggerSetting = {
|
||||
// 更新表单字段
|
||||
updateFormFields?: Record<string, any>
|
||||
}
|
||||
|
||||
export const TRIGGER_TYPES: DictDataVO[] = [
|
||||
{ label: 'HTTP 请求', value: TriggerTypeEnum.HTTP_REQUEST },
|
||||
{ label: '修改表单数据', value: TriggerTypeEnum.UPDATE_NORMAL_FORM }
|
||||
]
|
||||
|
@ -1,4 +1,3 @@
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { TaskStatusEnum } from '@/api/bpm/task'
|
||||
import * as RoleApi from '@/api/system/role'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
@ -14,9 +13,12 @@ import {
|
||||
NODE_DEFAULT_NAME,
|
||||
AssignStartUserHandlerType,
|
||||
AssignEmptyHandlerType,
|
||||
FieldPermissionType
|
||||
FieldPermissionType,
|
||||
HttpRequestParam,
|
||||
ProcessVariableEnum
|
||||
} from './consts'
|
||||
import { parseFormFields } from '@/components/FormCreate/src/utils/index'
|
||||
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
||||
|
||||
export function useWatchNode(props: { flowNode: SimpleFlowNode }): Ref<SimpleFlowNode> {
|
||||
const node = ref<SimpleFlowNode>(props.flowNode)
|
||||
watch(
|
||||
@ -46,9 +48,9 @@ export function useFormFieldsPermission(defaultPermission: FieldPermissionType)
|
||||
// 字段权限配置. 需要有 field, title, permissioin 属性
|
||||
const fieldsPermissionConfig = ref<Array<Record<string, any>>>([])
|
||||
|
||||
const formType = inject<Ref<number>>('formType') // 表单类型
|
||||
const formType = inject<Ref<number | undefined>>('formType', ref()) // 表单类型
|
||||
|
||||
const formFields = inject<Ref<string[]>>('formFields') // 流程表单字段
|
||||
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
||||
|
||||
const getNodeConfigFormFields = (nodeFormFields?: Array<Record<string, string>>) => {
|
||||
nodeFormFields = toRaw(nodeFormFields)
|
||||
@ -104,16 +106,32 @@ export function useFormFieldsPermission(defaultPermission: FieldPermissionType)
|
||||
getNodeConfigFormFields
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取表单的字段
|
||||
* @description 获取流程表单的字段
|
||||
*/
|
||||
export function useFormFields() {
|
||||
const formFields = inject<Ref<string[]>>('formFields') // 流程表单字段
|
||||
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
||||
return parseFormCreateFields(unref(formFields))
|
||||
}
|
||||
|
||||
// TODO @芋艿:后续需要把各种类似 useFormFieldsPermission 的逻辑,抽成一个通用方法。
|
||||
/**
|
||||
* @description 获取流程表单的字段和发起人字段
|
||||
*/
|
||||
export function useFormFieldsAndStartUser() {
|
||||
const injectFormFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
||||
const formFields = parseFormCreateFields(unref(injectFormFields))
|
||||
// 添加发起人
|
||||
formFields.unshift({
|
||||
field: ProcessVariableEnum.START_USER_ID,
|
||||
title: '发起人',
|
||||
required: true
|
||||
})
|
||||
return formFields
|
||||
}
|
||||
|
||||
export type UserTaskFormType = {
|
||||
//candidateParamArray: any[]
|
||||
candidateStrategy: CandidateStrategy
|
||||
approveMethod: ApproveMethodType
|
||||
roleIds?: number[] // 角色
|
||||
@ -136,10 +154,29 @@ export type UserTaskFormType = {
|
||||
timeDuration?: number
|
||||
maxRemindCount?: number
|
||||
buttonsSetting: any[]
|
||||
taskCreateListenerEnable?: boolean
|
||||
taskCreateListenerPath?: string
|
||||
taskCreateListener?: {
|
||||
header: HttpRequestParam[]
|
||||
body: HttpRequestParam[]
|
||||
}
|
||||
taskAssignListenerEnable?: boolean
|
||||
taskAssignListenerPath?: string
|
||||
taskAssignListener?: {
|
||||
header: HttpRequestParam[]
|
||||
body: HttpRequestParam[]
|
||||
}
|
||||
taskCompleteListenerEnable?: boolean
|
||||
taskCompleteListenerPath?: string
|
||||
taskCompleteListener?: {
|
||||
header: HttpRequestParam[]
|
||||
body: HttpRequestParam[]
|
||||
}
|
||||
signEnable: boolean
|
||||
reasonRequire: boolean
|
||||
}
|
||||
|
||||
export type CopyTaskFormType = {
|
||||
// candidateParamArray: any[]
|
||||
candidateStrategy: CandidateStrategy
|
||||
roleIds?: number[] // 角色
|
||||
deptIds?: number[] // 部门
|
||||
@ -156,13 +193,13 @@ export type CopyTaskFormType = {
|
||||
* @description 节点表单数据。 用于审批节点、抄送节点
|
||||
*/
|
||||
export function useNodeForm(nodeType: NodeType) {
|
||||
const roleOptions = inject<Ref<RoleApi.RoleVO[]>>('roleList') // 角色列表
|
||||
const postOptions = inject<Ref<PostApi.PostVO[]>>('postList') // 岗位列表
|
||||
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList') // 用户列表
|
||||
const deptOptions = inject<Ref<DeptApi.DeptVO[]>>('deptList') // 部门列表
|
||||
const userGroupOptions = inject<Ref<UserGroupApi.UserGroupVO[]>>('userGroupList') // 用户组列表
|
||||
const deptTreeOptions = inject('deptTree') // 部门树
|
||||
const formFields = inject<Ref<string[]>>('formFields') // 流程表单字段
|
||||
const roleOptions = inject<Ref<RoleApi.RoleVO[]>>('roleList', ref([])) // 角色列表
|
||||
const postOptions = inject<Ref<PostApi.PostVO[]>>('postList', ref([])) // 岗位列表
|
||||
const userOptions = inject<Ref<UserApi.UserVO[]>>('userList', ref([])) // 用户列表
|
||||
const deptOptions = inject<Ref<DeptApi.DeptVO[]>>('deptList', ref([])) // 部门列表
|
||||
const userGroupOptions = inject<Ref<UserGroupApi.UserGroupVO[]>>('userGroupList', ref([])) // 用户组列表
|
||||
const deptTreeOptions = inject('deptTree', ref()) // 部门树
|
||||
const formFields = inject<Ref<string[]>>('formFields', ref([])) // 流程表单字段
|
||||
const configForm = ref<UserTaskFormType | CopyTaskFormType>()
|
||||
if (nodeType === NodeType.USER_TASK_NODE) {
|
||||
configForm.value = {
|
||||
|
@ -26,121 +26,11 @@
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<div class="mb-3 font-size-16px" v-if="currentNode.defaultFlow"
|
||||
<div class="mb-3 font-size-16px" v-if="currentNode.conditionSetting?.defaultFlow"
|
||||
>未满足其它条件时,将进入此分支(该分支不可编辑和删除)</div
|
||||
>
|
||||
<div v-else>
|
||||
<el-form ref="formRef" :model="currentNode" :rules="formRules" label-position="top">
|
||||
<el-form-item label="配置方式" prop="conditionType">
|
||||
<el-radio-group v-model="currentNode.conditionType" @change="changeConditionType">
|
||||
<el-radio
|
||||
v-for="(dict, index) in conditionConfigTypes"
|
||||
:key="index"
|
||||
:value="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
v-if="currentNode.conditionType === 1"
|
||||
label="条件表达式"
|
||||
prop="conditionExpression"
|
||||
>
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="currentNode.conditionExpression"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="currentNode.conditionType === 2" label="条件规则">
|
||||
<div class="condition-group-tool">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-4">条件组关系</div>
|
||||
<el-switch
|
||||
v-model="conditionGroups.and"
|
||||
inline-prompt
|
||||
active-text="且"
|
||||
inactive-text="或"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-space direction="vertical" :spacer="conditionGroups.and ? '且' : '或'">
|
||||
<el-card
|
||||
class="condition-group"
|
||||
style="width: 530px"
|
||||
v-for="(condition, cIdx) in conditionGroups.conditions"
|
||||
:key="cIdx"
|
||||
>
|
||||
<div class="condition-group-delete" v-if="conditionGroups.conditions.length > 1">
|
||||
<Icon
|
||||
color="#0089ff"
|
||||
icon="ep:circle-close-filled"
|
||||
:size="18"
|
||||
@click="deleteConditionGroup(cIdx)"
|
||||
/>
|
||||
</div>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>条件组</div>
|
||||
<div class="flex">
|
||||
<div class="mr-4">规则关系</div>
|
||||
<el-switch
|
||||
v-model="condition.and"
|
||||
inline-prompt
|
||||
active-text="且"
|
||||
inactive-text="或"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="flex pt-2" v-for="(rule, rIdx) in condition.rules" :key="rIdx">
|
||||
<div class="mr-2">
|
||||
<el-select style="width: 160px" v-model="rule.leftSide">
|
||||
<el-option
|
||||
v-for="(item, index) in fieldOptions"
|
||||
:key="index"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled="!item.required"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-select v-model="rule.opCode" style="width: 100px">
|
||||
<el-option
|
||||
v-for="item in COMPARISON_OPERATORS"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-input v-model="rule.rightSide" style="width: 160px" />
|
||||
</div>
|
||||
<div class="mr-1 flex items-center" v-if="condition.rules.length > 1">
|
||||
<Icon
|
||||
icon="ep:delete"
|
||||
:size="18"
|
||||
@click="deleteConditionRule(condition, rIdx)"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<Icon icon="ep:plus" :size="18" @click="addConditionRule(condition, rIdx)" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-space>
|
||||
<div title="添加条件组" class="mt-4 cursor-pointer">
|
||||
<Icon color="#0089ff" icon="ep:plus" :size="24" @click="addConditionGroup" />
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<Condition ref="conditionRef" v-model="condition" />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
@ -155,33 +45,16 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
SimpleFlowNode,
|
||||
CONDITION_CONFIG_TYPES,
|
||||
ConditionType,
|
||||
COMPARISON_OPERATORS,
|
||||
ConditionGroup,
|
||||
Condition,
|
||||
ConditionRule,
|
||||
ProcessVariableEnum
|
||||
} from '../consts'
|
||||
import { getDefaultConditionNodeName } from '../utils'
|
||||
import { useFormFields } from '../node'
|
||||
import { BpmModelFormType } from '@/utils/constants'
|
||||
import { useFormFieldsAndStartUser } from '../node'
|
||||
import Condition from './components/Condition.vue'
|
||||
const message = useMessage() // 消息弹窗
|
||||
defineOptions({
|
||||
name: 'ConditionNodeConfig'
|
||||
})
|
||||
const formType = inject<Ref<number>>('formType') // 表单类型
|
||||
const conditionConfigTypes = computed(() => {
|
||||
return CONDITION_CONFIG_TYPES.filter((item) => {
|
||||
// 业务表单暂时去掉条件规则选项
|
||||
if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
conditionNode: {
|
||||
type: Object as () => SimpleFlowNode,
|
||||
@ -193,12 +66,10 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
const settingVisible = ref(false)
|
||||
const currentNode = ref<SimpleFlowNode>(props.conditionNode)
|
||||
const condition = ref<any>()
|
||||
const open = () => {
|
||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
||||
if (currentNode.value.conditionGroups) {
|
||||
conditionGroups.value = currentNode.value.conditionGroups
|
||||
}
|
||||
}
|
||||
condition.value = currentNode.value.conditionSetting
|
||||
settingVisible.value = true
|
||||
}
|
||||
|
||||
@ -219,10 +90,10 @@ const blurEvent = () => {
|
||||
showInput.value = false
|
||||
currentNode.value.name =
|
||||
currentNode.value.name ||
|
||||
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.defaultFlow)
|
||||
getDefaultConditionNodeName(props.nodeIndex, currentNode.value?.conditionSetting?.defaultFlow)
|
||||
}
|
||||
|
||||
const currentNode = ref<SimpleFlowNode>(props.conditionNode)
|
||||
|
||||
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
@ -239,31 +110,27 @@ const handleClose = async (done: (cancel?: boolean) => void) => {
|
||||
done()
|
||||
}
|
||||
}
|
||||
// 表单校验规则
|
||||
const formRules = reactive({
|
||||
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
|
||||
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
const conditionRef = ref()
|
||||
// 保存配置
|
||||
const saveConfig = async () => {
|
||||
if (!currentNode.value.defaultFlow) {
|
||||
if (!currentNode.value.conditionSetting?.defaultFlow) {
|
||||
// 校验表单
|
||||
if (!formRef) return false
|
||||
const valid = await formRef.value.validate()
|
||||
const valid = await conditionRef.value.validate()
|
||||
if (!valid) return false
|
||||
const showText = getShowText()
|
||||
if (!showText) {
|
||||
return false
|
||||
}
|
||||
currentNode.value.showText = showText
|
||||
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
|
||||
currentNode.value.conditionGroups = undefined
|
||||
currentNode.value.conditionSetting!.conditionType = condition.value?.conditionType
|
||||
if (currentNode.value.conditionSetting?.conditionType === ConditionType.EXPRESSION) {
|
||||
currentNode.value.conditionSetting.conditionGroups = undefined
|
||||
currentNode.value.conditionSetting.conditionExpression = condition.value?.conditionExpression
|
||||
}
|
||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
||||
currentNode.value.conditionExpression = undefined
|
||||
currentNode.value.conditionGroups = conditionGroups.value
|
||||
if (currentNode.value.conditionSetting!.conditionType === ConditionType.RULE) {
|
||||
currentNode.value.conditionSetting!.conditionExpression = undefined
|
||||
currentNode.value.conditionSetting!.conditionGroups = condition.value?.conditionGroups
|
||||
}
|
||||
}
|
||||
settingVisible.value = false
|
||||
@ -271,16 +138,16 @@ const saveConfig = async () => {
|
||||
}
|
||||
const getShowText = (): string => {
|
||||
let showText = ''
|
||||
if (currentNode.value.conditionType === ConditionType.EXPRESSION) {
|
||||
if (currentNode.value.conditionExpression) {
|
||||
showText = `表达式:${currentNode.value.conditionExpression}`
|
||||
if (condition.value?.conditionType === ConditionType.EXPRESSION) {
|
||||
if (condition.value.conditionExpression) {
|
||||
showText = `表达式:${condition.value.conditionExpression}`
|
||||
}
|
||||
}
|
||||
if (currentNode.value.conditionType === ConditionType.RULE) {
|
||||
if (condition.value?.conditionType === ConditionType.RULE) {
|
||||
// 条件组是否为与关系
|
||||
const groupAnd = conditionGroups.value.and
|
||||
const groupAnd = condition.value.conditionGroups?.and
|
||||
let warningMesg: undefined | string = undefined
|
||||
const conditionGroup = conditionGroups.value.conditions.map((item) => {
|
||||
const conditionGroup = condition.value.conditionGroups?.conditions.map((item) => {
|
||||
return (
|
||||
'(' +
|
||||
item.rules
|
||||
@ -303,85 +170,17 @@ const getShowText = (): string => {
|
||||
message.warning(warningMesg)
|
||||
showText = ''
|
||||
} else {
|
||||
showText = conditionGroup.join(groupAnd ? ' 且 ' : ' 或 ')
|
||||
showText = conditionGroup!.join(groupAnd ? ' 且 ' : ' 或 ')
|
||||
}
|
||||
}
|
||||
return showText
|
||||
}
|
||||
|
||||
// 改变条件配置方式
|
||||
const changeConditionType = () => {}
|
||||
|
||||
const conditionGroups = ref<ConditionGroup>({
|
||||
and: true,
|
||||
conditions: [
|
||||
{
|
||||
and: true,
|
||||
rules: [
|
||||
{
|
||||
type: 1,
|
||||
opName: '等于',
|
||||
opCode: '==',
|
||||
leftSide: '',
|
||||
rightSide: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
})
|
||||
// 添加条件组
|
||||
const addConditionGroup = () => {
|
||||
const condition = {
|
||||
and: true,
|
||||
rules: [
|
||||
{
|
||||
type: 1,
|
||||
opName: '等于',
|
||||
opCode: '==',
|
||||
leftSide: '',
|
||||
rightSide: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
conditionGroups.value.conditions.push(condition)
|
||||
}
|
||||
// 删除条件组
|
||||
const deleteConditionGroup = (idx: number) => {
|
||||
conditionGroups.value.conditions.splice(idx, 1)
|
||||
}
|
||||
|
||||
// 添加条件规则
|
||||
const addConditionRule = (condition: Condition, idx: number) => {
|
||||
const rule: ConditionRule = {
|
||||
type: 1,
|
||||
opName: '等于',
|
||||
opCode: '==',
|
||||
leftSide: '',
|
||||
rightSide: ''
|
||||
}
|
||||
condition.rules.splice(idx + 1, 0, rule)
|
||||
}
|
||||
|
||||
const deleteConditionRule = (condition: Condition, idx: number) => {
|
||||
condition.rules.splice(idx, 1)
|
||||
}
|
||||
const fieldsInfo = useFormFields()
|
||||
|
||||
/** 条件规则可选择的表单字段 */
|
||||
const fieldOptions = computed(() => {
|
||||
const fieldsCopy = fieldsInfo.slice()
|
||||
// 固定添加发起人 ID 字段
|
||||
fieldsCopy.unshift({
|
||||
field: ProcessVariableEnum.START_USER_ID,
|
||||
title: '发起人',
|
||||
required: true
|
||||
})
|
||||
return fieldsCopy
|
||||
})
|
||||
// 流程表单字段和发起人字段
|
||||
const fieldOptions = useFormFieldsAndStartUser()
|
||||
|
||||
/** 获取字段名称 */
|
||||
const getFieldTitle = (field: string) => {
|
||||
const item = fieldOptions.value.find((item) => item.field === field)
|
||||
const item = fieldOptions.find((item) => item.field === field)
|
||||
return item?.title
|
||||
}
|
||||
|
||||
|
@ -124,6 +124,7 @@ const saveConfig = async () => {
|
||||
if (!valid) return false
|
||||
const showText = getShowText()
|
||||
if (!showText) return false
|
||||
currentNode.value.name = nodeName.value!
|
||||
currentNode.value.showText = showText
|
||||
if (configForm.value.delayType === DelayTypeEnum.FIXED_TIME_DURATION) {
|
||||
currentNode.value.delaySetting = {
|
||||
|
@ -0,0 +1,201 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:append-to-body="true"
|
||||
v-model="settingVisible"
|
||||
:show-close="false"
|
||||
:size="630"
|
||||
:before-close="saveConfig"
|
||||
>
|
||||
<template #header>
|
||||
<div class="config-header">
|
||||
<input
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="config-editable-input"
|
||||
@blur="blurEvent()"
|
||||
v-mountedFocus
|
||||
v-model="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
<div v-else class="node-name">
|
||||
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
||||
</div>
|
||||
<div class="divide-line"></div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<el-form label-position="top">
|
||||
<el-card class="mb-15px" v-for="(item, index) in routerGroups" :key="index">
|
||||
<template #header>
|
||||
<div class="flex flex-items-center">
|
||||
<el-text size="large">路由{{ index + 1 }}</el-text>
|
||||
<el-select class="ml-15px" v-model="item.nodeId" style="width: 180px">
|
||||
<el-option
|
||||
v-for="node in nodeOptions"
|
||||
:key="node.value"
|
||||
:label="node.label"
|
||||
:value="node.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-button class="mla" type="danger" link @click="deleteRouterGroup(index)">
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<Condition
|
||||
:ref="($event) => (conditionRef[index] = $event)"
|
||||
v-model="routerGroups[index]"
|
||||
/>
|
||||
</el-card>
|
||||
</el-form>
|
||||
|
||||
<el-button class="w-1/1" type="primary" :icon="Plus" @click="addRouterGroup">
|
||||
新增路由分支
|
||||
</el-button>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-divider />
|
||||
<div>
|
||||
<el-button type="primary" @click="saveConfig">确 定</el-button>
|
||||
<el-button @click="closeDrawer">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { Plus } from '@element-plus/icons-vue'
|
||||
import { SimpleFlowNode, NodeType, ConditionType, RouterSetting } from '../consts'
|
||||
import { useWatchNode, useDrawer, useNodeName } from '../node'
|
||||
import Condition from './components/Condition.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'RouterNodeConfig'
|
||||
})
|
||||
const message = useMessage() // 消息弹窗
|
||||
const props = defineProps({
|
||||
flowNode: {
|
||||
type: Object as () => SimpleFlowNode,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
const processNodeTree = inject<Ref<SimpleFlowNode>>('processNodeTree')
|
||||
// 抽屉配置
|
||||
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
|
||||
// 当前节点
|
||||
const currentNode = useWatchNode(props)
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.ROUTER_BRANCH_NODE)
|
||||
const routerGroups = ref<RouterSetting[]>([])
|
||||
const nodeOptions = ref<any>([])
|
||||
const conditionRef = ref([])
|
||||
|
||||
/** 保存配置 */
|
||||
const saveConfig = async () => {
|
||||
// 校验表单
|
||||
let valid = true
|
||||
for (const item of conditionRef.value) {
|
||||
if (item && !(await item.validate())) {
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
if (!valid) return false
|
||||
const showText = getShowText()
|
||||
if (!showText) return false
|
||||
currentNode.value.name = nodeName.value!
|
||||
currentNode.value.showText = showText
|
||||
currentNode.value.routerGroups = routerGroups.value
|
||||
settingVisible.value = false
|
||||
return true
|
||||
}
|
||||
// 显示路由分支节点配置, 由父组件传过来
|
||||
const showRouteNodeConfig = (node: SimpleFlowNode) => {
|
||||
getRouterNode(processNodeTree?.value)
|
||||
routerGroups.value = []
|
||||
nodeName.value = node.name
|
||||
if (node.routerGroups) {
|
||||
routerGroups.value = node.routerGroups
|
||||
}
|
||||
}
|
||||
|
||||
const getShowText = () => {
|
||||
if (!routerGroups.value || !Array.isArray(routerGroups.value) || routerGroups.value.length <= 0) {
|
||||
message.warning('请配置路由!')
|
||||
return ''
|
||||
}
|
||||
for (const route of routerGroups.value) {
|
||||
if (!route.nodeId || !route.conditionType) {
|
||||
message.warning('请完善路由配置项!')
|
||||
return ''
|
||||
}
|
||||
if (route.conditionType === ConditionType.EXPRESSION && !route.conditionExpression) {
|
||||
message.warning('请完善路由配置项!')
|
||||
return ''
|
||||
}
|
||||
if (route.conditionType === ConditionType.RULE) {
|
||||
for (const condition of route.conditionGroups.conditions) {
|
||||
for (const rule of condition.rules) {
|
||||
if (!rule.leftSide || !rule.rightSide) {
|
||||
message.warning('请完善路由配置项!')
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return `${routerGroups.value.length}条路由分支`
|
||||
}
|
||||
|
||||
const addRouterGroup = () => {
|
||||
routerGroups.value.push({
|
||||
nodeId: '',
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionExpression: '',
|
||||
conditionGroups: {
|
||||
and: true,
|
||||
conditions: [
|
||||
{
|
||||
and: true,
|
||||
rules: [
|
||||
{
|
||||
opCode: '==',
|
||||
leftSide: '',
|
||||
rightSide: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const deleteRouterGroup = (index: number) => {
|
||||
routerGroups.value.splice(index, 1)
|
||||
}
|
||||
|
||||
// 递归获取所有节点
|
||||
const getRouterNode = (node) => {
|
||||
// TODO 最好还需要满足以下要求
|
||||
// 并行分支、包容分支内部节点不能跳转到外部节点
|
||||
// 条件分支节点可以向上跳转到外部节点
|
||||
while (true) {
|
||||
if (!node) break
|
||||
if (node.type !== NodeType.ROUTER_BRANCH_NODE && node.type !== NodeType.CONDITION_NODE) {
|
||||
nodeOptions.value.push({
|
||||
label: node.name,
|
||||
value: node.id
|
||||
})
|
||||
}
|
||||
if (!node.childNode || node.type === NodeType.END_EVENT_NODE) {
|
||||
break
|
||||
}
|
||||
if (node.conditionNodes && node.conditionNodes.length) {
|
||||
node.conditionNodes.forEach((item) => {
|
||||
getRouterNode(item)
|
||||
})
|
||||
}
|
||||
node = node.childNode
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ openDrawer, showRouteNodeConfig }) // 暴露方法给父组件
|
||||
</script>
|
@ -0,0 +1,334 @@
|
||||
<template>
|
||||
<el-drawer
|
||||
:append-to-body="true"
|
||||
v-model="settingVisible"
|
||||
:show-close="false"
|
||||
:size="550"
|
||||
:before-close="saveConfig"
|
||||
>
|
||||
<template #header>
|
||||
<div class="config-header">
|
||||
<input
|
||||
v-if="showInput"
|
||||
type="text"
|
||||
class="config-editable-input"
|
||||
@blur="blurEvent()"
|
||||
v-mountedFocus
|
||||
v-model="nodeName"
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
<div v-else class="node-name">
|
||||
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
||||
</div>
|
||||
<div class="divide-line"></div>
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<el-form ref="formRef" :model="configForm" label-position="top" :rules="formRules">
|
||||
<el-form-item label="触发器类型" prop="type">
|
||||
<el-select v-model="configForm.type">
|
||||
<el-option
|
||||
v-for="(item, index) in TRIGGER_TYPES"
|
||||
:key="index"
|
||||
:value="item.value"
|
||||
:label="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- HTTP 请求触发器 -->
|
||||
<div
|
||||
v-if="configForm.type === TriggerTypeEnum.HTTP_REQUEST && configForm.httpRequestSetting"
|
||||
>
|
||||
<el-form-item>
|
||||
<el-alert
|
||||
title="仅支持 POST 请求,以请求体方式接收参数"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<!-- 请求地址-->
|
||||
<el-form-item label="请求地址" prop="httpRequestSetting.url">
|
||||
<el-input v-model="configForm.httpRequestSetting.url" />
|
||||
</el-form-item>
|
||||
<!-- 请求头,请求体设置-->
|
||||
<HttpRequestParamSetting
|
||||
:header="configForm.httpRequestSetting.header"
|
||||
:body="configForm.httpRequestSetting.body"
|
||||
:bind="'httpRequestSetting'"
|
||||
/>
|
||||
<!-- 返回值设置-->
|
||||
<el-form-item label="返回值">
|
||||
<el-alert
|
||||
title="通过请求返回值, 可以修改流程表单的值"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<div
|
||||
class="flex pt-2"
|
||||
v-for="(item, index) in configForm.httpRequestSetting.response"
|
||||
:key="index"
|
||||
>
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`httpRequestSetting.response.${index}.key`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '表单字段不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-select class="w-160px!" v-model="item.key" placeholder="请选择表单字段">
|
||||
<el-option
|
||||
v-for="(field, fIdx) in formFields"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="!field.required"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`httpRequestSetting.response.${index}.value`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请求返回字段不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input class="w-160px" v-model="item.value" placeholder="请求返回字段" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-1 pt-1 cursor-pointer">
|
||||
<Icon
|
||||
icon="ep:delete"
|
||||
:size="18"
|
||||
@click="deleteHttpResponseSetting(configForm.httpRequestSetting.response!, index)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
@click="addHttpResponseSetting(configForm.httpRequestSetting.response!)"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div
|
||||
v-if="
|
||||
configForm.type === TriggerTypeEnum.UPDATE_NORMAL_FORM && configForm.normalFormSetting
|
||||
"
|
||||
>
|
||||
<el-divider content-position="left">修改表单设置</el-divider>
|
||||
<div
|
||||
class="flex items-center"
|
||||
v-for="key in Object.keys(configForm.normalFormSetting.updateFormFields!)"
|
||||
:key="key"
|
||||
>
|
||||
<div class="mr-2 flex items-center">
|
||||
<el-form-item>
|
||||
<el-select
|
||||
class="w-160px!"
|
||||
:model-value="key"
|
||||
@update:model-value="(newKey) => updateFormFieldKey(key, newKey)"
|
||||
placeholder="请选择表单字段"
|
||||
:disabled="key !== ''"
|
||||
>
|
||||
<el-option
|
||||
v-for="(field, fIdx) in optionalUpdateFormFields"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="field.disabled"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mx-2"><el-form-item>的值设置为</el-form-item></div>
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`normalFormSetting.updateFormFields.${key}`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '值不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input
|
||||
class="w-160px"
|
||||
v-model="configForm.normalFormSetting.updateFormFields![key]"
|
||||
placeholder="请输入"
|
||||
:disabled="!key"
|
||||
/>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-1 pt-1 cursor-pointer">
|
||||
<el-form-item>
|
||||
<Icon icon="ep:delete" :size="18" @click="deleteFormFieldSetting(key)" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" text @click="addFormFieldSetting()">
|
||||
<Icon icon="ep:plus" class="mr-5px" />添加修改字段
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-divider />
|
||||
<div>
|
||||
<el-button type="primary" @click="saveConfig">确 定</el-button>
|
||||
<el-button @click="closeDrawer">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { SimpleFlowNode, NodeType, TriggerSetting, TRIGGER_TYPES, TriggerTypeEnum } from '../consts'
|
||||
import { useWatchNode, useDrawer, useNodeName, useFormFields } from '../node'
|
||||
import HttpRequestParamSetting from './components/HttpRequestParamSetting.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'TriggerNodeConfig'
|
||||
})
|
||||
const props = defineProps({
|
||||
flowNode: {
|
||||
type: Object as () => SimpleFlowNode,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
const message = useMessage() // 消息弹窗
|
||||
// 抽屉配置
|
||||
const { settingVisible, closeDrawer, openDrawer } = useDrawer()
|
||||
// 当前节点
|
||||
const currentNode = useWatchNode(props)
|
||||
// 节点名称
|
||||
const { nodeName, showInput, clickIcon, blurEvent } = useNodeName(NodeType.TRIGGER_NODE)
|
||||
// 触发器表单配置
|
||||
const formRef = ref() // 表单 Ref
|
||||
// 表单校验规则
|
||||
const formRules = reactive({
|
||||
type: [{ required: true, message: '触发器类型不能为空', trigger: 'change' }],
|
||||
'httpRequestSetting.url': [{ required: true, message: '请求地址不能为空', trigger: 'blur' }]
|
||||
})
|
||||
// 触发器配置表单数据
|
||||
const configForm = ref<TriggerSetting>({
|
||||
type: TriggerTypeEnum.HTTP_REQUEST,
|
||||
httpRequestSetting: {
|
||||
url: '',
|
||||
header: [],
|
||||
body: [],
|
||||
response: []
|
||||
},
|
||||
normalFormSetting: { updateFormFields: {} }
|
||||
})
|
||||
// 流程表单字段
|
||||
const formFields = useFormFields()
|
||||
|
||||
// 可选的修改的表单字段
|
||||
const optionalUpdateFormFields = computed(() => {
|
||||
const usedFields = Object.keys(configForm.value.normalFormSetting?.updateFormFields || {})
|
||||
return formFields.map((field) => ({
|
||||
title: field.title,
|
||||
field: field.field,
|
||||
disabled: usedFields.includes(field.field)
|
||||
}))
|
||||
})
|
||||
|
||||
const updateFormFieldKey = (oldKey: string, newKey: string) => {
|
||||
if (!configForm.value.normalFormSetting?.updateFormFields) return
|
||||
const value = configForm.value.normalFormSetting.updateFormFields[oldKey]
|
||||
delete configForm.value.normalFormSetting.updateFormFields[oldKey]
|
||||
configForm.value.normalFormSetting.updateFormFields[newKey] = value
|
||||
}
|
||||
|
||||
/** 添加 HTTP 请求返回值设置项*/
|
||||
const addHttpResponseSetting = (responseSetting: Record<string, string>[]) => {
|
||||
responseSetting.push({
|
||||
key: '',
|
||||
value: ''
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除 HTTP 请求返回值设置项 */
|
||||
const deleteHttpResponseSetting = (responseSetting: Record<string, string>[], index: number) => {
|
||||
responseSetting.splice(index, 1)
|
||||
}
|
||||
|
||||
/** 添加修改表单设置项 */
|
||||
const addFormFieldSetting = () => {
|
||||
if (configForm.value.normalFormSetting!.updateFormFields === undefined) {
|
||||
configForm.value.normalFormSetting!.updateFormFields = {}
|
||||
}
|
||||
configForm.value.normalFormSetting!.updateFormFields[''] = undefined
|
||||
}
|
||||
/** 删除修改表单设置项 */
|
||||
const deleteFormFieldSetting = (key: string) => {
|
||||
if (!configForm.value.normalFormSetting?.updateFormFields) return
|
||||
delete configForm.value.normalFormSetting.updateFormFields[key]
|
||||
}
|
||||
|
||||
/** 保存配置 */
|
||||
const saveConfig = async () => {
|
||||
if (!formRef) return false
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return false
|
||||
const showText = getShowText()
|
||||
if (!showText) return false
|
||||
currentNode.value.name = nodeName.value!
|
||||
currentNode.value.showText = showText
|
||||
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
|
||||
configForm.value.normalFormSetting = undefined
|
||||
}
|
||||
if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
|
||||
configForm.value.httpRequestSetting = undefined
|
||||
}
|
||||
currentNode.value.triggerSetting = configForm.value
|
||||
settingVisible.value = false
|
||||
return true
|
||||
}
|
||||
|
||||
/** 获取节点展示内容 */
|
||||
const getShowText = (): string => {
|
||||
let showText = ''
|
||||
if (configForm.value.type === TriggerTypeEnum.HTTP_REQUEST) {
|
||||
showText = `${configForm.value.httpRequestSetting?.url}`
|
||||
} else if (configForm.value.type === TriggerTypeEnum.UPDATE_NORMAL_FORM) {
|
||||
const updatefields = Object.keys(configForm.value.normalFormSetting?.updateFormFields || {})
|
||||
if (updatefields.length === 0) {
|
||||
message.warning('请设置修改表单字段')
|
||||
} else {
|
||||
showText = '修改表单数据'
|
||||
}
|
||||
}
|
||||
return showText
|
||||
}
|
||||
|
||||
/** 显示触发器节点配置, 由父组件传过来 */
|
||||
const showTriggerNodeConfig = (node: SimpleFlowNode) => {
|
||||
nodeName.value = node.name
|
||||
if (node.triggerSetting) {
|
||||
configForm.value = {
|
||||
type: node.triggerSetting.type,
|
||||
httpRequestSetting: node.triggerSetting.httpRequestSetting || {
|
||||
url: '',
|
||||
header: [],
|
||||
body: [],
|
||||
response: []
|
||||
},
|
||||
normalFormSetting: node.triggerSetting.normalFormSetting || { updateFormFields: {} }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ openDrawer, showTriggerNodeConfig }) // 暴露方法给父组件
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -3,7 +3,7 @@
|
||||
:append-to-body="true"
|
||||
v-model="settingVisible"
|
||||
:show-close="false"
|
||||
:size="550"
|
||||
:size="580"
|
||||
:before-close="saveConfig"
|
||||
class="justify-start"
|
||||
>
|
||||
@ -19,7 +19,8 @@
|
||||
:placeholder="nodeName"
|
||||
/>
|
||||
<div v-else class="node-name">
|
||||
{{ nodeName }} <Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
||||
{{ nodeName }}
|
||||
<Icon class="ml-1" icon="ep:edit-pen" :size="16" @click="clickIcon()" />
|
||||
</div>
|
||||
<div class="divide-line"></div>
|
||||
</div>
|
||||
@ -46,14 +47,13 @@
|
||||
v-model="configForm.candidateStrategy"
|
||||
@change="changeCandidateStrategy"
|
||||
>
|
||||
<el-radio
|
||||
v-for="(dict, index) in CANDIDATE_STRATEGY"
|
||||
:key="index"
|
||||
:value="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
<el-row>
|
||||
<el-col v-for="(dict, index) in CANDIDATE_STRATEGY" :key="index" :span="8">
|
||||
<el-radio :value="dict.value" :label="dict.value">
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
@ -61,7 +61,7 @@
|
||||
label="指定角色"
|
||||
prop="roleIds"
|
||||
>
|
||||
<el-select v-model="configForm.roleIds" clearable multiple style="width: 100%">
|
||||
<el-select filterable v-model="configForm.roleIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in roleOptions"
|
||||
:key="item.id"
|
||||
@ -99,7 +99,7 @@
|
||||
prop="postIds"
|
||||
span="24"
|
||||
>
|
||||
<el-select v-model="configForm.postIds" clearable multiple style="width: 100%">
|
||||
<el-select filterable v-model="configForm.postIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in postOptions"
|
||||
:key="item.id"
|
||||
@ -114,7 +114,7 @@
|
||||
prop="userIds"
|
||||
span="24"
|
||||
>
|
||||
<el-select v-model="configForm.userIds" clearable multiple style="width: 100%">
|
||||
<el-select filterable v-model="configForm.userIds" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userOptions"
|
||||
:key="item.id"
|
||||
@ -128,7 +128,7 @@
|
||||
label="指定用户组"
|
||||
prop="userGroups"
|
||||
>
|
||||
<el-select v-model="configForm.userGroups" clearable multiple style="width: 100%">
|
||||
<el-select filterable v-model="configForm.userGroups" clearable multiple style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in userGroupOptions"
|
||||
:key="item.id"
|
||||
@ -142,13 +142,13 @@
|
||||
label="表单内用户字段"
|
||||
prop="formUser"
|
||||
>
|
||||
<el-select v-model="configForm.formUser" clearable style="width: 100%">
|
||||
<el-select filterable v-model="configForm.formUser" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="(item, idx) in userFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled ="!item.required"
|
||||
:disabled="!item.required"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@ -157,13 +157,13 @@
|
||||
label="表单内部门字段"
|
||||
prop="formDept"
|
||||
>
|
||||
<el-select v-model="configForm.formDept" clearable style="width: 100%">
|
||||
<el-select filterable v-model="configForm.formDept" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="(item, idx) in deptFieldOnFormOptions"
|
||||
:key="idx"
|
||||
:label="item.title"
|
||||
:value="item.field"
|
||||
:disabled ="!item.required"
|
||||
:disabled="!item.required"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@ -179,7 +179,7 @@
|
||||
prop="deptLevel"
|
||||
span="24"
|
||||
>
|
||||
<el-select v-model="configForm.deptLevel" clearable>
|
||||
<el-select filterable v-model="configForm.deptLevel" clearable>
|
||||
<el-option
|
||||
v-for="(item, index) in MULTI_LEVEL_DEPT"
|
||||
:key="index"
|
||||
@ -245,7 +245,7 @@
|
||||
label="驳回节点"
|
||||
prop="returnNodeId"
|
||||
>
|
||||
<el-select v-model="configForm.returnNodeId" clearable style="width: 100%">
|
||||
<el-select filterable v-model="configForm.returnNodeId" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in returnTaskList"
|
||||
:key="item.id"
|
||||
@ -293,6 +293,7 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-select
|
||||
filterable
|
||||
v-model="timeUnit"
|
||||
class="mr-2"
|
||||
:style="{ width: '100px' }"
|
||||
@ -332,6 +333,7 @@
|
||||
span="24"
|
||||
>
|
||||
<el-select
|
||||
filterable
|
||||
v-model="configForm.assignEmptyHandlerUserIds"
|
||||
clearable
|
||||
multiple
|
||||
@ -356,6 +358,16 @@
|
||||
</div>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">是否需要签名</el-divider>
|
||||
<el-form-item prop="signEnable">
|
||||
<el-switch v-model="configForm.signEnable" active-text="是" inactive-text="否" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">审批意见</el-divider>
|
||||
<el-form-item prop="reasonRequire">
|
||||
<el-switch v-model="configForm.reasonRequire" active-text="必填" inactive-text="非必填" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
@ -435,6 +447,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="监听器" name="listener">
|
||||
<UserTaskListener ref="userTaskListenerRef" v-model="configForm" :form-field-options="formFieldOptions" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #footer>
|
||||
<el-divider />
|
||||
@ -484,6 +499,7 @@ import {
|
||||
import { defaultProps } from '@/utils/tree'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { convertTimeUnit, getApproveTypeText } from '../utils'
|
||||
import UserTaskListener from './components/UserTaskListener.vue'
|
||||
defineOptions({
|
||||
name: 'UserTaskNodeConfig'
|
||||
})
|
||||
@ -609,9 +625,11 @@ const {
|
||||
cTimeoutMaxRemindCount
|
||||
} = useTimeoutHandler()
|
||||
|
||||
const userTaskListenerRef = ref()
|
||||
|
||||
// 保存配置
|
||||
const saveConfig = async () => {
|
||||
activeTabName.value = 'user'
|
||||
// activeTabName.value = 'user'
|
||||
// 设置审批节点名称
|
||||
currentNode.value.name = nodeName.value!
|
||||
// 设置审批类型
|
||||
@ -624,7 +642,8 @@ const saveConfig = async () => {
|
||||
}
|
||||
|
||||
if (!formRef) return false
|
||||
const valid = await formRef.value.validate()
|
||||
if (!userTaskListenerRef) return false
|
||||
const valid = (await formRef.value.validate()) && (await userTaskListenerRef.value.validate())
|
||||
if (!valid) return false
|
||||
const showText = getShowText()
|
||||
if (!showText) return false
|
||||
@ -663,6 +682,31 @@ const saveConfig = async () => {
|
||||
currentNode.value.fieldsPermission = fieldsPermissionConfig.value
|
||||
// 设置按钮权限
|
||||
currentNode.value.buttonsSetting = buttonsSetting.value
|
||||
// 创建任务监听器
|
||||
currentNode.value.taskCreateListener = {
|
||||
enable: configForm.value.taskCreateListenerEnable ?? false,
|
||||
path: configForm.value.taskCreateListenerPath,
|
||||
header: configForm.value.taskCreateListener?.header,
|
||||
body: configForm.value.taskCreateListener?.body
|
||||
}
|
||||
// 指派任务监听器
|
||||
currentNode.value.taskAssignListener = {
|
||||
enable: configForm.value.taskAssignListenerEnable ?? false,
|
||||
path: configForm.value.taskAssignListenerPath,
|
||||
header: configForm.value.taskAssignListener?.header,
|
||||
body: configForm.value.taskAssignListener?.body
|
||||
}
|
||||
// 完成任务监听器
|
||||
currentNode.value.taskCompleteListener = {
|
||||
enable: configForm.value.taskCompleteListenerEnable ?? false,
|
||||
path: configForm.value.taskCompleteListenerPath,
|
||||
header: configForm.value.taskCompleteListener?.header,
|
||||
body: configForm.value.taskCompleteListener?.body
|
||||
}
|
||||
// 签名
|
||||
currentNode.value.signEnable = configForm.value.signEnable
|
||||
// 审批意见
|
||||
currentNode.value.reasonRequire = configForm.value.reasonRequire
|
||||
|
||||
currentNode.value.showText = showText
|
||||
settingVisible.value = false
|
||||
@ -714,6 +758,32 @@ const showUserTaskNodeConfig = (node: SimpleFlowNode) => {
|
||||
buttonsSetting.value = cloneDeep(node.buttonsSetting) || DEFAULT_BUTTON_SETTING
|
||||
// 4. 表单字段权限配置
|
||||
getNodeConfigFormFields(node.fieldsPermission)
|
||||
// 5. 监听器
|
||||
// 5.1 创建任务
|
||||
configForm.value.taskCreateListenerEnable = node.taskCreateListener?.enable
|
||||
configForm.value.taskCreateListenerPath = node.taskCreateListener?.path
|
||||
configForm.value.taskCreateListener = {
|
||||
header: node.taskCreateListener?.header ?? [],
|
||||
body: node.taskCreateListener?.body ?? []
|
||||
}
|
||||
// 5.2 指派任务
|
||||
configForm.value.taskAssignListenerEnable = node.taskAssignListener?.enable
|
||||
configForm.value.taskAssignListenerPath = node.taskAssignListener?.path
|
||||
configForm.value.taskAssignListener = {
|
||||
header: node.taskAssignListener?.header ?? [],
|
||||
body: node.taskAssignListener?.body ?? []
|
||||
}
|
||||
// 5.3 完成任务
|
||||
configForm.value.taskCompleteListenerEnable = node.taskCompleteListener?.enable
|
||||
configForm.value.taskCompleteListenerPath = node.taskCompleteListener?.path
|
||||
configForm.value.taskCompleteListener = {
|
||||
header: node.taskCompleteListener?.header ?? [],
|
||||
body: node.taskCompleteListener?.body ?? []
|
||||
}
|
||||
// 6. 签名
|
||||
configForm.value.signEnable = node?.signEnable ?? false
|
||||
// 7. 审批意见
|
||||
configForm.value.reasonRequire = node?.reasonRequire ?? false
|
||||
}
|
||||
|
||||
defineExpose({ openDrawer, showUserTaskNodeConfig }) // 暴露方法给父组件
|
||||
|
@ -0,0 +1,264 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="condition" :rules="formRules" label-position="top">
|
||||
<el-form-item label="配置方式" prop="conditionType">
|
||||
<el-radio-group v-model="condition.conditionType" @change="changeConditionType">
|
||||
<el-radio
|
||||
v-for="(dict, indexConditionType) in conditionConfigTypes"
|
||||
:key="indexConditionType"
|
||||
:value="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="condition.conditionType === ConditionType.RULE && condition.conditionGroups" label="条件规则">
|
||||
<div class="condition-group-tool">
|
||||
<div class="flex items-center">
|
||||
<div class="mr-4">条件组关系</div>
|
||||
<el-switch
|
||||
v-model="condition.conditionGroups.and"
|
||||
inline-prompt
|
||||
active-text="且"
|
||||
inactive-text="或"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-space direction="vertical" :spacer="condition.conditionGroups.and ? '且' : '或'">
|
||||
<el-card
|
||||
class="condition-group"
|
||||
style="width: 530px"
|
||||
v-for="(equation, cIdx) in condition.conditionGroups.conditions"
|
||||
:key="cIdx"
|
||||
>
|
||||
<div
|
||||
class="condition-group-delete"
|
||||
v-if="condition.conditionGroups.conditions.length > 1"
|
||||
>
|
||||
<Icon
|
||||
color="#0089ff"
|
||||
icon="ep:circle-close-filled"
|
||||
:size="18"
|
||||
@click="deleteConditionGroup(condition.conditionGroups.conditions, cIdx)"
|
||||
/>
|
||||
</div>
|
||||
<template #header>
|
||||
<div class="flex items-center justify-between">
|
||||
<div>条件组</div>
|
||||
<div class="flex">
|
||||
<div class="mr-4">规则关系</div>
|
||||
<el-switch
|
||||
v-model="equation.and"
|
||||
inline-prompt
|
||||
active-text="且"
|
||||
inactive-text="或"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="flex pt-2" v-for="(rule, rIdx) in equation.rules" :key="rIdx">
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.leftSide`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '左值不能为空',
|
||||
trigger: 'change'
|
||||
}"
|
||||
>
|
||||
<el-select style="width: 160px" v-model="rule.leftSide">
|
||||
<el-option
|
||||
v-for="(field, fIdx) in fieldOptions"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="!field.required"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-select v-model="rule.opCode" style="width: 100px">
|
||||
<el-option
|
||||
v-for="operator in COMPARISON_OPERATORS"
|
||||
:key="operator.value"
|
||||
:label="operator.label"
|
||||
:value="operator.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`conditionGroups.conditions.${cIdx}.rules.${rIdx}.rightSide`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '右值不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input v-model="rule.rightSide" style="width: 160px" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-1 flex items-center" v-if="equation.rules.length > 1">
|
||||
<Icon icon="ep:delete" :size="18" @click="deleteConditionRule(equation, rIdx)" />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<Icon icon="ep:plus" :size="18" @click="addConditionRule(equation, rIdx)" />
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-space>
|
||||
<div title="添加条件组" class="mt-4 cursor-pointer">
|
||||
<Icon
|
||||
color="#0089ff"
|
||||
icon="ep:plus"
|
||||
:size="24"
|
||||
@click="addConditionGroup(condition.conditionGroups?.conditions)"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="condition.conditionType === ConditionType.EXPRESSION"
|
||||
label="条件表达式"
|
||||
prop="conditionExpression"
|
||||
>
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="condition.conditionExpression"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
COMPARISON_OPERATORS,
|
||||
CONDITION_CONFIG_TYPES,
|
||||
ConditionType,
|
||||
DEFAULT_CONDITION_GROUP_VALUE
|
||||
} from '../../consts'
|
||||
import { BpmModelFormType } from '@/utils/constants'
|
||||
import { useFormFieldsAndStartUser } from '../../node'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const condition = computed({
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(newValue) {
|
||||
emit('update:modelValue', newValue)
|
||||
}
|
||||
})
|
||||
const formType = inject<Ref<number>>('formType') // 表单类型
|
||||
const conditionConfigTypes = computed(() => {
|
||||
return CONDITION_CONFIG_TYPES.filter((item) => {
|
||||
// 业务表单暂时去掉条件规则选项
|
||||
if (formType?.value === BpmModelFormType.CUSTOM && item.value === ConditionType.RULE) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/** 条件规则可选择的表单字段 */
|
||||
const fieldOptions = useFormFieldsAndStartUser()
|
||||
|
||||
// 表单校验规则
|
||||
const formRules = reactive({
|
||||
conditionType: [{ required: true, message: '配置方式不能为空', trigger: 'blur' }],
|
||||
conditionExpression: [{ required: true, message: '条件表达式不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
/** 切换条件配置方式 */
|
||||
const changeConditionType = () => {
|
||||
if (condition.value.conditionType === ConditionType.RULE) {
|
||||
if (!condition.value.conditionGroups) {
|
||||
condition.value.conditionGroups = DEFAULT_CONDITION_GROUP_VALUE
|
||||
}
|
||||
}
|
||||
}
|
||||
const deleteConditionGroup = (conditions, index) => {
|
||||
conditions.splice(index, 1)
|
||||
}
|
||||
|
||||
const deleteConditionRule = (condition, index) => {
|
||||
condition.rules.splice(index, 1)
|
||||
}
|
||||
|
||||
const addConditionRule = (condition, index) => {
|
||||
const rule = {
|
||||
opCode: '==',
|
||||
leftSide: '',
|
||||
rightSide: ''
|
||||
}
|
||||
condition.rules.splice(index + 1, 0, rule)
|
||||
}
|
||||
|
||||
const addConditionGroup = (conditions) => {
|
||||
const condition = {
|
||||
and: true,
|
||||
rules: [
|
||||
{
|
||||
opCode: '==',
|
||||
leftSide: '',
|
||||
rightSide: ''
|
||||
}
|
||||
]
|
||||
}
|
||||
conditions.push(condition)
|
||||
}
|
||||
|
||||
const validate = async () => {
|
||||
if (!formRef) return false
|
||||
return await formRef.value.validate()
|
||||
}
|
||||
|
||||
defineExpose({ validate })
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.condition-group-tool {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 500px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.condition-group {
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
border-color: #0089ff;
|
||||
|
||||
.condition-group-delete {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.condition-group-delete {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
::v-deep(.el-card__header) {
|
||||
padding: 8px var(--el-card-padding);
|
||||
border-bottom: 1px solid var(--el-card-border-color);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,184 @@
|
||||
<template>
|
||||
<el-form-item label="请求头">
|
||||
<div class="flex pt-2" v-for="(item, index) in props.header" :key="index">
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`${bind}.header.${index}.key`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数名不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input class="w-160px" v-model="item.key" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-select class="w-100px!" v-model="item.type">
|
||||
<el-option
|
||||
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
:key="types.value"
|
||||
:label="types.label"
|
||||
:value="types.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`${bind}.header.${index}.value`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
|
||||
class="w-160px"
|
||||
v-model="item.value"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:prop="`${bind}.header.${index}.value`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: 'change'
|
||||
}"
|
||||
>
|
||||
<el-select
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
|
||||
class="w-160px!"
|
||||
v-model="item.value"
|
||||
>
|
||||
<el-option
|
||||
v-for="(field, fIdx) in formFieldOptions"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="!field.required"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-1 flex items-center">
|
||||
<Icon icon="ep:delete" :size="18" @click="deleteHttpRequestParam(props.header, index)" />
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" text @click="addHttpRequestParam(props.header)">
|
||||
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
<el-form-item label="请求体">
|
||||
<div class="flex pt-2" v-for="(item, index) in props.body" :key="index">
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`${bind}.body.${index}.key`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数名不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input class="w-160px" v-model="item.key" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-select class="w-100px!" v-model="item.type">
|
||||
<el-option
|
||||
v-for="types in BPM_HTTP_REQUEST_PARAM_TYPES"
|
||||
:key="types.value"
|
||||
:label="types.label"
|
||||
:value="types.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="mr-2">
|
||||
<el-form-item
|
||||
:prop="`${bind}.body.${index}.value`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FIXED_VALUE"
|
||||
class="w-160px"
|
||||
v-model="item.value"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
:prop="`${bind}.body.${index}.value`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '参数值不能为空',
|
||||
trigger: 'change'
|
||||
}"
|
||||
>
|
||||
<el-select
|
||||
v-if="item.type === BpmHttpRequestParamTypeEnum.FROM_FORM"
|
||||
class="w-160px!"
|
||||
v-model="item.value"
|
||||
>
|
||||
<el-option
|
||||
v-for="(field, fIdx) in formFieldOptions"
|
||||
:key="fIdx"
|
||||
:label="field.title"
|
||||
:value="field.field"
|
||||
:disabled="!field.required"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<div class="mr-1 flex items-center">
|
||||
<Icon icon="ep:delete" :size="18" @click="deleteHttpRequestParam(props.body, index)" />
|
||||
</div>
|
||||
</div>
|
||||
<el-button type="primary" text @click="addHttpRequestParam(props.body)">
|
||||
<Icon icon="ep:plus" class="mr-5px" />添加一行
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { HttpRequestParam, BPM_HTTP_REQUEST_PARAM_TYPES, BpmHttpRequestParamTypeEnum } from '../../consts'
|
||||
import { useFormFieldsAndStartUser } from '../../node'
|
||||
defineOptions({
|
||||
name: 'HttpRequestParamSetting'
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
header: {
|
||||
type: Array as () => HttpRequestParam[],
|
||||
required: false,
|
||||
default: () => []
|
||||
},
|
||||
body: {
|
||||
type: Array as () => HttpRequestParam[],
|
||||
required: false,
|
||||
default: () => []
|
||||
},
|
||||
bind: {
|
||||
type: String,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
// 流程表单字段,发起人字段
|
||||
const formFieldOptions = useFormFieldsAndStartUser()
|
||||
/** 添加请求配置项 */
|
||||
const addHttpRequestParam = (arr: HttpRequestParam[]) => {
|
||||
arr.push({
|
||||
key: '',
|
||||
type: BpmHttpRequestParamTypeEnum.FIXED_VALUE,
|
||||
value: ''
|
||||
})
|
||||
}
|
||||
|
||||
/** 删除请求配置项 */
|
||||
const deleteHttpRequestParam = (arr: HttpRequestParam[], index: number) => {
|
||||
arr.splice(index, 1)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<el-form ref="listenerFormRef" :model="configForm" label-position="top">
|
||||
<div v-for="(listener, listenerIdx) in taskListener" :key="listenerIdx">
|
||||
<el-divider content-position="left">
|
||||
<el-text tag="b" size="large">{{ listener.name }}</el-text>
|
||||
</el-divider>
|
||||
<el-form-item>
|
||||
<el-switch
|
||||
v-model="configForm[`task${listener.type}ListenerEnable`]"
|
||||
active-text="开启"
|
||||
inactive-text="关闭"
|
||||
/>
|
||||
</el-form-item>
|
||||
<div v-if="configForm[`task${listener.type}ListenerEnable`]">
|
||||
<el-form-item>
|
||||
<el-alert
|
||||
title="仅支持 POST 请求,以请求体方式接收参数"
|
||||
type="warning"
|
||||
show-icon
|
||||
:closable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
label="请求地址"
|
||||
:prop="`task${listener.type}ListenerPath`"
|
||||
:rules="{
|
||||
required: true,
|
||||
message: '请求地址不能为空',
|
||||
trigger: 'blur'
|
||||
}"
|
||||
>
|
||||
<el-input v-model="configForm[`task${listener.type}ListenerPath`]" />
|
||||
</el-form-item>
|
||||
<HttpRequestParamSetting
|
||||
:header="configForm[`task${listener.type}Listener`].header"
|
||||
:body="configForm[`task${listener.type}Listener`].body"
|
||||
:bind="`task${listener.type}Listener`"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import HttpRequestParamSetting from './HttpRequestParamSetting.vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
formFieldOptions: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
const listenerFormRef = ref()
|
||||
const configForm = computed({
|
||||
get() {
|
||||
return props.modelValue
|
||||
},
|
||||
set(newValue) {
|
||||
emit('update:modelValue', newValue)
|
||||
}
|
||||
})
|
||||
const taskListener = ref([
|
||||
{
|
||||
name: '创建任务',
|
||||
type: 'Create'
|
||||
},
|
||||
{
|
||||
name: '指派任务执行人员',
|
||||
type: 'Assign'
|
||||
},
|
||||
{
|
||||
name: '完成任务',
|
||||
type: 'Complete'
|
||||
}
|
||||
])
|
||||
|
||||
const validate = async () => {
|
||||
if (!listenerFormRef) return false
|
||||
return await listenerFormRef.value.validate()
|
||||
}
|
||||
|
||||
defineExpose({ validate })
|
||||
</script>
|
@ -9,8 +9,7 @@
|
||||
]"
|
||||
>
|
||||
<div class="node-title-container">
|
||||
<!-- TODO @芋艿 需要更换图标 -->
|
||||
<div class="node-title-icon copy-task"><span class="iconfont icon-copy"></span></div>
|
||||
<div class="node-title-icon delay-node"><span class="iconfont icon-delay"></span></div>
|
||||
<input
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
|
@ -77,7 +77,7 @@ const props = defineProps({
|
||||
const currentNode = useWatchNode(props)
|
||||
// 是否只读
|
||||
const readonly = inject<Boolean>('readonly')
|
||||
const processInstance = inject<Ref<any>>('processInstance')
|
||||
const processInstance = inject<Ref<any>>('processInstance', ref({}))
|
||||
// 审批信息的弹窗显示,用于只读模式
|
||||
const dialogVisible = ref(false) // 弹窗可见性
|
||||
const processInstanceInfos = ref<any[]>([]) // 流程的审批信息
|
||||
|
@ -108,7 +108,7 @@
|
||||
<script setup lang="ts">
|
||||
import NodeHandler from '../NodeHandler.vue'
|
||||
import ProcessNodeTree from '../ProcessNodeTree.vue'
|
||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
||||
import { SimpleFlowNode, NodeType, ConditionType, DEFAULT_CONDITION_GROUP_VALUE, NODE_DEFAULT_TEXT } from '../consts'
|
||||
import { getDefaultConditionNodeName } from '../utils'
|
||||
import { useTaskStatusClass } from '../node'
|
||||
import { generateUUID } from '@/utils'
|
||||
@ -149,7 +149,7 @@ const blurEvent = (index: number) => {
|
||||
showInputs.value[index] = false
|
||||
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
|
||||
conditionNode.name =
|
||||
conditionNode.name || getDefaultConditionNodeName(index, conditionNode.defaultFlow)
|
||||
conditionNode.name || getDefaultConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
|
||||
}
|
||||
|
||||
// 点击条件名称
|
||||
@ -178,8 +178,11 @@ const addCondition = () => {
|
||||
type: NodeType.CONDITION_NODE,
|
||||
childNode: undefined,
|
||||
conditionNodes: [],
|
||||
conditionType: 1,
|
||||
defaultFlow: false
|
||||
conditionSetting: {
|
||||
defaultFlow: false,
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
}
|
||||
}
|
||||
conditionNodes.splice(lastIndex, 0, conditionData)
|
||||
}
|
||||
|
@ -34,7 +34,7 @@
|
||||
]"
|
||||
>
|
||||
<div class="branch-node-title-container">
|
||||
<div v-if="showInputs[index]">
|
||||
<div v-if="!readonly && showInputs[index]">
|
||||
<input
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@ -110,7 +110,7 @@
|
||||
<script setup lang="ts">
|
||||
import NodeHandler from '../NodeHandler.vue'
|
||||
import ProcessNodeTree from '../ProcessNodeTree.vue'
|
||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
||||
import { SimpleFlowNode, NodeType, ConditionType, DEFAULT_CONDITION_GROUP_VALUE, NODE_DEFAULT_TEXT } from '../consts'
|
||||
import { useTaskStatusClass } from '../node'
|
||||
import { getDefaultInclusiveConditionNodeName } from '../utils'
|
||||
import { generateUUID } from '@/utils'
|
||||
@ -153,7 +153,7 @@ const blurEvent = (index: number) => {
|
||||
showInputs.value[index] = false
|
||||
const conditionNode = currentNode.value.conditionNodes?.at(index) as SimpleFlowNode
|
||||
conditionNode.name =
|
||||
conditionNode.name || getDefaultInclusiveConditionNodeName(index, conditionNode.defaultFlow)
|
||||
conditionNode.name || getDefaultInclusiveConditionNodeName(index, conditionNode.conditionSetting?.defaultFlow)
|
||||
}
|
||||
|
||||
// 点击条件名称
|
||||
@ -182,8 +182,11 @@ const addCondition = () => {
|
||||
type: NodeType.CONDITION_NODE,
|
||||
childNode: undefined,
|
||||
conditionNodes: [],
|
||||
conditionType: 1,
|
||||
defaultFlow: false
|
||||
conditionSetting: {
|
||||
defaultFlow: false,
|
||||
conditionType: ConditionType.RULE,
|
||||
conditionGroups: DEFAULT_CONDITION_GROUP_VALUE
|
||||
}
|
||||
}
|
||||
conditionNodes.splice(lastIndex, 0, conditionData)
|
||||
}
|
||||
|
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="node-wrapper">
|
||||
<div class="node-container">
|
||||
<div
|
||||
class="node-box"
|
||||
:class="[
|
||||
{ 'node-config-error': !currentNode.showText },
|
||||
`${useTaskStatusClass(currentNode?.activityStatus)}`
|
||||
]"
|
||||
>
|
||||
<div class="node-title-container">
|
||||
<div class="node-title-icon router-node">
|
||||
<span class="iconfont icon-router"></span>
|
||||
</div>
|
||||
<input
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
v-mountedFocus
|
||||
v-model="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
<div v-else class="node-title" @click="clickTitle">
|
||||
{{ currentNode.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="node-content" @click="openNodeConfig">
|
||||
<div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
|
||||
{{ currentNode.showText }}
|
||||
</div>
|
||||
<div class="node-text" v-else>
|
||||
{{ NODE_DEFAULT_TEXT.get(NodeType.ROUTER_BRANCH_NODE) }}
|
||||
</div>
|
||||
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
|
||||
</div>
|
||||
<div v-if="!readonly" class="node-toolbar">
|
||||
<div class="toolbar-icon"
|
||||
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
|
||||
/></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
|
||||
<NodeHandler
|
||||
v-if="currentNode"
|
||||
v-model:child-node="currentNode.childNode"
|
||||
:current-node="currentNode"
|
||||
/>
|
||||
</div>
|
||||
<RouterNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
||||
import NodeHandler from '../NodeHandler.vue'
|
||||
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
|
||||
import RouterNodeConfig from '../nodes-config/RouterNodeConfig.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'RouterNode'
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
flowNode: {
|
||||
type: Object as () => SimpleFlowNode,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
// 定义事件,更新父组件
|
||||
const emits = defineEmits<{
|
||||
'update:flowNode': [node: SimpleFlowNode | undefined]
|
||||
}>()
|
||||
// 是否只读
|
||||
const readonly = inject<Boolean>('readonly')
|
||||
// 监控节点的变化
|
||||
const currentNode = useWatchNode(props)
|
||||
// 节点名称编辑
|
||||
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.ROUTER_BRANCH_NODE)
|
||||
|
||||
const nodeSetting = ref()
|
||||
// 打开节点配置
|
||||
const openNodeConfig = () => {
|
||||
if (readonly) {
|
||||
return
|
||||
}
|
||||
nodeSetting.value.showRouteNodeConfig(currentNode.value)
|
||||
nodeSetting.value.openDrawer()
|
||||
}
|
||||
|
||||
// 删除节点。更新当前节点为孩子节点
|
||||
const deleteNode = () => {
|
||||
emits('update:flowNode', currentNode.value.childNode)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -13,7 +13,7 @@
|
||||
><span class="iconfont icon-start-user"></span
|
||||
></div>
|
||||
<input
|
||||
v-if="showInput"
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
@ -117,7 +117,7 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
const readonly = inject<Boolean>('readonly') // 是否只读
|
||||
const tasks = inject<Ref<any[]>>('tasks')
|
||||
const tasks = inject<Ref<any[]>>('tasks', ref([]))
|
||||
// 定义事件,更新父组件。
|
||||
const emits = defineEmits<{
|
||||
'update:modelValue': [node: SimpleFlowNode | undefined]
|
||||
|
@ -0,0 +1,97 @@
|
||||
<template>
|
||||
<div class="node-wrapper">
|
||||
<div class="node-container">
|
||||
<div
|
||||
class="node-box"
|
||||
:class="[
|
||||
{ 'node-config-error': !currentNode.showText },
|
||||
`${useTaskStatusClass(currentNode?.activityStatus)}`
|
||||
]"
|
||||
>
|
||||
<div class="node-title-container">
|
||||
<div class="node-title-icon trigger-node">
|
||||
<span class="iconfont icon-trigger"></span>
|
||||
</div>
|
||||
<input
|
||||
v-if="!readonly && showInput"
|
||||
type="text"
|
||||
class="editable-title-input"
|
||||
@blur="blurEvent()"
|
||||
v-mountedFocus
|
||||
v-model="currentNode.name"
|
||||
:placeholder="currentNode.name"
|
||||
/>
|
||||
<div v-else class="node-title" @click="clickTitle">
|
||||
{{ currentNode.name }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="node-content" @click="openNodeConfig">
|
||||
<div class="node-text" :title="currentNode.showText" v-if="currentNode.showText">
|
||||
{{ currentNode.showText }}
|
||||
</div>
|
||||
<div class="node-text" v-else>
|
||||
{{ NODE_DEFAULT_TEXT.get(NodeType.TRIGGER_NODE) }}
|
||||
</div>
|
||||
<Icon v-if="!readonly" icon="ep:arrow-right-bold" />
|
||||
</div>
|
||||
<div v-if="!readonly" class="node-toolbar">
|
||||
<div class="toolbar-icon"
|
||||
><Icon color="#0089ff" icon="ep:circle-close-filled" :size="18" @click="deleteNode"
|
||||
/></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 传递子节点给添加节点组件。会在子节点前面添加节点 -->
|
||||
<NodeHandler
|
||||
v-if="currentNode"
|
||||
v-model:child-node="currentNode.childNode"
|
||||
:current-node="currentNode"
|
||||
/>
|
||||
</div>
|
||||
<TriggerNodeConfig v-if="!readonly && currentNode" ref="nodeSetting" :flow-node="currentNode" />
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { SimpleFlowNode, NodeType, NODE_DEFAULT_TEXT } from '../consts'
|
||||
import NodeHandler from '../NodeHandler.vue'
|
||||
import { useNodeName2, useWatchNode, useTaskStatusClass } from '../node'
|
||||
import TriggerNodeConfig from '../nodes-config/TriggerNodeConfig.vue'
|
||||
|
||||
defineOptions({
|
||||
name: 'TriggerNode'
|
||||
})
|
||||
|
||||
const props = defineProps({
|
||||
flowNode: {
|
||||
type: Object as () => SimpleFlowNode,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
// 定义事件,更新父组件
|
||||
const emits = defineEmits<{
|
||||
'update:flowNode': [node: SimpleFlowNode | undefined]
|
||||
}>()
|
||||
// 是否只读
|
||||
const readonly = inject<Boolean>('readonly')
|
||||
// 监控节点的变化
|
||||
const currentNode = useWatchNode(props)
|
||||
// 节点名称编辑
|
||||
const { showInput, blurEvent, clickTitle } = useNodeName2(currentNode, NodeType.TRIGGER_NODE)
|
||||
|
||||
const nodeSetting = ref()
|
||||
// 打开节点配置
|
||||
const openNodeConfig = () => {
|
||||
if (readonly) {
|
||||
return
|
||||
}
|
||||
nodeSetting.value.showTriggerNodeConfig(currentNode.value)
|
||||
nodeSetting.value.openDrawer()
|
||||
}
|
||||
|
||||
// 删除节点。更新当前节点为孩子节点
|
||||
const deleteNode = () => {
|
||||
emits('update:flowNode', currentNode.value.childNode)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
@ -131,7 +131,7 @@ const emits = defineEmits<{
|
||||
|
||||
// 是否只读
|
||||
const readonly = inject<Boolean>('readonly')
|
||||
const tasks = inject<Ref<any[]>>('tasks')
|
||||
const tasks = inject<Ref<any[]>>('tasks', ref([]))
|
||||
// 监控节点变化
|
||||
const currentNode = useWatchNode(props)
|
||||
// 节点名称编辑
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -113,18 +113,21 @@
|
||||
|
||||
// 节点连线气泡卡片样式
|
||||
.handler-item-wrapper {
|
||||
width: 320px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
cursor: pointer;
|
||||
|
||||
.handler-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.handler-item-icon {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: #fff;
|
||||
border: 1px solid #e2e2e2;
|
||||
border-radius: 50%;
|
||||
@ -138,13 +141,14 @@
|
||||
|
||||
.icon-size {
|
||||
font-size: 25px;
|
||||
line-height: 60px;
|
||||
line-height: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
.approve {
|
||||
color: #ff943e;
|
||||
}
|
||||
|
||||
.copy {
|
||||
color: #3296fa;
|
||||
}
|
||||
@ -161,6 +165,18 @@
|
||||
color: #345da2;
|
||||
}
|
||||
|
||||
.delay {
|
||||
color: #e47470;
|
||||
}
|
||||
|
||||
.trigger {
|
||||
color: #3373d2;
|
||||
}
|
||||
|
||||
.router {
|
||||
color: #ca3a31
|
||||
}
|
||||
|
||||
.handler-item-text {
|
||||
margin-top: 4px;
|
||||
width: 80px;
|
||||
@ -266,6 +282,18 @@
|
||||
&.start-user {
|
||||
color: #676565;
|
||||
}
|
||||
|
||||
&.delay-node {
|
||||
color: #e47470;
|
||||
}
|
||||
|
||||
&.trigger-node {
|
||||
color: #3373d2;
|
||||
}
|
||||
|
||||
&.router-node {
|
||||
color: #ca3a31
|
||||
}
|
||||
}
|
||||
|
||||
.node-title {
|
||||
@ -711,45 +739,56 @@
|
||||
|
||||
// iconfont 样式
|
||||
@font-face {
|
||||
font-family: 'iconfont'; /* Project id 4495938 */
|
||||
src:
|
||||
url('iconfont.woff2?t=1724339470412') format('woff2'),
|
||||
url('iconfont.woff?t=1724339470412') format('woff'),
|
||||
url('iconfont.ttf?t=1724339470412') format('truetype');
|
||||
font-family: "iconfont"; /* Project id 4495938 */
|
||||
src: url('iconfont.woff2?t=1737639517142') format('woff2'),
|
||||
url('iconfont.woff?t=1737639517142') format('woff'),
|
||||
url('iconfont.ttf?t=1737639517142') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: 'iconfont' !important;
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-trigger:before {
|
||||
content: "\e6d3";
|
||||
}
|
||||
|
||||
.icon-router:before {
|
||||
content: "\e6b2";
|
||||
}
|
||||
|
||||
.icon-delay:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.icon-start-user:before {
|
||||
content: '\e679';
|
||||
content: "\e679";
|
||||
}
|
||||
|
||||
.icon-inclusive:before {
|
||||
content: '\e602';
|
||||
content: "\e602";
|
||||
}
|
||||
|
||||
.icon-copy:before {
|
||||
content: '\e7eb';
|
||||
content: "\e7eb";
|
||||
}
|
||||
|
||||
.icon-handle:before {
|
||||
content: '\e61c';
|
||||
content: "\e61c";
|
||||
}
|
||||
|
||||
.icon-exclusive:before {
|
||||
content: '\e717';
|
||||
content: "\e717";
|
||||
}
|
||||
|
||||
.icon-approve:before {
|
||||
content: '\e715';
|
||||
content: "\e715";
|
||||
}
|
||||
|
||||
.icon-parallel:before {
|
||||
content: '\e688';
|
||||
content: "\e688";
|
||||
}
|
||||
|
@ -1,4 +1,5 @@
|
||||
<!-- 列表选择通用组件,参考 ProductList 组件使用 -->
|
||||
<!-- TODO 芋艿:可能会移除 -->
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :appendToBody="true" :scroll="true" :title="title" width="60%">
|
||||
<el-table
|
||||
|
@ -308,28 +308,6 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
// 监听value变化,重新加载流程图
|
||||
watch(
|
||||
() => props.value,
|
||||
(newValue) => {
|
||||
if (newValue && bpmnModeler) {
|
||||
createNewDiagram(newValue)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
// 监听processId和processName变化
|
||||
watch(
|
||||
[() => props.processId, () => props.processName],
|
||||
([newId, newName]) => {
|
||||
if (newId && newName && !props.value) {
|
||||
createNewDiagram(null)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
provide('configGlobal', props)
|
||||
let bpmnModeler: any = null
|
||||
const defaultZoom = ref(1)
|
||||
@ -480,6 +458,7 @@ const initModelListeners = () => {
|
||||
emit('commandStack-changed', event)
|
||||
emit('input', xml)
|
||||
emit('change', xml)
|
||||
emit('save', xml)
|
||||
} catch (e: any) {
|
||||
console.error(`[Process Designer Warn]: ${e.message || e}`)
|
||||
}
|
||||
@ -568,6 +547,7 @@ const importLocalFile = () => {
|
||||
reader.onload = function () {
|
||||
let xmlStr = this.result
|
||||
createNewDiagram(xmlStr)
|
||||
emit('save', xmlStr)
|
||||
}
|
||||
}
|
||||
/* ------------------------------------------------ refs methods ------------------------------------------------------ */
|
||||
|
@ -1438,6 +1438,45 @@
|
||||
"isBody": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SignEnable",
|
||||
"superClass": ["Element"],
|
||||
"meta": {
|
||||
"allowedIn": ["bpmn:UserTask"]
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"name": "value",
|
||||
"type": "Boolean",
|
||||
"isBody": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "SkipExpression",
|
||||
"extends": ["bpmn:UserTask"],
|
||||
"properties": [
|
||||
{
|
||||
"name": "skipExpression",
|
||||
"isAttr": true,
|
||||
"type": "String"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "ReasonRequire",
|
||||
"superClass": ["Element"],
|
||||
"meta": {
|
||||
"allowedIn": ["bpmn:UserTask"]
|
||||
},
|
||||
"properties": [
|
||||
{
|
||||
"name": "value",
|
||||
"type": "Boolean",
|
||||
"isBody": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"emumerations": []
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="process-panel__container" :style="{ width: `${width}px` }">
|
||||
<div class="process-panel__container" :style="{ width: `${width}px`, maxHeight: '600px' }">
|
||||
<el-collapse v-model="activeTab" v-if="isReady">
|
||||
<el-collapse-item name="base">
|
||||
<!-- class="panel-tab__title" -->
|
||||
|
@ -152,6 +152,9 @@ watch(
|
||||
handleKeyUpdate(props.model.key)
|
||||
handleNameUpdate(props.model.name)
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
|
@ -5,6 +5,7 @@
|
||||
4. 操作按钮
|
||||
5. 字段权限
|
||||
6. 审批类型
|
||||
7. 是否需要签名
|
||||
-->
|
||||
<template>
|
||||
<div>
|
||||
@ -161,6 +162,16 @@
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-divider content-position="left">是否需要签名</el-divider>
|
||||
<el-form-item prop="signEnable">
|
||||
<el-switch v-model="signEnable.value" active-text="是" inactive-text="否" />
|
||||
</el-form-item>
|
||||
|
||||
<el-divider content-position="left">审批意见</el-divider>
|
||||
<el-form-item prop="reasonRequire">
|
||||
<el-switch v-model="reasonRequire.value" active-text="必填" inactive-text="非必填" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -218,6 +229,12 @@ const { formType, fieldsPermissionConfig, getNodeConfigFormFields } = useFormFie
|
||||
// 审批类型
|
||||
const approveType = ref({ value: ApproveType.USER })
|
||||
|
||||
// 是否需要签名
|
||||
const signEnable = ref({ value: false })
|
||||
|
||||
// 审批意见
|
||||
const reasonRequire = ref({ value: false })
|
||||
|
||||
const elExtensionElements = ref()
|
||||
const otherExtensions = ref()
|
||||
const bpmnElement = ref()
|
||||
@ -311,6 +328,16 @@ const resetCustomConfigList = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 是否需要签名
|
||||
signEnable.value =
|
||||
elExtensionElements.value.values?.filter((ex) => ex.$type === `${prefix}:SignEnable`)?.[0] ||
|
||||
bpmnInstances().moddle.create(`${prefix}:SignEnable`, { value: false })
|
||||
|
||||
// 审批意见
|
||||
reasonRequire.value =
|
||||
elExtensionElements.value.values?.filter((ex) => ex.$type === `${prefix}:ReasonRequire`)?.[0] ||
|
||||
bpmnInstances().moddle.create(`${prefix}:ReasonRequire`, { value: false })
|
||||
|
||||
// 保留剩余扩展元素,便于后面更新该元素对应属性
|
||||
otherExtensions.value =
|
||||
elExtensionElements.value.values?.filter(
|
||||
@ -322,7 +349,9 @@ const resetCustomConfigList = () => {
|
||||
ex.$type !== `${prefix}:AssignEmptyUserIds` &&
|
||||
ex.$type !== `${prefix}:ButtonsSetting` &&
|
||||
ex.$type !== `${prefix}:FieldsPermission` &&
|
||||
ex.$type !== `${prefix}:ApproveType`
|
||||
ex.$type !== `${prefix}:ApproveType` &&
|
||||
ex.$type !== `${prefix}:SignEnable` &&
|
||||
ex.$type !== `${prefix}:ReasonRequire`
|
||||
) ?? []
|
||||
|
||||
// 更新元素扩展属性,避免后续报错
|
||||
@ -373,7 +402,9 @@ const updateElementExtensions = () => {
|
||||
assignEmptyUserIdsEl.value,
|
||||
approveType.value,
|
||||
...buttonsSettingEl.value,
|
||||
...fieldsPermissionEl.value
|
||||
...fieldsPermissionEl.value,
|
||||
signEnable.value,
|
||||
reasonRequire.value
|
||||
]
|
||||
})
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
|
@ -1,6 +1,10 @@
|
||||
<template>
|
||||
<div class="panel-tab__content">
|
||||
<el-radio-group v-model="approveMethod" @change="onApproveMethodChange">
|
||||
<el-radio-group
|
||||
v-if="type === 'UserTask'"
|
||||
v-model="approveMethod"
|
||||
@change="onApproveMethodChange"
|
||||
>
|
||||
<div class="flex-col">
|
||||
<div v-for="(item, index) in APPROVE_METHODS" :key="index">
|
||||
<el-radio :value="item.value" :label="item.value">
|
||||
@ -23,6 +27,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-radio-group>
|
||||
<div v-else>
|
||||
除了UserTask以外节点的多实例待实现
|
||||
</div>
|
||||
<!-- 与Simple设计器配置合并,保留以前的代码 -->
|
||||
<el-form label-width="90px" style="display: none">
|
||||
<el-form-item label="快捷配置">
|
||||
@ -301,6 +308,7 @@ const approveMethod = ref()
|
||||
const approveRatio = ref(100)
|
||||
const otherExtensions = ref()
|
||||
const getElementLoopNew = () => {
|
||||
if (props.type === 'UserTask') {
|
||||
const extensionElements =
|
||||
bpmnElement.value.businessObject?.extensionElements ??
|
||||
bpmnInstances().moddle.create('bpmn:ExtensionElements', { values: [] })
|
||||
@ -315,6 +323,7 @@ const getElementLoopNew = () => {
|
||||
approveMethod.value = ApproveMethodType.SEQUENTIAL_APPROVE
|
||||
updateLoopCharacteristics()
|
||||
}
|
||||
}
|
||||
}
|
||||
const onApproveMethodChange = () => {
|
||||
approveRatio.value = 100
|
||||
|
@ -192,6 +192,16 @@
|
||||
<!-- 选择弹窗 -->
|
||||
<ProcessExpressionDialog ref="processExpressionDialogRef" @select="selectProcessExpression" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="跳过表达式" prop="skipExpression">
|
||||
<el-input
|
||||
type="textarea"
|
||||
v-model="userTaskForm.skipExpression"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
@change="updateSkipExpression"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
@ -220,7 +230,8 @@ const props = defineProps({
|
||||
const prefix = inject('prefix')
|
||||
const userTaskForm = ref({
|
||||
candidateStrategy: undefined, // 分配规则
|
||||
candidateParam: [] // 分配选项
|
||||
candidateParam: [], // 分配选项
|
||||
skipExpression: '' // 跳过表达式
|
||||
})
|
||||
const bpmnElement = ref()
|
||||
const bpmnInstances = () => (window as any)?.bpmnInstances
|
||||
@ -311,6 +322,13 @@ const resetTaskForm = () => {
|
||||
(ex) => ex.$type !== `${prefix}:CandidateStrategy` && ex.$type !== `${prefix}:CandidateParam`
|
||||
) ?? []
|
||||
|
||||
// 跳过表达式
|
||||
if (businessObject.skipExpression != undefined) {
|
||||
userTaskForm.value.skipExpression = businessObject.skipExpression
|
||||
} else {
|
||||
userTaskForm.value.skipExpression = ''
|
||||
}
|
||||
|
||||
// 改用通过extensionElements来存储数据
|
||||
return
|
||||
if (businessObject.candidateStrategy != undefined) {
|
||||
@ -390,6 +408,18 @@ const updateElementTask = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const updateSkipExpression = () => {
|
||||
if (userTaskForm.value.skipExpression && userTaskForm.value.skipExpression !== '') {
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
skipExpression: userTaskForm.value.skipExpression
|
||||
})
|
||||
} else {
|
||||
bpmnInstances().modeling.updateProperties(toRaw(bpmnElement.value), {
|
||||
skipExpression: null
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 打开监听器弹窗
|
||||
const processExpressionDialogRef = ref()
|
||||
const openProcessExpressionDialog = async () => {
|
||||
|
@ -1,8 +1,9 @@
|
||||
import type { App } from 'vue'
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
/** 判断权限的指令 directive */
|
||||
export function hasPermi(app: App<Element>) {
|
||||
app.directive('hasPermi', (el, binding) => {
|
||||
const { value } = binding
|
||||
@ -19,13 +20,12 @@ export function hasPermi(app: App<Element>) {
|
||||
})
|
||||
}
|
||||
|
||||
/** 判断权限的方法 function */
|
||||
const userStore = useUserStore()
|
||||
const all_permission = '*:*:*'
|
||||
export const hasPermission = (permission: string[]) => {
|
||||
const { wsCache } = useCache()
|
||||
const all_permission = '*:*:*'
|
||||
const userInfo = wsCache.get(CACHE_KEY.USER)
|
||||
const permissions = userInfo?.permissions || []
|
||||
|
||||
return permissions.some((p: string) => {
|
||||
return all_permission === p || permission.includes(p)
|
||||
})
|
||||
return (
|
||||
userStore.permissions.has(all_permission) ||
|
||||
permission.some((permission) => userStore.permissions.has(permission))
|
||||
)
|
||||
}
|
@ -12,6 +12,10 @@ import { useDesign } from '@/hooks/web/useDesign'
|
||||
import { useTemplateRefsList } from '@vueuse/core'
|
||||
import { ElScrollbar } from 'element-plus'
|
||||
import { useScrollTo } from '@/hooks/event/useScrollTo'
|
||||
import { useTagsView } from '@/hooks/web/useTagsView'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
|
||||
defineOptions({ name: 'TagsView' })
|
||||
|
||||
const { getPrefixCls } = useDesign()
|
||||
|
||||
@ -19,7 +23,9 @@ const prefixCls = getPrefixCls('tags-view')
|
||||
|
||||
const { t } = useI18n()
|
||||
|
||||
const { currentRoute, push, replace } = useRouter()
|
||||
const { currentRoute, push } = useRouter()
|
||||
|
||||
const { closeAll, closeLeft, closeRight, closeOther, closeCurrent, refreshPage } = useTagsView()
|
||||
|
||||
const permissionStore = usePermissionStore()
|
||||
|
||||
@ -31,6 +37,10 @@ const visitedViews = computed(() => tagsViewStore.getVisitedViews)
|
||||
|
||||
const affixTagArr = ref<RouteLocationNormalizedLoaded[]>([])
|
||||
|
||||
const selectedTag = computed(() => tagsViewStore.getSelectedTag)
|
||||
|
||||
const setSelectTag = tagsViewStore.setSelectedTag
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const tagsViewImmerse = computed(() => appStore.getTagsViewImmerse)
|
||||
@ -45,66 +55,30 @@ const initTags = () => {
|
||||
for (const tag of unref(affixTagArr)) {
|
||||
// Must have tag name
|
||||
if (tag.name) {
|
||||
tagsViewStore.addVisitedView(tag)
|
||||
tagsViewStore.addVisitedView(cloneDeep(tag))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const selectedTag = ref<RouteLocationNormalizedLoaded>()
|
||||
|
||||
// 新增tag
|
||||
const addTags = () => {
|
||||
const { name } = unref(currentRoute)
|
||||
if (name) {
|
||||
selectedTag.value = unref(currentRoute)
|
||||
setSelectTag(unref(currentRoute))
|
||||
tagsViewStore.addView(unref(currentRoute))
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 关闭选中的tag
|
||||
const closeSelectedTag = (view: RouteLocationNormalizedLoaded) => {
|
||||
if (view?.meta?.affix) return
|
||||
tagsViewStore.delView(view)
|
||||
closeCurrent(view, () => {
|
||||
if (isActive(view)) {
|
||||
toLastView()
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭全部
|
||||
const closeAllTags = () => {
|
||||
tagsViewStore.delAllViews()
|
||||
toLastView()
|
||||
}
|
||||
|
||||
// 关闭其它
|
||||
const closeOthersTags = () => {
|
||||
tagsViewStore.delOthersViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
||||
}
|
||||
|
||||
// 重新加载
|
||||
const refreshSelectedTag = async (view?: RouteLocationNormalizedLoaded) => {
|
||||
if (!view) return
|
||||
tagsViewStore.delCachedView()
|
||||
const { path, query } = view
|
||||
await nextTick()
|
||||
replace({
|
||||
path: '/redirect' + path,
|
||||
query: query
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭左侧
|
||||
const closeLeftTags = () => {
|
||||
tagsViewStore.delLeftViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
||||
}
|
||||
|
||||
// 关闭右侧
|
||||
const closeRightTags = () => {
|
||||
tagsViewStore.delRightViews(unref(selectedTag) as RouteLocationNormalizedLoaded)
|
||||
}
|
||||
|
||||
// 跳转到最后一个
|
||||
// 去最后一个
|
||||
const toLastView = () => {
|
||||
const visitedViews = tagsViewStore.getVisitedViews
|
||||
const latestView = visitedViews.slice(-1)[0]
|
||||
@ -118,11 +92,38 @@ const toLastView = () => {
|
||||
addTags()
|
||||
return
|
||||
}
|
||||
// TODO: You can set another route
|
||||
push('/')
|
||||
// You can set another route
|
||||
push(permissionStore.getAddRouters[0].path)
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭全部
|
||||
const closeAllTags = () => {
|
||||
closeAll(() => {
|
||||
toLastView()
|
||||
})
|
||||
}
|
||||
|
||||
// 关闭其它
|
||||
const closeOthersTags = () => {
|
||||
closeOther()
|
||||
}
|
||||
|
||||
// 重新加载
|
||||
const refreshSelectedTag = async (view?: RouteLocationNormalizedLoaded) => {
|
||||
refreshPage(view)
|
||||
}
|
||||
|
||||
// 关闭左侧
|
||||
const closeLeftTags = () => {
|
||||
closeLeft()
|
||||
}
|
||||
|
||||
// 关闭右侧
|
||||
const closeRightTags = () => {
|
||||
closeRight()
|
||||
}
|
||||
|
||||
// 滚动到选中的tag
|
||||
const moveToCurrentTag = async () => {
|
||||
await nextTick()
|
||||
@ -209,13 +210,14 @@ const isActive = (route: RouteLocationNormalizedLoaded): boolean => {
|
||||
// 所有右键菜单组件的元素
|
||||
const itemRefs = useTemplateRefsList<ComponentRef<typeof ContextMenu & ContextMenuExpose>>()
|
||||
|
||||
// 右键菜单装填改变的时候
|
||||
// 右键菜单状态改变的时候
|
||||
const visibleChange = (visible: boolean, tagItem: RouteLocationNormalizedLoaded) => {
|
||||
if (visible) {
|
||||
for (const v of unref(itemRefs)) {
|
||||
const elDropdownMenuRef = v.elDropdownMenuRef
|
||||
if (tagItem.fullPath !== v.tagItem.fullPath) {
|
||||
elDropdownMenuRef?.handleClose()
|
||||
setSelectTag(tagItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -243,7 +245,17 @@ const move = (to: number) => {
|
||||
start()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
const canShowIcon = (item: RouteLocationNormalizedLoaded) => {
|
||||
if (
|
||||
(item?.matched?.[1]?.meta?.icon && unref(tagsViewIcon)) ||
|
||||
(item?.meta?.affix && unref(tagsViewIcon) && item?.meta?.icon)
|
||||
) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
initTags()
|
||||
addTags()
|
||||
})
|
||||
|
@ -307,6 +307,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
activityId: route.query.activityId
|
||||
})
|
||||
},
|
||||
{
|
||||
path: 'process-instance/report',
|
||||
component: () => import('@/views/bpm/processInstance/report/index.vue'),
|
||||
name: 'BpmProcessInstanceReport',
|
||||
meta: {
|
||||
noCache: true,
|
||||
hidden: true,
|
||||
canTo: true,
|
||||
title: '数据报表',
|
||||
activeMenu: '/bpm/manager/model'
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'oa/leave/create',
|
||||
component: () => import('@/views/bpm/oa/leave/create.vue'),
|
||||
@ -344,7 +356,8 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'manager/model/update/:id',
|
||||
// TODO @zws:1)建议,在加一个路由。然后标题是“复制流程”,这样体验会好点;2)复制出来的数据,在名字前面,加“副本 ”,和钉钉保持一致!
|
||||
path: 'manager/model/:type/:id',
|
||||
component: () => import('@/views/bpm/model/form/index.vue'),
|
||||
name: 'BpmModelUpdate',
|
||||
meta: {
|
||||
|
@ -4,16 +4,19 @@ import { getRawRoute } from '@/utils/routerHelper'
|
||||
import { defineStore } from 'pinia'
|
||||
import { store } from '../index'
|
||||
import { findIndex } from '@/utils'
|
||||
import { useUserStoreWithOut } from './user'
|
||||
|
||||
export interface TagsViewState {
|
||||
visitedViews: RouteLocationNormalizedLoaded[]
|
||||
cachedViews: Set<string>
|
||||
selectedTag?: RouteLocationNormalizedLoaded
|
||||
}
|
||||
|
||||
export const useTagsViewStore = defineStore('tagsView', {
|
||||
state: (): TagsViewState => ({
|
||||
visitedViews: [],
|
||||
cachedViews: new Set()
|
||||
cachedViews: new Set(),
|
||||
selectedTag: undefined
|
||||
}),
|
||||
getters: {
|
||||
getVisitedViews(): RouteLocationNormalizedLoaded[] {
|
||||
@ -21,6 +24,9 @@ export const useTagsViewStore = defineStore('tagsView', {
|
||||
},
|
||||
getCachedViews(): string[] {
|
||||
return Array.from(this.cachedViews)
|
||||
},
|
||||
getSelectedTag(): RouteLocationNormalizedLoaded | undefined {
|
||||
return this.selectedTag
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@ -98,8 +104,12 @@ export const useTagsViewStore = defineStore('tagsView', {
|
||||
},
|
||||
// 删除所有tag
|
||||
delAllVisitedViews() {
|
||||
const userStore = useUserStoreWithOut()
|
||||
|
||||
// const affixTags = this.visitedViews.filter((tag) => tag.meta.affix)
|
||||
this.visitedViews = []
|
||||
this.visitedViews = userStore.getUser
|
||||
? this.visitedViews.filter((tag) => tag?.meta?.affix)
|
||||
: []
|
||||
},
|
||||
// 删除其他
|
||||
delOthersViews(view: RouteLocationNormalizedLoaded) {
|
||||
@ -145,6 +155,18 @@ export const useTagsViewStore = defineStore('tagsView', {
|
||||
break
|
||||
}
|
||||
}
|
||||
},
|
||||
// 设置当前选中的 tag
|
||||
setSelectedTag(tag: RouteLocationNormalizedLoaded) {
|
||||
this.selectedTag = tag
|
||||
},
|
||||
setTitle(title: string, path?: string) {
|
||||
for (const v of this.visitedViews) {
|
||||
if (v.path === (path ?? this.selectedTag?.path)) {
|
||||
v.meta.title = title
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
persist: false
|
||||
|
@ -15,7 +15,7 @@ interface UserVO {
|
||||
|
||||
interface UserInfoVO {
|
||||
// USER 缓存
|
||||
permissions: string[]
|
||||
permissions: Set<string>
|
||||
roles: string[]
|
||||
isSetUser: boolean
|
||||
user: UserVO
|
||||
@ -23,7 +23,7 @@ interface UserInfoVO {
|
||||
|
||||
export const useUserStore = defineStore('admin-user', {
|
||||
state: (): UserInfoVO => ({
|
||||
permissions: [],
|
||||
permissions: new Set<string>(),
|
||||
roles: [],
|
||||
isSetUser: false,
|
||||
user: {
|
||||
@ -34,7 +34,7 @@ export const useUserStore = defineStore('admin-user', {
|
||||
}
|
||||
}),
|
||||
getters: {
|
||||
getPermissions(): string[] {
|
||||
getPermissions(): Set<string> {
|
||||
return this.permissions
|
||||
},
|
||||
getRoles(): string[] {
|
||||
@ -57,7 +57,7 @@ export const useUserStore = defineStore('admin-user', {
|
||||
if (!userInfo) {
|
||||
userInfo = await getInfo()
|
||||
}
|
||||
this.permissions = userInfo.permissions
|
||||
this.permissions = new Set(userInfo.permissions)
|
||||
this.roles = userInfo.roles
|
||||
this.user = userInfo.user
|
||||
this.isSetUser = true
|
||||
@ -85,7 +85,7 @@ export const useUserStore = defineStore('admin-user', {
|
||||
this.resetState()
|
||||
},
|
||||
resetState() {
|
||||
this.permissions = []
|
||||
this.permissions = new Set<string>()
|
||||
this.roles = []
|
||||
this.isSetUser = false
|
||||
this.user = {
|
||||
|
@ -457,3 +457,9 @@ export const BpmProcessInstanceStatus = {
|
||||
REJECT: 3, // 审批不通过
|
||||
CANCEL: 4 // 已取消
|
||||
}
|
||||
|
||||
export const BpmAutoApproveType = {
|
||||
NONE: 0, // 不自动通过
|
||||
APPROVE_ALL: 1, // 仅审批一次,后续重复的审批节点均自动通过
|
||||
APPROVE_SEQUENT: 2, // 仅针对连续审批的节点自动通过
|
||||
}
|
||||
|
@ -33,6 +33,10 @@ const download = {
|
||||
markdown: (data: Blob, fileName: string) => {
|
||||
download0(data, fileName, 'text/markdown')
|
||||
},
|
||||
// 下载 Json 方法
|
||||
json: (data: Blob, fileName: string) => {
|
||||
download0(data, fileName, 'application/json')
|
||||
},
|
||||
// 下载图片(允许跨域)
|
||||
image: ({
|
||||
url,
|
||||
@ -65,6 +69,31 @@ const download = {
|
||||
a.download = 'image.png'
|
||||
a.click()
|
||||
}
|
||||
},
|
||||
base64ToFile: (base64: any, fileName: string) => {
|
||||
// 将base64按照 , 进行分割 将前缀 与后续内容分隔开
|
||||
const data = base64.split(',')
|
||||
// 利用正则表达式 从前缀中获取图片的类型信息(image/png、image/jpeg、image/webp等)
|
||||
const type = data[0].match(/:(.*?);/)[1]
|
||||
// 从图片的类型信息中 获取具体的文件格式后缀(png、jpeg、webp)
|
||||
const suffix = type.split('/')[1]
|
||||
// 使用atob()对base64数据进行解码 结果是一个文件数据流 以字符串的格式输出
|
||||
const bstr = window.atob(data[1])
|
||||
// 获取解码结果字符串的长度
|
||||
let n = bstr.length
|
||||
// 根据解码结果字符串的长度创建一个等长的整形数字数组
|
||||
// 但在创建时 所有元素初始值都为 0
|
||||
const u8arr = new Uint8Array(n)
|
||||
// 将整形数组的每个元素填充为解码结果字符串对应位置字符的UTF-16 编码单元
|
||||
while (n--) {
|
||||
// charCodeAt():获取给定索引处字符对应的 UTF-16 代码单元
|
||||
u8arr[n] = bstr.charCodeAt(n)
|
||||
}
|
||||
|
||||
// 将File文件对象返回给方法的调用者
|
||||
return new File([u8arr], `${fileName}.${suffix}`, {
|
||||
type: type
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -507,3 +507,18 @@ export function jsonParse(str: string) {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 截取字符串
|
||||
*
|
||||
* @param name
|
||||
* @param start
|
||||
* @param end
|
||||
*/
|
||||
|
||||
export const sliceName = (name: string,start: number, end : number) => {
|
||||
if (name.length > end) {
|
||||
return name.slice(start, end)
|
||||
}
|
||||
return name
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
import {hasPermission} from "@/directives/permission/hasPermi";
|
||||
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
@ -7,21 +9,8 @@ const { t } = useI18n() // 国际化
|
||||
* @param {Array} value 校验值
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function checkPermi(value: string[]) {
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const { wsCache } = useCache()
|
||||
const permissionDatas = value
|
||||
const all_permission = '*:*:*'
|
||||
const userInfo = wsCache.get(CACHE_KEY.USER)
|
||||
const permissions = userInfo?.permissions || []
|
||||
const hasPermission = permissions.some((permission: string) => {
|
||||
return all_permission === permission || permissionDatas.includes(permission)
|
||||
})
|
||||
return !!hasPermission
|
||||
} else {
|
||||
console.error(t('permission.hasPermission'))
|
||||
return false
|
||||
}
|
||||
export function checkPermi(permission: string[]) {
|
||||
return hasPermission(permission)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -13,6 +13,9 @@
|
||||
<el-form-item label="分类标志" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入分类标志" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类描述" prop="description">
|
||||
<el-input v-model="formData.description" type="textarea" placeholder="请输入分类描述" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分类状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
@ -58,6 +61,7 @@ const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
description: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
sort: undefined
|
||||
})
|
||||
@ -117,6 +121,7 @@ const resetForm = () => {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
description: undefined,
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
sort: undefined
|
||||
}
|
||||
|
@ -50,11 +50,13 @@ import FcDesigner from '@form-create/designer'
|
||||
import { encodeConf, encodeFields, setConfAndFields } from '@/utils/formCreate'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import { useFormCreateDesigner } from '@/components/FormCreate'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
defineOptions({ name: 'BpmFormEditor' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息
|
||||
const route = useRoute() // 路由
|
||||
const { push, currentRoute } = useRouter() // 路由
|
||||
const { query } = useRoute() // 路由信息
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
@ -150,6 +152,14 @@ onMounted(async () => {
|
||||
const data = await FormApi.getForm(id)
|
||||
formData.value = data
|
||||
setConfAndFields(designer, data.conf, data.fields)
|
||||
|
||||
if (route.query.type !== 'copy') {
|
||||
return
|
||||
}
|
||||
// 场景三: 复制表单
|
||||
const { id: foo, ...copied } = data
|
||||
formData.value = copied
|
||||
formData.value.name += '_copy'
|
||||
})
|
||||
</script>
|
||||
|
||||
|
@ -59,7 +59,15 @@
|
||||
v-hasPermi="['bpm:form:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm(scope.row.id)"
|
||||
@click="openForm('copy', scope.row.id)"
|
||||
>
|
||||
复制
|
||||
</el-button>
|
||||
<el-button
|
||||
v-hasPermi="['bpm:form:update']"
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', scope.row.id)"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
@ -139,16 +147,17 @@ const resetQuery = () => {
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const openForm = (id?: number) => {
|
||||
const toRouter: { name: string; query?: { id: number } } = {
|
||||
name: 'BpmFormEditor'
|
||||
const openForm = (type: string, id?: number) => {
|
||||
const toRouter: { name: string; query: { type: string; id?: number } } = {
|
||||
name: 'BpmFormEditor',
|
||||
query: {
|
||||
type
|
||||
}
|
||||
}
|
||||
console.log(typeof id)
|
||||
// 表单新建的时候id传的是event需要排除
|
||||
if (typeof id === 'number' || typeof id === 'string') {
|
||||
toRouter.query = {
|
||||
id
|
||||
}
|
||||
toRouter.query.id = id
|
||||
}
|
||||
push(toRouter)
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="flex items-center h-50px">
|
||||
<div class="flex items-center h-50px" v-memo="[categoryInfo.name, isCategorySorting]">
|
||||
<!-- 头部:分类名 -->
|
||||
<div class="flex items-center">
|
||||
<el-tooltip content="拖动排序" v-if="isCategorySorting">
|
||||
@ -13,7 +13,7 @@
|
||||
<div class="color-gray-600 text-16px"> ({{ categoryInfo.modelList?.length || 0 }}) </div>
|
||||
</div>
|
||||
<!-- 头部:操作 -->
|
||||
<div class="flex-1 flex" v-if="!isCategorySorting">
|
||||
<div class="flex-1 flex" v-show="!isCategorySorting">
|
||||
<div
|
||||
v-if="categoryInfo.modelList.length > 0"
|
||||
class="ml-20px flex items-center"
|
||||
@ -69,16 +69,17 @@
|
||||
<el-collapse-transition>
|
||||
<div v-show="isExpand">
|
||||
<el-table
|
||||
v-if="modelList && modelList.length > 0"
|
||||
:class="categoryInfo.name"
|
||||
ref="tableRef"
|
||||
:header-cell-style="{ backgroundColor: isDark ? '' : '#edeff0', paddingLeft: '10px' }"
|
||||
:cell-style="{ paddingLeft: '10px' }"
|
||||
:row-style="{ height: '68px' }"
|
||||
:data="modelList"
|
||||
row-key="id"
|
||||
:header-cell-style="tableHeaderStyle"
|
||||
:cell-style="tableCellStyle"
|
||||
:row-style="{ height: '68px' }"
|
||||
>
|
||||
<el-table-column label="流程名" prop="name" min-width="150">
|
||||
<template #default="scope">
|
||||
<template #default="{ row }">
|
||||
<div class="flex items-center">
|
||||
<el-tooltip content="拖动排序" v-if="isModelSorting">
|
||||
<Icon
|
||||
@ -86,27 +87,28 @@
|
||||
class="drag-icon cursor-move text-#8a909c mr-10px"
|
||||
/>
|
||||
</el-tooltip>
|
||||
<el-image :src="scope.row.icon" class="h-38px w-38px mr-10px rounded" />
|
||||
{{ scope.row.name }}
|
||||
<el-image v-if="row.icon" :src="row.icon" class="h-38px w-38px mr-10px rounded" />
|
||||
<div v-else class="flow-icon">
|
||||
<span style="font-size: 12px; color: #fff">{{ sliceName(row.name,0,2) }}</span>
|
||||
</div>
|
||||
{{ row.name }}
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="可见范围" prop="startUserIds" min-width="150">
|
||||
<template #default="scope">
|
||||
<el-text v-if="!scope.row.startUsers || scope.row.startUsers.length === 0">
|
||||
全部可见
|
||||
</el-text>
|
||||
<el-text v-else-if="scope.row.startUsers.length == 1">
|
||||
{{ scope.row.startUsers[0].nickname }}
|
||||
<template #default="{ row }">
|
||||
<el-text v-if="!row.startUsers?.length"> 全部可见 </el-text>
|
||||
<el-text v-else-if="row.startUsers.length === 1">
|
||||
{{ row.startUsers[0].nickname }}
|
||||
</el-text>
|
||||
<el-text v-else>
|
||||
<el-tooltip
|
||||
class="box-item"
|
||||
effect="dark"
|
||||
placement="top"
|
||||
:content="scope.row.startUsers.map((user: any) => user.nickname).join('、')"
|
||||
:content="row.startUsers.map((user: any) => user.nickname).join('、')"
|
||||
>
|
||||
{{ scope.row.startUsers[0].nickname }}等 {{ scope.row.startUsers.length }} 人可见
|
||||
{{ row.startUsers[0].nickname }}等 {{ row.startUsers.length }} 人可见
|
||||
</el-tooltip>
|
||||
</el-text>
|
||||
</template>
|
||||
@ -158,17 +160,26 @@
|
||||
link
|
||||
type="primary"
|
||||
@click="openModelForm('update', scope.row.id)"
|
||||
v-hasPermi="['bpm:model:update']"
|
||||
v-if="hasPermiUpdate"
|
||||
:disabled="!isManagerUser(scope.row)"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openModelForm('copy', scope.row.id)"
|
||||
v-if="hasPermiUpdate"
|
||||
:disabled="!isManagerUser(scope.row)"
|
||||
>
|
||||
复制
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
class="!ml-5px"
|
||||
type="primary"
|
||||
@click="handleDeploy(scope.row)"
|
||||
v-hasPermi="['bpm:model:deploy']"
|
||||
v-if="hasPermiDeploy"
|
||||
:disabled="!isManagerUser(scope.row)"
|
||||
>
|
||||
发布
|
||||
@ -176,28 +187,43 @@
|
||||
<el-dropdown
|
||||
class="!align-middle ml-5px"
|
||||
@command="(command) => handleModelCommand(command, scope.row)"
|
||||
v-hasPermi="['bpm:process-definition:query', 'bpm:model:update', 'bpm:model:delete']"
|
||||
v-if="hasPermiMore"
|
||||
>
|
||||
<el-button type="primary" link>更多</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
command="handleDefinitionList"
|
||||
v-if="checkPermi(['bpm:process-definition:query'])"
|
||||
>
|
||||
<el-dropdown-item command="handleDefinitionList" v-if="hasPermiPdQuery">
|
||||
历史
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
command="handleReport"
|
||||
v-if="
|
||||
checkPermi(['bpm:process-instance:manager-query']) &&
|
||||
scope.row.processDefinition
|
||||
"
|
||||
:disabled="!isManagerUser(scope.row)"
|
||||
>
|
||||
报表
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
command="handleChangeState"
|
||||
v-if="checkPermi(['bpm:model:update']) && scope.row.processDefinition"
|
||||
v-if="hasPermiUpdate && scope.row.processDefinition"
|
||||
:disabled="!isManagerUser(scope.row)"
|
||||
>
|
||||
{{ scope.row.processDefinition.suspensionState === 1 ? '停用' : '启用' }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
type="danger"
|
||||
command="handleClean"
|
||||
v-if="checkPermi(['bpm:model:clean'])"
|
||||
:disabled="!isManagerUser(scope.row)"
|
||||
>
|
||||
清理
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
type="danger"
|
||||
command="handleDelete"
|
||||
v-if="checkPermi(['bpm:model:delete'])"
|
||||
v-if="hasPermiDelete"
|
||||
:disabled="!isManagerUser(scope.row)"
|
||||
>
|
||||
删除
|
||||
@ -227,15 +253,15 @@
|
||||
</template>
|
||||
</Dialog>
|
||||
|
||||
<!-- 表单弹窗:添加流程模型 -->
|
||||
<ModelForm :categoryId="categoryInfo.code" ref="modelFormRef" @success="emit('success')" />
|
||||
<!-- 弹窗:表单详情 -->
|
||||
<Dialog title="表单详情" :fullscreen="true" v-model="formDetailVisible">
|
||||
<form-create :rule="formDetailPreview.rule" :option="formDetailPreview.option" />
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import ModelForm from './ModelForm.vue'
|
||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||
import Sortable from 'sortablejs'
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import * as ModelApi from '@/api/bpm/model'
|
||||
import * as FormApi from '@/api/bpm/form'
|
||||
@ -244,26 +270,90 @@ import { BpmModelFormType } from '@/utils/constants'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import { cloneDeep, isEqual } from 'lodash-es'
|
||||
import { useTagsView } from '@/hooks/web/useTagsView'
|
||||
import { useDebounceFn } from '@vueuse/core'
|
||||
import { sliceName } from '@/utils/index'
|
||||
|
||||
defineOptions({ name: 'BpmModel' })
|
||||
|
||||
const props = defineProps({
|
||||
categoryInfo: propTypes.object.def([]), // 分类后的数据
|
||||
isCategorySorting: propTypes.bool.def(false) // 是否分类在排序
|
||||
})
|
||||
// 优化 Props 类型定义
|
||||
interface UserInfo {
|
||||
nickname: string
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface ProcessDefinition {
|
||||
deploymentTime: string
|
||||
version: number
|
||||
suspensionState: number
|
||||
}
|
||||
|
||||
interface ModelInfo {
|
||||
id: number
|
||||
name: string
|
||||
icon?: string
|
||||
startUsers?: UserInfo[]
|
||||
processDefinition?: ProcessDefinition
|
||||
formType?: number
|
||||
formId?: number
|
||||
formName?: string
|
||||
formCustomCreatePath?: string
|
||||
managerUserIds?: number[]
|
||||
[key: string]: any
|
||||
}
|
||||
|
||||
interface CategoryInfoProps {
|
||||
id: number
|
||||
name: string
|
||||
modelList: ModelInfo[]
|
||||
}
|
||||
|
||||
const props = defineProps<{
|
||||
categoryInfo: CategoryInfoProps
|
||||
isCategorySorting: boolean
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
const { push } = useRouter() // 路由
|
||||
const userStore = useUserStoreWithOut() // 用户信息缓存
|
||||
const isDark = computed(() => useAppStore().getIsDark) // 是否黑暗模式
|
||||
const router = useRouter() // 路由
|
||||
|
||||
const isModelSorting = ref(false) // 是否正处于排序状态
|
||||
const originalData: any = ref([]) // 原始数据
|
||||
const modelList: any = ref([]) // 模型列表
|
||||
const originalData = ref<ModelInfo[]>([]) // 原始数据
|
||||
const modelList = ref<ModelInfo[]>([]) // 模型列表
|
||||
const isExpand = ref(false) // 是否处于展开状态
|
||||
|
||||
// 使用 computed 优化表格样式计算
|
||||
const tableHeaderStyle = computed(() => ({
|
||||
backgroundColor: isDark.value ? '' : '#edeff0',
|
||||
paddingLeft: '10px'
|
||||
}))
|
||||
|
||||
const tableCellStyle = computed(() => ({
|
||||
paddingLeft: '10px'
|
||||
}))
|
||||
|
||||
/** 权限校验:通过 computed 解决列表的卡顿问题 */
|
||||
const hasPermiUpdate = computed(() => {
|
||||
return checkPermi(['bpm:model:update'])
|
||||
})
|
||||
const hasPermiDelete = computed(() => {
|
||||
return checkPermi(['bpm:model:delete'])
|
||||
})
|
||||
const hasPermiDeploy = computed(() => {
|
||||
return checkPermi(['bpm:model:deploy'])
|
||||
})
|
||||
const hasPermiMore = computed(() => {
|
||||
return checkPermi(['bpm:process-definition:query', 'bpm:model:update', 'bpm:model:delete'])
|
||||
})
|
||||
const hasPermiPdQuery = computed(() => {
|
||||
return checkPermi(['bpm:process-definition:query'])
|
||||
})
|
||||
|
||||
/** '更多'操作按钮 */
|
||||
const handleModelCommand = (command: string, row: any) => {
|
||||
switch (command) {
|
||||
@ -276,6 +366,18 @@ const handleModelCommand = (command: string, row: any) => {
|
||||
case 'handleChangeState':
|
||||
handleChangeState(row)
|
||||
break
|
||||
case 'handleClean':
|
||||
handleClean(row)
|
||||
break
|
||||
case 'handleReport':
|
||||
router.push({
|
||||
name: 'BpmProcessInstanceReport',
|
||||
query: {
|
||||
processDefinitionId: row.processDefinition.id,
|
||||
processDefinitionKey: row.key
|
||||
}
|
||||
})
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
@ -309,6 +411,19 @@ const handleDelete = async (row: any) => {
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 清理按钮操作 */
|
||||
const handleClean = async (row: any) => {
|
||||
try {
|
||||
// 清理的二次确认
|
||||
await message.confirm('是否确认清理流程名字为"' + row.name + '"的数据项?')
|
||||
// 发起清理
|
||||
await ModelApi.cleanModel(row.id)
|
||||
message.success('清理成功')
|
||||
// 刷新列表
|
||||
emit('success')
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 更新状态操作 */
|
||||
const handleChangeState = async (row: any) => {
|
||||
const state = row.processDefinition.suspensionState
|
||||
@ -331,11 +446,10 @@ const handleChangeState = async (row: any) => {
|
||||
/** 发布流程 */
|
||||
const handleDeploy = async (row: any) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.confirm('是否部署该流程!!')
|
||||
await message.confirm('是否确认发布该流程?')
|
||||
// 发起部署
|
||||
await ModelApi.deployModel(row.id)
|
||||
message.success(t('部署成功'))
|
||||
message.success(t('发布成功'))
|
||||
// 刷新列表
|
||||
emit('success')
|
||||
} catch {}
|
||||
@ -358,7 +472,7 @@ const formDetailPreview = ref({
|
||||
option: {}
|
||||
})
|
||||
const handleFormDetail = async (row: any) => {
|
||||
if (row.formType == 10) {
|
||||
if (row.formType == BpmModelFormType.NORMAL) {
|
||||
// 设置表单
|
||||
const data = await FormApi.getForm(row.formId)
|
||||
setConfAndFields2(formDetailPreview, data.conf, data.fields)
|
||||
@ -405,14 +519,15 @@ const handleModelSortCancel = () => {
|
||||
|
||||
/** 创建拖拽实例 */
|
||||
const tableRef = ref()
|
||||
const initSort = () => {
|
||||
const initSort = useDebounceFn(() => {
|
||||
const table = document.querySelector(`.${props.categoryInfo.name} .el-table__body-wrapper tbody`)
|
||||
if (!table) return
|
||||
|
||||
Sortable.create(table, {
|
||||
group: 'shared',
|
||||
animation: 150,
|
||||
draggable: '.el-table__row',
|
||||
handle: '.drag-icon',
|
||||
// 结束拖动事件
|
||||
onEnd: ({ newDraggableIndex, oldDraggableIndex }) => {
|
||||
if (oldDraggableIndex !== newDraggableIndex) {
|
||||
modelList.value.splice(
|
||||
@ -423,15 +538,18 @@ const initSort = () => {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 200)
|
||||
|
||||
/** 更新 modelList 模型列表 */
|
||||
const updateModeList = () => {
|
||||
modelList.value = cloneDeep(props.categoryInfo.modelList)
|
||||
if (props.categoryInfo.modelList.length > 0) {
|
||||
const updateModeList = useDebounceFn(() => {
|
||||
const newModelList = props.categoryInfo.modelList
|
||||
if (!isEqual(modelList.value, newModelList)) {
|
||||
modelList.value = cloneDeep(newModelList)
|
||||
if (newModelList?.length > 0) {
|
||||
isExpand.value = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
|
||||
/** 重命名弹窗确定 */
|
||||
const renameCategoryVisible = ref(false)
|
||||
@ -466,26 +584,31 @@ const handleDeleteCategory = async () => {
|
||||
}
|
||||
|
||||
/** 添加流程模型弹窗 */
|
||||
const modelFormRef = ref()
|
||||
const openModelForm = (type: string, id?: number) => {
|
||||
const tagsView = useTagsView()
|
||||
const openModelForm = async (type: string, id?: number) => {
|
||||
if (type === 'create') {
|
||||
push({ name: 'BpmModelCreate' })
|
||||
await push({ name: 'BpmModelCreate' })
|
||||
} else {
|
||||
push({
|
||||
await push({
|
||||
name: 'BpmModelUpdate',
|
||||
params: { id }
|
||||
params: { id, type }
|
||||
})
|
||||
// 设置标题
|
||||
if (type === 'copy') {
|
||||
tagsView.setTitle('复制流程')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
watch(() => props.categoryInfo.modelList, updateModeList, { immediate: true })
|
||||
watch(
|
||||
() => props.isCategorySorting,
|
||||
(val) => {
|
||||
if (val) isExpand.value = false
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
watchEffect(() => {
|
||||
if (props.categoryInfo?.modelList) {
|
||||
updateModeList()
|
||||
}
|
||||
|
||||
if (props.isCategorySorting) {
|
||||
isExpand.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@ -502,10 +625,27 @@ watch(
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
:deep() {
|
||||
.el-table__cell {
|
||||
.flow-icon {
|
||||
display: flex;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
margin-right: 10px;
|
||||
background-color: var(--el-color-primary);
|
||||
border-radius: 0.25rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.category-draggable-model {
|
||||
:deep(.el-table__cell) {
|
||||
overflow: hidden;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
|
||||
// 优化表格渲染性能
|
||||
:deep(.el-table__body) {
|
||||
will-change: transform;
|
||||
transform: translateZ(0);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,440 +0,0 @@
|
||||
<template>
|
||||
<Dialog v-model="dialogVisible" :title="dialogTitle" width="600">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="110px"
|
||||
>
|
||||
<el-form-item label="流程标识" prop="key">
|
||||
<el-input v-model="formData.key" :disabled="!!formData.id" placeholder="请输入流标标识" />
|
||||
<el-tooltip
|
||||
v-if="!formData.id"
|
||||
class="item"
|
||||
content="新建后,流程标识不可修改!"
|
||||
effect="light"
|
||||
placement="top"
|
||||
>
|
||||
<i class="el-icon-question" style="padding-left: 5px"></i>
|
||||
</el-tooltip>
|
||||
<el-tooltip v-else class="item" content="流程标识不可修改!" effect="light" placement="top">
|
||||
<i class="el-icon-question" style="padding-left: 5px"></i>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程名称" prop="name">
|
||||
<el-input
|
||||
v-model="formData.name"
|
||||
:disabled="!!formData.id"
|
||||
clearable
|
||||
placeholder="请输入流程名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程分类" prop="category">
|
||||
<el-select
|
||||
v-model="formData.category"
|
||||
clearable
|
||||
placeholder="请选择流程分类"
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option
|
||||
v-for="category in categoryList"
|
||||
:key="category.code"
|
||||
:label="category.name"
|
||||
:value="category.code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程图标" prop="icon">
|
||||
<UploadImg v-model="formData.icon" :limit="1" height="64px" width="64px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="流程描述" prop="description">
|
||||
<el-input v-model="formData.description" clearable type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="流程类型" prop="type">
|
||||
<el-radio-group v-model="formData.type">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="表单类型" prop="formType">
|
||||
<el-radio-group v-model="formData.formType">
|
||||
<el-radio
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_MODEL_FORM_TYPE)"
|
||||
:key="dict.value"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.formType === 10" label="流程表单" prop="formId">
|
||||
<el-select v-model="formData.formId" clearable style="width: 100%">
|
||||
<el-option v-for="form in formList" :key="form.id" :label="form.name" :value="form.id" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="formData.formType === 20"
|
||||
label="表单提交路由"
|
||||
prop="formCustomCreatePath"
|
||||
>
|
||||
<el-input
|
||||
v-model="formData.formCustomCreatePath"
|
||||
placeholder="请输入表单提交路由"
|
||||
style="width: 330px"
|
||||
/>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
content="自定义表单的提交路径,使用 Vue 的路由地址,例如说:bpm/oa/leave/create.vue"
|
||||
effect="light"
|
||||
placement="top"
|
||||
>
|
||||
<i class="el-icon-question" style="padding-left: 5px"></i>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="formData.formType === 20" label="表单查看地址" prop="formCustomViewPath">
|
||||
<el-input
|
||||
v-model="formData.formCustomViewPath"
|
||||
placeholder="请输入表单查看的组件地址"
|
||||
style="width: 330px"
|
||||
/>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
content="自定义表单的查看组件地址,使用 Vue 的组件地址,例如说:bpm/oa/leave/detail.vue"
|
||||
effect="light"
|
||||
placement="top"
|
||||
>
|
||||
<i class="el-icon-question" style="padding-left: 5px"></i>
|
||||
</el-tooltip>
|
||||
</el-form-item>
|
||||
<el-form-item label="是否可见" prop="visible">
|
||||
<el-radio-group v-model="formData.visible">
|
||||
<el-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value as string"
|
||||
:label="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="谁可以发起" prop="startUserType">
|
||||
<el-select
|
||||
v-model="formData.startUserType"
|
||||
placeholder="请选择谁可以发起"
|
||||
@change="handleStartUserTypeChange"
|
||||
>
|
||||
<el-option label="全员" :value="0" />
|
||||
<el-option label="指定人员" :value="1" />
|
||||
<el-option label="均不可提交" :value="2" />
|
||||
</el-select>
|
||||
<div v-if="formData.startUserType === 1" class="mt-2 flex flex-wrap gap-2">
|
||||
<div
|
||||
v-for="user in selectedStartUsers"
|
||||
:key="user.id"
|
||||
class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
|
||||
>
|
||||
<el-avatar class="!m-5px" :size="28" v-if="user.avatar" :src="user.avatar" />
|
||||
<el-avatar class="!m-5px" :size="28" v-else>
|
||||
{{ user.nickname.substring(0, 1) }}
|
||||
</el-avatar>
|
||||
{{ user.nickname }}
|
||||
<Icon
|
||||
icon="ep:close"
|
||||
class="ml-2 cursor-pointer hover:text-red-500"
|
||||
@click="handleRemoveStartUser(user)"
|
||||
/>
|
||||
</div>
|
||||
<el-button type="primary" link @click="openStartUserSelect">
|
||||
<Icon icon="ep:plus" />选择人员
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程管理员" prop="managerUserType">
|
||||
<el-select
|
||||
v-model="formData.managerUserType"
|
||||
placeholder="请选择流程管理员"
|
||||
@change="handleManagerUserTypeChange"
|
||||
>
|
||||
<el-option label="全员" :value="0" />
|
||||
<el-option label="指定人员" :value="1" />
|
||||
<el-option label="均不可提交" :value="2" />
|
||||
</el-select>
|
||||
<div v-if="formData.managerUserType === 1" class="mt-2 flex flex-wrap gap-2">
|
||||
<div
|
||||
v-for="user in selectedManagerUsers"
|
||||
:key="user.id"
|
||||
class="bg-gray-100 h-35px rounded-3xl flex items-center pr-8px dark:color-gray-600 position-relative"
|
||||
>
|
||||
<el-avatar class="!m-5px" :size="28" v-if="user.avatar" :src="user.avatar" />
|
||||
<el-avatar class="!m-5px" :size="28" v-else>
|
||||
{{ user.nickname.substring(0, 1) }}
|
||||
</el-avatar>
|
||||
{{ user.nickname }}
|
||||
<Icon
|
||||
icon="ep:close"
|
||||
class="ml-2 cursor-pointer hover:text-red-500"
|
||||
@click="handleRemoveManagerUser(user)"
|
||||
/>
|
||||
</div>
|
||||
<el-button type="primary" link @click="openManagerUserSelect">
|
||||
<Icon icon="ep:plus" />选择人员
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
<UserSelectForm ref="userSelectFormRef" @confirm="handleUserSelectConfirm" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { propTypes } from '@/utils/propTypes'
|
||||
import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '@/utils/dict'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
import * as ModelApi from '@/api/bpm/model'
|
||||
import * as FormApi from '@/api/bpm/form'
|
||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||
import { BpmModelFormType, BpmModelType } from '@/utils/constants'
|
||||
import { UserVO } from '@/api/system/user'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import { FormVO } from '@/api/bpm/form'
|
||||
|
||||
defineOptions({ name: 'ModelForm' })
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const userStore = useUserStoreWithOut() // 用户信息缓存
|
||||
const props = defineProps({
|
||||
categoryId: propTypes.number
|
||||
})
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData: any = ref({
|
||||
id: undefined,
|
||||
name: '',
|
||||
key: '',
|
||||
category: undefined,
|
||||
icon: undefined,
|
||||
description: '',
|
||||
type: BpmModelType.BPMN,
|
||||
formType: BpmModelFormType.NORMAL,
|
||||
formId: '',
|
||||
formCustomCreatePath: '',
|
||||
formCustomViewPath: '',
|
||||
visible: true,
|
||||
startUserType: undefined,
|
||||
managerUserType: undefined,
|
||||
startUserIds: [],
|
||||
managerUserIds: []
|
||||
})
|
||||
const formRules = reactive({
|
||||
name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }],
|
||||
key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
|
||||
category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }],
|
||||
icon: [{ required: true, message: '流程图标不能为空', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
|
||||
formType: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
|
||||
formId: [{ required: true, message: '流程表单不能为空', trigger: 'blur' }],
|
||||
formCustomCreatePath: [{ required: true, message: '表单提交路由不能为空', trigger: 'blur' }],
|
||||
formCustomViewPath: [{ required: true, message: '表单查看地址不能为空', trigger: 'blur' }],
|
||||
visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
|
||||
managerUserIds: [{ required: true, message: '流程管理员不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
const formList = ref<FormVO[]>([]) // 流程表单的下拉框的数据
|
||||
const categoryList = ref<CategoryVO[]>([]) // 流程分类列表
|
||||
const userList = ref<UserVO[]>([]) // 用户列表
|
||||
const selectedStartUsers = ref<UserVO[]>([]) // 已选择的发起人列表
|
||||
const selectedManagerUsers = ref<UserVO[]>([]) // 已选择的管理员列表
|
||||
const userSelectFormRef = ref() // 用户选择弹窗 ref
|
||||
const currentSelectType = ref<'start' | 'manager'>('start') // 当前选择的是发起人还是管理员
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (type: string, id?: string) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
resetForm()
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await ModelApi.getModel(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
// 加载数据时,根据已有的用户ID列表初始化已选用户
|
||||
if (formData.value.startUserIds?.length) {
|
||||
formData.value.startUserType = 1
|
||||
selectedStartUsers.value = userList.value.filter((user) =>
|
||||
formData.value.startUserIds.includes(user.id)
|
||||
)
|
||||
}
|
||||
if (formData.value.managerUserIds?.length) {
|
||||
formData.value.managerUserType = 1
|
||||
selectedManagerUsers.value = userList.value.filter((user) =>
|
||||
formData.value.managerUserIds.includes(user.id)
|
||||
)
|
||||
}
|
||||
} else {
|
||||
formData.value.managerUserIds.push(userStore.getUser.id)
|
||||
}
|
||||
// 获得流程表单的下拉框的数据
|
||||
formList.value = await FormApi.getFormSimpleList()
|
||||
// 查询流程分类列表
|
||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||
// 查询用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
if (props.categoryId) {
|
||||
formData.value.category = props.categoryId
|
||||
}
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
const submitForm = async () => {
|
||||
// 校验表单
|
||||
if (!formRef) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
const data = formData.value as unknown as ModelApi.ModelVO
|
||||
if (formType.value === 'create') {
|
||||
await ModelApi.createModel(data)
|
||||
// 提示,引导用户做后续的操作
|
||||
await ElMessageBox.alert(
|
||||
'<strong>新建模型成功!</strong>后续需要执行如下 2 个步骤:' +
|
||||
'<div>1. 点击【设计流程】按钮,绘制流程图</div>' +
|
||||
'<div>2. 点击【发布流程】按钮,完成流程的最终发布</div>' +
|
||||
'另外,每次流程修改后,都需要点击【发布流程】按钮,才能正式生效!!!',
|
||||
'重要提示',
|
||||
{
|
||||
dangerouslyUseHTMLString: true,
|
||||
type: 'success'
|
||||
}
|
||||
)
|
||||
} else {
|
||||
await ModelApi.updateModel(data)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
id: undefined,
|
||||
name: '',
|
||||
key: '',
|
||||
category: undefined,
|
||||
icon: undefined,
|
||||
description: '',
|
||||
type: BpmModelType.BPMN,
|
||||
formType: BpmModelFormType.NORMAL,
|
||||
formId: '',
|
||||
formCustomCreatePath: '',
|
||||
formCustomViewPath: '',
|
||||
visible: true,
|
||||
startUserType: undefined,
|
||||
managerUserType: undefined,
|
||||
startUserIds: [],
|
||||
managerUserIds: []
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
selectedStartUsers.value = []
|
||||
selectedManagerUsers.value = []
|
||||
}
|
||||
|
||||
/** 处理发起人类型变化 */
|
||||
const handleStartUserTypeChange = (value: number) => {
|
||||
if (value !== 1) {
|
||||
selectedStartUsers.value = []
|
||||
formData.value.startUserIds = []
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理管理员类型变化 */
|
||||
const handleManagerUserTypeChange = (value: number) => {
|
||||
if (value !== 1) {
|
||||
selectedManagerUsers.value = []
|
||||
formData.value.managerUserIds = []
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开发起人选择 */
|
||||
const openStartUserSelect = () => {
|
||||
currentSelectType.value = 'start'
|
||||
userSelectFormRef.value.open(0, selectedStartUsers.value)
|
||||
}
|
||||
|
||||
/** 打开管理员选择 */
|
||||
const openManagerUserSelect = () => {
|
||||
currentSelectType.value = 'manager'
|
||||
userSelectFormRef.value.open(0, selectedManagerUsers.value)
|
||||
}
|
||||
|
||||
/** 处理用户选择确认 */
|
||||
const handleUserSelectConfirm = (_, users: UserVO[]) => {
|
||||
if (currentSelectType.value === 'start') {
|
||||
selectedStartUsers.value = users
|
||||
formData.value.startUserIds = users.map((u) => u.id)
|
||||
} else {
|
||||
selectedManagerUsers.value = users
|
||||
formData.value.managerUserIds = users.map((u) => u.id)
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除发起人 */
|
||||
const handleRemoveStartUser = (user: UserVO) => {
|
||||
selectedStartUsers.value = selectedStartUsers.value.filter((u) => u.id !== user.id)
|
||||
formData.value.startUserIds = formData.value.startUserIds.filter((id: number) => id !== user.id)
|
||||
}
|
||||
|
||||
/** 移除管理员 */
|
||||
const handleRemoveManagerUser = (user: UserVO) => {
|
||||
selectedManagerUsers.value = selectedManagerUsers.value.filter((u) => u.id !== user.id)
|
||||
formData.value.managerUserIds = formData.value.managerUserIds.filter(
|
||||
(id: number) => id !== user.id
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.bg-gray-100 {
|
||||
background-color: #f5f7fa;
|
||||
transition: all 0.3s;
|
||||
|
||||
&:hover {
|
||||
background-color: #e6e8eb;
|
||||
}
|
||||
|
||||
.ep-close {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
transition: color 0.3s;
|
||||
|
||||
&:hover {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -12,10 +12,12 @@
|
||||
:additionalModel="controlForm.additionalModel"
|
||||
:model="model"
|
||||
@save="save"
|
||||
:process-id="modelKey"
|
||||
:process-name="modelName"
|
||||
/>
|
||||
<!-- 流程属性器,负责编辑每个流程节点的属性 -->
|
||||
<MyProcessPenal
|
||||
v-if="isModelerReady && modeler"
|
||||
v-if="modeler"
|
||||
key="penal"
|
||||
:bpmnModeler="modeler"
|
||||
:prefix="controlForm.prefix"
|
||||
@ -37,8 +39,8 @@ defineOptions({ name: 'BpmModelEditor' })
|
||||
|
||||
const props = defineProps<{
|
||||
modelId?: string
|
||||
modelKey?: string
|
||||
modelName?: string
|
||||
modelKey: string
|
||||
modelName: string
|
||||
value?: string
|
||||
}>()
|
||||
|
||||
@ -51,10 +53,13 @@ const formType = ref(20)
|
||||
provide('formFields', formFields)
|
||||
provide('formType', formType)
|
||||
|
||||
const xmlString = ref<string>('') // BPMN XML
|
||||
// 注入流程数据
|
||||
const xmlString = inject('processData') as Ref
|
||||
// 注入模型数据
|
||||
const modelData = inject('modelData') as Ref
|
||||
|
||||
const modeler = shallowRef() // BPMN Modeler
|
||||
const processDesigner = ref()
|
||||
const isModelerReady = ref(false)
|
||||
const controlForm = ref({
|
||||
simulation: true,
|
||||
labelEditing: false,
|
||||
@ -65,154 +70,26 @@ const controlForm = ref({
|
||||
})
|
||||
const model = ref<ModelApi.ModelVO>() // 流程模型的信息
|
||||
|
||||
// 初始化 bpmnInstances
|
||||
const initBpmnInstances = () => {
|
||||
if (!modeler.value) return false
|
||||
try {
|
||||
const instances = {
|
||||
modeler: modeler.value,
|
||||
modeling: modeler.value.get('modeling'),
|
||||
moddle: modeler.value.get('moddle'),
|
||||
eventBus: modeler.value.get('eventBus'),
|
||||
bpmnFactory: modeler.value.get('bpmnFactory'),
|
||||
elementFactory: modeler.value.get('elementFactory'),
|
||||
elementRegistry: modeler.value.get('elementRegistry'),
|
||||
replace: modeler.value.get('replace'),
|
||||
selection: modeler.value.get('selection')
|
||||
}
|
||||
|
||||
// 检查所有实例是否都存在
|
||||
return Object.values(instances).every((instance) => instance)
|
||||
} catch (error) {
|
||||
console.error('初始化 bpmnInstances 失败:', error)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
/** 初始化 modeler */
|
||||
const initModeler = async (item) => {
|
||||
try {
|
||||
const initModeler = async (item: any) => {
|
||||
//先初始化模型数据
|
||||
model.value = modelData.value
|
||||
modeler.value = item
|
||||
// 等待 modeler 初始化完成
|
||||
await nextTick()
|
||||
|
||||
// 确保 modeler 的所有实例都已经准备好
|
||||
if (initBpmnInstances()) {
|
||||
isModelerReady.value = true
|
||||
emit('init-finished')
|
||||
|
||||
// 初始化完成后,设置初始值
|
||||
if (props.modelId) {
|
||||
// 编辑模式
|
||||
const data = await ModelApi.getModel(props.modelId)
|
||||
model.value = {
|
||||
...data,
|
||||
bpmnXml: undefined // 清空 bpmnXml 属性
|
||||
}
|
||||
xmlString.value = data.bpmnXml || getDefaultBpmnXml(data.key, data.name)
|
||||
} else if (props.modelKey && props.modelName) {
|
||||
// 新建模式
|
||||
xmlString.value = props.value || getDefaultBpmnXml(props.modelKey, props.modelName)
|
||||
model.value = {
|
||||
key: props.modelKey,
|
||||
name: props.modelName
|
||||
} as ModelApi.ModelVO
|
||||
}
|
||||
|
||||
// 导入XML并刷新视图
|
||||
await nextTick()
|
||||
try {
|
||||
await modeler.value.importXML(xmlString.value)
|
||||
if (processDesigner.value?.refresh) {
|
||||
processDesigner.value.refresh()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('导入XML失败:', error)
|
||||
}
|
||||
} else {
|
||||
console.error('modeler 实例未完全初始化')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化 modeler 失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取默认的BPMN XML */
|
||||
const getDefaultBpmnXml = (key: string, name: string) => {
|
||||
return `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.activiti.org/processdef">
|
||||
<process id="${key}" name="${name}" isExecutable="true" />
|
||||
<bpmndi:BPMNDiagram id="BPMNDiagram">
|
||||
<bpmndi:BPMNPlane id="${key}_di" bpmnElement="${key}" />
|
||||
</bpmndi:BPMNDiagram>
|
||||
</definitions>`
|
||||
}
|
||||
|
||||
/** 添加/修改模型 */
|
||||
const save = async (bpmnXml: string) => {
|
||||
try {
|
||||
xmlString.value = bpmnXml
|
||||
if (props.modelId) {
|
||||
// 编辑模式
|
||||
const data = {
|
||||
...model.value,
|
||||
bpmnXml: bpmnXml
|
||||
} as unknown as ModelApi.ModelVO
|
||||
await ModelApi.updateModelBpmn(data)
|
||||
emit('success')
|
||||
} else {
|
||||
// 新建模式,直接返回XML
|
||||
emit('success', bpmnXml)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error)
|
||||
message.error('保存失败')
|
||||
}
|
||||
}
|
||||
|
||||
// 监听 key、name 和 value 的变化
|
||||
watch(
|
||||
[() => props.modelKey, () => props.modelName, () => props.value],
|
||||
async ([newKey, newName, newValue]) => {
|
||||
if (!props.modelId && isModelerReady.value) {
|
||||
let shouldRefresh = false
|
||||
|
||||
if (newKey && newName) {
|
||||
const newXml = newValue || getDefaultBpmnXml(newKey, newName)
|
||||
if (newXml !== xmlString.value) {
|
||||
xmlString.value = newXml
|
||||
shouldRefresh = true
|
||||
}
|
||||
model.value = {
|
||||
...model.value,
|
||||
key: newKey,
|
||||
name: newName
|
||||
} as ModelApi.ModelVO
|
||||
} else if (newValue && newValue !== xmlString.value) {
|
||||
xmlString.value = newValue
|
||||
shouldRefresh = true
|
||||
}
|
||||
|
||||
if (shouldRefresh) {
|
||||
// 确保更新后重新渲染
|
||||
await nextTick()
|
||||
if (processDesigner.value?.refresh) {
|
||||
try {
|
||||
await modeler.value?.importXML(xmlString.value)
|
||||
processDesigner.value.refresh()
|
||||
} catch (error) {
|
||||
console.error('导入XML失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 在组件卸载时清理
|
||||
onBeforeUnmount(() => {
|
||||
isModelerReady.value = false
|
||||
modeler.value = null
|
||||
// 清理全局实例
|
||||
const w = window as any
|
||||
@ -220,55 +97,6 @@ onBeforeUnmount(() => {
|
||||
w.bpmnInstances = null
|
||||
}
|
||||
})
|
||||
|
||||
/** 获取 XML 字符串 */
|
||||
const saveXML = async () => {
|
||||
if (!modeler.value) {
|
||||
return { xml: xmlString.value }
|
||||
}
|
||||
try {
|
||||
const result = await modeler.value.saveXML({ format: true })
|
||||
xmlString.value = result.xml
|
||||
return result
|
||||
} catch (error) {
|
||||
console.error('获取XML失败:', error)
|
||||
return { xml: xmlString.value }
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取SVG字符串 */
|
||||
const saveSVG = async () => {
|
||||
if (!modeler.value) {
|
||||
return { svg: undefined }
|
||||
}
|
||||
try {
|
||||
return await modeler.value.saveSVG()
|
||||
} catch (error) {
|
||||
console.error('获取SVG失败:', error)
|
||||
return { svg: undefined }
|
||||
}
|
||||
}
|
||||
|
||||
/** 刷新视图 */
|
||||
const refresh = async () => {
|
||||
if (processDesigner.value?.refresh && modeler.value) {
|
||||
try {
|
||||
await modeler.value.importXML(xmlString.value)
|
||||
processDesigner.value.refresh()
|
||||
} catch (error) {
|
||||
console.error('刷新视图失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 暴露必要的属性和方法给父组件
|
||||
defineExpose({
|
||||
modeler,
|
||||
isModelerReady,
|
||||
saveXML,
|
||||
saveSVG,
|
||||
refresh
|
||||
})
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.process-panel__container {
|
||||
|
@ -6,7 +6,7 @@
|
||||
class="!w-440px"
|
||||
v-model="modelData.key"
|
||||
:disabled="!!modelData.id"
|
||||
placeholder="请输入流标标识"
|
||||
placeholder="请输入流程标识,以字母或下划线开头"
|
||||
/>
|
||||
<el-tooltip
|
||||
class="item"
|
||||
@ -41,7 +41,7 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程图标" prop="icon" class="mb-20px">
|
||||
<el-form-item label="流程图标" class="mb-20px">
|
||||
<UploadImg v-model="modelData.icon" :limit="1" height="64px" width="64px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="流程描述" prop="description" class="mb-20px">
|
||||
@ -62,7 +62,7 @@
|
||||
<el-radio-group v-model="modelData.visible">
|
||||
<el-radio
|
||||
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
||||
:key="dict.value"
|
||||
:key="dict.value as string"
|
||||
:value="dict.value"
|
||||
>
|
||||
{{ dict.label }}
|
||||
@ -77,7 +77,6 @@
|
||||
>
|
||||
<el-option label="全员" :value="0" />
|
||||
<el-option label="指定人员" :value="1" />
|
||||
<el-option label="均不可提交" :value="2" />
|
||||
</el-select>
|
||||
<div v-if="modelData.startUserType === 1" class="mt-2 flex flex-wrap gap-2">
|
||||
<div
|
||||
@ -97,21 +96,12 @@
|
||||
/>
|
||||
</div>
|
||||
<el-button type="primary" link @click="openStartUserSelect">
|
||||
<Icon icon="ep:plus" />选择人员
|
||||
<Icon icon="ep:plus" /> 选择人员
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程管理员" prop="managerUserType" class="mb-20px">
|
||||
<el-select
|
||||
v-model="modelData.managerUserType"
|
||||
placeholder="请选择流程管理员"
|
||||
@change="handleManagerUserTypeChange"
|
||||
>
|
||||
<el-option label="全员" :value="0" />
|
||||
<el-option label="指定人员" :value="1" />
|
||||
<el-option label="均不可提交" :value="2" />
|
||||
</el-select>
|
||||
<div v-if="modelData.managerUserType === 1" class="mt-2 flex flex-wrap gap-2">
|
||||
<el-form-item label="流程管理员" prop="managerUserIds" class="mb-20px">
|
||||
<div class="flex flex-wrap gap-2">
|
||||
<div
|
||||
v-for="user in selectedManagerUsers"
|
||||
:key="user.id"
|
||||
@ -142,14 +132,11 @@
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getBoolDictOptions, getIntDictOptions } from '@/utils/dict'
|
||||
import { UserVO } from '@/api/system/user'
|
||||
import { CategoryVO } from '@/api/bpm/category'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
categoryList: {
|
||||
type: Array,
|
||||
type: Array as PropType<CategoryVO[]>,
|
||||
required: true
|
||||
},
|
||||
userList: {
|
||||
@ -158,8 +145,6 @@ const props = defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const formRef = ref()
|
||||
const selectedStartUsers = ref<UserVO[]>([])
|
||||
const selectedManagerUsers = ref<UserVO[]>([])
|
||||
@ -170,34 +155,36 @@ const rules = {
|
||||
name: [{ required: true, message: '流程名称不能为空', trigger: 'blur' }],
|
||||
key: [{ required: true, message: '流程标识不能为空', trigger: 'blur' }],
|
||||
category: [{ required: true, message: '流程分类不能为空', trigger: 'blur' }],
|
||||
icon: [{ required: true, message: '流程图标不能为空', trigger: 'blur' }],
|
||||
type: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
|
||||
visible: [{ required: true, message: '是否可见不能为空', trigger: 'blur' }],
|
||||
managerUserIds: [{ required: true, message: '流程管理员不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
// 创建本地数据副本
|
||||
const modelData = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
const modelData = defineModel<any>()
|
||||
|
||||
// 初始化选中的用户
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
() => modelData.value,
|
||||
(newVal) => {
|
||||
if (newVal.startUserIds?.length) {
|
||||
selectedStartUsers.value = props.userList.filter((user: UserVO) =>
|
||||
newVal.startUserIds.includes(user.id)
|
||||
) as UserVO[]
|
||||
} else {
|
||||
selectedStartUsers.value = []
|
||||
}
|
||||
if (newVal.managerUserIds?.length) {
|
||||
selectedManagerUsers.value = props.userList.filter((user: UserVO) =>
|
||||
newVal.managerUserIds.includes(user.id)
|
||||
) as UserVO[]
|
||||
} else {
|
||||
selectedManagerUsers.value = []
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
/** 打开发起人选择 */
|
||||
@ -215,58 +202,42 @@ const openManagerUserSelect = () => {
|
||||
/** 处理用户选择确认 */
|
||||
const handleUserSelectConfirm = (_, users: UserVO[]) => {
|
||||
if (currentSelectType.value === 'start') {
|
||||
selectedStartUsers.value = users
|
||||
emit('update:modelValue', {
|
||||
modelData.value = {
|
||||
...modelData.value,
|
||||
startUserIds: users.map((u) => u.id)
|
||||
})
|
||||
}
|
||||
} else {
|
||||
selectedManagerUsers.value = users
|
||||
emit('update:modelValue', {
|
||||
modelData.value = {
|
||||
...modelData.value,
|
||||
managerUserIds: users.map((u) => u.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理发起人类型变化 */
|
||||
const handleStartUserTypeChange = (value: number) => {
|
||||
if (value !== 1) {
|
||||
selectedStartUsers.value = []
|
||||
emit('update:modelValue', {
|
||||
modelData.value = {
|
||||
...modelData.value,
|
||||
startUserIds: []
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理管理员类型变化 */
|
||||
const handleManagerUserTypeChange = (value: number) => {
|
||||
if (value !== 1) {
|
||||
selectedManagerUsers.value = []
|
||||
emit('update:modelValue', {
|
||||
...modelData.value,
|
||||
managerUserIds: []
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除发起人 */
|
||||
const handleRemoveStartUser = (user: UserVO) => {
|
||||
selectedStartUsers.value = selectedStartUsers.value.filter((u) => u.id !== user.id)
|
||||
emit('update:modelValue', {
|
||||
modelData.value = {
|
||||
...modelData.value,
|
||||
startUserIds: modelData.value.startUserIds.filter((id: number) => id !== user.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 移除管理员 */
|
||||
const handleRemoveManagerUser = (user: UserVO) => {
|
||||
selectedManagerUsers.value = selectedManagerUsers.value.filter((u) => u.id !== user.id)
|
||||
emit('update:modelValue', {
|
||||
modelData.value = {
|
||||
...modelData.value,
|
||||
managerUserIds: modelData.value.managerUserIds.filter((id: number) => id !== user.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/** 表单校验 */
|
||||
|
289
src/views/bpm/model/form/ExtraSettings.vue
Normal file
289
src/views/bpm/model/form/ExtraSettings.vue
Normal file
@ -0,0 +1,289 @@
|
||||
<template>
|
||||
<el-form ref="formRef" :model="modelData" label-width="120px" class="mt-20px">
|
||||
<el-form-item class="mb-20px">
|
||||
<template #label>
|
||||
<el-text size="large" tag="b">提交人权限</el-text>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<el-checkbox v-model="modelData.allowCancelRunningProcess" label="允许撤销审批中的申请" />
|
||||
<div class="ml-22px">
|
||||
<el-text type="info"> 第一个审批节点通过后,提交人仍可撤销申请 </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="modelData.processIdRule" class="mb-20px">
|
||||
<template #label>
|
||||
<el-text size="large" tag="b">流程编码</el-text>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<div>
|
||||
<el-input
|
||||
v-model="modelData.processIdRule.prefix"
|
||||
class="w-130px!"
|
||||
placeholder="前缀"
|
||||
:disabled="!modelData.processIdRule.enable"
|
||||
>
|
||||
<template #prepend>
|
||||
<el-checkbox v-model="modelData.processIdRule.enable" />
|
||||
</template>
|
||||
</el-input>
|
||||
<el-select
|
||||
v-model="modelData.processIdRule.infix"
|
||||
class="w-130px! ml-5px"
|
||||
placeholder="中缀"
|
||||
:disabled="!modelData.processIdRule.enable"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in timeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
<el-input
|
||||
v-model="modelData.processIdRule.postfix"
|
||||
class="w-80px! ml-5px"
|
||||
placeholder="后缀"
|
||||
:disabled="!modelData.processIdRule.enable"
|
||||
/>
|
||||
<el-input-number
|
||||
v-model="modelData.processIdRule.length"
|
||||
class="w-120px! ml-5px"
|
||||
:min="5"
|
||||
:disabled="!modelData.processIdRule.enable"
|
||||
/>
|
||||
</div>
|
||||
<div class="ml-22px" v-if="modelData.processIdRule.enable">
|
||||
<el-text type="info"> 编码示例:{{ numberExample }} </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item class="mb-20px">
|
||||
<template #label>
|
||||
<el-text size="large" tag="b">自动去重</el-text>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<div>
|
||||
<el-text> 同一审批人在流程中重复出现时: </el-text>
|
||||
</div>
|
||||
<el-radio-group v-model="modelData.autoApprovalType">
|
||||
<div class="flex flex-col">
|
||||
<el-radio :value="0">不自动通过</el-radio>
|
||||
<el-radio :value="1">仅审批一次,后续重复的审批节点均自动通过</el-radio>
|
||||
<el-radio :value="2">仅针对连续审批的节点自动通过</el-radio>
|
||||
</div>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="modelData.titleSetting" class="mb-20px">
|
||||
<template #label>
|
||||
<el-text size="large" tag="b">标题设置</el-text>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<el-radio-group v-model="modelData.titleSetting.enable">
|
||||
<div class="flex flex-col">
|
||||
<el-radio :value="false"
|
||||
>系统默认 <el-text type="info"> 展示流程名称 </el-text></el-radio
|
||||
>
|
||||
<el-radio :value="true">
|
||||
自定义标题
|
||||
<el-text>
|
||||
<el-tooltip content="输入字符 '{' 即可插入表单字段" effect="light" placement="top">
|
||||
<Icon icon="ep:question-filled" class="ml-5px" />
|
||||
</el-tooltip>
|
||||
</el-text>
|
||||
</el-radio>
|
||||
</div>
|
||||
</el-radio-group>
|
||||
<el-mention
|
||||
v-if="modelData.titleSetting.enable"
|
||||
v-model="modelData.titleSetting.title"
|
||||
type="textarea"
|
||||
prefix="{"
|
||||
split="}"
|
||||
whole
|
||||
:options="formFieldOptions4Title"
|
||||
placeholder="请插入表单字段(输入 '{' 可以选择表单字段)或输入文本"
|
||||
class="w-600px!"
|
||||
/>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="modelData.summarySetting && modelData.formType === BpmModelFormType.NORMAL"
|
||||
class="mb-20px"
|
||||
>
|
||||
<template #label>
|
||||
<el-text size="large" tag="b">摘要设置</el-text>
|
||||
</template>
|
||||
<div class="flex flex-col">
|
||||
<el-radio-group v-model="modelData.summarySetting.enable">
|
||||
<div class="flex flex-col">
|
||||
<el-radio :value="false">
|
||||
系统默认 <el-text type="info"> 展示表单前 3 个字段 </el-text>
|
||||
</el-radio>
|
||||
<el-radio :value="true"> 自定义摘要 </el-radio>
|
||||
</div>
|
||||
</el-radio-group>
|
||||
<el-select
|
||||
class="w-500px!"
|
||||
v-if="modelData.summarySetting.enable"
|
||||
v-model="modelData.summarySetting.summary"
|
||||
multiple
|
||||
placeholder="请选择要展示的表单字段"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in formFieldOptions4Summary"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import dayjs from 'dayjs'
|
||||
import { BpmAutoApproveType, BpmModelFormType } from '@/utils/constants'
|
||||
import * as FormApi from '@/api/bpm/form'
|
||||
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
||||
import { ProcessVariableEnum } from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||
|
||||
const modelData = defineModel<any>()
|
||||
|
||||
/** 自定义 ID 流程编码 */
|
||||
const timeOptions = ref([
|
||||
{
|
||||
value: '',
|
||||
label: '无'
|
||||
},
|
||||
{
|
||||
value: 'DAY',
|
||||
label: '精确到日'
|
||||
},
|
||||
{
|
||||
value: 'HOUR',
|
||||
label: '精确到时'
|
||||
},
|
||||
{
|
||||
value: 'MINUTE',
|
||||
label: '精确到分'
|
||||
},
|
||||
{
|
||||
value: 'SECOND',
|
||||
label: '精确到秒'
|
||||
}
|
||||
])
|
||||
const numberExample = computed(() => {
|
||||
if (modelData.value.processIdRule.enable) {
|
||||
let infix = ''
|
||||
switch (modelData.value.processIdRule.infix) {
|
||||
case 'DAY':
|
||||
infix = dayjs().format('YYYYMMDD')
|
||||
break
|
||||
case 'HOUR':
|
||||
infix = dayjs().format('YYYYMMDDHH')
|
||||
break
|
||||
case 'MINUTE':
|
||||
infix = dayjs().format('YYYYMMDDHHmm')
|
||||
break
|
||||
case 'SECOND':
|
||||
infix = dayjs().format('YYYYMMDDHHmmss')
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
return (
|
||||
modelData.value.processIdRule.prefix +
|
||||
infix +
|
||||
modelData.value.processIdRule.postfix +
|
||||
'1'.padStart(modelData.value.processIdRule.length - 1, '0')
|
||||
)
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
})
|
||||
|
||||
/** 表单选项 */
|
||||
const formField = ref<Array<{ field: string; title: string }>>([])
|
||||
const formFieldOptions4Title = computed(() => {
|
||||
let cloneFormField = formField.value.map((item) => {
|
||||
return {
|
||||
label: item.title,
|
||||
value: item.field
|
||||
}
|
||||
})
|
||||
// 固定添加发起人 ID 字段
|
||||
cloneFormField.unshift({
|
||||
label: '流程名称',
|
||||
value: ProcessVariableEnum.PROCESS_DEFINITION_NAME
|
||||
})
|
||||
cloneFormField.unshift({
|
||||
label: '发起时间',
|
||||
value: ProcessVariableEnum.START_TIME
|
||||
})
|
||||
cloneFormField.unshift({
|
||||
label: '发起人',
|
||||
value: ProcessVariableEnum.START_USER_ID
|
||||
})
|
||||
return cloneFormField
|
||||
})
|
||||
const formFieldOptions4Summary = computed(() => {
|
||||
return formField.value.map((item) => {
|
||||
return {
|
||||
label: item.title,
|
||||
value: item.field
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
/** 兼容以前未配置更多设置的流程 */
|
||||
const initData = () => {
|
||||
if (!modelData.value.processIdRule) {
|
||||
modelData.value.processIdRule = {
|
||||
enable: false,
|
||||
prefix: '',
|
||||
infix: '',
|
||||
postfix: '',
|
||||
length: 5
|
||||
}
|
||||
}
|
||||
if (!modelData.value.autoApprovalType) {
|
||||
modelData.value.autoApprovalType = BpmAutoApproveType.NONE
|
||||
}
|
||||
if (!modelData.value.titleSetting) {
|
||||
modelData.value.titleSetting = {
|
||||
enable: false,
|
||||
title: ''
|
||||
}
|
||||
}
|
||||
if (!modelData.value.summarySetting) {
|
||||
modelData.value.summarySetting = {
|
||||
enable: false,
|
||||
summary: []
|
||||
}
|
||||
}
|
||||
}
|
||||
defineExpose({ initData })
|
||||
|
||||
/** 监听表单 ID 变化,加载表单数据 */
|
||||
watch(
|
||||
() => modelData.value.formId,
|
||||
async (newFormId) => {
|
||||
if (newFormId && modelData.value.formType === BpmModelFormType.NORMAL) {
|
||||
const data = await FormApi.getForm(newFormId)
|
||||
const result: Array<{ field: string; title: string }> = []
|
||||
if (data.fields) {
|
||||
data.fields.forEach((fieldStr: string) => {
|
||||
parseFormFields(JSON.parse(fieldStr), result)
|
||||
})
|
||||
}
|
||||
formField.value = result
|
||||
} else {
|
||||
formField.value = []
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
@ -70,25 +70,16 @@ import * as FormApi from '@/api/bpm/form'
|
||||
import { setConfAndFields2 } from '@/utils/formCreate'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
formList: {
|
||||
type: Array,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
|
||||
const formRef = ref()
|
||||
|
||||
// 创建本地数据副本
|
||||
const modelData = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
const modelData = defineModel<any>()
|
||||
|
||||
// 表单预览数据
|
||||
const formPreview = ref({
|
||||
|
@ -6,10 +6,7 @@
|
||||
:model-id="modelData.id"
|
||||
:model-key="modelData.key"
|
||||
:model-name="modelData.name"
|
||||
:value="currentBpmnXml"
|
||||
ref="bpmnEditorRef"
|
||||
@success="handleDesignSuccess"
|
||||
@init-finished="handleEditorInit"
|
||||
/>
|
||||
</template>
|
||||
|
||||
@ -21,10 +18,7 @@
|
||||
:model-key="modelData.key"
|
||||
:model-name="modelData.name"
|
||||
:start-user-ids="modelData.startUserIds"
|
||||
:value="currentSimpleModel"
|
||||
ref="simpleEditorRef"
|
||||
@success="handleDesignSuccess"
|
||||
@init-finished="handleEditorInit"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
@ -34,137 +28,16 @@ import { BpmModelType } from '@/utils/constants'
|
||||
import BpmModelEditor from '../editor/index.vue'
|
||||
import SimpleModelDesign from '../../simple/SimpleModelDesign.vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'success'])
|
||||
|
||||
const bpmnEditorRef = ref()
|
||||
const simpleEditorRef = ref()
|
||||
const isEditorInitialized = ref(false)
|
||||
|
||||
// 创建本地数据副本
|
||||
const modelData = computed({
|
||||
get: () => props.modelValue,
|
||||
set: (val) => emit('update:modelValue', val)
|
||||
})
|
||||
const modelData = defineModel<any>()
|
||||
|
||||
// 保存当前的流程XML或数据
|
||||
const currentBpmnXml = ref('')
|
||||
const currentSimpleModel = ref('')
|
||||
|
||||
// 初始化或更新当前的XML数据
|
||||
const initOrUpdateXmlData = () => {
|
||||
if (modelData.value) {
|
||||
if (modelData.value.type === BpmModelType.BPMN) {
|
||||
currentBpmnXml.value = modelData.value.bpmnXml || ''
|
||||
} else {
|
||||
currentSimpleModel.value = modelData.value.simpleModel || ''
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听modelValue的变化,更新数据
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
if (newVal) {
|
||||
if (newVal.type === BpmModelType.BPMN) {
|
||||
if (newVal.bpmnXml && newVal.bpmnXml !== currentBpmnXml.value) {
|
||||
currentBpmnXml.value = newVal.bpmnXml
|
||||
// 如果编辑器已经初始化,刷新视图
|
||||
if (isEditorInitialized.value && bpmnEditorRef.value?.refresh) {
|
||||
nextTick(() => {
|
||||
bpmnEditorRef.value.refresh()
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (newVal.simpleModel && newVal.simpleModel !== currentSimpleModel.value) {
|
||||
currentSimpleModel.value = newVal.simpleModel
|
||||
// 如果编辑器已经初始化,刷新视图
|
||||
if (isEditorInitialized.value && simpleEditorRef.value?.refresh) {
|
||||
nextTick(() => {
|
||||
simpleEditorRef.value.refresh()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
/** 编辑器初始化完成的回调 */
|
||||
const handleEditorInit = async () => {
|
||||
isEditorInitialized.value = true
|
||||
|
||||
// 等待下一个tick,确保编辑器已经准备好
|
||||
await nextTick()
|
||||
|
||||
// 初始化完成后,设置初始值
|
||||
if (modelData.value.type === BpmModelType.BPMN) {
|
||||
if (modelData.value.bpmnXml) {
|
||||
currentBpmnXml.value = modelData.value.bpmnXml
|
||||
if (bpmnEditorRef.value?.refresh) {
|
||||
await nextTick()
|
||||
bpmnEditorRef.value.refresh()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (modelData.value.simpleModel) {
|
||||
currentSimpleModel.value = modelData.value.simpleModel
|
||||
if (simpleEditorRef.value?.refresh) {
|
||||
await nextTick()
|
||||
simpleEditorRef.value.refresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取当前流程数据 */
|
||||
const getProcessData = async () => {
|
||||
try {
|
||||
if (modelData.value.type === BpmModelType.BPMN) {
|
||||
if (!bpmnEditorRef.value || !isEditorInitialized.value) {
|
||||
return currentBpmnXml.value || undefined
|
||||
}
|
||||
const { xml } = await bpmnEditorRef.value.saveXML()
|
||||
if (xml) {
|
||||
currentBpmnXml.value = xml
|
||||
return xml
|
||||
}
|
||||
} else {
|
||||
if (!simpleEditorRef.value || !isEditorInitialized.value) {
|
||||
return currentSimpleModel.value || undefined
|
||||
}
|
||||
const flowData = await simpleEditorRef.value.getCurrentFlowData()
|
||||
if (flowData) {
|
||||
currentSimpleModel.value = flowData
|
||||
return flowData
|
||||
}
|
||||
}
|
||||
return modelData.value.type === BpmModelType.BPMN
|
||||
? currentBpmnXml.value
|
||||
: currentSimpleModel.value
|
||||
} catch (error) {
|
||||
console.error('获取流程数据失败:', error)
|
||||
return modelData.value.type === BpmModelType.BPMN
|
||||
? currentBpmnXml.value
|
||||
: currentSimpleModel.value
|
||||
}
|
||||
}
|
||||
const processData = inject('processData') as Ref
|
||||
|
||||
/** 表单校验 */
|
||||
const validate = async () => {
|
||||
try {
|
||||
// 获取最新的流程数据
|
||||
const processData = await getProcessData()
|
||||
if (!processData) {
|
||||
if (!processData.value) {
|
||||
throw new Error('请设计流程')
|
||||
}
|
||||
return true
|
||||
@ -172,27 +45,19 @@ const validate = async () => {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理设计器保存成功 */
|
||||
const handleDesignSuccess = async (data?: any) => {
|
||||
if (data) {
|
||||
if (modelData.value.type === BpmModelType.BPMN) {
|
||||
currentBpmnXml.value = data
|
||||
} else {
|
||||
currentSimpleModel.value = data
|
||||
}
|
||||
|
||||
// 创建新的对象以触发响应式更新
|
||||
const newModelData = {
|
||||
...modelData.value,
|
||||
bpmnXml: modelData.value.type === BpmModelType.BPMN ? data : null,
|
||||
simpleModel: modelData.value.type === BpmModelType.BPMN ? null : data
|
||||
}
|
||||
|
||||
// 使用emit更新父组件的数据
|
||||
await nextTick()
|
||||
emit('update:modelValue', newModelData)
|
||||
emit('success', data)
|
||||
//更新表单的模型数据部分
|
||||
modelData.value = newModelData
|
||||
}
|
||||
}
|
||||
|
||||
@ -200,36 +65,7 @@ const handleDesignSuccess = async (data?: any) => {
|
||||
const showDesigner = computed(() => {
|
||||
return Boolean(modelData.value?.key && modelData.value?.name)
|
||||
})
|
||||
|
||||
// 组件创建时初始化数据
|
||||
onMounted(() => {
|
||||
initOrUpdateXmlData()
|
||||
})
|
||||
|
||||
// 组件卸载前保存数据
|
||||
onBeforeUnmount(async () => {
|
||||
try {
|
||||
// 获取并保存最新的流程数据
|
||||
const data = await getProcessData()
|
||||
if (data) {
|
||||
// 创建新的对象以触发响应式更新
|
||||
const newModelData = {
|
||||
...modelData.value,
|
||||
bpmnXml: modelData.value.type === BpmModelType.BPMN ? data : null,
|
||||
simpleModel: modelData.value.type === BpmModelType.BPMN ? null : data
|
||||
}
|
||||
|
||||
// 使用emit更新父组件的数据
|
||||
await nextTick()
|
||||
emit('update:modelValue', newModelData)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存数据失败:', error)
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
validate,
|
||||
getProcessData
|
||||
validate
|
||||
})
|
||||
</script>
|
||||
|
@ -67,12 +67,12 @@
|
||||
</div>
|
||||
|
||||
<!-- 第三步:流程设计 -->
|
||||
<ProcessDesign
|
||||
v-if="currentStep === 2"
|
||||
v-model="formData"
|
||||
ref="processDesignRef"
|
||||
@success="handleDesignSuccess"
|
||||
/>
|
||||
<ProcessDesign v-if="currentStep === 2" v-model="formData" ref="processDesignRef" />
|
||||
|
||||
<!-- 第四步:更多设置 -->
|
||||
<div v-show="currentStep === 3" class="mx-auto w-700px">
|
||||
<ExtraSettings v-model="formData" ref="extraSettingsRef" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ContentWrap>
|
||||
@ -83,14 +83,15 @@ import { useRoute, useRouter } from 'vue-router'
|
||||
import { useMessage } from '@/hooks/web/useMessage'
|
||||
import * as ModelApi from '@/api/bpm/model'
|
||||
import * as FormApi from '@/api/bpm/form'
|
||||
import { CategoryApi } from '@/api/bpm/category'
|
||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
import { BpmModelFormType, BpmModelType } from '@/utils/constants'
|
||||
import { BpmModelFormType, BpmModelType, BpmAutoApproveType } from '@/utils/constants'
|
||||
import BasicInfo from './BasicInfo.vue'
|
||||
import FormDesign from './FormDesign.vue'
|
||||
import ProcessDesign from './ProcessDesign.vue'
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView'
|
||||
import ExtraSettings from './ExtraSettings.vue'
|
||||
|
||||
const router = useRouter()
|
||||
const { delView } = useTagsViewStore() // 视图操作
|
||||
@ -102,6 +103,7 @@ const userStore = useUserStoreWithOut()
|
||||
const basicInfoRef = ref()
|
||||
const formDesignRef = ref()
|
||||
const processDesignRef = ref()
|
||||
const extraSettingsRef = ref()
|
||||
|
||||
/** 步骤校验函数 */
|
||||
const validateBasic = async () => {
|
||||
@ -118,11 +120,13 @@ const validateProcess = async () => {
|
||||
await processDesignRef.value?.validate()
|
||||
}
|
||||
|
||||
const currentStep = ref(0) // 步骤控制
|
||||
const currentStep = ref(-1) // 步骤控制。-1 用于,一开始全部不展示等当前页面数据初始化完成
|
||||
|
||||
const steps = [
|
||||
{ title: '基本信息', validator: validateBasic },
|
||||
{ title: '表单设计', validator: validateForm },
|
||||
{ title: '流程设计', validator: validateProcess }
|
||||
{ title: '流程设计', validator: validateProcess },
|
||||
{ title: '更多设置', validator: null }
|
||||
]
|
||||
|
||||
// 表单数据
|
||||
@ -140,14 +144,36 @@ const formData: any = ref({
|
||||
formCustomViewPath: '',
|
||||
visible: true,
|
||||
startUserType: undefined,
|
||||
managerUserType: undefined,
|
||||
startUserIds: [],
|
||||
managerUserIds: []
|
||||
managerUserIds: [],
|
||||
allowCancelRunningProcess: true,
|
||||
processIdRule: {
|
||||
enable: false,
|
||||
prefix: '',
|
||||
infix: '',
|
||||
postfix: '',
|
||||
length: 5
|
||||
},
|
||||
autoApprovalType: BpmAutoApproveType.NONE,
|
||||
titleSetting: {
|
||||
enable: false,
|
||||
title: ''
|
||||
},
|
||||
summarySetting: {
|
||||
enable: false,
|
||||
summary: []
|
||||
}
|
||||
})
|
||||
|
||||
//流程数据
|
||||
const processData = ref<any>()
|
||||
|
||||
provide('processData', processData)
|
||||
provide('modelData', formData)
|
||||
|
||||
// 数据列表
|
||||
const formList = ref([])
|
||||
const categoryList = ref([])
|
||||
const categoryList = ref<CategoryVO[]>([])
|
||||
const userList = ref<UserApi.UserVO[]>([])
|
||||
|
||||
/** 初始化数据 */
|
||||
@ -156,8 +182,16 @@ const initData = async () => {
|
||||
if (modelId) {
|
||||
// 修改场景
|
||||
formData.value = await ModelApi.getModel(modelId)
|
||||
formData.value.startUserType = formData.value.startUserIds?.length > 0 ? 1 : 0
|
||||
// 复制场景
|
||||
if (route.params.type === 'copy') {
|
||||
delete formData.value.id
|
||||
formData.value.name += '副本'
|
||||
formData.value.key += '_copy'
|
||||
}
|
||||
} else {
|
||||
// 新增场景
|
||||
formData.value.startUserType = 0 // 全体
|
||||
formData.value.managerUserIds.push(userStore.getUser.id)
|
||||
}
|
||||
|
||||
@ -167,61 +201,59 @@ const initData = async () => {
|
||||
categoryList.value = await CategoryApi.getCategorySimpleList()
|
||||
// 获取用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
|
||||
// 最终,设置 currentStep 切换到第一步
|
||||
currentStep.value = 0
|
||||
|
||||
// 兼容,以前未配置更多设置的流程
|
||||
extraSettingsRef.value.initData()
|
||||
}
|
||||
|
||||
/** 根据类型切换流程数据 */
|
||||
watch(
|
||||
async () => formData.value.type,
|
||||
() => {
|
||||
if (formData.value.type === BpmModelType.BPMN) {
|
||||
processData.value = formData.value.bpmnXml
|
||||
} else if (formData.value.type === BpmModelType.SIMPLE) {
|
||||
processData.value = formData.value.simpleModel
|
||||
}
|
||||
console.log('加载流程数据', processData.value)
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
/** 校验所有步骤数据是否完整 */
|
||||
const validateAllSteps = async () => {
|
||||
try {
|
||||
// 基本信息校验
|
||||
await basicInfoRef.value?.validate()
|
||||
if (!formData.value.key || !formData.value.name || !formData.value.category) {
|
||||
try {
|
||||
await validateBasic()
|
||||
} catch (error) {
|
||||
currentStep.value = 0
|
||||
throw new Error('请完善基本信息')
|
||||
}
|
||||
|
||||
// 表单设计校验
|
||||
await formDesignRef.value?.validate()
|
||||
if (formData.value.formType === 10 && !formData.value.formId) {
|
||||
currentStep.value = 1
|
||||
throw new Error('请选择流程表单')
|
||||
}
|
||||
if (
|
||||
formData.value.formType === 20 &&
|
||||
(!formData.value.formCustomCreatePath || !formData.value.formCustomViewPath)
|
||||
) {
|
||||
try {
|
||||
await validateForm()
|
||||
} catch (error) {
|
||||
currentStep.value = 1
|
||||
throw new Error('请完善自定义表单信息')
|
||||
}
|
||||
|
||||
// 流程设计校验
|
||||
// 如果已经有流程数据,则不需要重新校验
|
||||
if (!formData.value.bpmnXml && !formData.value.simpleModel) {
|
||||
// 如果当前不在第三步,需要先保存当前步骤数据
|
||||
if (currentStep.value !== 2) {
|
||||
await steps[currentStep.value].validator()
|
||||
// 切换到第三步
|
||||
|
||||
// 表单设计校验
|
||||
try {
|
||||
await validateProcess()
|
||||
} catch (error) {
|
||||
currentStep.value = 2
|
||||
// 等待组件渲染完成
|
||||
await nextTick()
|
||||
}
|
||||
|
||||
// 校验流程设计
|
||||
await processDesignRef.value?.validate()
|
||||
const processData = await processDesignRef.value?.getProcessData()
|
||||
if (!processData) {
|
||||
throw new Error('请设计流程')
|
||||
}
|
||||
|
||||
// 保存流程数据
|
||||
if (formData.value.type === BpmModelType.BPMN) {
|
||||
formData.value.bpmnXml = processData
|
||||
formData.value.simpleModel = null
|
||||
} else {
|
||||
formData.value.bpmnXml = null
|
||||
formData.value.simpleModel = processData
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
} catch (error) {
|
||||
throw error
|
||||
@ -239,20 +271,6 @@ const handleSave = async () => {
|
||||
...formData.value
|
||||
}
|
||||
|
||||
// 如果当前在第三步,获取最新的流程设计数据
|
||||
if (currentStep.value === 2) {
|
||||
const processData = await processDesignRef.value?.getProcessData()
|
||||
if (processData) {
|
||||
if (formData.value.type === BpmModelType.BPMN) {
|
||||
modelData.bpmnXml = processData
|
||||
modelData.simpleModel = null
|
||||
} else {
|
||||
modelData.bpmnXml = null
|
||||
modelData.simpleModel = processData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (formData.value.id) {
|
||||
// 修改场景
|
||||
await ModelApi.updateModel(modelData)
|
||||
@ -267,9 +285,8 @@ const handleSave = async () => {
|
||||
} else {
|
||||
// 新增场景
|
||||
formData.value.id = await ModelApi.createModel(modelData)
|
||||
message.success('新增成功')
|
||||
try {
|
||||
await message.confirm('创建流程成功,是否继续编辑?')
|
||||
await message.confirm('流程创建成功,是否继续编辑?')
|
||||
// 用户点击继续编辑,跳转到编辑页面
|
||||
await nextTick()
|
||||
// 先删除当前页签
|
||||
@ -299,7 +316,6 @@ const handleDeploy = async () => {
|
||||
if (!formData.value.id) {
|
||||
await message.confirm('是否确认发布该流程?')
|
||||
}
|
||||
|
||||
// 校验所有步骤
|
||||
await validateAllSteps()
|
||||
|
||||
@ -308,20 +324,6 @@ const handleDeploy = async () => {
|
||||
...formData.value
|
||||
}
|
||||
|
||||
// 如果当前在第三步,获取最新的流程设计数据
|
||||
if (currentStep.value === 2) {
|
||||
const processData = await processDesignRef.value?.getProcessData()
|
||||
if (processData) {
|
||||
if (formData.value.type === BpmModelType.BPMN) {
|
||||
modelData.bpmnXml = processData
|
||||
modelData.simpleModel = null
|
||||
} else {
|
||||
modelData.bpmnXml = null
|
||||
modelData.simpleModel = processData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 先保存所有数据
|
||||
if (formData.value.id) {
|
||||
await ModelApi.updateModel(modelData)
|
||||
@ -344,33 +346,15 @@ const handleDeploy = async () => {
|
||||
/** 步骤切换处理 */
|
||||
const handleStepClick = async (index: number) => {
|
||||
try {
|
||||
// 如果是切换到第三步(流程设计),需要校验key和name
|
||||
if (index === 2) {
|
||||
if (!formData.value.key || !formData.value.name) {
|
||||
message.warning('请先填写流程标识和流程名称')
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// 保存当前步骤的数据
|
||||
if (currentStep.value === 2) {
|
||||
const processData = await processDesignRef.value?.getProcessData()
|
||||
if (processData) {
|
||||
if (formData.value.type === BpmModelType.BPMN) {
|
||||
formData.value.bpmnXml = processData
|
||||
formData.value.simpleModel = null
|
||||
} else {
|
||||
formData.value.bpmnXml = null
|
||||
formData.value.simpleModel = processData
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 只有在向后切换时才进行校验
|
||||
if (index > currentStep.value) {
|
||||
if (typeof steps[currentStep.value].validator === 'function') {
|
||||
await steps[currentStep.value].validator()
|
||||
console.log('index', index)
|
||||
if (index !== 0) {
|
||||
await validateBasic()
|
||||
}
|
||||
if (index !== 1) {
|
||||
await validateForm()
|
||||
}
|
||||
if (index !== 2) {
|
||||
await validateProcess()
|
||||
}
|
||||
|
||||
// 切换步骤
|
||||
@ -391,13 +375,6 @@ const handleStepClick = async (index: number) => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 处理设计器保存成功 */
|
||||
const handleDesignSuccess = (bpmnXml?: string) => {
|
||||
if (bpmnXml) {
|
||||
formData.value.bpmnXml = bpmnXml
|
||||
}
|
||||
}
|
||||
|
||||
/** 返回列表页 */
|
||||
const handleBack = () => {
|
||||
// 先删除当前页签
|
||||
|
@ -85,8 +85,6 @@
|
||||
</div>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 表单弹窗:添加/修改流程 -->
|
||||
<ModelForm ref="formRef" @success="getList" />
|
||||
<!-- 表单弹窗:添加分类 -->
|
||||
<CategoryForm ref="categoryFormRef" @success="getList" />
|
||||
<!-- 弹窗:表单详情 -->
|
||||
@ -99,7 +97,6 @@
|
||||
import draggable from 'vuedraggable'
|
||||
import { CategoryApi } from '@/api/bpm/category'
|
||||
import * as ModelApi from '@/api/bpm/model'
|
||||
import ModelForm from './ModelForm.vue'
|
||||
import CategoryForm from '../category/CategoryForm.vue'
|
||||
import { cloneDeep } from 'lodash-es'
|
||||
import CategoryDraggableModel from './CategoryDraggableModel.vue'
|
||||
@ -123,7 +120,6 @@ const handleQuery = () => {
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
if (type === 'create') {
|
||||
push({ name: 'BpmModelCreate' })
|
||||
@ -206,7 +202,7 @@ const getList = async () => {
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
onActivated(() => {
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
@ -58,7 +58,16 @@
|
||||
>
|
||||
<template #default>
|
||||
<div class="flex">
|
||||
<el-image :src="definition.icon" class="w-32px h-32px" />
|
||||
<el-image
|
||||
v-if="definition.icon"
|
||||
:src="definition.icon"
|
||||
class="w-32px h-32px"
|
||||
/>
|
||||
<div v-else class="flow-icon">
|
||||
<span style="font-size: 12px; color: #fff">{{
|
||||
sliceName(definition.name,0,2)
|
||||
}}</span>
|
||||
</div>
|
||||
<el-text class="!ml-10px" size="large">{{ definition.name }}</el-text>
|
||||
</div>
|
||||
</template>
|
||||
@ -88,6 +97,7 @@ import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||
import ProcessDefinitionDetail from './ProcessDefinitionDetail.vue'
|
||||
import { groupBy } from 'lodash-es'
|
||||
import { sliceName } from '@/utils/index'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceCreate' })
|
||||
|
||||
@ -282,13 +292,25 @@ onMounted(() => {
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.flow-icon {
|
||||
display: flex;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 10px;
|
||||
background-color: var(--el-color-primary);
|
||||
border-radius: 0.25rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.process-definition-container::before {
|
||||
content: '';
|
||||
border-left: 1px solid #e6e6e6;
|
||||
position: absolute;
|
||||
left: 20.8%;
|
||||
height: 100%;
|
||||
border-left: 1px solid #e6e6e6;
|
||||
content: '';
|
||||
}
|
||||
|
||||
:deep() {
|
||||
.definition-item-card {
|
||||
.el-card__body {
|
||||
|
@ -44,8 +44,26 @@
|
||||
:rows="4"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="runningTask.signEnable"
|
||||
label="签名"
|
||||
prop="signPicUrl"
|
||||
ref="approveSignFormRef"
|
||||
>
|
||||
<el-button @click="signRef.open()">点击签名</el-button>
|
||||
<el-image
|
||||
class="w-90px h-40px ml-5px"
|
||||
v-if="approveReasonForm.signPicUrl"
|
||||
:src="approveReasonForm.signPicUrl"
|
||||
:preview-src-list="[approveReasonForm.signPicUrl]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :disabled="formLoading" type="success" @click="handleAudit(true, approveFormRef)">
|
||||
<el-button
|
||||
:disabled="formLoading"
|
||||
type="success"
|
||||
@click="handleAudit(true, approveFormRef)"
|
||||
>
|
||||
{{ getButtonDisplayName(OperationButtonType.APPROVE) }}
|
||||
</el-button>
|
||||
<el-button @click="closePropover('approve', approveFormRef)"> 取消 </el-button>
|
||||
@ -86,7 +104,11 @@
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button :disabled="formLoading" type="danger" @click="handleAudit(false,rejectFormRef)">
|
||||
<el-button
|
||||
:disabled="formLoading"
|
||||
type="danger"
|
||||
@click="handleAudit(false, rejectFormRef)"
|
||||
>
|
||||
{{ getButtonDisplayName(OperationButtonType.REJECT) }}
|
||||
</el-button>
|
||||
<el-button @click="closePropover('reject', rejectFormRef)"> 取消 </el-button>
|
||||
@ -471,6 +493,9 @@
|
||||
<Icon :size="14" icon="ep:refresh" /> 再次提交
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 签名弹窗 -->
|
||||
<SignDialog ref="signRef" @success="handleSignFinish" />
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
@ -479,11 +504,13 @@ import * as TaskApi from '@/api/bpm/task'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import {
|
||||
OperationButtonType,
|
||||
OPERATION_BUTTON_NAME
|
||||
OPERATION_BUTTON_NAME,
|
||||
OperationButtonType
|
||||
} from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||
import { BpmProcessInstanceStatus, BpmModelFormType } from '@/utils/constants'
|
||||
import { BpmModelFormType, BpmProcessInstanceStatus } from '@/utils/constants'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import SignDialog from './SignDialog.vue'
|
||||
|
||||
defineOptions({ name: 'ProcessInstanceBtnContainer' })
|
||||
|
||||
const router = useRouter() // 路由
|
||||
@ -492,12 +519,12 @@ const message = useMessage() // 消息弹窗
|
||||
const userId = useUserStoreWithOut().getUser.id // 当前登录的编号
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
|
||||
const props = defineProps< {
|
||||
processInstance: any, // 流程实例信息
|
||||
processDefinition: any, // 流程定义信息
|
||||
userOptions: UserApi.UserVO[],
|
||||
normalForm: any, // 流程表单 formCreate
|
||||
normalFormApi: any, // 流程表单 formCreate Api
|
||||
const props = defineProps<{
|
||||
processInstance: any // 流程实例信息
|
||||
processDefinition: any // 流程定义信息
|
||||
userOptions: UserApi.UserVO[]
|
||||
normalForm: any // 流程表单 formCreate
|
||||
normalFormApi: any // 流程表单 formCreate Api
|
||||
writableFields: string[] // 流程表单可以编辑的字段
|
||||
}>()
|
||||
|
||||
@ -521,20 +548,29 @@ const approveForm = ref<any>({}) // 审批通过时,额外的补充信息
|
||||
const approveFormFApi = ref<any>({}) // approveForms 的 fAPi
|
||||
|
||||
// 审批通过意见表单
|
||||
const reasonRequire = ref()
|
||||
const approveFormRef = ref<FormInstance>()
|
||||
const signRef = ref()
|
||||
const approveSignFormRef = ref()
|
||||
const approveReasonForm = reactive({
|
||||
reason: ''
|
||||
reason: '',
|
||||
signPicUrl: ''
|
||||
})
|
||||
const approveReasonRule = reactive<FormRules<typeof approveReasonForm>>({
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
const approveReasonRule = computed(() => {
|
||||
return {
|
||||
reason: [{ required: reasonRequire.value, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
signPicUrl: [{ required: true, message: '签名不能为空', trigger: 'change' }]
|
||||
}
|
||||
})
|
||||
// 拒绝表单
|
||||
const rejectFormRef = ref<FormInstance>()
|
||||
const rejectReasonForm = reactive({
|
||||
reason: ''
|
||||
})
|
||||
const rejectReasonRule = reactive<FormRules<typeof rejectReasonForm>>({
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
const rejectReasonRule = computed(() => {
|
||||
return {
|
||||
reason: [{ required: reasonRequire.value, message: '审批意见不能为空', trigger: 'blur' }]
|
||||
}
|
||||
})
|
||||
|
||||
// 抄送表单
|
||||
@ -555,7 +591,7 @@ const transferForm = reactive({
|
||||
})
|
||||
const transferFormRule = reactive<FormRules<typeof transferForm>>({
|
||||
assigneeUserId: [{ required: true, message: '新审批人不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
// 委派表单
|
||||
@ -566,7 +602,7 @@ const delegateForm = reactive({
|
||||
})
|
||||
const delegateFormRule = reactive<FormRules<typeof delegateForm>>({
|
||||
delegateUserId: [{ required: true, message: '接收人不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
// 加签表单
|
||||
@ -577,7 +613,7 @@ const addSignForm = reactive({
|
||||
})
|
||||
const addSignFormRule = reactive<FormRules<typeof addSignForm>>({
|
||||
addSignUserIds: [{ required: true, message: '加签处理人不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
// 减签表单
|
||||
@ -588,7 +624,7 @@ const deleteSignForm = reactive({
|
||||
})
|
||||
const deleteSignFormRule = reactive<FormRules<typeof deleteSignForm>>({
|
||||
deleteSignTaskId: [{ required: true, message: '减签人员不能为空', trigger: 'change' }],
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }],
|
||||
reason: [{ required: true, message: '审批意见不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
// 退回表单
|
||||
@ -608,7 +644,7 @@ const cancelForm = reactive({
|
||||
cancelReason: ''
|
||||
})
|
||||
const cancelFormRule = reactive<FormRules<typeof cancelForm>>({
|
||||
cancelReason: [{ required: true, message: '取消理由不能为空', trigger: 'blur' }],
|
||||
cancelReason: [{ required: true, message: '取消理由不能为空', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
/** 监听 approveFormFApis,实现它对应的 form-create 初始化后,隐藏掉对应的表单提交按钮 */
|
||||
@ -627,10 +663,10 @@ watch(
|
||||
const openPopover = async (type: string) => {
|
||||
if (type === 'approve') {
|
||||
// 校验流程表单
|
||||
const valid = await validateNormalForm();
|
||||
const valid = await validateNormalForm()
|
||||
if (!valid) {
|
||||
message.warning('表单校验不通过,请先完善表单!!')
|
||||
return;
|
||||
return
|
||||
}
|
||||
}
|
||||
if (type === 'return') {
|
||||
@ -665,13 +701,17 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) =>
|
||||
await formRef.validate()
|
||||
if (pass) {
|
||||
// 获取修改的流程变量, 暂时只支持流程表单
|
||||
const variables = getUpdatedProcessInstanceVaiables();
|
||||
const variables = getUpdatedProcessInstanceVariables()
|
||||
// 审批通过数据
|
||||
const data = {
|
||||
id: runningTask.value.id,
|
||||
reason: approveReasonForm.reason,
|
||||
variables // 审批通过, 把修改的字段值赋于流程实例变量
|
||||
}
|
||||
// 签名
|
||||
if (runningTask.value.signEnable) {
|
||||
data.signPicUrl = approveReasonForm.signPicUrl
|
||||
}
|
||||
// 多表单处理,并且有额外的 approveForm 表单,需要校验 + 拼接到 data 表单里提交
|
||||
// TODO 芋艿 任务有多表单这里要如何处理,会和可编辑的字段冲突
|
||||
const formCreateApi = approveFormFApi.value
|
||||
@ -687,7 +727,7 @@ const handleAudit = async (pass: boolean, formRef: FormInstance | undefined) =>
|
||||
// 审批不通过数据
|
||||
const data = {
|
||||
id: runningTask.value.id,
|
||||
reason: rejectReasonForm.reason,
|
||||
reason: rejectReasonForm.reason
|
||||
}
|
||||
await TaskApi.rejectTask(data)
|
||||
popOverVisible.value.reject = false
|
||||
@ -713,7 +753,7 @@ const handleCopy = async () => {
|
||||
const data = {
|
||||
id: runningTask.value.id,
|
||||
reason: copyForm.copyReason,
|
||||
copyUserIds:copyForm.copyUserIds
|
||||
copyUserIds: copyForm.copyUserIds
|
||||
}
|
||||
await TaskApi.copyTask(data)
|
||||
copyFormRef.value.resetFields()
|
||||
@ -752,7 +792,6 @@ const handleTransfer = async () => {
|
||||
const handleDelegate = async () => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
|
||||
// 1.1 校验表单
|
||||
if (!delegateFormRef.value) return
|
||||
await delegateFormRef.value.validate()
|
||||
@ -932,6 +971,7 @@ const loadTodoTask = (task: any) => {
|
||||
approveForm.value = {}
|
||||
approveFormFApi.value = {}
|
||||
runningTask.value = task
|
||||
reasonRequire.value = task?.reasonRequire ?? false
|
||||
// 处理 approve 表单.
|
||||
if (task && task.formId && task.formConf) {
|
||||
const tempApproveForm = {}
|
||||
@ -949,23 +989,29 @@ const validateNormalForm = async () => {
|
||||
try {
|
||||
await props.normalFormApi?.validate()
|
||||
} catch {
|
||||
valid = false;
|
||||
valid = false
|
||||
}
|
||||
return valid;
|
||||
return valid
|
||||
} else {
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/** 从可以编辑的流程表单字段,获取需要修改的流程实例的变量 */
|
||||
const getUpdatedProcessInstanceVaiables = ()=> {
|
||||
const getUpdatedProcessInstanceVariables = () => {
|
||||
const variables = {}
|
||||
props.writableFields.forEach( (field) => {
|
||||
const fieldValue = props.normalFormApi.getValue(field)
|
||||
variables[field] = fieldValue;
|
||||
props.writableFields.forEach((field) => {
|
||||
variables[field] = props.normalFormApi.getValue(field)
|
||||
})
|
||||
return variables
|
||||
}
|
||||
|
||||
/** 处理签名完成 */
|
||||
const handleSignFinish = (url: string) => {
|
||||
approveReasonForm.signPicUrl = url
|
||||
approveSignFormRef.value.validate('change')
|
||||
}
|
||||
|
||||
defineExpose({ loadTodoTask })
|
||||
</script>
|
||||
|
||||
|
@ -4,7 +4,6 @@
|
||||
:flow-node="simpleModel"
|
||||
:tasks="tasks"
|
||||
:process-instance="processInstance"
|
||||
class="process-viewer"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
@ -20,7 +19,7 @@ const props = defineProps({
|
||||
modelView: propTypes.object,
|
||||
simpleJson: propTypes.string // Simple 模型结构数据 (json 格式)
|
||||
})
|
||||
const simpleModel = ref()
|
||||
const simpleModel = ref<any>({})
|
||||
// 用户任务
|
||||
const tasks = ref([])
|
||||
// 流程实例
|
||||
@ -82,7 +81,6 @@ const setSimpleModelNodeTaskStatus = (
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 审批节点
|
||||
if (
|
||||
simpleModel.type === NodeType.START_USER_NODE ||
|
||||
@ -98,31 +96,49 @@ const setSimpleModelNodeTaskStatus = (
|
||||
}
|
||||
// TODO 是不是还缺一个 cancel 的状态
|
||||
}
|
||||
|
||||
// 抄送节点
|
||||
if (simpleModel.type === NodeType.COPY_TASK_NODE) {
|
||||
// 抄送节点 只有通过和未执行状态
|
||||
// 抄送节点,只有通过和未执行状态
|
||||
if (finishedActivityIds.includes(simpleModel.id)) {
|
||||
simpleModel.activityStatus = TaskStatusEnum.APPROVE
|
||||
} else {
|
||||
simpleModel.activityStatus = TaskStatusEnum.NOT_START
|
||||
}
|
||||
}
|
||||
// 条件节点 对应 SequenceFlow
|
||||
if (simpleModel.type === NodeType.CONDITION_NODE) {
|
||||
// 条件节点。只有通过和未执行状态
|
||||
if (finishedSequenceFlowActivityIds.includes(simpleModel.id)) {
|
||||
// 延迟器节点
|
||||
if (simpleModel.type === NodeType.DELAY_TIMER_NODE) {
|
||||
// 延迟器节点,只有通过和未执行状态
|
||||
if (finishedActivityIds.includes(simpleModel.id)) {
|
||||
simpleModel.activityStatus = TaskStatusEnum.APPROVE
|
||||
} else {
|
||||
simpleModel.activityStatus = TaskStatusEnum.NOT_START
|
||||
}
|
||||
}
|
||||
// 触发器节点
|
||||
if (simpleModel.type === NodeType.TRIGGER_NODE) {
|
||||
// 触发器节点,只有通过和未执行状态
|
||||
if (finishedActivityIds.includes(simpleModel.id)) {
|
||||
simpleModel.activityStatus = TaskStatusEnum.APPROVE
|
||||
} else {
|
||||
simpleModel.activityStatus = TaskStatusEnum.NOT_START
|
||||
}
|
||||
}
|
||||
|
||||
// 条件节点对应 SequenceFlow
|
||||
if (simpleModel.type === NodeType.CONDITION_NODE) {
|
||||
// 条件节点,只有通过和未执行状态
|
||||
if (finishedSequenceFlowActivityIds.includes(simpleModel.id)) {
|
||||
simpleModel.activityStatus = TaskStatusEnum.APPROVE
|
||||
} else {
|
||||
simpleModel.activityStatus = TaskStatusEnum.NOT_START
|
||||
}
|
||||
}
|
||||
// 网关节点
|
||||
if (
|
||||
simpleModel.type === NodeType.CONDITION_BRANCH_NODE ||
|
||||
simpleModel.type === NodeType.PARALLEL_BRANCH_NODE ||
|
||||
simpleModel.type === NodeType.INCLUSIVE_BRANCH_NODE
|
||||
simpleModel.type === NodeType.INCLUSIVE_BRANCH_NODE ||
|
||||
simpleModel.type === NodeType.ROUTER_BRANCH_NODE
|
||||
) {
|
||||
// 网关节点。只有通过和未执行状态
|
||||
if (finishedActivityIds.includes(simpleModel.id)) {
|
||||
@ -154,15 +170,4 @@ const setSimpleModelNodeTaskStatus = (
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.process-viewer-container {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
:deep(.process-viewer) {
|
||||
height: 100% !important;
|
||||
min-height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -123,6 +123,17 @@
|
||||
>
|
||||
审批意见:{{ task.reason }}
|
||||
</div>
|
||||
<div
|
||||
v-if="task.signPicUrl && activity.nodeType === NodeType.USER_TASK_NODE"
|
||||
class="text-#a5a5a5 text-13px mt-1 w-full bg-#f8f8fa p2 rounded-md"
|
||||
>
|
||||
签名:
|
||||
<el-image
|
||||
class="w-90px h-40px ml-5px"
|
||||
:src="task.signPicUrl"
|
||||
:preview-src-list="[task.signPicUrl]"
|
||||
/>
|
||||
</div>
|
||||
</teleport>
|
||||
</div>
|
||||
<!-- 情况二:遍历每个审批节点下的【候选的】task 任务。例如说,1)依次审批,2)未来的审批任务等 -->
|
||||
|
50
src/views/bpm/processInstance/detail/SignDialog.vue
Normal file
50
src/views/bpm/processInstance/detail/SignDialog.vue
Normal file
@ -0,0 +1,50 @@
|
||||
<template>
|
||||
<el-dialog v-model="signDialogVisible" title="签名" width="935">
|
||||
<div class="position-relative">
|
||||
<Vue3Signature class="b b-solid b-gray" ref="signature" w="900px" h="400px" />
|
||||
<el-button
|
||||
class="pos-absolute bottom-20px right-10px"
|
||||
type="primary"
|
||||
text
|
||||
size="small"
|
||||
@click="signature.clear()"
|
||||
>
|
||||
<Icon icon="ep:delete" class="mr-5px" />
|
||||
清除
|
||||
</el-button>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="signDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="submit"> 提交 </el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import Vue3Signature from 'vue3-signature'
|
||||
import * as FileApi from '@/api/infra/file'
|
||||
import download from '@/utils/download'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
const signDialogVisible = ref(false)
|
||||
const signature = ref()
|
||||
|
||||
const open = async () => {
|
||||
signDialogVisible.value = true
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
const emits = defineEmits(['success'])
|
||||
const submit = async () => {
|
||||
message.success('签名上传中请稍等。。。')
|
||||
const res = await FileApi.updateFile({
|
||||
file: download.base64ToFile(signature.value.save('image/png'), '签名')
|
||||
})
|
||||
emits('success', res.data)
|
||||
signDialogVisible.value = false
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
@ -75,7 +75,7 @@
|
||||
<Icon icon="ep:plus" class="mr-5px" />高级筛选
|
||||
</el-button>
|
||||
</template>
|
||||
<el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
||||
<!-- <el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
||||
<el-select
|
||||
v-model="queryParams.category"
|
||||
placeholder="请选择流程发起人"
|
||||
@ -89,7 +89,7 @@
|
||||
:value="category.code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item
|
||||
label="所属流程"
|
||||
class="bold-label"
|
||||
@ -130,6 +130,15 @@
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="流程名称" align="center" prop="name" min-width="200px" fixed="left" />
|
||||
<el-table-column label="摘要" prop="summary" min-width="180" fixed="left">
|
||||
<template #default="scope">
|
||||
<div class="flex flex-col" v-if="scope.row.summary && scope.row.summary.length > 0">
|
||||
<div v-for="(item, index) in scope.row.summary" :key="index">
|
||||
<el-text type="info"> {{ item.key }} : {{ item.value }} </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="流程分类"
|
||||
align="center"
|
||||
|
274
src/views/bpm/processInstance/report/index.vue
Normal file
274
src/views/bpm/processInstance/report/index.vue
Normal file
@ -0,0 +1,274 @@
|
||||
<template>
|
||||
<doc-alert title="工作流手册" url="https://doc.iocoder.cn/bpm/" />
|
||||
|
||||
<ContentWrap>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form
|
||||
class="-mb-15px"
|
||||
:model="queryParams"
|
||||
ref="queryFormRef"
|
||||
:inline="true"
|
||||
label-width="68px"
|
||||
>
|
||||
<el-form-item label="发起人" prop="startUserId">
|
||||
<el-select v-model="queryParams.startUserId" placeholder="请选择发起人" class="!w-240px">
|
||||
<el-option
|
||||
v-for="user in userList"
|
||||
:key="user.id"
|
||||
:label="user.nickname"
|
||||
:value="user.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程名称" prop="name">
|
||||
<el-input
|
||||
v-model="queryParams.name"
|
||||
placeholder="请输入流程名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="流程状态" prop="status">
|
||||
<el-select
|
||||
v-model="queryParams.status"
|
||||
placeholder="请选择流程状态"
|
||||
clearable
|
||||
class="!w-240px"
|
||||
>
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="发起时间" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束时间" prop="endTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.endTime"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
:default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-for="(item, index) in formFields"
|
||||
:key="index"
|
||||
:label="item.title"
|
||||
:prop="item.field"
|
||||
>
|
||||
<!-- TODO @lesan:目前只支持input类型的字符串搜索 -->
|
||||
<el-input
|
||||
:disabled="item.type !== 'input'"
|
||||
v-model="queryParams.formFieldsParams[item.field]"
|
||||
:placeholder="`请输入${item.title}`"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
|
||||
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" border :data="list">
|
||||
<el-table-column label="流程名称" align="center" prop="name" fixed="left" width="200" />
|
||||
<el-table-column label="流程发起人" align="center" prop="startUser.nickname" width="120" />
|
||||
<el-table-column label="流程状态" prop="status" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.BPM_PROCESS_INSTANCE_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="发起时间"
|
||||
align="center"
|
||||
prop="startTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
label="结束时间"
|
||||
align="center"
|
||||
prop="endTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column
|
||||
v-for="(item, index) in formFields"
|
||||
:key="index"
|
||||
:label="item.title"
|
||||
:prop="item.field"
|
||||
width="120"
|
||||
>
|
||||
<!-- TODO @lesan:可以根据formField的type进行展示方式的控制,现在全部以字符串 -->
|
||||
<template #default="scope">
|
||||
{{ scope.row.formVariables[item.field] ?? '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" fixed="right" width="180">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-hasPermi="['bpm:process-instance:cancel']"
|
||||
@click="handleDetail(scope.row)"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
v-if="scope.row.status === 1"
|
||||
v-hasPermi="['bpm:process-instance:query']"
|
||||
@click="handleCancel(scope.row)"
|
||||
>
|
||||
取消
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<!-- 分页 -->
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import * as ProcessInstanceApi from '@/api/bpm/processInstance'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
import * as DefinitionApi from '@/api/bpm/definition'
|
||||
import { parseFormFields } from '@/components/FormCreate/src/utils'
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
defineOptions({ name: 'BpmProcessInstanceReport' })
|
||||
|
||||
const router = useRouter() // 路由
|
||||
const { query } = useRoute()
|
||||
const message = useMessage() // 消息弹窗
|
||||
const { t } = useI18n() // 国际化
|
||||
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const formFields = ref()
|
||||
const processDefinitionId = query.processDefinitionId as string
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
startUserId: undefined,
|
||||
name: '',
|
||||
processDefinitionKey: query.processDefinitionKey,
|
||||
status: undefined,
|
||||
createTime: [],
|
||||
endTime: [],
|
||||
formFieldsParams: {}
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const userList = ref<any[]>([]) // 用户列表
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await ProcessInstanceApi.getProcessInstanceManagerPage({
|
||||
...queryParams,
|
||||
formFieldsParams: JSON.stringify(queryParams.formFieldsParams)
|
||||
})
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取流程定义 */
|
||||
const getProcessDefinition = async () => {
|
||||
const processDefinition = await DefinitionApi.getProcessDefinition(processDefinitionId)
|
||||
formFields.value = parseFormCreateFields(processDefinition.formFields)
|
||||
}
|
||||
|
||||
/** 解析表单字段 */
|
||||
const parseFormCreateFields = (formFields?: string[]) => {
|
||||
const result: Array<Record<string, any>> = []
|
||||
if (formFields) {
|
||||
formFields.forEach((fieldStr: string) => {
|
||||
parseFormFields(JSON.parse(fieldStr), result)
|
||||
})
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
queryParams.formFieldsParams = {}
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 查看详情 */
|
||||
const handleDetail = (row) => {
|
||||
router.push({
|
||||
name: 'BpmProcessInstanceDetail',
|
||||
query: {
|
||||
id: row.id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/** 取消按钮操作 */
|
||||
const handleCancel = async (row) => {
|
||||
// 二次确认
|
||||
const { value } = await ElMessageBox.prompt('请输入取消原因', '取消流程', {
|
||||
confirmButtonText: t('common.ok'),
|
||||
cancelButtonText: t('common.cancel'),
|
||||
inputPattern: /^[\s\S]*.*\S[\s\S]*$/, // 判断非空,且非空格
|
||||
inputErrorMessage: '取消原因不能为空'
|
||||
})
|
||||
// 发起取消
|
||||
await ProcessInstanceApi.cancelProcessInstanceByAdmin(row.id, value)
|
||||
message.success('取消成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
}
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(async () => {
|
||||
// 获取流程定义,用于 table column 的展示
|
||||
await getProcessDefinition()
|
||||
// 获取流程列表
|
||||
await getList()
|
||||
// 获取用户列表
|
||||
userList.value = await UserApi.getSimpleUserList()
|
||||
})
|
||||
</script>
|
@ -4,9 +4,7 @@
|
||||
:model-id="modelId"
|
||||
:model-key="modelKey"
|
||||
:model-name="modelName"
|
||||
:value="currentValue"
|
||||
@success="handleSuccess"
|
||||
@init-finished="handleInit"
|
||||
:start-user-ids="startUserIds"
|
||||
ref="designerRef"
|
||||
/>
|
||||
@ -19,137 +17,22 @@ defineOptions({
|
||||
name: 'SimpleModelDesign'
|
||||
})
|
||||
|
||||
const props = defineProps<{
|
||||
defineProps<{
|
||||
modelId?: string
|
||||
modelKey?: string
|
||||
modelName?: string
|
||||
value?: string
|
||||
startUserIds?: number[]
|
||||
}>()
|
||||
|
||||
const emit = defineEmits(['success', 'init-finished'])
|
||||
const emit = defineEmits(['success'])
|
||||
const designerRef = ref()
|
||||
const isInitialized = ref(false)
|
||||
const currentValue = ref('')
|
||||
|
||||
// 初始化或更新当前值
|
||||
const initOrUpdateValue = async () => {
|
||||
console.log('initOrUpdateValue', props.value)
|
||||
if (props.value) {
|
||||
currentValue.value = props.value
|
||||
// 如果设计器已经初始化,立即加载数据
|
||||
if (isInitialized.value && designerRef.value) {
|
||||
try {
|
||||
await designerRef.value.loadProcessData(props.value)
|
||||
await nextTick()
|
||||
if (designerRef.value.refresh) {
|
||||
await designerRef.value.refresh()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载流程数据失败:', error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 监听属性变化
|
||||
watch(
|
||||
[() => props.modelKey, () => props.modelName, () => props.value],
|
||||
async ([newKey, newName, newValue], [oldKey, oldName, oldValue]) => {
|
||||
if (designerRef.value && isInitialized.value) {
|
||||
try {
|
||||
if (newKey && newName && (newKey !== oldKey || newName !== oldName)) {
|
||||
await designerRef.value.updateModel(newKey, newName)
|
||||
}
|
||||
if (newValue && newValue !== oldValue) {
|
||||
currentValue.value = newValue
|
||||
await designerRef.value.loadProcessData(newValue)
|
||||
await nextTick()
|
||||
if (designerRef.value.refresh) {
|
||||
await designerRef.value.refresh()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('更新流程数据失败:', error)
|
||||
}
|
||||
}
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
)
|
||||
|
||||
// 初始化完成回调
|
||||
const handleInit = async () => {
|
||||
try {
|
||||
isInitialized.value = true
|
||||
emit('init-finished')
|
||||
|
||||
// 等待下一个tick,确保设计器已经准备好
|
||||
await nextTick()
|
||||
|
||||
// 初始化完成后,设置初始值
|
||||
if (props.modelKey && props.modelName) {
|
||||
await designerRef.value.updateModel(props.modelKey, props.modelName)
|
||||
}
|
||||
if (props.value) {
|
||||
currentValue.value = props.value
|
||||
await designerRef.value.loadProcessData(props.value)
|
||||
// 再次刷新确保数据正确加载
|
||||
await nextTick()
|
||||
if (designerRef.value.refresh) {
|
||||
await designerRef.value.refresh()
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('初始化流程数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 修改成功回调
|
||||
const handleSuccess = (data?: any) => {
|
||||
console.warn('handleSuccess', data)
|
||||
if (data && data !== currentValue.value) {
|
||||
currentValue.value = data
|
||||
emit('success', data)
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取当前流程数据 */
|
||||
const getCurrentFlowData = async () => {
|
||||
try {
|
||||
if (designerRef.value) {
|
||||
const data = await designerRef.value.getCurrentFlowData()
|
||||
if (data) {
|
||||
currentValue.value = data
|
||||
}
|
||||
return data
|
||||
}
|
||||
return currentValue.value || undefined
|
||||
} catch (error) {
|
||||
console.error('获取流程数据失败:', error)
|
||||
return currentValue.value || undefined
|
||||
}
|
||||
}
|
||||
|
||||
// 组件创建时初始化数据
|
||||
onMounted(() => {
|
||||
initOrUpdateValue()
|
||||
})
|
||||
|
||||
// 组件卸载前保存数据
|
||||
onBeforeUnmount(async () => {
|
||||
try {
|
||||
const data = await getCurrentFlowData()
|
||||
console.info('handleSuccess', data)
|
||||
if (data) {
|
||||
emit('success', data)
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存数据失败:', error)
|
||||
}
|
||||
})
|
||||
|
||||
defineExpose({
|
||||
getCurrentFlowData,
|
||||
refresh: () => designerRef.value?.refresh?.()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
@ -44,7 +44,17 @@
|
||||
<!-- 列表 -->
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<!-- TODO 芋艿:增加摘要 -->
|
||||
<el-table-column align="center" label="流程名" prop="processInstanceName" min-width="180" />
|
||||
<el-table-column label="摘要" prop="summary" min-width="180">
|
||||
<template #default="scope">
|
||||
<div class="flex flex-col" v-if="scope.row.summary && scope.row.summary.length > 0">
|
||||
<div v-for="(item, index) in scope.row.summary" :key="index">
|
||||
<el-text type="info"> {{ item.key }} : {{ item.value }} </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="流程发起人"
|
||||
|
@ -81,7 +81,7 @@
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
<el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
||||
<!-- <el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
||||
<el-select
|
||||
v-model="queryParams.category"
|
||||
placeholder="请选择流程发起人"
|
||||
@ -95,7 +95,7 @@
|
||||
:value="category.code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
@ -122,6 +122,15 @@
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
||||
<el-table-column label="摘要" prop="processInstance.summary" min-width="180">
|
||||
<template #default="scope">
|
||||
<div class="flex flex-col" v-if="scope.row.processInstance.summary && scope.row.processInstance.summary.length > 0">
|
||||
<div v-for="(item, index) in scope.row.processInstance.summary" :key="index">
|
||||
<el-text type="info"> {{ item.key }} : {{ item.value }} </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="发起人"
|
||||
@ -161,7 +170,7 @@
|
||||
{{ formatPast2(scope.row.durationInMillis) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="流程编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="流程编号" prop="processInstanceId" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||
<template #default="scope">
|
||||
@ -184,7 +193,7 @@ import { dateFormatter, formatPast2 } from '@/utils/formatTime'
|
||||
import * as TaskApi from '@/api/bpm/task'
|
||||
import { CategoryApi, CategoryVO } from '@/api/bpm/category'
|
||||
|
||||
defineOptions({ name: 'BpmTodoTask' })
|
||||
defineOptions({ name: 'BpmDoneTask' })
|
||||
|
||||
const { push } = useRouter() // 路由
|
||||
|
||||
|
@ -87,7 +87,7 @@
|
||||
{{ formatPast2(scope.row.durationInMillis) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="流程编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="流程编号" prop="processInstanceId" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||
<template #default="scope">
|
||||
|
@ -64,7 +64,7 @@
|
||||
</el-button>
|
||||
|
||||
</template>
|
||||
<el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
||||
<!-- <el-form-item label="流程发起人" class="bold-label" label-position="top" prop="category">
|
||||
<el-select
|
||||
v-model="queryParams.category"
|
||||
placeholder="请选择流程发起人"
|
||||
@ -78,7 +78,7 @@
|
||||
:value="category.code"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="发起时间" class="bold-label" label-position="top" prop="createTime">
|
||||
<el-date-picker
|
||||
v-model="queryParams.createTime"
|
||||
@ -105,6 +105,15 @@
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column align="center" label="流程" prop="processInstance.name" width="180" />
|
||||
<el-table-column label="摘要" prop="processInstance.summary" min-width="180">
|
||||
<template #default="scope">
|
||||
<div class="flex flex-col" v-if="scope.row.processInstance.summary && scope.row.processInstance.summary.length > 0">
|
||||
<div v-for="(item, index) in scope.row.processInstance.summary" :key="index">
|
||||
<el-text type="info"> {{ item.key }} : {{ item.value }} </el-text>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="发起人"
|
||||
@ -126,7 +135,7 @@
|
||||
prop="createTime"
|
||||
width="180"
|
||||
/>
|
||||
<el-table-column align="center" label="流程编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="流程编号" prop="processInstanceId" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="任务编号" prop="id" :show-overflow-tooltip="true" />
|
||||
<el-table-column align="center" label="操作" fixed="right" width="80">
|
||||
<template #default="scope">
|
||||
|
@ -7,7 +7,7 @@
|
||||
<el-descriptions-item label="任务名称">
|
||||
{{ detailData.name }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="任务名称">
|
||||
<el-descriptions-item label="任务状态">
|
||||
<dict-tag :type="DICT_TYPE.INFRA_JOB_STATUS" :value="detailData.status" />
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="处理器的名字">
|
||||
|
@ -200,7 +200,7 @@ onBeforeUnmount(() => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.kefu {
|
||||
background-color: #e5e4e4;
|
||||
background-color: var(--app-content-bg-color);
|
||||
|
||||
&-conversation {
|
||||
height: 60px;
|
||||
|
@ -373,7 +373,7 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.kefu {
|
||||
background-color: #f5f5f5;
|
||||
background-color: var(--app-content-bg-color);
|
||||
position: relative;
|
||||
width: calc(100% - 300px - 260px);
|
||||
|
||||
@ -389,7 +389,7 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
|
||||
}
|
||||
|
||||
.kefu-header {
|
||||
background-color: #f5f5f5;
|
||||
background-color: var(--app-content-bg-color);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -511,7 +511,7 @@ const showTime = computed(() => (item: KeFuMessageRespVO, index: number) => {
|
||||
|
||||
::v-deep(textarea) {
|
||||
resize: none;
|
||||
background-color: #f5f5f5;
|
||||
background-color: var(--app-content-bg-color);
|
||||
}
|
||||
|
||||
:deep(.el-input__wrapper) {
|
||||
|
@ -167,7 +167,7 @@ const getUserData = async () => {
|
||||
.kefu {
|
||||
position: relative;
|
||||
width: 300px !important;
|
||||
background-color: #f5f5f5;
|
||||
background-color: var(--app-content-bg-color);
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
@ -181,7 +181,7 @@ const getUserData = async () => {
|
||||
}
|
||||
|
||||
&-header {
|
||||
background-color: #f5f5f5;
|
||||
background-color: var(--app-content-bg-color);
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -112,7 +112,7 @@ function formatOrderStatus(order: any) {
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
border: 1px var(--el-border-color) solid;
|
||||
background-color: #fff; // 透明色,暗黑模式下也能体现
|
||||
background-color: rgba(128, 128, 128, 0.3); // 透明色,暗黑模式下也能体现
|
||||
|
||||
.order-card-header {
|
||||
height: 28px;
|
||||
|
@ -77,7 +77,8 @@ const openDetail = (spuId: number) => {
|
||||
|
||||
.product-warp {
|
||||
width: 100%;
|
||||
background-color: #fff;
|
||||
background-color: rgba(128, 128, 128, 0.3);
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -334,13 +334,13 @@ onMounted(async () => {
|
||||
// 如果配送方式为快递,则查询物流公司
|
||||
if (formData.value.deliveryType === DeliveryTypeEnum.EXPRESS.type) {
|
||||
deliveryExpressList.value = await DeliveryExpressApi.getSimpleDeliveryExpressList()
|
||||
if (form.value.logisticsId) {
|
||||
if (formData.value.logisticsId) {
|
||||
expressTrackList.value = await TradeOrderApi.getExpressTrackList(formData.value.id!)
|
||||
}
|
||||
} else if (formData.value.deliveryType === DeliveryTypeEnum.PICK_UP.type) {
|
||||
pickUpStore.value = await DeliveryPickUpStoreApi.getDeliveryPickUpStore(
|
||||
formData.value.pickUpStoreId
|
||||
)
|
||||
if (formData.value.pickUpStoreId) {
|
||||
pickUpStore.value = await DeliveryPickUpStoreApi.getDeliveryPickUpStore(formData.value.pickUpStoreId)
|
||||
}
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
@ -26,9 +26,23 @@
|
||||
placeholder="请输入微信 APPID"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label-width="180px">
|
||||
<a
|
||||
href="https://pay.weixin.qq.com/index.php/extend/merchant_appid/mapay_platform/account_manage"
|
||||
target="_blank"
|
||||
>
|
||||
前往微信商户平台查看 APPID
|
||||
</a>
|
||||
</el-form-item>
|
||||
<el-form-item label="商户号" label-width="180px" prop="config.mchId">
|
||||
<el-input v-model="formData.config.mchId" :style="{ width: '100%' }" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label-width="180px">
|
||||
<a href="https://pay.weixin.qq.com/index.php/extend/pay_setting" target="_blank">
|
||||
前往微信商户平台查看商户号
|
||||
</a>
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道状态" label-width="180px" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
@ -123,6 +137,14 @@
|
||||
placeholder="请输入证书序列号"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label-width="180px">
|
||||
<a
|
||||
href="https://pay.weixin.qq.com/index.php/core/cert/api_cert#/api-cert-manage"
|
||||
target="_blank"
|
||||
>
|
||||
前往微信商户平台查看证书序列号
|
||||
</a>
|
||||
</el-form-item>
|
||||
</div>
|
||||
<el-form-item label="备注" label-width="180px" prop="remark">
|
||||
<el-input v-model="formData.remark" :style="{ width: '100%' }" />
|
||||
|
Loading…
x
Reference in New Issue
Block a user