Bläddra i källkod

Merge branch 'cxmo_branch'

cxmo 1 år sedan
förälder
incheckning
0674dcd28a

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
     "js-md5": "^0.8.3",
     "highcharts": "11.2.0",
     "moment": "^2.30.1",
+    "lodash": "^4.17.21",
     "pinia": "^2.1.7",
     "vue": "^3.4.19",
     "vue-router": "^4.3.0",

+ 46 - 43
pnpm-lock.yaml

@@ -1,5 +1,9 @@
 lockfileVersion: '6.0'
 
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
 dependencies:
   '@element-plus/icons-vue':
     specifier: ^2.3.1
@@ -25,6 +29,9 @@ dependencies:
   moment:
     specifier: ^2.30.1
     version: 2.30.1
+  lodash:
+    specifier: ^4.17.21
+    version: 4.17.21
   pinia:
     specifier: ^2.1.7
     version: 2.1.7(vue@3.4.20)
@@ -175,7 +182,7 @@ packages:
     dev: false
 
   /@esbuild/aix-ppc64@0.19.12:
-    resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==}
+    resolution: {integrity: sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==, tarball: https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [aix]
@@ -184,7 +191,7 @@ packages:
     optional: true
 
   /@esbuild/android-arm64@0.19.12:
-    resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==}
+    resolution: {integrity: sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==, tarball: https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [android]
@@ -193,7 +200,7 @@ packages:
     optional: true
 
   /@esbuild/android-arm@0.19.12:
-    resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==}
+    resolution: {integrity: sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==, tarball: https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [android]
@@ -202,7 +209,7 @@ packages:
     optional: true
 
   /@esbuild/android-x64@0.19.12:
-    resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==}
+    resolution: {integrity: sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==, tarball: https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [android]
@@ -211,7 +218,7 @@ packages:
     optional: true
 
   /@esbuild/darwin-arm64@0.19.12:
-    resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==}
+    resolution: {integrity: sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==, tarball: https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [darwin]
@@ -220,7 +227,7 @@ packages:
     optional: true
 
   /@esbuild/darwin-x64@0.19.12:
-    resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==}
+    resolution: {integrity: sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==, tarball: https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [darwin]
@@ -229,7 +236,7 @@ packages:
     optional: true
 
   /@esbuild/freebsd-arm64@0.19.12:
-    resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==}
+    resolution: {integrity: sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==, tarball: https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [freebsd]
@@ -238,7 +245,7 @@ packages:
     optional: true
 
   /@esbuild/freebsd-x64@0.19.12:
-    resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==}
+    resolution: {integrity: sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==, tarball: https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [freebsd]
@@ -247,7 +254,7 @@ packages:
     optional: true
 
   /@esbuild/linux-arm64@0.19.12:
-    resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==}
+    resolution: {integrity: sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==, tarball: https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [linux]
@@ -256,7 +263,7 @@ packages:
     optional: true
 
   /@esbuild/linux-arm@0.19.12:
-    resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==}
+    resolution: {integrity: sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==, tarball: https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [arm]
     os: [linux]
@@ -265,7 +272,7 @@ packages:
     optional: true
 
   /@esbuild/linux-ia32@0.19.12:
-    resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==}
+    resolution: {integrity: sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==, tarball: https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [linux]
@@ -274,7 +281,7 @@ packages:
     optional: true
 
   /@esbuild/linux-loong64@0.19.12:
-    resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==}
+    resolution: {integrity: sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==, tarball: https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [loong64]
     os: [linux]
@@ -283,7 +290,7 @@ packages:
     optional: true
 
   /@esbuild/linux-mips64el@0.19.12:
-    resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==}
+    resolution: {integrity: sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==, tarball: https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [mips64el]
     os: [linux]
@@ -292,7 +299,7 @@ packages:
     optional: true
 
   /@esbuild/linux-ppc64@0.19.12:
-    resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==}
+    resolution: {integrity: sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==, tarball: https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [ppc64]
     os: [linux]
@@ -301,7 +308,7 @@ packages:
     optional: true
 
   /@esbuild/linux-riscv64@0.19.12:
-    resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==}
+    resolution: {integrity: sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==, tarball: https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [riscv64]
     os: [linux]
@@ -310,7 +317,7 @@ packages:
     optional: true
 
   /@esbuild/linux-s390x@0.19.12:
-    resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==}
+    resolution: {integrity: sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==, tarball: https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [s390x]
     os: [linux]
@@ -319,7 +326,7 @@ packages:
     optional: true
 
   /@esbuild/linux-x64@0.19.12:
-    resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==}
+    resolution: {integrity: sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==, tarball: https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [linux]
@@ -328,7 +335,7 @@ packages:
     optional: true
 
   /@esbuild/netbsd-x64@0.19.12:
-    resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==}
+    resolution: {integrity: sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==, tarball: https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [netbsd]
@@ -337,7 +344,7 @@ packages:
     optional: true
 
   /@esbuild/openbsd-x64@0.19.12:
-    resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==}
+    resolution: {integrity: sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==, tarball: https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [openbsd]
@@ -346,7 +353,7 @@ packages:
     optional: true
 
   /@esbuild/sunos-x64@0.19.12:
-    resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==}
+    resolution: {integrity: sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==, tarball: https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [sunos]
@@ -355,7 +362,7 @@ packages:
     optional: true
 
   /@esbuild/win32-arm64@0.19.12:
-    resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==}
+    resolution: {integrity: sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==, tarball: https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [arm64]
     os: [win32]
@@ -364,7 +371,7 @@ packages:
     optional: true
 
   /@esbuild/win32-ia32@0.19.12:
-    resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==}
+    resolution: {integrity: sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==, tarball: https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [ia32]
     os: [win32]
@@ -373,7 +380,7 @@ packages:
     optional: true
 
   /@esbuild/win32-x64@0.19.12:
-    resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==}
+    resolution: {integrity: sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==, tarball: https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz}
     engines: {node: '>=12'}
     cpu: [x64]
     os: [win32]
@@ -428,7 +435,7 @@ packages:
     dev: true
 
   /@rollup/rollup-android-arm-eabi@4.12.0:
-    resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
+    resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==, tarball: https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.12.0.tgz}
     cpu: [arm]
     os: [android]
     requiresBuild: true
@@ -436,7 +443,7 @@ packages:
     optional: true
 
   /@rollup/rollup-android-arm64@4.12.0:
-    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
+    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
@@ -444,7 +451,7 @@ packages:
     optional: true
 
   /@rollup/rollup-darwin-arm64@4.12.0:
-    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
+    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
@@ -452,7 +459,7 @@ packages:
     optional: true
 
   /@rollup/rollup-darwin-x64@4.12.0:
-    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
+    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==, tarball: https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
@@ -460,7 +467,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-arm-gnueabihf@4.12.0:
-    resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
+    resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.12.0.tgz}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
@@ -468,7 +475,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-arm64-gnu@4.12.0:
-    resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
+    resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.12.0.tgz}
     cpu: [arm64]
     os: [linux]
     libc: [glibc]
@@ -477,7 +484,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-arm64-musl@4.12.0:
-    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
+    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz}
     cpu: [arm64]
     os: [linux]
     libc: [musl]
@@ -486,7 +493,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-riscv64-gnu@4.12.0:
-    resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
+    resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.12.0.tgz}
     cpu: [riscv64]
     os: [linux]
     libc: [glibc]
@@ -495,7 +502,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-x64-gnu@4.12.0:
-    resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
+    resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.12.0.tgz}
     cpu: [x64]
     os: [linux]
     libc: [glibc]
@@ -504,7 +511,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-x64-musl@4.12.0:
-    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
+    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz}
     cpu: [x64]
     os: [linux]
     libc: [musl]
@@ -513,7 +520,7 @@ packages:
     optional: true
 
   /@rollup/rollup-win32-arm64-msvc@4.12.0:
-    resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
+    resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.12.0.tgz}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
@@ -521,7 +528,7 @@ packages:
     optional: true
 
   /@rollup/rollup-win32-ia32-msvc@4.12.0:
-    resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
+    resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.12.0.tgz}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
@@ -529,7 +536,7 @@ packages:
     optional: true
 
   /@rollup/rollup-win32-x64-msvc@4.12.0:
-    resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
+    resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==, tarball: https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.12.0.tgz}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
@@ -537,7 +544,7 @@ packages:
     optional: true
 
   /@sxzz/popperjs-es@2.11.7:
-    resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
+    resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==, tarball: https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz}
     dev: false
 
   /@types/estree@1.0.5:
@@ -897,7 +904,7 @@ packages:
     dev: false
 
   /fsevents@2.3.3:
-    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
+    resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==, tarball: https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz}
     engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
     os: [darwin]
     requiresBuild: true
@@ -992,7 +999,7 @@ packages:
     dev: false
 
   /lodash@4.17.21:
-    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==, tarball: https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz}
     dev: false
 
   /magic-string@0.30.7:
@@ -1256,7 +1263,3 @@ packages:
     dependencies:
       isexe: 2.0.0
     dev: true
-
-settings:
-  autoInstallPeers: true
-  excludeLinksFromLockfile: false

+ 2 - 1
src/components/searchDistPicker.vue

@@ -65,6 +65,7 @@ const areaSource = computed(() => {
     })
 })
 
+//数据回显
 watch(
     ()=>props.provinceInfo,
     (newval)=>{
@@ -82,7 +83,7 @@ watch(
                 const areaResult = Object.entries(area_sorce[city.value.cityKey]).find(([key,val])=>val===props.areaInfo)||['',props.areaInfo]
                 area.value = {areaKey:areaResult[0],areaName:areaResult[1]}
             }else{
-                area.value={areaKey:'',areaName:''}
+                area.value={areaKey:'',areaName:props.areaInfo}
             }
         }else{
             province.value={provinceKey:'',provinceName:''}

+ 2 - 2
src/views/system_manage/approvalManage.vue

@@ -546,10 +546,10 @@ getApprovalDetail()
         }
     }
     .add-wrap{
-        :deep .el-input{
+        :deep(.el-input){
             width: 100% !important;
         }
-        :deep .search-box{
+        :deep(.search-box){
             width: 500px;
         }
     }

+ 160 - 5
src/views/system_manage/depart_manage/components/ModifyNodeDialog.vue

@@ -1,13 +1,168 @@
 <script setup>
-import { ref, reactive } from 'vue'
+import { ref, reactive, watch, nextTick } from 'vue'
+import $icon from '@/utils/icon'
+import _ from 'lodash'
+const props = defineProps({
+    isShow: Boolean,
+    type:String,
+    nodeInfo:{
+        type:Object,
+        default:{}
+    },
+})
+const titleMap = [
+    {'add':'部门'},//0
+    {'add':'分组','edit':'部门'},//1
+    {'add':'分组','edit':'分组'},//2
+    {'edit':'分组'},//3
+]
+const emit = defineEmits(["closeDia","submitForm"])
+let dialogTitle = ref('')
+let node = reactive({
+    departName:'',
+    groupName:'',
+    group:[]
+})
+watch(()=>props.isShow,(newval)=>{
+    if(newval){
+        //level为0 增加部门
+        //level为1 编辑部门&增加分组
+        //level为2 编辑分组&增加分组
+        //level为3 编辑分组
+        const type = props.type==='add'?'添加':'编辑'
+        const title = titleMap[props.nodeInfo.level||0][props.type||'add']
+        dialogTitle.value = type+title
+        Object.assign(node,{
+            departName:props.nodeInfo.data.DepartmentName,
+            groupName:props.nodeInfo.data.DepartmentName,
+            group:_.cloneDeep(props.nodeInfo.data.Child||[])
+        })
+    }
+})
+let saveTagInput = ref(null)
+let inputVisible = ref(false)
+let inputValue = ref('')
+function showInput(){
+    inputVisible.value = true;
+    nextTick(() => {
+        saveTagInput.value?.input.focus();
+    });
+}
+function InputConfirm(){
+    if (inputValue.value) {
+        node.group.push({
+            DepartmentName:inputValue.value
+        })
+    }
+    inputVisible.value = false
+    inputValue.value = '';
+}
+
+const rules = reactive({
+    group:[
+        { required: true, message: '请添加分组', trigger: 'blur' },
+    ],
+    departName:[
+        { required: true, message: '请输入部门名称', trigger: 'blur' },
+    ],
+    groupName:[
+        { required: true, message: '请输入分组名称', trigger: 'blur' },
+    ]
+})
+let formRef = ref(null)
+async function handleSubmitForm(){
+    try{
+        await formRef.value?.validate()
+    }catch(e){
+        console.log(e)
+        return
+    }
+    //通过校验
+    emit("submitForm",{
+        type:props.type,
+        nodeForm:node,
+        nodeInfo:props.nodeInfo,
+    })
+}
+
 </script>
 
 <template>
-    <div>
-        编辑树节点弹窗
-    </div>
+    <el-dialog
+        :model-value="props.isShow"
+        :close-on-click-modal="false"
+        :modal-append-to-body="false"
+        width="30%"
+        @close="emit('closeDia')"
+    >
+        <template #header style="display:flex;align-items:center;">
+            <img :src="props.type==='add'?$icon.add:$icon.edit"
+                style="color:#fff;width:16px;height:16px;margin-right:5px;">
+            <span style="font-size:16px;">{{ dialogTitle }}</span>
+        </template>
+        <div class="dialog-content">
+            <el-form :model="node" :rules="rules" ref="formRef" 
+                label-width="100px" class="demo-ruleForm">
+                <el-form-item label="部门" prop="departName" v-if="dialogTitle.includes('部门')">
+                    <el-input v-model="node.departName" placeholder="请填写部门名称" style="width: 90%"></el-input>
+                </el-form-item>
+                <el-form-item label="分组" prop="groupName" v-if="dialogTitle==='编辑分组'">
+                    <el-input v-model="node.groupName" placeholder="请填写分组名称" style="width: 90%"></el-input>
+                </el-form-item>
+                <div class="add-group-wrap" v-if="dialogTitle==='添加分组'">
+                    <el-form-item label="部门" >
+                        <span>{{ node.departName }}</span>
+                    </el-form-item>
+                    <el-form-item label="分组" prop="group">
+                        <el-tag
+                            :key="index"
+                            v-for="(tag,index) in node.group"
+                            closable
+                            :disable-transitions="false"
+                            @close="node.group.splice(index, 1)">
+                            {{tag.DepartmentName}}
+                        </el-tag>
+                        <el-input
+                            class="input-new-tag"
+                            v-if="inputVisible"
+                            v-model="inputValue"
+                            placeholder="分组名"
+                            ref="saveTagInput"
+                            size="small"
+                            style="width:120px"
+                            @keyup.enter.native="InputConfirm"
+                            @blur="InputConfirm"
+                        >
+                        </el-input>
+                        <el-button v-else class="button-new-tag" size="small" @click="showInput">+添加分组</el-button>
+                    </el-form-item>
+                </div>
+            </el-form>
+        </div>
+        <template #footer>
+            <div class="btn-content">
+                <el-button type="primary" style="width:80px;margin-right:24px;" @click="handleSubmitForm">保存</el-button>
+                <el-button style="width:80px;" @click="emit('closeDia')">取消</el-button>
+            </div>
+        </template>
+    </el-dialog>
 </template>
 
 <style scoped lang="scss">
-
+.el-dialog{
+    .dialog-content{
+        .add-group-wrap{
+            :deep(.el-form-item__content){
+                &:last-child{
+                    display: flex;
+                    gap:10px;
+                }
+            }
+        }
+    }
+    .btn-content{
+        text-align: center;
+        margin-bottom: 20px;
+    }
+}
 </style>

+ 334 - 9
src/views/system_manage/depart_manage/components/ModifyUserDialog.vue

@@ -1,33 +1,358 @@
 <script setup>
-import { ref, reactive } from 'vue'
-import * as $icon from '@/utils/icon'
+import { ref, reactive, watch, nextTick } from 'vue'
+import { departInterence } from '@/api/api.js';
+import $icon from '@/utils/icon'
+import {patternEmail,isMobileNo,checkPassWord} from '@/utils/commonOptions'
 import searchDistPicker from '@/components/searchDistPicker.vue'
+
 const props = defineProps({
     isShow: Boolean,
     type:String,
     userInfo:Object,
+    researchGroup:Array,
+    areaCode:Array,
+    departArr:Array,
+    roleArr:Array
+})
+const emit = defineEmits(["closeDia","submitForm"])
+
+//用户表单原始信息
+const baseForm = {
+    admin_id:0,//当前编辑用户的id
+    account:'',//登录账号
+    pwd:'',//登录密码
+    name:'',//姓名
+    depart:'',//所属部门
+    role:'',//分配角色
+    direct:'',//研究方向
+    province:'',//工作地点-省
+    city:'',//工作地点-市
+    post:'',//职务
+    employeeNumber:'',//同步每刻的工号
+    areacode:'86',//号码前缀
+    mobile:'',//手机号码
+    auth:0,//表单没有用到,但是传参用到了,对应Authority字段
+    status:1,//状态 1启用 0禁用
+    email:'',//邮箱
+}
+let userForm = reactive({})
+let hasEmployeeNo = ref(false)
+let formShow = ref(false) //控制表单暂缓加载,直接加载弹窗弹出会卡顿
+let type = ref('add')
+watch(()=>props.isShow,(newval)=>{
+    if(newval){
+        //工号一旦填写了就不允许更改
+        hasEmployeeNo.value = props.type==='add'?false:!!props.userInfo.EmployeeId
+        setUserForm()
+        type.value = props.type
+    }else{
+        //清除表单校验
+        formRef.value?.resetFields()
+        Object.assign(userForm,baseForm)
+        hasEmployeeNo.value = false
+    }
+
+})
+//设置userForm的值
+async function setUserForm(){
+    const {
+            AdminId,AdminName,Password,EmployeeId,Authority,Position,RealName,RoleId,Enabled,Email,
+            DepartmentId,GroupId,TeamId,TelAreaCode,Mobile,Province,City
+        } = props.userInfo
+    
+    let departArr = [];
+    DepartmentId?departArr.push(DepartmentId):''
+    GroupId?departArr.push(GroupId):''
+    TeamId ?departArr.push(TeamId):''
+
+    
+    Object.assign(
+        userForm, 
+        {
+            admin_id:AdminId||0,
+            account:AdminName||'',
+            pwd:Password||'',
+            employeeNumber:EmployeeId||'',
+            name:RealName||'',
+            depart:departArr.length?departArr:'',
+            role:RoleId||'',
+            province:Province||'',
+            city:City||'',
+            post:Position||'',
+            mobile:Mobile||'',
+            auth:Authority||0,
+            status:Enabled||1,
+            direct:'',
+            email:Email||'',
+            areacode:TelAreaCode||'86'
+        }
+    )
+    console.log('setform')
+    if(props.type==='add') return 
+    let ResearchGroupIds = []
+    const res = await departInterence.getResearchGroupByAdminId({AdminId})
+    if(res.Ret===200&&res.Data){
+        res.Data.forEach(item=>{
+            ResearchGroupIds.push(item.research_group_id)
+            userForm.direct = changeResearchGroupIdsToDirect(ResearchGroupIds,'tag_id')
+        })
+    }
+}
+function roleChange(value){
+    if([2,6,18].includes(value)) return 
+    formRef.value?.clearValidate(['city'])
+}
+function selectRegion(data){
+    userForm.province = data.province.value;
+    userForm.city = data.city.value =='市'?'':data.city.value;
+}
+//表单校验
+let formRef = ref(null)
+const formRules = reactive({
+    account:[{required: true, message: '登录账号不能为空', trigger: 'blur'}],
+    pwd:[{validator:(rule,value,callback)=>{
+        if(value===''){
+            callback(new Error('请输入新密码'))
+        }
+        if(!checkPassWord(value||'')){
+            callback(new Error('密码要求8位及以上,包含数字、大写字母、小写字母、特殊字符中的三个类型'))
+        }else{
+            callback()
+        }
+    }}],
+    name:[{ required: true, message: '姓名不能为空', trigger: 'blur' }],
+    mobile:[{ validator: (rule, value, callback) => {
+        if (value === ''&&!userForm.email) {
+            callback(new Error('手机号码和邮箱至少填一个'));
+        }else if(value&&userForm.areacode==='86'&&!isMobileNo(value||'')) {
+            callback(new Error('请输入正确的手机号格式'));
+        } else if(userForm.areacode!=='86'&&isNaN(value?.trim())){
+            callback(new Error('请输入正确的手机号格式'));
+        }else {
+            callback();
+        }
+    }, trigger: 'blur'}],
+    email:[{ validator: (rule, value, callback) => {
+        if (value === ''&&!userForm.mobile) {
+            callback(new Error('手机号码和邮箱至少填一个'));
+        }else if(value&&!patternEmail.test(value)) {
+            callback(new Error('请输入正确的邮箱格式'));
+        } else {
+            callback();
+        }
+    }, trigger: 'blur'}],
+    depart:[{ required: true, message: '部门分组不能为空', trigger: 'change' }],
+    employeeNumber:[{validator:(rule,value,callback)=>{
+        if(!value){
+            callback()
+            return 
+        }
+        // 取消空格
+        let tempValue = parseInt(value.replaceAll(' ',''))
+        if(!tempValue || value.replaceAll(' ','').length!=4){
+            callback(new Error('请输入四位数字'))
+        }else{
+            callback()
+        }
+    },trigger:'blur'}],
+    role:[{ required: true, message: '角色不能为空', trigger: 'change' }],
 })
-const emit = defineEmits(["closeDia"])
+async function handleSubmitForm(){
+    try{
+        await formRef.value?.validate()
+    }catch(e){
+        console.log(e)
+        return
+    }
+    
+    //通过校验
+    emit("submitForm",{
+        type:props.type,
+        userForm
+    })
+}
+//转换研究员研究方向分组数据,用于编辑的回显
+function changeResearchGroupIdsToDirect(GroupIds,keyWord){
+    let direct = []
+    GroupIds.forEach(item=>{
+        direct.push(getResearchGroupByKeyword(item,keyWord))
+    })
+    return direct
+}
+function getResearchGroupByKeyword(GroupId,keyWord = "research_group_id"){
+    const getResearchGroupOption = (list, value) => {
+        for (let i in list) {
+            //排除掉一级
+        if (list[i].tag_id == value&&!list[i].classify_name) {
+            return [list[i]];
+        }
+        if (list[i].tags) {
+            let node = getResearchGroupOption(list[i].tags, value);
+            if (node !== undefined) {
+            return node.concat(list[i]);
+            }
+        }
+        }
+    }
+    const answer = getResearchGroupOption(props.researchGroup, GroupId)||[];
+    return answer.map((i) => i[keyWord]).reverse();
+}
+
+
+function DiaOpened(){
+    nextTick(()=>{
+        //console.log("表单加载")
+        formShow.value = true
+    })
+}
 </script>
 
 <template>
     <el-dialog
-        v-model="props.isShow"
+        :model-value="props.isShow"
         :close-on-click-modal="false"
+        :modal-append-to-body="false"
+        width="886px"
+        @opened="DiaOpened"
         @close="emit('closeDia')"
-        center
-    >
+        center>
         <template #header style="display:flex;align-items:center;">
             <img :src="type==='add'?$icon.add:$icon.edit"
                 style="color:#fff;width:16px;height:16px;margin-right:5px;">
-            <span style="font-size:16px;">{{props.type==='add'?'添加用户':'编辑用户'}}</span>
+            <span style="font-size:16px;">{{type==='add'?'添加用户':'编辑用户'}}</span>
         </template>
-        <div class="form-content">
-            <searchDistPicker />
+        <div class="form-content" v-loading="!formShow">
+            <el-form label-width="100px" class="user-form" v-if="formShow"
+                :hide-required-asterisk="true"
+                ref="formRef"
+                :model="userForm"
+                :rules="formRules" 
+            >
+                <el-form-item label="登录账号" prop="account">
+                    <el-input v-model="userForm.account" placeholder="建议使用邮箱前缀或者手机号码" clearable />
+                </el-form-item>
+                <el-form-item label="登录密码" prop="pwd" v-if="type==='add'">
+                    <el-input v-model.trim="userForm.pwd" placeholder="6-12位数字与字母的组合" type="password" clearable />
+                </el-form-item>
+                <el-form-item label="姓名" prop="name">
+                    <el-input v-model="userForm.name" placeholder="请输入用户名称"  clearable />
+                </el-form-item>
+                <el-form-item label="手机号码" prop="mobile" class="mobile-input-item">
+                    <el-input v-model.trim="userForm.mobile" placeholder="请输入手机号码" clearable class="mobile-input" />
+                    <el-select v-model="userForm.areacode" class="mobile-select" placeholder="请选择">
+                        <el-option v-for="item in areaCode" :key="item.Value"
+                            :label="item.Name" :value="item.Value" />
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="所属部门" prop="depart" v-if="type==='add'">
+                    <el-cascader :options="departArr" 
+                        :props="{
+                            value:'DepartmentId',
+                            label:'DepartmentName',
+                            children:'Child',
+                            checkStrictly:true
+                        }" 
+                        v-model="userForm.depart"
+                        placeholder="请选择部门分组" clearable>
+                    </el-cascader>
+                </el-form-item>
+                <el-form-item label="邮箱" prop="email">
+                    <el-input v-model="userForm.email" placeholder="请输入邮箱" />
+                </el-form-item>
+                <el-form-item label="工号" prop="employeeNumber">
+                    <el-input :disabled="hasEmployeeNo" v-model="userForm.employeeNumber" placeholder="请输入工号" />
+                </el-form-item>
+                <el-form-item label="分配角色" prop="role">
+                    <el-select v-model="userForm.role" placeholder="分配角色" @change="roleChange">
+                        <el-option v-for="item in roleArr" :key="item.RoleId" :label="item.RoleName" :value="item.RoleId">
+                        </el-option>
+                    </el-select>
+                </el-form-item>
+                <el-form-item label="研究方向" prop="direct">
+                    <el-cascader collapse-tags :show-all-levels="false" placeholder="请选择研究方向" 
+                        :options="researchGroup" 
+                        :props="{
+                            value:'tag_id',
+                            label:'tag_name',
+                            children:'tags',
+                            multiple: true
+                        }" 
+                        v-model="userForm.direct"
+                        clearable></el-cascader>
+                </el-form-item>
+                <el-form-item label="职务" prop="post">
+                    <el-input v-model="userForm.post" placeholder="请输入职务" clearable />
+                </el-form-item>
+                <el-form-item label="工作地点" prop="city"
+                    :rules="[2,6,18].includes(userForm.role)?{ required: true, message: '工作地点不能为空', trigger: 'change' }:{}">
+                        <search-dist-picker 
+                            :provinceInfo="userForm.province"
+                            :cityInfo="userForm.city"
+                            @selected="selectRegion"/>
+                </el-form-item>
+                <el-form-item label="状态" prop="status">
+                    <el-radio-group v-model="userForm.status">
+                        <el-radio :label="1">启用</el-radio>
+                        <el-radio :label="0">禁用</el-radio>
+                    </el-radio-group>
+                </el-form-item>
+            </el-form>
         </div>
+        <template #footer>
+            <div class="btn-content">
+                <el-button type="primary" style="width:80px;margin-right:24px;" @click="handleSubmitForm">保存</el-button>
+                <el-button style="width:80px;" @click="emit('closeDia')">取消</el-button>
+            </div>
+        </template>
     </el-dialog>
+
 </template>
 
 <style scoped lang="scss">
+.el-dialog{
+    .form-content{
+        min-height: 200px;
+        .user-form{
+            display: flex;
+            flex-wrap: wrap;
+            gap:0 10px;
+            :deep(.el-form-item){
+                .el-input,.el-cascader,.el-select{
+                    width:300px;
+                }
+                .select-dist-picker{
+                    .el-select{
+                        width:145px;
+                        .el-input{
+                            width: 145px;
+                        }
+                    }
+                }
+            }
+            :deep(.mobile-input-item){
+                    position:relative;
+                    .mobile-select{
+                        position:absolute;
+                        top:0;
+                        left:0;
+                        width:115px !important;
+                        .el-input{
+                            width: 115px !important;
+                        }
+                    }
+                    .mobile-input{
+                        .el-input__inner{
+                            padding-left: 125px !important;
+                        }
+                    }
+                }
+        }
+    }
+    .btn-content{
+        display:flex;
+        justify-content:center;
+        margin-bottom: 20px;
+    }
+}
 
 </style>

+ 69 - 5
src/views/system_manage/depart_manage/components/MoveDepartDialog.vue

@@ -1,13 +1,77 @@
 <script setup>
-import { ref, reactive } from 'vue'
+import { ref, reactive, watch } from 'vue'
+import{checkPassWord} from '@/utils/commonOptions'
+const props = defineProps({
+    isShow:Boolean,
+    userInfo:Object,
+    departArr:Array
+})
+const emit = defineEmits(["closeDia","submitForm"])
+
+
+let form = reactive({
+    depart:[]
+})
+watch(()=>props.isShow,(newval)=>{
+    if(newval){
+        const {DepartmentId,GroupId,TeamId} = props.userInfo
+        let departArr = [];
+        DepartmentId?departArr.push(DepartmentId):''
+        GroupId?departArr.push(GroupId):''
+        TeamId ?departArr.push(TeamId):''
+        form.depart = departArr
+    }
+})
+let formRef = ref(null) 
+async function handleSubmitForm(){
+    emit("submitForm",{user:props.userInfo,form})
+}
 </script>
 
 <template>
-    <div>
-        移动分组弹窗
-    </div>
+    <el-dialog
+        :model-value="props.isShow"
+        :close-on-click-modal="false"
+        :modal-append-to-body="false"
+        width="30%"
+        title="重置密码"
+        @close="emit('closeDia')"
+    >
+        <div class="dialog-content">
+            <el-form ref="formRef" :model="form" label-width="100px">
+                <el-form-item label="账号">
+                    <span>{{userInfo.RealName}}</span>
+                </el-form-item>
+                <el-form-item label="选择分组" prop="depart">
+                    <el-cascader 
+                        v-model="form.depart" 
+                        :options="props.departArr"
+                        :props="{
+                            value:'DepartmentId',
+                            label:'DepartmentName',
+                            children:'Child',
+                            checkStrictly:true
+                        }" 
+                        style="width:100%"
+                        placeholder="请选择部门分组">
+                    </el-cascader>
+                </el-form-item>
+            </el-form>
+        </div>
+        <template #footer>
+            <div class="btn-content">
+                <el-button type="primary" style="width:80px;margin-right:24px;" @click="handleSubmitForm">保存</el-button>
+                <el-button style="width:80px;" @click="emit('closeDia')">取消</el-button>
+            </div>
+        </template>
+    </el-dialog>
 </template>
 
 <style scoped lang="scss">
-
+.el-dialog{
+    .btn-content{
+        text-align: center;
+        margin-bottom: 20px;
+    }
+}
 </style>

+ 87 - 5
src/views/system_manage/depart_manage/components/ResetPassDialog.vue

@@ -1,13 +1,95 @@
 <script setup>
-import { ref, reactive } from 'vue'
+import { ref, reactive, watch } from 'vue'
+import{checkPassWord} from '@/utils/commonOptions'
+const props = defineProps({
+    isShow:Boolean,
+    userInfo:Object
+})
+const emit = defineEmits(["closeDia","submitForm"])
+
+
+let form = reactive({
+    password:'',
+    check:''
+})
+watch(()=>props.isShow,(newval)=>{
+    if(newval){
+        formRef.value?.resetFields()
+        form.password = ''
+        form.check=''
+    }
+})
+let formRef = ref(null) 
+const rules = reactive({
+    password:[{validator: (rule,value,callback)=>{
+            if(value===''){
+                callback(new Error('请输入新密码'))
+            }
+            if(form.check!==''){
+                formRef.value?.validateField('check')
+            }
+            if(!checkPassWord(value)){
+                callback(new Error('密码要求8位及以上,包含数字、大写字母、小写字母、特殊字符中的三个类型'))
+            }else{
+                callback()
+            }
+        },trigger: 'blur'}],
+    check:[{validator: (rule,value,callback)=>{
+            if(value===''){
+                callback(new Error('请输入确认密码'))
+            }else if(value!==form.password){
+                callback(new Error('两次输入的密码不一致'))
+            }else{
+                callback()
+            }
+        },trigger: 'blur'}]
+})
+async function handleSubmitForm(){
+    try{
+        await formRef.value?.validate()
+    }catch(e){
+        return 
+    }
+    emit("submitForm",{user:props.userInfo,form})
+}
 </script>
 
 <template>
-    <div>
-        重置密码弹窗
-    </div>
+    <el-dialog
+        :model-value="props.isShow"
+        :close-on-click-modal="false"
+        :modal-append-to-body="false"
+        width="30%"
+        title="重置密码"
+        @close="emit('closeDia')"
+    >
+        <div class="dialog-content">
+            <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+                <el-form-item label="账号">
+                    <span>{{userInfo.RealName}}</span>
+                </el-form-item>
+                <el-form-item label="新密码" prop="password">
+                    <el-input v-model="form.password" style="width:100%" :show-password="true"></el-input>
+                </el-form-item>
+                <el-form-item label="确认新密码" prop="check">
+                    <el-input v-model="form.check" style="width:100%" :show-password="true"></el-input>
+                </el-form-item>
+            </el-form>
+        </div>
+        <template #footer>
+            <div class="btn-content">
+                <el-button type="primary" style="width:80px;margin-right:24px;" @click="handleSubmitForm">保存</el-button>
+                <el-button style="width:80px;" @click="emit('closeDia')">取消</el-button>
+            </div>
+        </template>
+    </el-dialog>
 </template>
 
 <style scoped lang="scss">
-
+.el-dialog{
+    .btn-content{
+        text-align: center;
+        margin-bottom: 20px;
+    }
+}
 </style>

+ 2 - 1
src/views/system_manage/depart_manage/components/UserDetail.vue

@@ -4,7 +4,8 @@ import { departInterence } from '@/api/api.js'
 const props = defineProps({
     isShow: Boolean,
     userInfo:{
-        RoleId:0
+        type:Object,
+        default:{RoleId:0}
     },
     researchGroup:Array
 })

+ 73 - 11
src/views/system_manage/depart_manage/departManage.vue

@@ -4,25 +4,34 @@ import {useDepart} from './hooks/use-depart'
 import { departInterence } from '@/api/api.js'
 
 import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
-import { Search } from '@element-plus/icons-vue'
+import { Search, InfoFilled } from '@element-plus/icons-vue'
 import { ElMessage } from 'element-plus'
 
 import UserDetail from './components/UserDetail.vue'
 import ModifyUserDialog from './components/ModifyUserDialog.vue'
+import ModifyNodeDialog from './components/ModifyNodeDialog.vue'
+import ResetPassDialog from './components/ResetPassDialog.vue'
+import MoveDepartDialog from './components/MoveDepartDialog.vue'
 
 const {
         defaultDepart,defaultGroup,departArr,tableParams,tableData,groupTeamId,
         getDepartArr,getTableUser,
         useTreeMove,useTreeEvent,useDepartTable
     } = useDepart()
-const {checkAllowDrag,checkAllowDrop,handleDropOver} = useTreeMove()
-const {act_node,clickNode,initDepart,openNodeDialog,removeNode} = useTreeEvent()
+const {departTree,checkAllowDrag,checkAllowDrop,handleDropOver} = useTreeMove()
+const {
+        act_node,clickNode,initDepart,removeNode,
+        modifyNodeType,nodeInfo,isModifyNodeShow,openNodeDialog,
+        modifyNode
+    } = useTreeEvent()
 const {
         isShowUserdtl,userObj,
         changeStatus,getUserDetail,
-        openResetPassDialog,openMoveDepartDialog,
+        isResetPassShow,openResetPassDialog,resetUserPassWord,
+        isMoveDepartShow,openMoveDepartDialog,moveDepart,
         getResearchGroup,researchGroup,
-        isModifyUserShow,openUserDialog,
+        isModifyUserShow,modifyUserType,userInfo,openUserDialog,
+        modifyUser,delUser
     } = useDepartTable()
 
 const defaultProp = {label:'DepartmentName',children:'Child'}
@@ -55,6 +64,24 @@ function synchronizationMEIKE(){
     })
 }
 
+let areaCode = ref([])
+function getAreaCode(){
+    departInterence.getPhoneAreaCode().then(res=>{
+        if(res.Ret!==200) return 
+        areaCode.value = res.Data||[]
+    })
+}
+
+//获取已有角色列表
+let roleArr = ref([])
+function getRoles() {
+    departInterence.getRole({ RoleLevel:0 }).then(res => {
+        if(res.Ret === 200) {
+            roleArr.value = res.Data.List||[];
+        }
+    })
+}
+
 
 //获取用户数据
 getTableUser()
@@ -64,6 +91,10 @@ getDepartArr()
 setShareCustomDepartId()
 //获取研究方向分组列表
 getResearchGroup()
+//获取手机区号
+getAreaCode()
+//获取已有角色列表
+getRoles()
 </script>
 
 <template>
@@ -95,12 +126,12 @@ getResearchGroup()
                             <img src="~@/assets/img/set_m/add.png" alt="" class="icon-img" @click.stop="openNodeDialog('add',data,node)" v-if="data.IsGroup">
                             <img src="~@/assets/img/set_m/edit.png" alt="" class="icon-img" @click.stop="openNodeDialog('edit',data,node)">
                             <el-popconfirm
-                                @onConfirm="removeNode(node,data)"
-                                confirmButtonText='删除'
-                                cancelButtonText='取消'
-                                confirmButtonType="text"
-                                icon="el-icon-info"
-                                iconColor="red"
+                                @confirm="removeNode(data)"
+                                confirm-button-text='删除'
+                                cancel-button-text='取消'
+                                confirm-button-type="primary"
+                                :icon="InfoFilled"
+                                icon-color="red"
                                 title="确定删除吗"
                                 v-if="data.DepartmentId!=shareCustomDepartmentId"
                             >
@@ -203,6 +234,37 @@ getResearchGroup()
     <!-- 添加/编辑用户弹窗 -->
     <ModifyUserDialog 
         :isShow="isModifyUserShow"
+        :type="modifyUserType"
+        :userInfo="userInfo"
+        :researchGroup="researchGroup"
+        :areaCode="areaCode"
+        :departArr="departArr"
+        :roleArr="roleArr"
+        @submitForm="modifyUser"
+        @closeDia="isModifyUserShow = false"
+    />
+    <!-- 添加/编辑节点弹窗 -->
+    <ModifyNodeDialog 
+        :isShow="isModifyNodeShow"
+        :type="modifyNodeType"
+        :nodeInfo="nodeInfo"
+        @submitForm="modifyNode"
+        @closeDia="isModifyNodeShow = false"
+    />
+    <!-- 重置密码弹窗 -->
+    <ResetPassDialog 
+        :isShow="isResetPassShow"
+        :userInfo="userInfo"
+        @submitForm="resetUserPassWord"
+        @closeDia="isResetPassShow = false"
+    />
+    <!-- 移动分组弹窗 -->
+    <MoveDepartDialog 
+        :isShow="isMoveDepartShow"
+        :userInfo="userInfo"
+        :departArr="departArr"
+        @submitForm="moveDepart"
+        @closeDia="isMoveDepartShow = false"
     />
 </template>
 

+ 286 - 20
src/views/system_manage/depart_manage/hooks/use-depart.js

@@ -1,6 +1,8 @@
 //部门管理 hook
 import { ref, reactive } from 'vue'
+import http from '@/api/http'
 import { departInterence } from '@/api/api.js';
+import { ElMessage, ElMessageBox } from 'element-plus'
 
 export function useDepart(){
     //选择的部门
@@ -62,27 +64,126 @@ export function useDepart(){
             }
         })
     }
+
     //用户表格事件
     function useDepartTable(){
         let isShowUserdtl = ref(false)
         let userObj = ref(null)
         //打开添加/编辑用户弹窗 (原项目 addUser editUser 合成一个)
         let isModifyUserShow = ref(false)
-        function openUserDialog(){
+        let modifyUserType = ref('add')
+        let userInfo = ref({})
+        function openUserDialog(type,data){
+            modifyUserType.value = type
+            userInfo.value = data
             isModifyUserShow.value = true
         }
         //打开重置密码弹窗
-        function openResetPassDialog(){}
+        let isResetPassShow = ref(false)
+        function openResetPassDialog(data){
+            userInfo.value = data
+            isResetPassShow.value = true
+        }
         //打开移动分组弹窗
-        function openMoveDepartDialog(){}
+        let isMoveDepartShow = ref(false)
+        function openMoveDepartDialog(data){
+            userInfo.value = data
+            isMoveDepartShow.value = true
+        }
         //添加编辑用户
-        function modifyUser(){}
+        function modifyUser(data){
+            const {type,userForm} = data
+            const {direct} = userForm
+            const ResearchGroupIds = direct.length?direct.map(i=>i[i.length-1]):[]
+            //将userForm转为接口的形式
+            const params = {
+                AdminName: userForm.account,
+                Password: new http.Base64().encode(userForm.pwd),
+                EmployeeId:userForm.employeeNumber.replaceAll(' ',''),
+                Authority: userForm.auth,
+                DepartmentId: Number(userForm.depart[0]),
+                GroupId:userForm.depart[1] ? Number(userForm.depart[1]) : 0,
+                TeamId:userForm.depart[2] ? Number(userForm.depart[2]) : 0,
+                Mobile: userForm.mobile,
+                Position: userForm.post,
+                RealName: userForm.name,
+                RoleId: Number(userForm.role),
+                Enabled:Number(userForm.status),
+                ResearchGroupIds:ResearchGroupIds.join(','),
+                Province:userForm.province,
+                City:userForm.city,
+                Email:userForm.email,
+                TelAreaCode:userForm.areacode,
+            }
+            if(type==='edit'){
+                params.AdminId = Number(userForm.admin_id)
+            }
+            const map = {
+                'add':addUser,
+                'edit':editUser
+            }
+            map[type]&&map[type](params)
+        }
+        function addUser(params){
+            departInterence.addUser(params).then(res => {
+                if(res.Ret === 200) {
+                    ElMessage.success('添加成功');
+                    getTableUser()
+                    isModifyUserShow.value = false
+                }
+            })
+        }
+        function editUser(params){
+            departInterence.editUser(params).then(res => {
+                if(res.Ret === 200) {
+                    ElMessage.success('编辑成功');
+                    getTableUser()
+                    isModifyUserShow.value = false
+                }
+            })
+        }
         //删除用户,前端按钮因产品要求隐藏,函数保留
-        function delUser(){}
+        function delUser(data){
+            ElMessageBox.confirm(`是否确认删除用户【${data.AdminName}】`,'提示',{
+                type:'warning'
+            }).then(() => {
+                departInterence.delUser({
+                    AdminId:data.AdminId
+                }).then(res => {
+                    if(res.Ret === 200) {
+                        ElMessage.success('删除成功')
+                        getTableUser()
+                    }
+                })
+            }).catch(() => {});
+        }
         //重置用户密码
-        function resetUserPassWord(){}
+        function resetUserPassWord({user,form}){
+            departInterence.resetSysuserPass({
+                AdminId:Number(user.AdminId),
+                Password:new http.Base64().encode(form.password),
+                RePassword:new http.Base64().encode(form.password)
+            }).then(res=>{
+                if(res.Ret!==200) return 
+                ElMessage.success('重置密码成功')
+                isResetPassShow.value = false
+                getTableUser()
+            })
+        }
         //移动用户分组
-        function moveDepart(){}
+        function moveDepart({user,form}){
+            departInterence.moveSysuser({
+                AdminId:Number(user.AdminId),
+                DepartmentId:Number(form.depart[0]),
+                GroupId:Number(form.depart[1]?form.depart[1]:0),
+                TeamId:Number(form.depart[2]?form.depart[2]:0),
+            }).then(res=>{
+                if(res.Ret!==200) return
+                ElMessage.success('移动分组成功')
+                isMoveDepartShow.value = false
+                getTableUser()
+            })
+        }
         //改变用户启用禁用状态
         async function changeStatus(data){
             const res=await departInterence.statusChange({
@@ -90,6 +191,7 @@ export function useDepart(){
                 Enabled:data.Enabled===1?0:1
             })
             if(res.Ret===200){
+                ElMessage.success('设置成功')
                 getTableUser()
             }
         }
@@ -118,9 +220,10 @@ export function useDepart(){
 
         return {
             isShowUserdtl,userObj,
-            isModifyUserShow,openUserDialog,
-            openResetPassDialog,openMoveDepartDialog,
-            modifyUser,delUser,moveDepart,resetUserPassWord,
+            isModifyUserShow,modifyUserType,userInfo,openUserDialog,
+            isResetPassShow,openResetPassDialog,resetUserPassWord,
+            isMoveDepartShow,openMoveDepartDialog,moveDepart,
+            modifyUser,delUser,
             getUserDetail,changeStatus,
             getResearchGroup,researchGroup
         }
@@ -164,31 +267,194 @@ export function useDepart(){
         }
 
         //打开添加/编辑部门/分组弹窗 (原项目中的addNode editNode addDepart合成一个)
+        let modifyNodeType = ref('add')
+        let nodeInfo = ref(null)
+        let isModifyNodeShow = ref(false)
+        /**
+         * 
+         * @param type  add or edit
+         * @param data 树节点数据
+         * @param node 树节点 只需要用到level
+         */
         function openNodeDialog(type,data,{level}){
-            console.log('type',type)
-            console.log('data',data)
-            console.log('level',level)
+            modifyNodeType.value = type
+            nodeInfo.value = {data,level}
+            isModifyNodeShow.value = true
+        }
+        //添加编辑部门or分组
+        /**
+         * @param params
+         * @param params.type add or edit 
+         * @param params.nodeForm 节点修改的数据 {name,group}
+         * @param params.nodeInfo 当前点击的树节点 {data,level}
+         */
+        async function modifyNode({type='add',nodeForm,nodeInfo}){
+            const {level=0} = nodeInfo
+            //根据level和type确定modify类型
             //level为0 增加部门
             //level为1 编辑部门&增加分组
             //level为2 编辑分组&增加分组
             //level为3 编辑分组
+            const modifyMap = [
+                {'add':addDepart},
+                {'add':addGroup,'edit':editDepart},
+                {'add':addGroup,'edit':editGroup},
+                {'edit':editGroup}
+            ]
+            await modifyMap[level][type](nodeForm,nodeInfo)
+            getDepartArr()
+        }
+        async function addDepart(form,{data}){
+            await departInterence.addDepart({
+                DepartmentName:form.departName
+            }).then(res=>{
+                if(res.Ret!==200) return 
+                ElMessage.success("添加部门成功")
+                isModifyNodeShow.value = false
+            })
+        }
+        async function editDepart(form,{data}){
+            console.log("编辑部门")
+           await  departInterence.editDepart({
+                DepartmentId:Number(data.DepartmentId),
+                DepartmentName:form.departName
+            }).then(res => {
+                if(res.Ret!==200) return 
+                ElMessage.success("编辑部门成功")
+                isModifyNodeShow.value = false
+            })
+        }
+        //部门添加分组 or 分组添加分组
+        async function addGroup(form,{data}){
+            const groups = form.group.map(i=>i.DepartmentName)
+            const params = data.IsGroup
+                ?{
+                    DepartmentId:Number(data.TopId),
+                    GroupId:Number(data.DepartmentId),
+                    TeamName:groups.join(',')
+                }:{
+                    DepartmentId:Number(data.DepartmentId),
+                    GroupName:groups.join(',')
+                }
+            const res = data.IsGroup
+                ?await departInterence.addTeamGroup(params)
+                :await departInterence.addGroup(params)
+            if(res.Ret===200){
+                ElMessage.success("添加分组成功")
+                isModifyNodeShow.value = false
+            }
+
+        }
+        async function editGroup(form,{data}){
+            const params = data.IsGroup
+                ?{
+                    GroupId:Number(data.DepartmentId),
+                    GroupName:form.groupName,
+                }:{
+                    TeamId:Number(data.DepartmentId),
+                    TeamName:form.groupName
+                }
+            const res = data.IsGroup
+                ?await departInterence.editGroup(params)
+                :await departInterence.editTeamGroup(params)
+            if(res.Ret===200) {
+                ElMessage.success("编辑分组成功")
+                isModifyNodeShow.value = false
+            }
         }
         //删除节点 
-        function removeNode(){}
+        async function removeNode(data){
+            let res = null
+            if(data.IsDepartment) {
+                res = await departInterence.delDepart({
+                    DepartmentId:data.DepartmentId
+                })
+            }else if(data.IsGroup) {
+                /* 删除分组 */
+                res = await departInterence.delGroup({
+                    GroupId:data.DepartmentId
+                })
+            }else {
+                res = await departInterence.delTeamGroup({
+                    TeamId:data.DepartmentId
+                })
+            }
+
+            if(res&&res.Ret===200){
+                ElMessage.success('删除成功')
+                initDepart()
+            }
+        }
         return {
             act_node,
-            clickNode,initDepart,
-            openNodeDialog,
-            removeNode
+            clickNode,initDepart,removeNode,
+            modifyNodeType,nodeInfo,isModifyNodeShow,openNodeDialog,modifyNode,
+            
         }
 
     }
     //树结构移动
     function useTreeMove(){
-        function checkAllowDrag(){}
-        function checkAllowDrop(){}
-        function handleDropOver(){}
+        //节点是否可拖拽:所有节点均可拖拽
+        function checkAllowDrag(){return true}
+        //节点是否可放入:仅支持同部门下,同级节点相互排序
+        function checkAllowDrop(draggingNode,dropNode,type){
+            if(type==='inner'){
+                /**
+                 * 情况1 
+                 * draggingNode.level==2 dropNode.level==1
+                 * draggingNode.TopId == dropNode.DepartmentId
+                 * 情况2
+                 * draggingNode.level==3 dropNode.level==2
+                 * draggingNode.GroupId == dropNode.DepartmentId
+                 */
+                const case_1 = draggingNode.level===2&&dropNode.level===1
+                                && draggingNode.data.TopId === dropNode.data.DepartmentId
+                const case_2 = draggingNode.level===3&&dropNode.level===2
+                                && draggingNode.data.GroupId == dropNode.data.DepartmentId
+                return case_1||case_2
+            }else if(draggingNode.level===dropNode.level){
+                /**
+                 * 情况1
+                 * draggingNode.level===dropNode.level===1
+                 * 情况2
+                 * draggingNode.level===dropNode.level
+                 * draggingNode.TopId === dropNode.TopId||draggingNode.GroupId===dropNode.GroupId
+                 */
+                const case_1 = dropNode.level===1
+                const case_2 = draggingNode.data.TopId&&dropNode.data.TopId&&draggingNode.data.TopId === dropNode.data.TopId
+                        ||draggingNode.data.GroupId&&dropNode.data.GroupId&&draggingNode.data.GroupId===dropNode.data.GroupId
+                return case_1||case_2
+            }
+            return false
+        }
+        //放入节点
+        let departTree = ref(null)
+        function handleDropOver(draggingNode,dropNode,type,e){
+            //获取当前移动节点的层级和id,parentId
+            //获取同层级的ids
+            const treeData = departTree.value.data||[]
+            const node = departTree.value.getNode(draggingNode.data.parentKey)||{data:{Child:[]}}
+            let params = {
+                DepartmentIds:[],
+                GroupIds:[],
+                TeamIds:[]
+            }
+            if(draggingNode.level===1){
+                params.DepartmentIds = treeData.map(i=>i.DepartmentId)
+            }else{
+                const ids = node.data.Child.map(i=>i.DepartmentId)
+                params.GroupIds = draggingNode.level===2?ids:[]
+                params.TeamIds =draggingNode.level===3?ids:[]
+            }
+            departInterence.sortDepart(params).then(res=>{
+                if(res.Ret!==200) return 
+                ElMessage.success('移动排序成功')
+                getDepartArr()
+            })
+        }
         return {
+            departTree,
             checkAllowDrag,
             checkAllowDrop,
             handleDropOver