Sfoglia il codice sorgente

销售管理-销售路演统计

hbchen 11 mesi fa
parent
commit
7ee61c1ca6

+ 4 - 1
package.json

@@ -12,6 +12,9 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.3.1",
+    "@fullcalendar/interaction": "^6.1.11",
+    "@fullcalendar/timegrid": "^6.1.11",
+    "@fullcalendar/vue3": "^6.1.11",
     "@vueuse/core": "^10.9.0",
     "axios": "^1.6.7",
     "clipboard": "^2.0.11",
@@ -28,8 +31,8 @@
     "sortablejs": "^1.15.2",
     "v-distpicker": "2.1.0",
     "vue": "^3.4.19",
-    "vue-froala-wysiwyg": "^4.1.4",
     "vue-datepicker-next": "^1.0.3",
+    "vue-froala-wysiwyg": "^4.1.4",
     "vue-router": "^4.3.0",
     "vue3-tree-org": "^4.2.2",
     "vuedraggable": "^4.1.0"

+ 95 - 41
pnpm-lock.yaml

@@ -8,6 +8,15 @@ dependencies:
   '@element-plus/icons-vue':
     specifier: ^2.3.1
     version: 2.3.1(vue@3.4.20)
+  '@fullcalendar/interaction':
+    specifier: ^6.1.11
+    version: 6.1.11(@fullcalendar/core@6.1.11)
+  '@fullcalendar/timegrid':
+    specifier: ^6.1.11
+    version: 6.1.11(@fullcalendar/core@6.1.11)
+  '@fullcalendar/vue3':
+    specifier: ^6.1.11
+    version: 6.1.11(@fullcalendar/core@6.1.11)(vue@3.4.20)
   '@vueuse/core':
     specifier: ^10.9.0
     version: 10.9.0(vue@3.4.20)
@@ -56,12 +65,12 @@ dependencies:
   vue:
     specifier: ^3.4.19
     version: 3.4.20
-  vue-froala-wysiwyg:
-    specifier: ^4.1.4
-    version: 4.1.4
   vue-datepicker-next:
     specifier: ^1.0.3
     version: 1.0.3(vue@3.4.20)
+  vue-froala-wysiwyg:
+    specifier: ^4.1.4
+    version: 4.1.4
   vue-router:
     specifier: ^4.3.0
     version: 4.3.0(vue@3.4.20)
@@ -217,7 +226,7 @@ packages:
     dev: false
 
   /@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]
@@ -226,7 +235,7 @@ packages:
     optional: true
 
   /@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]
@@ -235,7 +244,7 @@ packages:
     optional: true
 
   /@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]
@@ -244,7 +253,7 @@ packages:
     optional: true
 
   /@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]
@@ -253,7 +262,7 @@ packages:
     optional: true
 
   /@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]
@@ -262,7 +271,7 @@ packages:
     optional: true
 
   /@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]
@@ -271,7 +280,7 @@ packages:
     optional: true
 
   /@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]
@@ -280,7 +289,7 @@ packages:
     optional: true
 
   /@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]
@@ -289,7 +298,7 @@ packages:
     optional: true
 
   /@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]
@@ -298,7 +307,7 @@ packages:
     optional: true
 
   /@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]
@@ -307,7 +316,7 @@ packages:
     optional: true
 
   /@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]
@@ -316,7 +325,7 @@ packages:
     optional: true
 
   /@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]
@@ -325,7 +334,7 @@ packages:
     optional: true
 
   /@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]
@@ -334,7 +343,7 @@ packages:
     optional: true
 
   /@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]
@@ -343,7 +352,7 @@ packages:
     optional: true
 
   /@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]
@@ -352,7 +361,7 @@ packages:
     optional: true
 
   /@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]
@@ -361,7 +370,7 @@ packages:
     optional: true
 
   /@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]
@@ -370,7 +379,7 @@ packages:
     optional: true
 
   /@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]
@@ -379,7 +388,7 @@ packages:
     optional: true
 
   /@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]
@@ -388,7 +397,7 @@ packages:
     optional: true
 
   /@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]
@@ -397,7 +406,7 @@ packages:
     optional: true
 
   /@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]
@@ -406,7 +415,7 @@ packages:
     optional: true
 
   /@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]
@@ -415,7 +424,7 @@ packages:
     optional: true
 
   /@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]
@@ -440,6 +449,47 @@ packages:
     resolution: {integrity: sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==}
     dev: false
 
+  /@fullcalendar/core@6.1.11:
+    resolution: {integrity: sha512-TjG7c8sUz+Vkui2FyCNJ+xqyu0nq653Ibe99A66LoW95oBo6tVhhKIaG1Wh0GVKymYiqAQN/OEdYTuj4ay27kA==}
+    dependencies:
+      preact: 10.12.1
+    dev: false
+
+  /@fullcalendar/daygrid@6.1.11(@fullcalendar/core@6.1.11):
+    resolution: {integrity: sha512-hF5jJB7cgUIxWD5MVjj8IU407HISyLu7BWXcEIuTytkfr8oolOXeCazqnnjmRbnFOncoJQVstTtq6SIhaT32Xg==}
+    peerDependencies:
+      '@fullcalendar/core': ~6.1.11
+    dependencies:
+      '@fullcalendar/core': 6.1.11
+    dev: false
+
+  /@fullcalendar/interaction@6.1.11(@fullcalendar/core@6.1.11):
+    resolution: {integrity: sha512-ynOKjzuPwEAMgTQ6R/Z2zvzIIqG4p8/Qmnhi1q0vzPZZxSIYx3rlZuvpEK2WGBZZ1XEafDOP/LGfbWoNZe+qdg==}
+    peerDependencies:
+      '@fullcalendar/core': ~6.1.11
+    dependencies:
+      '@fullcalendar/core': 6.1.11
+    dev: false
+
+  /@fullcalendar/timegrid@6.1.11(@fullcalendar/core@6.1.11):
+    resolution: {integrity: sha512-0seUHK/ferH89IeuCvV4Bib0zWjgK0nsptNdmAc9wDBxD/d9hm5Mdti0URJX6bDoRtsSfRDu5XsRcrzwoc+AUQ==}
+    peerDependencies:
+      '@fullcalendar/core': ~6.1.11
+    dependencies:
+      '@fullcalendar/core': 6.1.11
+      '@fullcalendar/daygrid': 6.1.11(@fullcalendar/core@6.1.11)
+    dev: false
+
+  /@fullcalendar/vue3@6.1.11(@fullcalendar/core@6.1.11)(vue@3.4.20):
+    resolution: {integrity: sha512-jBoDS0WSpuOM9ZgjL3lNh6o385u/LthFZDaMUACjVVJZh3JuBbuA7ghdUvIelcTNXa5VRCkSZOpivTJWOnLfcg==}
+    peerDependencies:
+      '@fullcalendar/core': ~6.1.11
+      vue: ^3.0.11
+    dependencies:
+      '@fullcalendar/core': 6.1.11
+      vue: 3.4.20
+    dev: false
+
   /@jridgewell/gen-mapping@0.3.4:
     resolution: {integrity: sha512-Oud2QPM5dHviZNn4y/WhhYKSXksv+1xLEIsNrAbGcFzUN3ubqWRFT5gwPchNc5NuzILOU4tPBDTZ4VwhL8Y7cw==}
     engines: {node: '>=6.0.0'}
@@ -470,7 +520,7 @@ packages:
     dev: true
 
   /@rollup/rollup-android-arm-eabi@4.12.0:
-    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}
+    resolution: {integrity: sha512-+ac02NL/2TCKRrJu2wffk1kZ+RyqxVUlbjSagNgPm94frxtr+XDL12E5Ll1enWskLrtrZ2r8L3wED1orIibV/w==}
     cpu: [arm]
     os: [android]
     requiresBuild: true
@@ -478,7 +528,7 @@ packages:
     optional: true
 
   /@rollup/rollup-android-arm64@4.12.0:
-    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.12.0.tgz}
+    resolution: {integrity: sha512-OBqcX2BMe6nvjQ0Nyp7cC90cnumt8PXmO7Dp3gfAju/6YwG0Tj74z1vKrfRz7qAv23nBcYM8BCbhrsWqO7PzQQ==}
     cpu: [arm64]
     os: [android]
     requiresBuild: true
@@ -486,7 +536,7 @@ packages:
     optional: true
 
   /@rollup/rollup-darwin-arm64@4.12.0:
-    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.12.0.tgz}
+    resolution: {integrity: sha512-X64tZd8dRE/QTrBIEs63kaOBG0b5GVEd3ccoLtyf6IdXtHdh8h+I56C2yC3PtC9Ucnv0CpNFJLqKFVgCYe0lOQ==}
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
@@ -494,7 +544,7 @@ packages:
     optional: true
 
   /@rollup/rollup-darwin-x64@4.12.0:
-    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==, tarball: https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.12.0.tgz}
+    resolution: {integrity: sha512-cc71KUZoVbUJmGP2cOuiZ9HSOP14AzBAThn3OU+9LcA1+IUqswJyR1cAJj3Mg55HbjZP6OLAIscbQsQLrpgTOg==}
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
@@ -502,7 +552,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-arm-gnueabihf@4.12.0:
-    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}
+    resolution: {integrity: sha512-a6w/Y3hyyO6GlpKL2xJ4IOh/7d+APaqLYdMf86xnczU3nurFTaVN9s9jOXQg97BE4nYm/7Ga51rjec5nfRdrvA==}
     cpu: [arm]
     os: [linux]
     requiresBuild: true
@@ -510,7 +560,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-arm64-gnu@4.12.0:
-    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}
+    resolution: {integrity: sha512-0fZBq27b+D7Ar5CQMofVN8sggOVhEtzFUwOwPppQt0k+VR+7UHMZZY4y+64WJ06XOhBTKXtQB/Sv0NwQMXyNAA==}
     cpu: [arm64]
     os: [linux]
     libc: [glibc]
@@ -519,7 +569,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-arm64-musl@4.12.0:
-    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.12.0.tgz}
+    resolution: {integrity: sha512-eTvzUS3hhhlgeAv6bfigekzWZjaEX9xP9HhxB0Dvrdbkk5w/b+1Sxct2ZuDxNJKzsRStSq1EaEkVSEe7A7ipgQ==}
     cpu: [arm64]
     os: [linux]
     libc: [musl]
@@ -528,7 +578,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-riscv64-gnu@4.12.0:
-    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}
+    resolution: {integrity: sha512-ix+qAB9qmrCRiaO71VFfY8rkiAZJL8zQRXveS27HS+pKdjwUfEhqo2+YF2oI+H/22Xsiski+qqwIBxVewLK7sw==}
     cpu: [riscv64]
     os: [linux]
     libc: [glibc]
@@ -537,7 +587,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-x64-gnu@4.12.0:
-    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}
+    resolution: {integrity: sha512-TenQhZVOtw/3qKOPa7d+QgkeM6xY0LtwzR8OplmyL5LrgTWIXpTQg2Q2ycBf8jm+SFW2Wt/DTn1gf7nFp3ssVA==}
     cpu: [x64]
     os: [linux]
     libc: [glibc]
@@ -546,7 +596,7 @@ packages:
     optional: true
 
   /@rollup/rollup-linux-x64-musl@4.12.0:
-    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==, tarball: https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.12.0.tgz}
+    resolution: {integrity: sha512-LfFdRhNnW0zdMvdCb5FNuWlls2WbbSridJvxOvYWgSBOYZtgBfW9UGNJG//rwMqTX1xQE9BAodvMH9tAusKDUw==}
     cpu: [x64]
     os: [linux]
     libc: [musl]
@@ -555,7 +605,7 @@ packages:
     optional: true
 
   /@rollup/rollup-win32-arm64-msvc@4.12.0:
-    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}
+    resolution: {integrity: sha512-JPDxovheWNp6d7AHCgsUlkuCKvtu3RB55iNEkaQcf0ttsDU/JZF+iQnYcQJSk/7PtT4mjjVG8N1kpwnI9SLYaw==}
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
@@ -563,7 +613,7 @@ packages:
     optional: true
 
   /@rollup/rollup-win32-ia32-msvc@4.12.0:
-    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}
+    resolution: {integrity: sha512-fjtuvMWRGJn1oZacG8IPnzIV6GF2/XG+h71FKn76OYFqySXInJtseAqdprVTDTyqPxQOG9Exak5/E9Z3+EJ8ZA==}
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
@@ -571,7 +621,7 @@ packages:
     optional: true
 
   /@rollup/rollup-win32-x64-msvc@4.12.0:
-    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}
+    resolution: {integrity: sha512-ZYmr5mS2wd4Dew/JjT0Fqi2NPB/ZhZ2VvPp7SmvPZb4Y1CG/LRcS6tcRo2cYU7zLK5A7cdbhWnnWmUjoI4qapg==}
     cpu: [x64]
     os: [win32]
     requiresBuild: true
@@ -579,7 +629,7 @@ packages:
     optional: true
 
   /@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==}
     dev: false
 
   /@types/estree@1.0.5:
@@ -1099,7 +1149,7 @@ packages:
     dev: false
 
   /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]
     requiresBuild: true
@@ -1298,6 +1348,10 @@ packages:
       picocolors: 1.0.0
       source-map-js: 1.0.2
 
+  /preact@10.12.1:
+    resolution: {integrity: sha512-l8386ixSsBdbreOAkqtrwqHwdvR35ID8c3rKPa8lCWuO86dBi32QWHV4vfsZK1utLLFMvw+Z5Ad4XLkZzchscg==}
+    dev: false
+
   /proxy-from-env@1.1.0:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
     dev: false

+ 744 - 0
src/components/calendar.vue

@@ -0,0 +1,744 @@
+<!-- 日历组件 -->
+<template>
+  <div class="calendar-container">
+    <header class="calendar-header">
+      <!-- 头部左插槽 -->
+      <div class="left">
+        <slot name="left"></slot>
+      </div>
+      <div class="center">
+        <el-button type="primary" @click="toogelDate('prev')"
+          ><i class="el-icon-arrow-left" /> 上一周</el-button
+        >
+        <el-button type="primary" @click="toogelDate('today')">本周</el-button>
+        <el-button type="primary" @click="toogelDate('next')"
+          >下一周 <i class="el-icon-arrow-right"
+        /></el-button>
+      </div>
+      <!-- 头部右插槽 -->
+      <div class="right">
+        <slot name="right"></slot>
+      </div>
+    </header>
+    <FullCalendar
+      ref="fullCalendar"
+      class="calendar-body"
+      :options="calendarOptions"
+    >
+      <template #eventContent="arg">
+          <div class="popper-content">
+            <template v-if="fromCode!=='meet'">
+              <p>{{ arg.timeText }}</p>
+              <p>{{ arg.event.title }}</p>
+            </template>
+
+            <div v-else-if="arg.event.extendedProps.titleArr.length">
+              <p v-for="item in arg.event.extendedProps.titleArr" :key="item.title">{{ item.title }}</p>
+            </div>
+          </div>
+      </template>
+    </FullCalendar>
+    <!-- 日历详情 旧版 -->
+<!--     <el-popover
+      placement="bottom-start"
+      width="250"
+      popper-class="calendar-eventinfo-popover"
+      ref="popover"
+    >
+      <div class="popover-body" v-if="fromCode!=='meet'">
+        <p>
+          <template v-if="selectEventInfo.StartDate === selectEventInfo.EndDate">
+            {{
+              `${$moment(selectEventInfo.StartDate).format("MM.DD")}
+              (${$moment(selectEventInfo.StartDate).format('ddd')}) 
+              ${selectEventInfo.StartTime&&selectEventInfo.StartTime.substr(0,5)} - 
+              ${selectEventInfo.EndTime&&selectEventInfo.EndTime.substr(0,5)}`
+            }}
+          </template>
+          <template v-else>
+            {{
+              `${$moment(selectEventInfo.StartDate).format("MM.DD")}
+              (${$moment(selectEventInfo.StartDate).format('ddd')}) 
+              ${selectEventInfo.StartTime&&selectEventInfo.StartTime.substr(0,5)} - 
+              ${$moment(selectEventInfo.EndDate).format("MM.DD")}
+              (${$moment(selectEventInfo.EndDate).format('ddd')}) 
+              ${selectEventInfo.EndTime&&selectEventInfo.EndTime.substr(0,5)}`
+            }}
+          </template>
+        </p>
+        <p v-if="isSeller">{{ selectEventInfo.ResearcherName }}</p>
+        <p>{{ selectEventInfo.title }}</p>
+        <p v-if="selectEventInfo.CompanyName" >{{ `${selectEventInfo.CompanyName}${selectEventInfo.CompanyStatus ? '('+ selectEventInfo.CompanyStatus + ')' : ''}` }}</p>
+        <p v-if="selectEventInfo.Theme">{{ selectEventInfo.Theme }}</p>
+        <p v-if="selectEventInfo.CooperationName">{{ selectEventInfo.CooperationName }}</p>
+        <p v-if="['公开会议','内部会议','路演'].includes(selectEventInfo.ActivityType)">发起人:{{selectEventInfo.SysUserRealName}}</p>
+      </div>
+      <div class="popover-body" v-else>
+        <div v-for="item in selectEventInfo" :key="item.RsCalendarId" class="popover-event-item">
+          <p>
+            <template v-if="$moment(item.minTime).format('YYYY-MM-DD') === $moment(item.maxTime).format('YYYY-MM-DD')">
+              {{
+                `${$moment(item.minTime).format("MM.DD")}
+                (${$moment(item.minTime).format('ddd')}) 
+                ${$moment(item.minTime).format('HH:mm')} - 
+                ${$moment(item.maxTime).format('HH:mm')}`
+              }}
+            </template>
+            <template v-else>
+              {{
+                `${$moment(item.minTime).format("MM.DD")}
+                (${$moment(item.minTime).format('ddd')}) 
+                ${$moment(item.minTime).format('HH:mm')} - 
+                ${$moment(item.maxTime).format("MM.DD")}
+                (${$moment(item.maxTime).format('ddd')}) 
+                ${$moment(item.maxTime).format('HH:mm')}`
+              }}
+            </template>
+          </p>
+          <p>{{ item.total_rs_name }}</p>
+          <p>{{ item.title }}</p>
+          <p v-if="item.CompanyName" >{{ `${item.CompanyName}${item.CompanyStatus ? '('+ item.CompanyStatus + ')' : ''}` }}</p>
+          <p v-if="item.Theme">{{ item.Theme }}</p>
+          <p v-if="item.CooperationName">{{ item.CooperationName }}</p>
+          <p>发起人:{{item.SysUserRealName}}</p>
+        </div>
+      </div>
+      <i
+        class="close-icon el-icon-close"
+        @click="closeIconClick"
+      ></i>
+    </el-popover> -->
+    <!-- 日历详情弹窗 新版 -->
+    <el-popover
+      placement="bottom-start"
+      width="300"
+      popper-class="calendar-eventinfo-popover"
+      ref="popover"
+    >
+    <div class="pop-header">
+        <template v-if="fromCode==='meet'">
+            <p>{{selectEventInfo[0]?selectEventInfo[0].ActivityType:''}}</p>
+        </template>
+        <template v-else>
+            <p>{{selectEventInfo.ActivityType?`${selectEventInfo.RoadshowType}${selectEventInfo.ActivityType}`:'事项'}}</p>
+        </template> 
+        <i
+            class="close-icon el-icon-close"
+            @click="closeIconClick"
+        ></i>
+    </div>
+      <div class="popover-body">
+        <template v-if="fromCode==='meet'">
+            <div v-for="item in selectEventInfo" :key="item.RsCalendarId" class="popover-event-item">
+          <p>
+            <template v-if="$moment(item.minTime).format('YYYY-MM-DD') === $moment(item.maxTime).format('YYYY-MM-DD')">
+              {{
+                `${$moment(item.minTime).format("MM.DD")}
+                (${$moment(item.minTime).format('ddd')}) 
+                ${$moment(item.minTime).format('HH:mm')} - 
+                ${$moment(item.maxTime).format('HH:mm')}`
+              }}
+            </template>
+            <template v-else>
+              {{
+                `${$moment(item.minTime).format("MM.DD")}
+                (${$moment(item.minTime).format('ddd')}) 
+                ${$moment(item.minTime).format('HH:mm')} - 
+                ${$moment(item.maxTime).format("MM.DD")}
+                (${$moment(item.maxTime).format('ddd')}) 
+                ${$moment(item.maxTime).format('HH:mm')}`
+              }}
+            </template>
+          </p>
+          <p>{{ item.title }}</p>
+          <p>研究员:{{ item.total_rs_name }}</p>
+          <p v-if="item.CompanyName" >{{ `${item.CompanyName}${item.CompanyStatus ? '('+ item.CompanyStatus + ')' : ''}` }}</p>
+          <p v-if="item.Theme">会议主题:{{ item.Theme }}</p>
+          <p v-if="item.CooperationName">合作方名称:{{ item.CooperationName }}</p>
+          <p>发起人:{{item.SysUserRealName}}</p>
+        </div>
+        </template>
+        <template v-else>
+            <!-- 换行的内容需要对齐,所以dom结构设置成了下面这样 -->
+            <div class="content-item" v-for="item in popContentArr" :key="item.key">
+                <span class="label">{{item.label}}</span>
+                <div class="content">
+                    <p>{{item.content}}</p>
+                    <p v-if="item.otherContent" class="other-text">{{item.otherContent}}</p>
+                </div>
+            </div>
+        </template>
+      </div>
+      
+    </el-popover>
+  </div>
+</template>
+
+<script>
+import { roadshowInterence } from "@/api/api.js"; 
+import FullCalendar from "@fullcalendar/vue3"; // 提供一个vue组件
+import interactionPlugin from "@fullcalendar/interaction"; // 检测dateClick操作、 可选操作和 事件拖放和调整大小所需的
+import timeGridPlugin from "@fullcalendar/timegrid"; // 提供TimeGrid视图
+export default {
+  name: "Calendar",
+  components: {
+    FullCalendar,
+  },
+  props: {
+    eventList: {
+      type: Array,
+      default: [],
+    },
+    fromCode: {
+      type: String,
+      default: ''
+    }
+  },
+  computed: {
+    roleId() {
+      return Number(localStorage.getItem("AdminId"));
+    },
+    isSeller() {
+      return ['ficc_seller', 'rai_seller', 'ficc_group', 'rai_group'].includes(localStorage.getItem('Role'))
+    }
+  },
+  data() {
+    return {
+      calendarOptions: {
+        height: 'auto', // 组件高度
+        plugins: [interactionPlugin, timeGridPlugin], // 引入插件
+        headerToolbar: false, // 顶部工具栏
+        allDaySlot: false, // 全天日程(不需要)
+        nowIndicator: true, //周/日视图中显示今天当前时间点(以红线标记),默认false不显示
+        selectable: true,
+        initialView: "timeGridWeek", // 设置默认显示周,可选月、日
+        dateClick: this.handleDateClick,
+        eventClick: this.handleEventClick,
+        eventsSet: this.handleEvents,
+        select: this.handleDateSelect,
+        // 日程列表
+        events: [],
+        eventTimeFormat: {
+          // 格式化日程显示时间,如'14:30'
+          hour: "2-digit",
+          minute: "2-digit",
+          hour12: false,
+        },
+        slotMinTime:'08:00:00',//每天最早8点
+        slotLabelFormat: {
+          //格式化左侧显示时间
+          hour12: false, // 14:00  true = 2pm
+          hour: "2-digit", //'06:xx'
+          minute: "2-digit", // 'xx:10'
+        },
+        eventColor: "#ECF5FF", // 默认日程背景色
+        eventTextColor: "#333", // 默认日程文本颜色
+        // eventClassNames: [ 'myclassname', 'otherclassname' ],  // 自定义日程类名
+        locale: "zh-cn", // 语言
+        weekNumberCalculation: "ISO", // 周次的显示格式(ISO表示从周一开始)
+        // slotEventOverlap: false, // 日程是否重叠
+        // eventMaxStack: 1, // 堆叠最大日程数
+      },
+      // 用来存放组件api
+      calendarApi: null,
+      // 用来存放弹出框
+      eventInfoPopoverApi: null,
+      // 选中的日程信息
+      selectEventInfo: {
+        id: 0,
+        title: "",
+        userId: 0,
+        beginDate: "",
+        endDate: "",
+        remark: "",
+        status: "",
+      },
+      //弹窗展示的日程信息
+      popContentArr:[
+          {
+              key:'',
+              label:'',
+              content:'',
+              otherContent:''
+          }
+      ]
+    };
+  },
+  methods: {
+    // 获取组件api,存放到data中
+    getCalendarApi() {
+      this.calendarApi = this.$refs.fullCalendar.getApi();
+    },
+    // /* 切换日期 */
+    toogelDate(type) {
+      switch(type) {
+        case 'prev':
+          this.calendarApi.prev();
+          break;
+        case 'today':
+          this.calendarApi.today();
+          break;
+        case 'next':
+          this.calendarApi.next();
+          break;
+      }
+      this.$emit('weekChange');
+    },
+
+    // 点击某一区域事件
+    handleDateClick(arg) {
+      this.$emit("cellClick", arg.date);
+    },
+
+    // 点击某一日程事件
+    handleEventClick(calEvent) {
+      let id = calEvent.event.id; // 获取当前点击日程的ID
+      let info = this.eventList.find((item) => item.id == id);
+
+      const { clientX,clientY } = calEvent.jsEvent;
+
+      this.selectEventInfo = this.fromCode==='meet' 
+      ? info.filterArr.map(item => ({ ...item,title: this.setDynamicTitle(item) }))
+      : {...info,title: this.setDynamicTitle(info)};
+      // console.log(this.selectEventInfo)
+      //鼠标点击的位置
+      const popoverBounding = {width:300,height:270}
+      const {innerWidth,innerHeight} = window
+      let currentPosition = {left:clientX,top:clientY}
+      if(clientX+popoverBounding.width>=innerWidth){
+        currentPosition.left = innerWidth - popoverBounding.width
+      }
+      if(clientY+popoverBounding.height>=innerHeight){
+        //currentPosition.top = innerHeight - popoverBounding.height
+        currentPosition.bottom = 0
+      }
+
+      //获取popover展示的内容
+      this.getPopoverContent()
+
+      //popover的宽高
+      //限制popover的位置不会超出屏幕,否则点不到关闭按钮
+      $('.calendar-eventinfo-popover')[0].style.left = currentPosition.left + 'px';
+      //$('.calendar-eventinfo-popover')[0].style.top = currentPosition.top + 'px';
+      if(currentPosition.bottom === 0){
+        $('.calendar-eventinfo-popover')[0].style.bottom=40+'px';
+        $('.calendar-eventinfo-popover')[0].style.top='auto';
+      }else{
+        $('.calendar-eventinfo-popover')[0].style.top = currentPosition.top + 'px'
+        $('.calendar-eventinfo-popover')[0].style.bottom='auto';
+      }
+      this.$refs.popover.doShow();
+      // 禁止日历选择
+      this.calendarApi.setOption("selectable", false);
+    },
+    getPopoverContent(){
+        const {StartDate,StartTime,StartWeek,EndDate,EndTime,EndWeek} = this.selectEventInfo
+        //前两项默认是开始时间和结束时间
+        const popContent = [{
+            key:'StartDate',
+            label:'开始时间:',
+            content:`${StartDate} ${StartTime} (${StartWeek})`,
+        },{
+            key:'EndDate',
+            label:'结束时间:',
+            content:`${EndDate} ${EndTime} (${EndWeek})`,
+        }]
+        //根据ActivityType判断是什么类型,根据RoadshowType展示对应信息
+        const {ActivityType,RoadshowType} = this.selectEventInfo
+        const {Province,City} = this.selectEventInfo
+        //ActivityType为路演时,需要显示的内容
+        const {RoadshowPlatform,CompanyName,CompanyStatus} = this.selectEventInfo
+        const RoadshowContent = [{
+            key:'RoadshowPlatform',
+            label:'路演平台:',
+            content:`${RoadshowPlatform}`,
+            RoadshowType:'线上'
+        },{
+            key:'RoadshowCity',
+            label:'路演城市:',
+            content:`${Province}${City}`,
+            RoadshowType:'线下'
+        },{
+            key:'CompanyName',
+            label:'客户名称:',
+            content:`${CompanyName}${CompanyStatus ? '('+ CompanyStatus + ')' : ''}`,
+            RoadshowType:RoadshowType,
+        }].filter(item=>item.RoadshowType===RoadshowType)
+        //ActivityType为公开会议时,需要显示的内容
+        const {Theme,CooperationName} = this.selectEventInfo
+        const MeetingContent = [
+            {
+                key:'RoadshowPlatform',
+                label:'会议平台:',
+                content:`${RoadshowPlatform}`,
+                RoadshowType:'线上'
+            },{
+                key:'RoadshowCity',
+                label:'会议城市:',
+                content:`${Province}${City}`,
+                RoadshowType:'线下'
+            },{
+                key:'Theme',
+                label:'会议主题:',
+                content:`${Theme}`,
+                RoadshowType:RoadshowType
+            },{
+                key:'CooperationName',
+                label:'合作方名称:',
+                content:`${CooperationName}`,
+                RoadshowType:RoadshowType
+            }].filter(item=>item.RoadshowType===RoadshowType)
+        ////ActivityType不为空时,需要显示的内容
+        const {ResearcherName,SysUserRealName} = this.selectEventInfo
+        const SuppleContent=[{
+            key:'ResearcherName',
+            label:'研究员:',
+            content:`${ResearcherName}`,
+        },{
+            key:'SysUserRealName',
+            label:'发起人:',
+            content:`${SysUserRealName}`,
+        }]
+        //ActivityType为空时,需要显示的内容
+        const {MatterContent,EditReason,ModifyTime} = this.selectEventInfo
+        const MattersContent=[{
+            key:'MatterContent',
+            label:'事项内容:',
+            content:`${MatterContent}`,
+        },{
+            key:'EditReason',
+            label:'最近修改记录:',
+            content:`${EditReason}`,
+            otherContent:`${this.$moment(ModifyTime).format('YYYY-MM-DD HH:mm:ss')}`
+        }].filter(item=>{return item.content.length})
+
+        const contentMap = {
+            '路演':RoadshowContent,
+            '公开会议':MeetingContent,
+        }
+        if(ActivityType){
+            this.popContentArr = [...popContent,...contentMap[ActivityType]||[],...SuppleContent]
+        }else{
+            this.popContentArr = [...popContent,...MattersContent]
+        }
+
+        //console.log('check',this.popContentArr)
+
+    },
+    // 点击日程信息弹窗关闭按钮
+    closeIconClick() {
+      this.calendarApi.setOption("selectable", true);
+      // this.$refs["popover-" + id].doClose();
+      this.$refs.popover.doClose();
+    },
+
+    // 点击日程信息弹窗修改按钮
+    editIconClick(id) {
+      this.closeIconClick(id);
+      this.$emit('editCallback',this.selectEventInfo);
+    },
+
+    // 点击日程信息弹窗删除按钮
+    delIconClick(id) {
+
+      const { RsCalendarId,RsCalendarResearcherId,RsMattersId, ActivityType  } = this.selectEventInfo;
+
+      !['公开会议','路演'].includes(ActivityType) && this.$confirm("删除该活动后,将从日历中移除,确定继续吗?", "删除", {
+        type: "warning"
+      }).then( async() => {
+        //删除事项
+        let { Ret } = RsMattersId ? await roadshowInterence.delMatters({ RsMattersId }) 
+        //删除活动
+        : await roadshowInterence.deleteRoadshow({ RsCalendarId, RsCalendarResearcherId });
+
+        if(Ret !== 200) return;
+        
+        this.$message.success('删除成功');
+        this.$emit('weekChange');
+
+        })
+        .catch(() => {});
+
+        ['公开会议','路演'].includes(ActivityType) && this.$emit('delAuthHandle',this.selectEventInfo);
+
+        this.closeIconClick(id);
+      
+    },
+
+    // 处理赋值函数
+    getReservationList() {
+      const arr = this.eventList.map((item) => {
+        let title = (this.isSeller && item.ActivityType==='路演')
+        ? `${item.CompanyName}${item.CompanyStatus ? '('+ item.CompanyStatus + ')' : ''}` 
+        : (this.isSeller && item.ActivityType==='公开会议')
+        ? item.Theme 
+        : this.setDynamicTitle(item);
+
+        let start = item.StartDate ? `${item.StartDate}T${item.StartTime}` : item.MinTime.replace(/ /,'T');
+        let end = item.EndDate ? `${item.EndDate}T${item.EndTime}` : item.MaxTime.replace(/ /,'T');
+
+         //合并公开会议显示
+        item.CalendarList && this.filterSameIdArr(item);
+        let titleArr = item.filterArr ? item.filterArr.map(sub_item => ({
+          title: sub_item.title
+        })) : [];
+
+        return {
+          start,
+          end,
+          event_id: item.RsCalendarId || item.RsMattersId,
+          id: item.id,
+          title,
+          titleArr
+        };
+      });
+      this.calendarOptions.events = arr
+      console.log(this.eventList)
+    },
+
+    /* 拆分出活动id相同的数组和不同的数组 */
+    filterSameIdArr(obj){
+      let public_arr = [], id_arr = [];
+
+      obj.CalendarList && obj.CalendarList.forEach(item => {
+        if(!id_arr.includes(item.RsCalendarId)) {
+          public_arr.push({
+            id: item.RsCalendarId,
+            ActivityType: item.ActivityType,
+            Theme: item.Theme,
+            CompanyName: item.CompanyName,
+            CompanyStatus: item.CompanyStatus,
+            CooperationName: item.CooperationName,
+            SysUserRealName: item.SysUserRealName,
+            RoadshowType: item.RoadshowType,
+            RoadshowPlatform: item.RoadshowPlatform,
+            Province: item.Province,
+            City: item.City,
+            children: [{
+              name: item.ResearcherName,
+              start: `${item.StartDate} ${item.StartTime}`,
+              end: `${item.EndDate} ${item.EndTime}`,
+            }]
+          })
+          id_arr.push(item.RsCalendarId)
+        }else {
+          public_arr.forEach(_item => {
+            if(_item.id === item.RsCalendarId) {
+              _item.children.push({
+              name: item.ResearcherName,
+              start: `${item.StartDate} ${item.StartTime}`,
+              end: `${item.EndDate} ${item.EndTime}`,
+            })
+            }
+          })
+        }
+      })
+
+      //拼接活动id标题 获取活动公用最早时间和最晚时间
+      public_arr.forEach(item => {
+        let str = '';
+        item.children.forEach(sub_item => {
+          str += sub_item.name+','
+        })
+        str = str.substr(0, str.length-1)
+        item.title = `${item.Theme}(${str})`;
+        item.total_rs_name = str;
+
+        let startDateArr = item.children.map(sub_item => this.$moment(sub_item.start).valueOf());
+        let endDateArr = item.children.map(sub_item => this.$moment(sub_item.end).valueOf());
+
+        let minTime = this.$moment(Math.min(...startDateArr)).format('YYYY-MM-DD HH:mm:ss');
+        let maxTime = this.$moment(Math.max(...endDateArr)).format('YYYY-MM-DD HH:mm:ss');
+        item.minTime = minTime;
+        item.maxTime = maxTime;
+        
+      })
+
+      this.$set(obj,'filterArr',public_arr)
+      
+      // console.log(public_arr)
+    
+    },
+
+    // 拼接标题 type 内部会议 公开会议 路演 报告电话会 事项
+    setDynamicTitle({ActivityType,RsMattersId,MatterContent,RoadshowType,RoadshowPlatform,Province,City,ActivityCategory,Source,Title}) {
+
+      //第三方添加的日历活动
+      if(Source === 1) return Title;
+
+      switch(ActivityType || RsMattersId) {
+        case '内部会议': return ActivityType;
+        case '公开会议': return `${RoadshowType}${ActivityType}(${RoadshowType==='线上' ? RoadshowPlatform : Province+City})`;
+        case '路演': return `${RoadshowType}${ActivityType}(${RoadshowType==='线上' ? RoadshowPlatform : Province+City})`;
+        case '报告电话会': return `${ActivityCategory}电话会`;
+        case RsMattersId: return MatterContent;
+      }
+    },
+
+    // 格式化日期YYYY-MM-DD
+    formateDate(date){
+      return this.$moment(date).format('YYYY-MM-DD')
+    },
+
+  },
+  mounted() {
+    this.getCalendarApi();
+    // this.today()
+    this.toogelDate('today');
+  },
+  watch: {
+    eventList() {
+      this.getReservationList();
+    },
+  },
+};
+</script>
+
+<style lang="scss">
+.calendar-container {
+  .calendar-header {
+    display: flex;
+    .left,
+    .right {
+      flex: 1;
+    }
+    .right {
+      display: flex;
+      justify-content: end;
+    }
+    .center .el-button{
+      width: 140px;
+    }
+  }
+  .calendar-body {
+    height: 100%;
+    margin-top: 10px;
+    .fc-scrollgrid {
+      colgroup col {
+        width: 150px !important;
+      }
+      .fc-timegrid-slots table {
+        line-height: 40px;
+        .fc-timegrid-slot-label-frame {
+          text-align: center;
+        }
+      }
+      .fc-timegrid-cols table {
+        .fc-day-today {
+          background-color: #fee1cc66;
+        }
+      }
+      .fc-col-header {
+        line-height: 40px;
+        .fc-col-header-cell {
+          background-color: #ecf5ff;
+        }
+        .fc-day-today {
+          background-color: #fee1cc66;
+        }
+      }
+    }
+    .fc-timegrid-col-events {
+      cursor: pointer;
+      margin: 0;
+      .fc-event-main .popper-content {
+        height: 100%;
+        overflow: hidden;
+        position: relative;
+        left:-3px;
+        &::before{
+            content: '';
+            position: absolute;
+            top:0;left:0;
+            height: 100%;
+            width: 4px;
+            background-color: #409EFF;
+        }
+        p {
+          overflow: hidden;
+          margin: 2px 10px;
+          /* text-overflow: ellipsis; */
+          display: -webkit-box;
+          -webkit-line-clamp: 2;
+          line-clamp: 2;
+          -webkit-box-orient: vertical;
+        }
+      }
+    }
+  }
+}
+</style>
+
+<style lang="scss" scoped>
+.calendar-eventinfo-popover {
+  position: relative !important;
+  padding: 10px;
+  border-radius: 5px;
+  border: 1px solid #ebeef5;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  .popover-body {
+    padding: 0 14px;
+    max-height: 270px;
+    overflow-y: auto;
+    margin-top: 25px;
+/*     p {
+      margin-top: 5px;
+    } */
+    /* .close-icon {
+        position: absolute;
+        top: 5px;
+        right: 5px;
+        font-size: 20px;
+    } */
+    .content-item{
+        display: flex;
+        margin-top: 5px;
+        .content{
+            .other-text{
+                color:#999;
+            }
+        }
+    }
+  }
+  .pop-header{
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        background-color: #5882EF;
+        color: #fff;
+        margin:-14px 0;
+        padding:12px;
+        .close-icon{
+            cursor: pointer;
+            font-size: 16px;
+        }
+    }
+  .handle-btn-icon {
+    display: flex;
+    justify-content: end;
+    i {
+      margin: 5px;
+      font-size: 20px;
+      cursor: pointer;
+      color: #3385ff;
+    }
+  }
+  .popover-event-item {
+    margin-bottom: 10px;
+    padding-bottom: 10px;
+    border-bottom: 1px dashed #666;
+    &:last-child {
+      border: none;
+    }
+  }
+}
+</style>
+
+<style lang="scss">
+.calendar-eventinfo-popover {
+  position: fixed !important;
+  top: 0;
+  z-index: 99;
+  padding: 14px 0;
+}
+</style>

+ 5 - 0
src/main.js

@@ -26,6 +26,11 @@ import 'froala-editor/css/froala_style.min.css';
 
 import icons from '@/utils/icon.js'
 
+import moment from 'moment';
+import 'moment/dist/locale/zh-cn'; // 引入中文语言包
+
+// 设置全局默认语言环境为简体中文
+moment.locale('zh-cn');
 
 import VueFroala from 'vue-froala-wysiwyg';
 

+ 67 - 0
src/router/modules/roadShowRoutes.js

@@ -0,0 +1,67 @@
+//路演管理路由模块
+import Home from '@/layouts/index.vue'
+
+export default [
+    {
+		path: '/',
+		component: Home,
+		name: 'roadshowManage',
+		meta:{
+			title:'路演管理'
+		},
+		icon_path: require('@/assets/img/home/data_ic.png'),
+		children: [
+			{
+				path: "researcherCalendar",
+				name: "researcherCalendar",
+				component: () => import('@/views/roadshow_manage/researcherCalendar.vue'),
+				meta:{
+					title:'研究员日历'
+				}
+			},
+			// {
+			// 	path: "myCalendar",
+			// 	name: "我的日历",
+			// 	component: () => import('@/views/roadshow_manage/myCalendar.vue')
+			// },
+			// {
+			// 	path: "meetingCalendar",
+			// 	name: "公开会议总览",
+			// 	component: () => import('@/views/roadshow_manage/meetingCalendar.vue')
+			// },
+			// {
+			// 	path: "statisticResearcher",
+			// 	name: "研究员路演统计",
+			// 	component: () => import('@/views/roadshow_manage/statistics/researcher.vue')
+			// },
+			// {
+			// 	path: "statisticSeller",
+			// 	name: "销售路演统计",
+			// 	component: () => import('@/views/roadshow_manage/statistics/seller.vue')
+			// },
+			// {
+			// 	path: "specialSeller",
+			// 	name: "专项路演统计",
+			// 	component: () => import('@/views/roadshow_manage/statistics/specialSeller.vue')
+			// },
+			// {
+			// 	path: "roadshowFeedback",
+			// 	name: "路演反馈",
+			// 	component: () => import('@/views/roadshow_manage/feedbackList.vue')
+			// },
+			{
+				path: "researcherBusinessTrip",
+				name: "researcherBusinessTrip",
+				component: () => import('@/views/roadshow_manage/researcherBusinessTrip.vue'),
+				meta:{
+					title:'研究员出差表'
+				}
+			},
+			// {
+			// 	path: "sellerCalendar",
+			// 	name: "销售员日历",
+			// 	component: () => import('@/views/roadshow_manage/sellerCalendar.vue')
+			// },
+		]
+	},
+]

+ 8 - 5
src/router/modules/sellerRoutes.js

@@ -12,11 +12,14 @@ export default [
 			title: "销售管理",
 		},
 		children: [
-			// {
-			// 	path: "sellerStatisticSeller",
-			// 	name: "销售路演统计",
-			// 	component: () => import('@/views/roadshow_manage/statistics/seller.vue')
-			// },
+			{
+				path: "sellerStatisticSeller",
+				name: "sellerStatisticSeller",
+				component: () => import('@/views/roadshow_manage/statistics/seller.vue'),
+				meta: {
+					title: "销售路演统计",
+				},
+			},
 			// {
 			// 	path: "sellerSpecialSeller",
 			// 	name: "专项路演统计",

+ 2 - 5
src/views/contract_manage/raiAllocationPage.vue

@@ -9,7 +9,7 @@ import AllocationNumber from "./components/allocationNumber.vue";
 import AllocationDetailPage from "./components/allocationDetail.vue";
 import mPage from "@/components/mPage.vue";
 import RaiHistoryContract from "./components/raiHistoryContract.vue";
-
+import DatePicker from 'vue-datepicker-next';
 
 /* 筛选条件 */
 const filterObj=reactive({
@@ -211,10 +211,7 @@ getTableData();
       <button :class="['button-sty', { act: filterObj.month === item.label }]" v-for="item in monthLabel" @click="toggleMonth(item.label)" :key="item.label">
         {{ item.label }}
       </button>
-      <el-date-picker v-model="filterObj.date" type="daterange" format="YYYY-MM-DD" value-format="YYYY-MM-DD" size="large"
-      @change="dateChange" start-placeholder="开始" end-placeholder="结束" style="max-width: 280px;"> </el-date-picker>
-      <!-- vue-datepicker-next -->
-      <!-- <date-picker v-model="filterObj.date" type="date" range value-type="format" placeholder="自定义时间段" clearable :editable="false" @change="dateChange" /> -->
+      <date-picker v-model:value="filterObj.date" type="date" range value-type="format" placeholder="自定义时间段" clearable :editable="false" @change="dateChange" />
       <el-input placeholder="请输入客户名称" v-model.trim="searchVal" style="width: 400px; margin-left: auto" @input="handleSearch" clearable
       size="large" :prefix-icon="Search" />
     </div>

+ 2 - 4
src/views/contract_manage/researcherStatistics.vue

@@ -5,6 +5,7 @@ import { Search } from '@element-plus/icons-vue'
 import { contractInterface } from "@/api/api.js";
 import RelatedContract from "./components/relatedContract.vue";
 import moment from "moment";
+import DatePicker from 'vue-datepicker-next';
 
 /* 筛选条件 */
 const filterObj=reactive({
@@ -112,10 +113,7 @@ getTableData();
       <button :class="['button-sty', { act: filterObj.month === item.label }]" v-for="item in monthLabel" @click="toggleMonth(item.label)" :key="item.label">
         {{ item.label }}
       </button>
-      <el-date-picker v-model="filterObj.date" type="daterange" format="YYYY-MM-DD" value-format="YYYY-MM-DD" size="large"
-      @change="dateChange" start-placeholder="开始" end-placeholder="结束" style="max-width: 280px;"> </el-date-picker>
-      <!-- vue-datepicker-next -->
-      <!-- <date-picker v-model="filterObj.date" type="date" range value-type="format" placeholder="自定义时间段" clearable :editable="false" :disabled-date="disabledBeforeToday" @change="dateChange" /> -->
+      <date-picker v-model:value="filterObj.date" type="date" range value-type="format" placeholder="自定义时间段" clearable :editable="false" :disabled-date="disabledBeforeToday" @change="dateChange" />
       <el-input placeholder="合同编号/客户名称/社会信用码" v-model="searchVal" style="width: 400px; margin-left: auto" @input="handleSearch" 
       clearable size="large" :prefix-icon="Search" />
     </div>

+ 36 - 38
src/views/custom_manage/custom/hooks/customlistHook.js

@@ -4,43 +4,43 @@ import {ElMessage,ElMessageBox} from "element-plus"
 
 import { customInterence } from '@/api/api.js'
 
-const act_trialTag=ref(0)//默认的标签状态
-const trialTagArr=ref([])
-const trialTags=reactive([ 
-	{
-		label: '推进',
-		value: 2
-	},
-	{
-		label: '跟踪',
-		value: 3
-	},
-	{
-		label: '预备',
-		value: 4
-	},
-	{
-		label: '未分类',
-		value: 1
-	},
-])// 试用的标签状态
-const isTotalDayDialogShow=ref(false)//控制累计试用天数弹窗
-const customInfo=ref({})
-const isShareRecodeDialogShow=ref(false)//控制服务记录弹窗
-const allowEdit=ref(true)//是否允许编辑服务记录
-const isCloseCustomDialogShow=ref(false)
-const closeReason=ref('')
-const fromArea=ref('')
-const areaArr=reactive([{name:'国内'},{name:'海外'}])
-
-//打开服务记录弹窗
-const handleShowShareRecode=(data,type='')=>{
-	if(data.ServiceTimes===0&&type==='list') return
-	allowEdit.value = type==='list'?false:true
-	isShareRecodeDialogShow.value = true
-	customInfo.value = data
-}
+export function customListHook(){
+	const act_trialTag=ref(0)//默认的标签状态
+	const trialTagArr=ref([])
+	const trialTags=reactive([ 
+		{
+			label: '推进',
+			value: 2
+		},
+		{
+			label: '跟踪',
+			value: 3
+		},
+		{
+			label: '预备',
+			value: 4
+		},
+		{
+			label: '未分类',
+			value: 1
+		},
+	])// 试用的标签状态
+	const isTotalDayDialogShow=ref(false)//控制累计试用天数弹窗
+	const customInfo=ref({})
+	const isShareRecodeDialogShow=ref(false)//控制服务记录弹窗
+	const allowEdit=ref(true)//是否允许编辑服务记录
+	const isCloseCustomDialogShow=ref(false)
+	const closeReason=ref('')
+	const fromArea=ref('')
+	const areaArr=reactive([{name:'国内'},{name:'海外'}])
 
+	//打开服务记录弹窗
+	const handleShowShareRecode=(data,type='')=>{
+		if(data.ServiceTimes===0&&type==='list') return
+		allowEdit.value = type==='list'?false:true
+		isShareRecodeDialogShow.value = true
+		customInfo.value = data
+	}
 /* 切换标签状态 */
 // const changeTagStatus=({ TryStage })=>{
 // 	act_trialTag.value = TryStage;
@@ -118,8 +118,6 @@ const handleShowShareRecode=(data,type='')=>{
 // 	this.page_no = 1;
 // 	this.getTableData()
 // },
-
-export function customListHook(){
 	return {
 		isShareRecodeDialogShow,
 		customInfo,

+ 8 - 7
src/views/dataReport_manage/statistic/abnormalRenewal.vue

@@ -5,13 +5,10 @@ import DatePicker from 'vue-datepicker-next';
 import { InfoFilled } from '@element-plus/icons-vue'
 
 import { dataMainInterface } from '@/api/api.js';
-import {createdHook,dataHook,computedHook,functionHook} from './hook'
+import {dataReportStatisticHook} from './hook'
 import abnormalRenewalChart from '../components/abnormalRenewalChart.vue';
 
 const $router = useRouter()
-const {dataLoading,default_tab,select_date,tableTheadColumns,staticTabs,actFilterDay,filterDaysOptions}=dataHook()
-const {activeTab}=computedHook()
-const {dateChange,changeTabHandle}=functionHook()
 
 const sellerList=ref([])
 const summaryList=ref([])
@@ -73,8 +70,12 @@ const getColumnTitle=(index)=>{
     return title
 }
 
+const HOOK = dataReportStatisticHook({getTableData})
+const {dataLoading,default_tab,select_date,tableTheadColumns,staticTabs,actFilterDay,filterDaysOptions}=HOOK.datas
+const {activeTab}=HOOK.computeds
+const {dateChange,changeTabHandle}=HOOK.functions
+//start
 getTableData()
-createdHook()
 </script>
 
 <template>
@@ -91,7 +92,7 @@ createdHook()
         </div>
 
         <div class="frequency-cont" style="position: relative;">
-            <el-tabs v-model="default_tab" @tab-click="item =>{changeTabHandle(item.paneName,getTableData)}" style="margin-right:20px">
+            <el-tabs v-model="default_tab" @tab-click="item =>{changeTabHandle(item.paneName)}" style="margin-right:20px">
                 <!--  -->
                 <el-tab-pane 
                     v-for="tab in staticTabs" 
@@ -106,7 +107,7 @@ createdHook()
                 range
                 value-type="format"
                 :clearable="false"
-                @change="dateChange(getTableData)"
+                @change="dateChange"
                 placeholder="请选择统计时间"
             />
             <span style="color:#A3A3A3;display:inline-block;margin-left:20px">续约异常:{{filterDaysOptions.find(_=>_.value===actFilterDay).msg}}</span>

+ 11 - 10
src/views/dataReport_manage/statistic/contractCustom.vue

@@ -20,12 +20,9 @@ import DatePicker from 'vue-datepicker-next';
 import { InfoFilled,ArrowDown,ArrowUp } from '@element-plus/icons-vue'
 
 import { dataMainInterface } from '@/api/api.js';
-import {createdHook,dataHook,computedHook,functionHook} from './hook'
+import {dataReportStatisticHook} from './hook'
 
 const $router = useRouter()
-const {dataLoading,default_tab,select_date,totalGroupArr,datalist,tableTheadColumns,staticTabs}=dataHook()
-const {Role,activeTab}=computedHook()
-const {filterTableData,switchTab,dateChange,changeTabHandle}=functionHook()
 
 const tipMap=new Map([
 	['未续约','之前是正式客户,现在是冻结或流失状态的客户'],
@@ -178,24 +175,28 @@ const getColumnTitle=(index)=>{
 	return title
 }
 
+const HOOK = dataReportStatisticHook({getTableData})
+const {dataLoading,default_tab,select_date,totalGroupArr,datalist,tableTheadColumns,staticTabs}=HOOK.datas
+const {Role,activeTab}=HOOK.computeds
+const {filterTableData,switchTab,dateChange,changeTabHandle}=HOOK.functions
+
 // start
 getTableData()
-createdHook()
 </script>
 
 <template>
   <div class="statistic-container" ref="reference">
     <div class="custom-tab-box" v-if="Role==='admin'">
-      <div class="custom-tab" :class="activeTab.tabName =='FICC' && 'activeTab'" @click="switchTab('FICC',getTableData)">
+      <div class="custom-tab" :class="activeTab.tabName =='FICC' && 'activeTab'" @click="switchTab('FICC')">
         FICC
       </div>
-      <div class="custom-tab" :class="activeTab.tabName =='QY' && 'activeTab'" @click="switchTab('QY',getTableData)">
+      <div class="custom-tab" :class="activeTab.tabName =='QY' && 'activeTab'" @click="switchTab('QY')">
         权益
       </div>
     </div>
     <div class="frequency-cont">
 			<ul class="frequency-ul">
-				<li v-for="tab in staticTabs" :key="tab" :class="{act: tab=== default_tab}" @click="changeTabHandle(tab,getTableData)">{{ tab }}</li>
+				<li v-for="tab in staticTabs" :key="tab" :class="{act: tab=== default_tab}" @click="changeTabHandle(tab)">{{ tab }}</li>
 			</ul>
 			<date-picker
 			v-model:value="select_date"
@@ -203,7 +204,7 @@ createdHook()
 			range
 			value-type="format"
 			:clearable="false"
-			@change="dateChange(getTableData)"
+			@change="dateChange"
 			placeholder="请选择统计时间"/>
     </div>
     <div class="table-cont" v-show="dataLoading">
@@ -279,7 +280,7 @@ createdHook()
 			</table>	 -->
 			<div class="table-body-wrapper">
 				<table>
-					<thead>
+					<thead style="z-index: 1;">
 						<tr>
 							<td rowspan="2" class="thead-rs">组别</td>
 							<td rowspan="2" class="thead-rs">销售</td>

+ 208 - 210
src/views/dataReport_manage/statistic/hook.js

@@ -16,21 +16,6 @@ const getWeekOrMonthDate=(weeknum,type='week')=>{
 	}
 }
 
-const dataLoading=ref(false)
-const default_tab=ref('周度统计表')
-const select_date=ref('')
-const totalGroupArr=ref([])//总合计列表
-const datalist=ref([])//表格数据
-
-const tableTheadColumns=ref([
-	`本周(${getWeekOrMonthDate(0)})`,
-	`上一周(${getWeekOrMonthDate(1)})`,
-	`上两周(${getWeekOrMonthDate(2)})`,
-	`上三周(${getWeekOrMonthDate(3)})`,
-	`上四周(${getWeekOrMonthDate(4)})`,
-	`上五周(${getWeekOrMonthDate(5)})`])//动态表头配置
-const staticTabs=ref([ '周度统计表','月度统计表','近1个月','近3个月','近6个月' ])
-const actFilterDay=ref(1)
 const filterDaysOptions=[
 	{ label:'+60D',value: 1,msg:'合同到期后两个月内未签约' },
 	{ label:'+30D',value: 4,msg:'合同到期后一个月内未签约' },
@@ -45,224 +30,237 @@ const Role=computed(()=>{
 	return localStorage.getItem('Role');
 })
 
-const activeTab=computed(()=>{
-	let tabName = sessionStorage.getItem('renewalTab')?sessionStorage.getItem('renewalTab'):'FICC'
-	return {
-		tabName, // FICC QY
-		tabSummationName:tabName == 'FICC'?'ficc总合计':'权益总合计',
-		productionId:tabName == 'FICC'?1:2, // 1 FICC 2 权益
-	}
-})
+export const dataReportStatisticHook=(params)=>{
+	const {getTableData} = params||{}
 
-/* 处理数据结构 便于页面渲染 */
-const filterTableData=(data,other_param={},type='new')=>{
-	let list=[]
-	if(type=='new'){
-		list = data.map(item => ([
-			{
-				key: '试用',
-				value: item.TryOutNum,
-				ids: item.TryOutIds,
-				...other_param
-			},
-			{
-				key: '活跃',
-				value: activeTab.value.tabName=='FICC'?[item.AllActiveNum,item.NoIncrementalActiveNum]:item.AllActiveNum,
-				ids: activeTab.value.tabName=='FICC'?[item.AllActiveIds,item.NoIncrementalActiveIds]:item.AllActiveIds,
-				...other_param
-			},
-			{
-				key: '正式',
-				value: item.FormalNum,
-				ids: item.FormalIds,
-				...other_param
-			}
-		]))
-	}else if(type == 'renew'){
-		list=data.map(item=>{
-			if(activeTab.value.tabName !== 'QY'){
-				return [
-					{
-						key: '到期',
-						value: item.ExpireNum,
-						ids: item.ExpireIds,
-						...other_param
-					},
-					{
-						key: '续约',
-						value:item.RenewNum,
-						ids: item.RenewIds,
-						...other_param
-					},
-					{
-						key: '续约异常',
-						value:item.UnusualRenewNum,
-						ids: item.UnusualRenewIds,
-						...other_param
-					}
-				]
-			}else{
-				return [
-					{
-						key: '到期',
-						value: item.ExpireNum,
-						ids: item.ExpireIds,
-						...other_param
-					},
-					{
-						key: '续约',
-						value:item.RenewNum,
-						ids: item.RenewIds,
-						...other_param
-					},
-				]
-			}
-		})
-		// list = data.map(item => ([
-		// 	{
-		// 		key: '到期',
-		// 		value: item.ExpireNum,
-		// 		ids: item.ExpireIds,
-		// 		...other_param
-		// 	},
-		// 	{
-		// 		key: '续约',
-		// 		value:item.RenewNum,
-		// 		ids: item.RenewIds,
-		// 		...other_param
-		// 	}
-		// ]))
-	}else if(type==='ficcproduct'){
-		list = data.map(item => ([
-			{
-				key: '正式',
-				value: item.FormalNum,
-				ids: item.FormalIds,
-				...other_param
-			},
-			{
-				key: '试用',
-				value: item.TryOutNum,
-				ids: item.TryOutIds,
-				...other_param
-			},
-			{
-				key: '合计',
-				value: item.AllNum,
-				ids: item.AllIds,
-				...other_param
-			},
-		]))
-		
+	const dataLoading=ref(false)
+	const default_tab=ref('周度统计表')
+	const select_date=ref('')
+	const totalGroupArr=ref([])//总合计列表
+	const datalist=ref([])//表格数据
 
-	}
+	const tableTheadColumns=ref([
+		`本周(${getWeekOrMonthDate(0)})`,
+		`上一周(${getWeekOrMonthDate(1)})`,
+		`上两周(${getWeekOrMonthDate(2)})`,
+		`上三周(${getWeekOrMonthDate(3)})`,
+		`上四周(${getWeekOrMonthDate(4)})`,
+		`上五周(${getWeekOrMonthDate(5)})`])//动态表头配置
+	const staticTabs=ref([ '周度统计表','月度统计表','近1个月','近3个月','近6个月' ])
+	const actFilterDay=ref(1)
 
-	return list.flat(Infinity);
-}
+	const activeTab=computed(()=>{
+		let tabName = sessionStorage.getItem('renewalTab')?sessionStorage.getItem('renewalTab'):'FICC'
+		return {
+			tabName, // FICC QY
+			tabSummationName:tabName == 'FICC'?'ficc总合计':'权益总合计',
+			productionId:tabName == 'FICC'?1:2, // 1 FICC 2 权益
+		}
+	})
 
-// 切换 FICC 和 权益 TAB
-const switchTab=(tabName,callback)=>{
-	activeTab.value.tabName = tabName
-	activeTab.value.tabSummationName = tabName == 'FICC'?"ficc总合计":"权益总合计"
-	activeTab.value.productionId = tabName == 'FICC'?1:2
-	callback()
-}
+			// 切换 FICC 和 权益 TAB
+	const switchTab=(tabName)=>{
+		activeTab.value.tabName = tabName
+		activeTab.value.tabSummationName = tabName == 'FICC'?"ficc总合计":"权益总合计"
+		activeTab.value.productionId = tabName == 'FICC'?1:2
+		getTableData && getTableData()
+	}
+	/* 处理数据结构 便于页面渲染 */
+	const filterTableData=(data,other_param={},type='new')=>{
+		let list=[]
+		if(type=='new'){
+			list = data.map(item => ([
+				{
+					key: '试用',
+					value: item.TryOutNum,
+					ids: item.TryOutIds,
+					...other_param
+				},
+				{
+					key: '活跃',
+					value: activeTab.value.tabName=='FICC'?[item.AllActiveNum,item.NoIncrementalActiveNum]:item.AllActiveNum,
+					ids: activeTab.value.tabName=='FICC'?[item.AllActiveIds,item.NoIncrementalActiveIds]:item.AllActiveIds,
+					...other_param
+				},
+				{
+					key: '正式',
+					value: item.FormalNum,
+					ids: item.FormalIds,
+					...other_param
+				}
+			]))
+		}else if(type == 'renew'){
+			list=data.map(item=>{
+				if(activeTab.value.tabName !== 'QY'){
+					return [
+						{
+							key: '到期',
+							value: item.ExpireNum,
+							ids: item.ExpireIds,
+							...other_param
+						},
+						{
+							key: '续约',
+							value:item.RenewNum,
+							ids: item.RenewIds,
+							...other_param
+						},
+						{
+							key: '续约异常',
+							value:item.UnusualRenewNum,
+							ids: item.UnusualRenewIds,
+							...other_param
+						}
+					]
+				}else{
+					return [
+						{
+							key: '到期',
+							value: item.ExpireNum,
+							ids: item.ExpireIds,
+							...other_param
+						},
+						{
+							key: '续约',
+							value:item.RenewNum,
+							ids: item.RenewIds,
+							...other_param
+						},
+					]
+				}
+			})
+			// list = data.map(item => ([
+			// 	{
+			// 		key: '到期',
+			// 		value: item.ExpireNum,
+			// 		ids: item.ExpireIds,
+			// 		...other_param
+			// 	},
+			// 	{
+			// 		key: '续约',
+			// 		value:item.RenewNum,
+			// 		ids: item.RenewIds,
+			// 		...other_param
+			// 	}
+			// ]))
+		}else if(type==='ficcproduct'){
+			list = data.map(item => ([
+				{
+					key: '正式',
+					value: item.FormalNum,
+					ids: item.FormalIds,
+					...other_param
+				},
+				{
+					key: '试用',
+					value: item.TryOutNum,
+					ids: item.TryOutIds,
+					...other_param
+				},
+				{
+					key: '合计',
+					value: item.AllNum,
+					ids: item.AllIds,
+					...other_param
+				},
+			]))
+		}
 
-/* 选择日期改变清空高亮按钮 */
-const dateChange=(callback)=>{
-	default_tab.value = '';
-	tableTheadColumns.value = ['试用','活跃','正式'];
+		return list.flat(Infinity);
+	}
 
-	callback();
-}
+	/* 选择日期改变清空高亮按钮 */
+	const dateChange=()=>{
+		default_tab.value = '';
+		tableTheadColumns.value = ['试用','活跃','正式'];
 
-/* 切换顶部tab */
-const changeTabHandle=(tab,callback)=>{
-	$('.table-body-wrapper')[0].scrollTop = 0;
-	default_tab.value = tab;
+		getTableData && getTableData()
+	}
 
-	switch(tab) {
-		case '周度统计表':
-			tableTheadColumns.value = [
-				`本周(${getWeekOrMonthDate(0)})`,
-				`上一周(${getWeekOrMonthDate(1)})`,
-				`上两周(${getWeekOrMonthDate(2)})`,
-				`上三周(${getWeekOrMonthDate(3)})`,
-				`上四周(${getWeekOrMonthDate(4)})`,
-				`上五周(${getWeekOrMonthDate(5)})`];
-			break;
-		case '月度统计表':
-			tableTheadColumns.value = [
-				getWeekOrMonthDate(0,'month'),
-				getWeekOrMonthDate(1,'month'),
-				getWeekOrMonthDate(2,'month'),
-				getWeekOrMonthDate(3,'month'),
-				getWeekOrMonthDate(4,'month'),
-				getWeekOrMonthDate(5,'month')];
+	/* 切换顶部tab */
+	const changeTabHandle=(tab)=>{
+		$('.table-body-wrapper')[0].scrollTop = 0;
+		default_tab.value = tab;
 
-			break;
-		default:
-			tableTheadColumns.value = ['试用','活跃','正式'];
+		switch(tab) {
+			case '周度统计表':
+				tableTheadColumns.value = [
+					`本周(${getWeekOrMonthDate(0)})`,
+					`上一周(${getWeekOrMonthDate(1)})`,
+					`上两周(${getWeekOrMonthDate(2)})`,
+					`上三周(${getWeekOrMonthDate(3)})`,
+					`上四周(${getWeekOrMonthDate(4)})`,
+					`上五周(${getWeekOrMonthDate(5)})`];
+				break;
+			case '月度统计表':
+				tableTheadColumns.value = [
+					getWeekOrMonthDate(0,'month'),
+					getWeekOrMonthDate(1,'month'),
+					getWeekOrMonthDate(2,'month'),
+					getWeekOrMonthDate(3,'month'),
+					getWeekOrMonthDate(4,'month'),
+					getWeekOrMonthDate(5,'month')];
 
-			break;
-	}
+				break;
+			default:
+				tableTheadColumns.value = ['试用','活跃','正式'];
 
-	let typeObj = {
-		'近1个月': 1,
-		'近3个月': 3,
-		'近6个月': 6
-	};
-	typeObj[tab] ? filterDate(typeObj[tab]) : filterDate(0);
+				break;
+		}
 
-	callback();
+		let typeObj = {
+			'近1个月': 1,
+			'近3个月': 3,
+			'近6个月': 6
+		};
+		typeObj[tab] ? filterDate(typeObj[tab]) : filterDate(0);
 
-}
+		getTableData && getTableData();
 
-/* 获取近几个月的日期范围 */
-const filterDate=(month)=>{
-	if(month) {
-		let date_before = moment().subtract(month,'M').format("YYYY-MM-DD");
-		let date_now = moment().format("YYYY-MM-DD");
-		let date = [date_before,date_now]
-		select_date.value = date;
-	}else {
-		select_date.value = '';
 	}
-}
+	
+	/* 获取近几个月的日期范围 */
+	const filterDate=(month)=>{
+		if(month) {
+			let date_before = moment().subtract(month,'M').format("YYYY-MM-DD");
+			let date_now = moment().format("YYYY-MM-DD");
+			let date = [date_before,date_now]
+			select_date.value = date;
+		}else {
+			select_date.value = '';
+		}
+	}
 
-export const createdHook=()=>{
+	// start
 	if(Role.value.indexOf('rai')!=-1){
+		console.log(activeTab.value,'activeTab.value');
 		activeTab.value.tabName = 'QY'
 		activeTab.value.tabSummationName = '权益总合计'
 		activeTab.value.productionId = 2
 	}
-}
-export function dataHook(){
-	return {
-		dataLoading,
-		default_tab,
-		select_date,
-		totalGroupArr,
-		datalist,
-		tableTheadColumns,
-		staticTabs,
-		actFilterDay,
-		filterDaysOptions
-	}
-}
-export function computedHook(){
-	return {
-		Role,
-		activeTab,
-	}
-}
 
-export function functionHook(){
 	return {
-		filterTableData,
-		switchTab,
-		dateChange,
-		changeTabHandle
+		datas:{
+			dataLoading,
+			default_tab,
+			select_date,
+			totalGroupArr,
+			datalist,
+			tableTheadColumns,
+			staticTabs,
+			actFilterDay,
+			filterDaysOptions
+		},
+		computeds:{
+			Role,
+			activeTab,
+		},
+		functions:{
+			filterTableData,
+			switchTab,
+			dateChange,
+			changeTabHandle
+		}
 	}
 }
 

+ 11 - 10
src/views/dataReport_manage/statistic/newCustom.vue

@@ -19,12 +19,9 @@ import DatePicker from 'vue-datepicker-next';
 import { InfoFilled,ArrowDown,ArrowUp } from '@element-plus/icons-vue'
 
 import { dataMainInterface } from '@/api/api.js';
-import {createdHook,dataHook,computedHook,functionHook} from './hook'
+import {dataReportStatisticHook} from './hook'
 
 const $router = useRouter()
-const {dataLoading,default_tab,select_date,totalGroupArr,datalist,tableTheadColumns,staticTabs}=dataHook()
-const {Role,activeTab}=computedHook()
-const {filterTableData,switchTab,dateChange,changeTabHandle}=functionHook()
 
 //1 ficc 2权益
 const tipMap=new Map([
@@ -137,8 +134,12 @@ const getColumnTitle=(index)=>{
 	
 	return title
 }
+// HOOKS
+const HOOK = dataReportStatisticHook({getTableData})
+const {dataLoading,default_tab,select_date,totalGroupArr,datalist,tableTheadColumns,staticTabs}=HOOK.datas
+const {Role,activeTab}=HOOK.computeds
+const {filterTableData,switchTab,dateChange,changeTabHandle}=HOOK.functions
 
-createdHook()
 getTableData()
 
 </script>
@@ -146,16 +147,16 @@ getTableData()
 <template>
 	<div class="statistic-container">
 		<div class="custom-tab-box" v-if="Role==='admin'">
-			<div class="custom-tab" :class="activeTab.tabName =='FICC' && 'activeTab'" @click="switchTab('FICC',getTableData)">
+			<div class="custom-tab" :class="activeTab.tabName =='FICC' && 'activeTab'" @click="switchTab('FICC')">
 				FICC
 			</div>
-			<div class="custom-tab" :class="activeTab.tabName =='QY' && 'activeTab'" @click="switchTab('QY',getTableData)">
+			<div class="custom-tab" :class="activeTab.tabName =='QY' && 'activeTab'" @click="switchTab('QY')">
 				权益
 			</div>
 		</div>
 		<div class="frequency-cont">
 			<ul class="frequency-ul">
-				<li v-for="tab in staticTabs" :key="tab" :class="{act: tab=== default_tab}" @click="changeTabHandle(tab,getTableData)">{{ tab }}</li>
+				<li v-for="tab in staticTabs" :key="tab" :class="{act: tab=== default_tab}" @click="changeTabHandle(tab)">{{ tab }}</li>
 			</ul>
 			<date-picker
 			v-model:value="select_date"
@@ -163,13 +164,13 @@ getTableData()
 			range
 			value-type="format"
 			:clearable="false"
-			@change="dateChange(getTableData)"
+			@change="dateChange"
 			placeholder="请选择统计时间"/>
 		</div>
 		<div class="table-cont" v-show="dataLoading">
 			<div class="table-body-wrapper">
 				<table>
-					<thead>
+					<thead style="z-index: 1;">
 						<tr>
 							<td rowspan="2" class="thead-rs">组别</td>
 							<td rowspan="2" class="thead-rs">销售</td>

+ 1 - 1
src/views/dataReport_manage/statistic/stockCustom.vue

@@ -159,7 +159,7 @@ getTableData();
 		<div class="table-cont" v-show="dataLoading">
 			<div class="table-body-wrapper">
 				<table>
-					<thead>
+					<thead style="z-index: 1;">
 						<tr>
 							<td rowspan="2" class="thead-rs">组别</td>
 							<td rowspan="2" class="thead-rs">销售</td>

+ 203 - 0
src/views/roadshow_manage/compononts/activityDetailDia.vue

@@ -0,0 +1,203 @@
+<script setup>
+import { ref,watch} from 'vue';
+import moment from 'moment';
+import { useRouter} from 'vue-router';
+import DatePicker from 'vue-datepicker-next';
+import { InfoFilled } from '@element-plus/icons-vue'
+
+import { roadshowInterence } from '@/api/api.js';
+
+const props = defineProps({
+	title: {
+		type: String
+	},
+	isShow: {
+		type: Boolean
+	},
+	form: {
+		type: Object
+	},
+	fromType: {
+		type: String,
+		default: 'researcher'
+	}
+})
+
+const emits=defineEmits(['update:isShow'])
+
+const companyInfo=ref(null)
+const tableColumns=ref([])
+const tableData=ref([])
+
+watch(()=>props.isShow,(newval)=>{
+	if(newval) {
+		initState();
+		console.log(props.form)
+		getStatisticDetail();
+	}
+})
+
+/* 获取列表 */
+const getStatisticDetail=()=>{
+	const { startDate,endDate,userid,key } = props.form;
+	const typeMapObj = {
+		试用路演: 'try_out',
+		正式路演: 'formal',
+		公开会议: 'meeting',
+	}
+	roadshowInterence.statisticDetailList({
+		DataType: typeMapObj[key] && typeMapObj[key],
+		StartDate: startDate,
+		EndDate: endDate,
+		AdminId: userid,
+		AdminType: props.fromType
+	}).then(res => {
+		const { Ret,Data } = res;
+
+		if(Ret !== 200) return
+
+		tableData.value = Data || [];
+
+	})
+}
+
+/* 取消 */
+const cancel=()=>{
+	emits('update:isShow', false);
+}
+
+/* 获取客户信息 */
+const getCompanyInfo=async(CompanyId)=>{
+	const { Data }  = await roadshowInterence.componyDetail({ CompanyId });
+	companyInfo.value = Data;
+}
+
+const initState=()=>{
+	const dynamic_column = props.fromType === 'researcher' ? {
+		label: '发起人',
+		key: 'SellerName',
+		minwidthsty: '100px'
+	} : {
+		label: '研究员',
+		key: 'ResearcherName',
+		minwidthsty: '100px',
+	}
+
+	tableColumns.value = props.title === '路演详情'
+		? [
+				{
+					label: '时间',
+					key: 'time',
+					minwidthsty: '180px',
+				},
+				{
+					label: '客户名称',
+					key: 'company',
+					minwidthsty: '100px',
+				},
+				{
+					label: '路演形式',
+					key: 'RoadshowType',
+					minwidthsty: '120px',
+				},
+				{ ...dynamic_column }
+			]
+		: [
+				{
+					label: '时间',
+					key: 'time',
+					minwidthsty: '180px',
+				},
+				{
+					label: '会议主题',
+					key: 'Theme',
+					minwidthsty: '100px',
+				},
+				{
+					label: '合作方',
+					key: 'CooperationName',
+					minwidthsty: '120px',
+				},
+				{
+					label: '会议形式',
+					key: 'RoadshowType',
+					minwidthsty: '120px',
+				},
+				{ ...dynamic_column }
+		]	
+}
+
+</script>
+
+<template>
+	<el-dialog
+      v-dialogDrag
+      :title="props.title"
+      :model-value="props.isShow"
+      :modal-append-to-body="false"
+      @close="cancel"
+			width="850px"
+			class="statistic-dialog-cont"
+    >
+		<el-table
+			:data="tableData"
+			class="table-cont"
+			max-height="300"
+			border
+		>
+			<el-table-column
+				v-for="item in tableColumns"
+				:key="item.label"
+				:label="item.label"
+				:width="item.widthsty"
+				:min-width="item.minwidthsty"
+				align="center"
+			>
+				<template #default="{row}">
+
+					<span v-if="item.key === 'time'">
+						{{
+							moment(new Date(row.StartDate + " " + row.StartTime)).format(
+								"MM.DD(ddd) HH:mm - "
+							) + moment(new Date(row.EndDate + " " + row.EndTime)).format("HH:mm")
+						}}
+					</span>
+
+					<span v-else-if="item.key === 'RoadshowType'">
+						{{row.RoadshowType}} {{ row.RoadshowType === '线上' ? `(${row.RoadshowPlatform}  )` : `(${row.Province}${row.City})`}}
+					</span>
+
+					<span v-else-if="item.key === 'company'">
+						{{ row.CooperationName || row.CompanyName }} <span v-if="props.fromType == 'seller' || props.fromType == 'special'">{{`(${row.CompanyStatus})`}} </span>
+
+						<el-tooltip effect="dark" placement="top-start" v-if="row.CompanyId"  popper-class="company-tip-poper">
+							<el-icon :size="14" style="vertical-align: text-top;" @mouseenter.native="getCompanyInfo(row.CompanyId)" ><InfoFilled /></el-icon>
+							<template #content v-if="companyInfo">
+								<div>
+									<p style="margin: 6px 0;">客户状态:{{companyInfo.Status}}</p>
+									<p style="margin: 6px 0;">所属行业:{{companyInfo.IndustryName}}</p>
+									<p style="margin: 6px 0;text-indent: -70px;margin-left: 70px;">开通品种:{{companyInfo.PermissionName}}</p>
+									<p style="margin: 6px 0;">累计报告阅读次数:{{companyInfo.ReportReadTotal}}</p>
+								</div>
+							</template>
+						</el-tooltip>
+					</span>
+
+					<span v-else>{{ row[item.key] || '——' }}</span>
+
+				</template>
+			</el-table-column>
+		</el-table>
+	</el-dialog>	
+</template>
+
+<style lang="scss" scoped>
+.table-cont {
+	margin: 10px 0 40px;
+}
+</style>
+<style lang="scss">
+.company-tip-poper {
+	max-width: 400px;
+}
+</style>

+ 216 - 0
src/views/roadshow_manage/researcherBusinessTrip.vue

@@ -0,0 +1,216 @@
+
+<script setup>
+// import { ref,reactive,nextTick } from "vue";
+// import moment from "moment";
+// import $ from "jquery"
+// import { ArrowLeft,ArrowRight } from '@element-plus/icons-vue'
+
+// import { roadshowInterence } from "@/api/api.js";
+// import {bussinessTripH} from '@/views/business_trip_manage/hooks/bussinessTripH.js'
+
+// const bussinessTripCom = bussinessTripH()
+
+// const daysArr=['周一','周二','周三','周四','周五','周六','周日']
+
+// const researcherList=ref([]) // 研究员列表
+// const defaultSalesProps=reactive({
+//   children: "ResearcherList",
+//   value: "AdminId",
+//   emitPath: false,
+// }) //研究员级联配置
+// const tableDayColumns=ref([])
+// const researcher=ref('')
+// const datalist=ref([])
+// const weekQuery=ref(0)
+
+// // 获取研究员列表
+// const getResearcherList=async()=>{
+//   // 发送请求
+//   const res = await roadshowInterence.getResearcherList();
+//   if (res.Ret === 200) {
+//     researcherList.value = formatResearcherList(res.Data);
+//   }
+// }
+// // 对获取到的研究员列表做处理
+// const formatResearcherList=(list)=>{
+//   list.forEach((group) => {
+//     // 对组做处理
+//     group.label = group.GroupName;
+//     // 如果有列表
+//     group.ResearcherList &&
+//       group.ResearcherList.forEach((item) => {
+//         // 对研究员做处理
+//         item.label = item.RealName;
+//       });
+//   });
+//   // 去掉ficc全体选项
+//   return list.filter((group) => group.GroupName !== "ficc全体");
+// }
+// const getWeekDays=(weeknum,dateList)=>{
+//   // dateList 日期数组  weeknum 周数
+//   let arrList=[]
+
+//   for (let i = 0; i < weeknum; i++) {
+//     arrList[i]=[]
+//     if(dateList && dateList.length>0){
+//       // 有数据返回 能拿到后端返回的日期数组
+//       for (let j = 0; j < 7; j++) {
+//         let momentItem=moment(dateList[(i*7+j)])
+//         arrList[i].push(momentItem.format('MM/DD')+daysArr[momentItem.weekday()])
+//       }
+//     }
+//   }
+//   return arrList;
+// }
+// /* 获取表格数据 */
+// const getTableData=()=>{
+//   let param={
+//     AdminId:researcher.value||0,
+//     WeekQuery:weekQuery.value,
+//     BaseQueryDate:bussinessTripCom.BaseDate.value
+//   }
+//   roadshowInterence.getResearcherBusinessTrip(param).then((res) => {
+//     if (res.Ret !== 200) return;
+//     bussinessTripCom.BaseDate.value = res.Data.BaseDate
+//     const Data = res.Data.GroupList || []
+
+//     //处理数据结构
+//     let tempData=Data.filter(it => it.ResearcherList && it.ResearcherList.length > 0)
+//     tempData.forEach((item)=>{
+//       item.showDetail=true
+//     })
+//     // 取出出差表所有的日期
+//     let weekDateList =  tempData[0]?tempData[0].ResearcherList[0].BusinessTripList.map(item => item.WeekDate):[]
+//     // 获取表格头文本
+//     tableDayColumns.value= getWeekDays(2,weekDateList)
+
+//     datalist.value = tempData;
+//     nextTick(()=>{
+//       $("table")
+//         .find("td")
+//         .css({ width: '5.556%' });
+//       $("table")
+//         .find(".thead-rs")
+//         .css({ width: '11.111%' });
+//     })
+//   });
+// }
+
+// getResearcherList()
+// getTableData()
+
+</script>
+
+<template>
+  <div class="statistic-container">
+    研究员出差表
+    <!-- <div class="statistic-select">
+      <el-cascader
+        v-model="researcher"
+        placeholder="请选择研究员"
+        style="width: 230px"
+        :options="researcherList"
+        :props="defaultSalesProps"
+        :show-all-levels="false"
+        key="cascaderIdx"
+        clearable
+        filterable
+        @change="getTableData"
+        size="large"
+      >
+      </el-cascader>
+      <div class="center">
+          <el-button type="primary" @click="bussinessTripCom.toogelDate(1)" style="width: 140px;" 
+          size="large" ><el-icon :size="14" style="margin-right: 4px;"><ArrowLeft /></el-icon> 上两周</el-button>
+          <el-button type="primary" @click="bussinessTripCom.toogelDate(0)" style="width: 140px;" size="large">本期</el-button>
+          <el-button type="primary" @click="bussinessTripCom.toogelDate(2)" style="width: 140px;"
+          size="large" >下两周 <el-icon :size="14" style="margin-left: 4px;"><ArrowRight /></el-icon></el-button>
+      </div>
+      <div style="width: 230px;"></div>
+    </div>
+    <div class="table-cont">
+      <div class="table-body-wrapper" style="max-height: calc(100vh - 240px);overflow: auto;">
+				<table>
+          <thead>
+            <tr>
+              <td rowspan="2" class="thead-rs">研究组</td>
+              <td rowspan="2" class="thead-rs">研究员</td>
+              <td :colspan="7" v-for="item in bussinessTripCom.tableTheadColumns.value" :key="item" class="head-column">
+                {{ item }}
+              </td>
+            </tr>
+            <tr>
+              <template v-for="item in [0,1]">
+                <td :key="item +'_'+ ind" v-for="(it,ind) in tableDayColumns[item]" >
+                  {{ it }}
+                </td>
+              </template>
+            </tr>
+          </thead>
+          <template v-if="datalist.length>0">
+            <tbody v-for="item in datalist" :key="item.GroupId">
+              <template v-if="item.ResearcherList.length">
+                <tr>
+                  <td :rowspan="item.ResearcherList.length + 1" class="thead-rs" style="cursor: pointer;"
+                  @click="item.showDetail = !item.showDetail" >{{ item.GroupName }}
+                    <i :class="item.showDetail?'el-icon-arrow-down':'el-icon-arrow-up'" v-show="item.ResearcherList.length>1"></i>
+                  </td>
+                </tr>
+                <tr v-for="rs in item.showDetail?item.ResearcherList:item.ResearcherList.slice(0,1)" :key="rs.AdminId">
+                  <td class="thead-rs">{{ rs.RealName }}</td>
+                    <td :key="data_key+'_0'" v-for="(data,data_key) in rs.BusinessTripList.filter(it => it.WeekType == 'current')" >
+                      {{ data.City }}
+                    </td>
+                    <td :key="data_key+'_1'" v-for="(data,data_key) in rs.BusinessTripList.filter(it => it.WeekType == 'next')" >
+                      {{ data.City }}
+                    </td>
+                </tr>
+              </template>
+            </tbody>
+          </template>
+        </table>
+        <template v-if="datalist.length==0">
+            <div class="table-data-empty">
+              暂无信息
+            </div>
+          </template>
+      </div>
+    </div> -->
+  </div>
+</template>
+<style lang="scss" scoped>
+* {
+  box-sizing: border-box;
+}
+@import "./statistics/index.scss";
+.statistic-container {
+  min-height: calc(100vh - 120px);
+  height: auto;
+  .statistic-select {
+    display: flex;
+    margin-bottom: 30px;
+    align-items: center;
+    justify-content: space-between;
+    .center{
+      display: flex;
+      flex-wrap: nowrap;
+    }
+  }
+  thead{
+    tr{
+      td{
+        background-color:#EBEEF5!important ;
+      }
+    }
+  }
+  .table-data-empty{
+    color: #666666;
+    height: 530px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-left: 1px solid #DCDFE6;
+    border-right: 1px solid #DCDFE6;
+  }
+}
+</style>

+ 331 - 0
src/views/roadshow_manage/researcherCalendar.vue

@@ -0,0 +1,331 @@
+<script setup>
+// import { ref,reactive,computed,watch } from "vue";
+// import {ElMessage,ElMessageBox} from "element-plus"
+// import { useRouter} from 'vue-router';
+// import _ from 'lodash'
+// import { InfoFilled } from '@element-plus/icons-vue'
+// import moment from "moment"
+
+// import { roadshowInterence } from "@/api/api.js";
+// import calendar from "@/components/calendar.vue";
+// import addActivityBtnDia from "./compononts/addActivityBtnDia.vue";
+// import addActivityCellDia from "./compononts/addActivityCellDia.vue";
+// import addMatterDia from "./compononts/addMatterDia.vue";
+// import { researcherCalendarConfig } from "./roleConfig";
+
+// // 活动列表
+// const eventList=ref([])
+// // 研究员列表
+// const researcherList=ref([])
+// // 选择的研究员
+// const selectResearcher=ref("")
+// // 按钮添加活动弹窗是否显示
+// const isActivityBtnDiaShow=ref(false)
+// const activityStartDate=ref(new Date())
+// // 日历添加活动弹窗是否显示
+// const isActivityCellDiaShow=ref(false)
+// // 点击单元格对应日期时间
+// const cellDate=ref(null)
+// // 子组件默认数据
+// const sonData=ref(null)
+// const edit_id=ref(0)
+// const edit_rs_id=ref(0)
+
+// // 删除弹窗显示
+// const isDeleteDiaShow=ref(false)
+// // 删除原因
+// const deleteReason=ref("")
+// const rowInfo=ref('')
+
+// const edit_matter_id=ref(0)
+// const isAddMatterDiaShow=ref(false)
+// const matter_form=ref({})
+
+// // 是否显示添加按钮
+// const isShowAddActivityBtn=computed(()=>{
+//   const role = localStorage.getItem("Role");
+//   return researcherCalendarConfig.showAddActivityBtnList.includes(role);
+// })
+
+// const calendarRef=ref(null)
+
+// // 获取研究员列表
+// const getResearcherList=async()=>{
+//   // 发送请求
+//   const res = await roadshowInterence.getResearcherList();
+//   if (res.Ret === 200) {
+//     researcherList.value = formatResearcherList(res.Data);
+//   }
+// }
+
+// // 获取活动列表
+// const getEventList=async()=>{
+
+//   //当前的起始日期
+//   const { currentStart,currentEnd } = calendarRef.value.calendarApi.view
+
+//   const { formateDate } = calendarRef.value
+
+//   // 获取本周日程
+//   const res = await roadshowInterence.getCalendarDetail({
+//     StartDate: formateDate(currentStart),
+//     EndDate: formateDate(new Date(currentEnd.getTime()- 24*60*60*1000)),
+//     ResearcherId: selectResearcher.value || -1
+//   });
+
+//   eventList.value = [
+//     ...(res.Data.CalendarList || []),
+//     ...(res.Data.RsMattersList || []),
+//   ].map((item, index) => {
+//     return { ...item, id: index };
+//   });
+// }
+
+// // 对获取到的研究员列表做处理
+// const formatResearcherList=(list)=>{
+//   list.forEach((group) => {
+//     // 对组做处理
+//     group.label = group.GroupName;
+//     // 如果有列表
+//     group.ResearcherList && group.ResearcherList.forEach((item) => {
+//       // 对研究员做处理
+//       item.label = item.RealName;
+//       item.value = item.AdminId
+//       // {
+//       //   ResearcherId: item.AdminId,
+//       //   ResearcherName: item.RealName,
+//       // };
+//     });
+//   });
+//   // 去掉ficc全体选项
+//   return list.filter((group) => group.GroupName !== "ficc全体");
+// }
+
+// // 添加说明弹窗
+// const addActivityBtn=()=>{
+//   //当前的起始日期
+//   const { currentStart } = calendarRef.value.calendarApi.view;
+//   edit_id.value = 0;
+//   edit_rs_id.value = 0;
+//   activityStartDate.value = currentStart
+//   isActivityBtnDiaShow.value = true;
+// }
+
+// // 单元格添加说明事件
+// const cellClick=(startDate)=>{
+//   if (!selectResearcher.value)
+//     return ElMessage.warning("请先选择研究员");
+//   const endDate = new Date(startDate.getTime() + 1000 * 60 * 60);
+//   sonData.value = {
+//     selectResearchers: [
+//       {
+//         researcherId: selectResearcher.value,
+//         startDate: startDate,
+//         startTime: startDate,
+//         endDate: endDate,
+//         endTime: endDate,
+//       },
+//     ],
+//   };
+//   edit_id.value = 0;
+//   edit_rs_id.value = 0;
+//   isActivityCellDiaShow.value = true;
+// }
+
+// /* 编辑事项时 回显内容 */
+// const editCallback=({ ActivityType,RoadshowType,RoadshowPlatform,City,Province,CompanyId,CompanyName,ResearcherId,RsCalendarResearcherId,
+//   Theme,CooperationName,ActivityCategory,StartDate,EndDate,RsCalendarId,RsMattersId,StartTime,EndTime,MatterContent })=>{
+//   if(RsCalendarId) { // 编辑活动
+//     sonData.value = {
+//       activityType: ActivityType, // 活动类型
+//       roadshowType: RoadshowType, // 路演形式
+//       roadshowPlatform: RoadshowPlatform, // 路演平台
+//       roadshowCity: Province ? [Province,City] : [], // 路演城市
+//       companyId: CompanyId, // 客户id
+//       companyName: CompanyName, // 客户名称
+//       meetingType: RoadshowType, // 会议形式
+//       meetingPlatform: RoadshowPlatform, // 会议平台
+//       meetingCity: Province ? [Province,City] : [], // 会议城市
+//       meetingTheme: Theme, // 会议主题
+//       partnersName: CooperationName, // 合作方名称
+//       activityClass: ActivityCategory, // 活动类别
+//       selectResearchers: [
+//         // 选择的研究员
+//         {
+//           researcherId: Number(ResearcherId),
+//           startDate: new Date(StartDate),
+//           startTime: new Date(`${StartDate} ${StartTime}`),
+//           endDate: new Date(EndDate),
+//           endTime: new Date(`${EndDate} ${EndTime}`),
+//         },
+//       ],
+//     };
+//     edit_id.value = RsCalendarId;
+//     edit_rs_id.value = RsCalendarResearcherId;
+//     isActivityCellDiaShow.value = true;
+//   }else { //编辑事项
+//     edit_matter_id.value = RsMattersId;
+//     matter_form.value = { 
+//       startDate: new Date(`${StartDate} ${StartTime}`),
+//       startTime: new Date(`${StartDate} ${StartTime}`),
+//       endDate: new Date(`${EndDate} ${EndTime}`),
+//       endTime: new Date(`${EndDate} ${EndTime}`),
+//       matterContent: MatterContent,
+//     };
+//     isAddMatterDiaShow.value = true;
+//   }
+// }
+
+// // 添加活动后刷新
+// const addCallback=()=>{
+//   getEventList();
+// }
+
+
+//   // 审核删除
+// const deleteRoadshow=(row)=>{
+//   if(['内部会议'].includes(row.ActivityType)) return delNormalHandle(row);
+
+//   rowInfo.value = row;
+//   isDeleteDiaShow.value = true;
+  
+// }
+
+// // 删除弹窗取消操作
+// const deleteDiaCancel=()=>{
+//   deleteReason.value = "";
+//   rowInfo.value = null;
+//   isDeleteDiaShow.value = false;
+// }
+
+// // 删除弹窗确定操作
+// const deleteDiaConfirm=async()=>{
+//   if (!deleteReason.value) return ElMessage.warning("删除原因不能为空");
+
+//   const { Ret } = await roadshowInterence.deleteRoadshow({
+//     RsCalendarId: rowInfo.value.RsCalendarId,
+//     RsCalendarResearcherId: rowInfo.value.RsCalendarResearcherId,
+//     DeleteReason: deleteReason.value,
+//   });
+  
+//   if( Ret !== 200) return;
+//   ElMessage.success('删除成功');
+
+//   deleteDiaCancel();
+//   getEventList();
+// }
+// // 处理从出差日历过来的参数
+// const dealwithFromBusiness=()=>{
+//   if(sessionStorage.getItem('businessToResearcherParams')){
+//     let fromBusinessParams=JSON.parse(sessionStorage.getItem('businessToResearcherParams'))
+//     let nowDate = moment(new Date()).startOf('week').diff(moment(new Date(fromBusinessParams.date)),'weeks')
+//     selectResearcher.value=fromBusinessParams.researcherId
+//     if(nowDate>0){
+//       // 前一周
+//       calendarRef.value.toogelDate('prev')
+//     }else if(nowDate<0){
+//       // 后一周
+//       calendarRef.value.toogelDate('next')
+//     }else{
+//       //为零,没有改变
+//       getEventList()
+//     }
+//     // 用完即弃
+//     sessionStorage.removeItem('businessToResearcherParams')
+//   }
+// }
+
+// getResearcherList()
+// dealwithFromBusiness()
+</script>
+
+<template>
+  <div class="researcher-calendar">
+    研究员日历
+    <!-- <el-card class="researcher-calendar-context"> -->
+      <!-- 日程组件 -->
+      <!-- <calendar
+        ref="calendarRef"
+        :eventList="eventList"
+        @eventClick="eventClick"
+        @cellClick="cellClick"
+        @weekChange="getEventList"
+        @editCallback="editCallback"
+        @delAuthHandle="deleteRoadshow"
+      >
+        <template #left>
+          <el-cascader
+            v-model="selectResearcher"
+            :options="researcherList"
+            :show-all-levels="false"
+            :props="{
+              expandTrigger: 'hover',
+              children: 'ResearcherList',
+              emitPath: false,
+            }"
+            clearable
+            filterable
+            placeholder="请选择研究员"
+            @change="getEventList"
+          />
+        </template>
+        <template #right>
+          <el-button
+            v-if="isShowAddActivityBtn"
+            type="primary"
+            @click="addActivityBtn"
+            >添加活动</el-button
+          >
+        </template>
+      </calendar>
+    </el-card> -->
+    <!-- 按钮活动弹窗 -->
+    <!-- <addActivityBtnDia
+      :isShow.sync="isActivityBtnDiaShow"
+      :start_date="activityStartDate"
+      ref="addActivityBtnDia"
+      @ensureCallback="addCallback"
+    ></addActivityBtnDia> -->
+    <!-- 日历活动弹窗 -->
+    <!-- <addActivityCellDia
+      :isShow.sync="isActivityCellDiaShow"
+      :initData="sonData"
+      :edit_id="edit_id"
+      :edit_rs_id="edit_rs_id"
+      ref="addActivityCellDia"
+      @ensureCallback="addCallback"
+    ></addActivityCellDia> -->
+
+    <!-- 事项弹窗  -->
+    <!-- <addMatterDia 
+      :isShow.sync="isAddMatterDiaShow" 
+      :edit_matter_id="edit_matter_id" 
+      :matter_form="matter_form"
+      @ensureCallback="addCallback"
+    /> -->
+
+    <!-- 删除确认弹窗 -->
+    <!-- <el-dialog
+      title="删除"
+      :visible.sync="isDeleteDiaShow"
+      :modal-append-to-body="false"
+      width="500px"
+      @close="deleteDiaCancel"
+    >
+      <el-input
+        type="textarea"
+        v-model.trim="deleteReason"
+        :autosize="{ minRows: 4, maxRows: 8 }"
+        placeholder="请输入删除理由"
+        resize="none"
+      ></el-input>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="deleteDiaCancel">取消</el-button>
+        <el-button type="primary" @click="deleteDiaConfirm">确定</el-button>
+      </span>
+    </el-dialog> -->
+  </div>
+</template>
+
+<style>
+</style>

+ 176 - 0
src/views/roadshow_manage/statistics/hooks.js

@@ -0,0 +1,176 @@
+import moment from 'moment';
+import {ref} from "vue"
+import $ from 'jquery'
+import {useRoute} from "vue-router"
+
+/* 处理数据结构 便于页面渲染 userid 时间用于弹窗获取列表*/
+const filterTableData=(data,userid= '')=>{
+	let list = data.map(item => ([
+		{
+			key: '试用路演',
+			value: item.TryOutNum,
+			startDate: item.StartDate,
+			endDate: item.EndDate,
+			userid
+		},
+		{
+			key: '正式路演',
+			value: item.FormalNum,
+			startDate: item.StartDate,
+			endDate: item.EndDate,
+			userid
+		},
+		{
+			key: '公开会议',
+			value: item.MeetingNum,
+			startDate: item.StartDate,
+			endDate: item.EndDate,
+			userid
+		}
+	]))
+	
+	return list.flat(Infinity);
+}
+
+/* 	获取几周前 周一周日日期 或前几月月份*/
+const getWeekOrMonthDate=(weeknum,type='week')=>{
+	if(type === 'week') {
+		const weekStart = moment().subtract(weeknum, 'week').startOf('isoWeek').format('YYYY.MM.DD'); //周一
+		const weekEnd = moment().subtract(weeknum, 'week').endOf('isoWeek').format('YYYY.MM.DD'); //周日
+
+		// console.log(weekStart,weekEnd)
+		return `${weekStart}~${weekEnd}`;
+	} else {
+		const month = moment().subtract(weeknum,'M').format('YYYY.MM');
+		return month;
+	}
+}
+
+export const roadshowStatisticsHook=(params)=>{
+	const {getTableData} = params||{}
+	const $route = useRoute()
+
+	const dataLoading=ref(false)
+	const default_tab=ref('周度统计表')
+	const select_date=ref('')
+	const staticTabs=ref([ '周度统计表','月度统计表','近1个月','近3个月','近6个月' ])
+	const tableTheadColumns=ref($route.path === '/sellerStatisticSeller' 
+		? [
+			`本周(${getWeekOrMonthDate(0)})`,
+			`上一周(${getWeekOrMonthDate(1)})`,
+			`上两周(${getWeekOrMonthDate(2)})`,
+			`上三周(${getWeekOrMonthDate(3)})`,
+			`上四周(${getWeekOrMonthDate(4)})`,
+			`上五周(${getWeekOrMonthDate(5)})`]
+		: ['上一周','本周','下一周'])//动态表头配置
+	const dialogForm=ref({})
+	const isShowDia=ref(false)
+	const diaTitle=ref('路演详情')
+	const datalist=ref([]) //表格数据
+	const totalGroupArr=ref([]) //总合计列表
+
+	/* 切换顶部tab */
+	const changeTabHandle=(tab,type)=>{
+		if(tab === default_tab.value) return
+		$('.table-body-wrapper')[0].scrollTop = 0;
+		default_tab.value = tab;
+		switch(tab) {
+			case '周度统计表':
+				tableTheadColumns.value = $route.path === '/sellerStatisticSeller' 
+					? [
+						`本周(${getWeekOrMonthDate(0)})`,
+						`上一周(${getWeekOrMonthDate(1)})`,
+						`上两周(${getWeekOrMonthDate(2)})`,
+						`上三周(${getWeekOrMonthDate(3)})`,
+						`上四周(${getWeekOrMonthDate(4)})`,
+						`上五周(${getWeekOrMonthDate(5)})`]
+					: ['上一周','本周','下一周'];
+				break;
+			case '月度统计表':
+				tableTheadColumns.value = $route.path === '/sellerStatisticSeller' 
+					? [
+						getWeekOrMonthDate(0,'month'),
+						getWeekOrMonthDate(1,'month'),
+						getWeekOrMonthDate(2,'month'),
+						getWeekOrMonthDate(3,'month'),
+						getWeekOrMonthDate(4,'month'),
+						getWeekOrMonthDate(5,'month')]
+					: [
+							getWeekOrMonthDate(0,'month'),
+							getWeekOrMonthDate(1,'month'),
+							getWeekOrMonthDate(2,'month'),
+							getWeekOrMonthDate(3,'month'),
+						];
+				break;
+			default:
+				tableTheadColumns.value = ['试用路演','正式路演','公开会议'];
+
+				break;
+		}
+
+		let typeObj = {
+			'近1个月': 1,
+			'近3个月': 3,
+			'近6个月': 6,
+		};
+		typeObj[tab] ? filterDate(typeObj[tab]) : filterDate(0);
+
+		getTableData && getTableData();
+		
+	}
+
+	/* 路演详情弹窗 */
+	const openDiaHandle=({ startDate,endDate,userid,value,key })=>{
+		if(value === 0) return;
+
+		diaTitle.value = key === '公开会议' ? `${key}详情` : '路演详情';
+		dialogForm.value = {
+			startDate,
+			endDate,
+			userid,
+			key
+		}
+		isShowDia.value = true;
+	}
+
+	/* 获取近几个月的日期范围 */
+	const filterDate=(month)=>{
+		if(month) {
+			let date_before = moment().subtract(month,'M').format("YYYY-MM-DD");
+			let date_now = moment().format("YYYY-MM-DD");
+			let date = [date_before,date_now]
+			select_date.value = date;
+		}else {
+			select_date.value = '';
+		}
+	}
+
+	/* 选择日期改变清空高亮按钮 */
+	const dateChange=()=>{
+		default_tab.value = '';
+		tableTheadColumns.value = ['试用路演','正式路演','公开会议'];
+
+		getTableData && getTableData();
+	}
+
+	return {
+		datas:{
+			dataLoading,
+			default_tab,
+			select_date,
+			totalGroupArr,
+			datalist,
+			tableTheadColumns,
+			staticTabs,
+			dialogForm,
+			isShowDia,
+			diaTitle
+		},
+		functions:{
+			filterTableData,
+			dateChange,
+			changeTabHandle,
+			openDiaHandle
+		}
+	}
+}

+ 87 - 0
src/views/roadshow_manage/statistics/index.scss

@@ -0,0 +1,87 @@
+.statistic-container {
+	height: calc(100vh - 120px);
+	background-color: #fff;
+	box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.05);
+	border-radius: 4px;
+	padding: 30px 20px;
+
+	.frequency-cont {
+		display: flex;
+		align-items: center;
+		margin-bottom: 30px;
+		.frequency-ul {
+			display: flex;
+			align-items: center;
+			li {
+				width: 110px;
+				height: 40px;
+				text-align: center;
+				line-height: 40px;
+				border: 1px solid #B3D8FF;
+				background: #ECF5FF;
+				color: #409EFF;
+				border-radius: 4px;
+				cursor: pointer;
+				margin-right: 20px;
+				&.act {
+					background: #409EFF;
+					color: #fff;
+				}
+			}
+		}
+	}
+
+	.table-cont {
+
+		.table-body-wrapper {
+			max-height: calc(100vh - 340px);
+			margin-right: -6px;
+			overflow-y: scroll;
+			overflow-x: auto;
+			border-bottom: 1px solid #dcdfe6;
+			border-top: 1px solid #dcdfe6;
+		}
+
+		table {
+			width: 100%;
+			font-size: 14px;
+			color: #666;
+			thead{
+				position: sticky;
+				top: 0;
+				left: 0;
+				border-left: 1px solid #dcdfe6;
+				border-right: 1px solid #dcdfe6;
+				td{
+					border: none;
+					outline-color: #dcdfe6;
+					outline-style: solid;
+					outline-width: 0.5px;
+				}
+			}
+			td,
+			th {
+				min-width: 35px;
+				// word-break: break-all;
+				border: 1px solid #dcdfe6;
+				height: 45px;
+				text-align: center;
+				background-color: #fff;
+			}
+	
+			.head-column {
+				background-color: #F0F2F5;
+			}
+	
+			.data-cell{
+				color: #409EFF;
+				cursor: pointer;
+			}
+
+			.thead-sticky {
+				position: sticky;
+				top: 0;
+			}
+		}
+	}
+}

+ 178 - 0
src/views/roadshow_manage/statistics/seller.vue

@@ -0,0 +1,178 @@
+<script setup>
+import _ from "lodash"
+import DatePicker from 'vue-datepicker-next';
+import { ArrowDown,ArrowUp } from '@element-plus/icons-vue'
+
+import { roadshowInterence } from '@/api/api.js';
+import actiyityDetailDia from '../compononts/activityDetailDia.vue';
+import {roadshowStatisticsHook} from "./hooks"
+
+
+/* 获取表格数据 */
+const getTableData=()=>{
+	dataLoading.value = false;
+	roadshowInterence.sellerStatistic({
+		DataType: default_tab.value === '周度统计表' ? 'week' : default_tab.value === '月度统计表' ? 'month' : 'time_interval',
+		StartDate: select_date.value ? select_date.value[0] : '',
+		EndDate: select_date.value ? select_date.value[1] : '',
+	}).then(res => {
+
+		const { Data,Ret } = res;
+		
+		if(Ret !== 200) return
+			
+		//总合计数据处理
+		totalGroupArr.value = filterTableData(Data.RsReportRecordNumList);
+
+		//处理数据结构
+		let data = _.cloneDeep(Data.List);
+		data.forEach(item => {
+			item.showDetail=false
+			let groupDataArr = filterTableData(item.RsReportRecordNumList);
+			item.subGroupArr=groupDataArr
+
+			item.Item && item.Item.forEach(sub_item => {
+				
+				let dataArr = filterTableData(sub_item.RsReportRecordNumList,sub_item.AdminId)
+				sub_item.dataArr=dataArr
+			})
+		})
+
+		datalist.value = data;
+		dataLoading.value = true;
+	})
+}
+
+const HOOK = roadshowStatisticsHook({getTableData})
+const {dataLoading,default_tab,select_date,totalGroupArr,datalist,tableTheadColumns,staticTabs,
+	dialogForm,isShowDia,diaTitle}=HOOK.datas
+const {filterTableData,dateChange,openDiaHandle,changeTabHandle}=HOOK.functions
+//start
+getTableData();
+
+</script>
+
+<template>
+	<div class="statistic-container">
+		<div class="frequency-cont">
+			<ul class="frequency-ul">
+				<li v-for="tab in staticTabs" :key="tab" :class="{act: tab=== default_tab}" @click="changeTabHandle(tab)">{{ tab }}</li>
+			</ul>
+			<date-picker
+			v-model:value="select_date"
+			type="date" 
+			range
+			value-type="format"
+			:clearable="false"
+			@change="dateChange"
+			placeholder="请选择统计时间"/>
+		</div>
+		<div class="table-cont" v-show="dataLoading">
+			<!-- <table>
+				<thead>
+					<tr>
+						<td rowspan="2" class="thead-rs">组别</td>
+						<td rowspan="2" class="thead-rs">销售</td>
+						<td
+							:colspan="['周度统计表','月度统计表'].includes(default_tab) ? 3 : 1"
+							v-for="item in tableTheadColumns" 
+							:key="item" 
+							class="head-column"
+						>
+							{{item}}
+						</td>	
+					</tr>
+					<tr v-if="['月度统计表'].includes(default_tab)">
+						<template v-for="(item,index) in new Array(4)">
+							<td :key="index+'_0'">试用路演</td>
+							<td :key="index+'_1'">正式路演</td>
+							<td :key="index+'_2'">公开会议</td>
+						</template>
+					</tr>
+					<tr v-if="['周度统计表'].includes(default_tab)">
+						<template v-for="(item,index) in new Array(3)">
+							<td :key="index+'_0'">试用路演</td>
+							<td :key="index+'_1'">正式路演</td>
+							<td :key="index+'_2'">公开会议</td>
+						</template>
+					</tr>
+				</thead>
+			</table>	 -->
+
+			<div class="table-body-wrapper">
+				<table>
+					<thead style="z-index: 1;">
+						<tr>
+							<td rowspan="2" class="thead-rs">组别</td>
+							<td rowspan="2" class="thead-rs">销售</td>
+							<td
+								:colspan="['周度统计表','月度统计表'].includes(default_tab) ? 3 : 1"
+								v-for="item in tableTheadColumns" 
+								:key="item" 
+								class="head-column"
+							>
+								{{item}}
+							</td>	
+						</tr>
+						<tr v-if="['周度统计表','月度统计表'].includes(default_tab)">
+							<template v-for="(item,index) in new Array(6)" :key="index">
+								<td>试用路演</td>
+								<td>正式路演</td>
+								<td>公开会议</td>
+							</template>
+						</tr>
+					</thead>
+					<tbody v-for="item in datalist" :key="item.Name">
+							<tr>
+								<td colspan="2" @click="item.showDetail=!item.showDetail"> {{`${item.Name}合计`}}
+									<el-icon :size="14" style="vertical-align: text-top;" v-if="item.Item.length">
+										<ArrowUp v-show="item.showDetail" />
+										<ArrowDown v-show="!item.showDetail" />
+									</el-icon>	
+								</td>
+								<td v-for="(group_data,group_data_key) in item.subGroupArr" :key="group_data_key">
+									{{ group_data.value !== 0 ? group_data.value : '' }}
+								</td>
+							</tr>
+							<template v-if="item.Item.length">
+								<tr v-show="item.showDetail">
+								<td :rowspan="item.Item.length+1" class="thead-rs">{{item.Name}}</td>
+							</tr>
+
+							<tr v-for="rs in item.Item" :key="rs.AdminId" v-show="item.showDetail">
+								<td class="thead-rs">{{rs.Name}}</td>
+
+								<td class="data-cell" v-for="(data,data_key) in rs.dataArr" :key="data_key" @click="openDiaHandle(data)">
+									{{ data.value !== 0 ? data.value : '' }}
+								</td>
+								
+							</tr>
+							</template>
+					</tbody>
+					<tfoot>
+						<tr>
+							<td colspan="2">ficc总合计</td>
+							<td v-for="(total_data,total_data_key) in totalGroupArr" :key="total_data_key">
+								{{ total_data.value !== 0 ? total_data.value : '' }}
+							</td>
+						</tr>
+					</tfoot>
+				</table>
+			</div>	
+		</div>
+
+		<!-- 详情弹窗 -->
+		<actiyityDetailDia
+			v-model:isShow="isShowDia"
+			:title="diaTitle"
+			:form="dialogForm"
+			fromType="seller"
+		/>
+
+	</div>
+</template>
+
+<style lang='scss' scoped>
+*{ box-sizing: border-box;}
+@import './index.scss';
+</style>