Kaynağa Gözat

数据报表

xiuli.gao 9 ay önce
ebeveyn
işleme
eeb5620e2d
78 değiştirilmiş dosya ile 1612 ekleme ve 244 silme
  1. 1 0
      dist/assets/index.193eab92.css
  2. 1 0
      dist/assets/index.5668eb54.js
  3. 1 1
      dist/assets/index.396dcb47.css
  4. 0 1
      dist/assets/index.936eabe9.js
  5. 1 1
      dist/assets/index.af38243f.css
  6. 2 2
      dist/index.html
  7. 0 1
      dist/js/Home/index.7613df96.js
  8. 1 0
      dist/js/Home/index.b338f105.js
  9. 1 1
      dist/js/_dialog/_dialog.06ad7f5f.js
  10. 1 1
      dist/js/_input/_input.fb81cdd2.js
  11. 1 1
      dist/js/_select/_select.275fc137.js
  12. 3 0
      dist/js/acStatement.vue/index.31e5b75e.js
  13. 1 1
      dist/js/adTask/index.1810f4d1.js
  14. 0 1
      dist/js/adqManage/index.ac6615ca.js
  15. 1 0
      dist/js/adqManage/index.d42304ef.js
  16. 1 1
      dist/js/api/api.67259073.js
  17. 1 1
      dist/js/basisMoudle/error.ca7b6b72.js
  18. 1 0
      dist/js/basisMoudle/login.0782c651.js
  19. 0 1
      dist/js/basisMoudle/login.a95a9ffa.js
  20. 1 0
      dist/js/caret-top/caret-top.80cf6e6c.js
  21. 1 1
      dist/js/collectClip/index.2c51a6b6.js
  22. 0 7
      dist/js/configArea/index.846c8753.js
  23. 7 0
      dist/js/configArea/index.ec1bf995.js
  24. 1 1
      dist/js/define/define.8e1f4aae.js
  25. 1 0
      dist/js/echarts/Echarts.8aab55f0.js
  26. 1 0
      dist/js/export/Export2Excel.4f8dda80.js
  27. 1 0
      dist/js/file-saver/file-saver.864809c5.js
  28. 1 0
      dist/js/gdtList/index.6876fbf7.js
  29. 0 1
      dist/js/gdtList/index.aac2d064.js
  30. 0 1
      dist/js/index/index.01d9998c.js
  31. 1 1
      dist/js/index/index.e379fe2a.js
  32. 1 0
      dist/js/index/index.f748d341.js
  33. 1 1
      dist/js/layout/index.e762a238.js
  34. 1 1
      dist/js/layout/index_head.447068a7.js
  35. 1 0
      dist/js/limitManage/index.742f7320.js
  36. 0 1
      dist/js/limitManage/index.b24c8a63.js
  37. 1 0
      dist/js/list/list.4152882d.js
  38. 1 1
      dist/js/materialBlock/materialBlock.c0f0b92f.js
  39. 0 1
      dist/js/materialLibrary/index.15385e2a.js
  40. 1 0
      dist/js/materialLibrary/index.d4b7654a.js
  41. 1 1
      dist/js/materialTs/materialTs.37d65a02.js
  42. 0 1
      dist/js/memberManage/index.c29f7d5a.js
  43. 1 0
      dist/js/memberManage/index.e24915ef.js
  44. 1 1
      dist/js/menu/index.226f021f.js
  45. 1 1
      dist/js/miniprogram/index.2491d1c5.js
  46. 0 1
      dist/js/projectManage/index.32a3da08.js
  47. 1 0
      dist/js/projectManage/index.cdf4b9d1.js
  48. 0 0
      dist/js/question-filled/question-filled.6b9c8151.js
  49. 1 1
      dist/js/radioGroup/radioGroup.1cef0455.js
  50. 0 1
      dist/js/tableInfo/tableInfo.284ce5d3.js
  51. 1 0
      dist/js/tableInfo/tableInfo.37c7ce9b.js
  52. 1 0
      dist/js/taskList/index.2c86aa25.js
  53. 0 1
      dist/js/taskList/index.8f49ca92.js
  54. 0 1
      dist/js/teamManage/index.f6a1dd5f.js
  55. 1 0
      dist/js/teamManage/index.fee1a185.js
  56. 1 1
      dist/js/timeScreen/timeScreen.e59fea90.js
  57. 1 1
      dist/js/warning/warning.1c682038.js
  58. 1 1
      dist/js/wechatPage/index.c66f81c8.js
  59. 1 1
      dist/js/weekTime/weekTime.d5ddfa42.js
  60. 3 0
      dist/js/xlsx/xlsx.71efe0d2.js
  61. 79 0
      package-lock.json
  62. 4 1
      package.json
  63. 4 1
      src/api/api.ts
  64. 1 0
      src/assets/style/element/index.scss
  65. 1 1
      src/assets/style/index.scss
  66. 258 0
      src/common/export/Export2Excel.js
  67. 25 0
      src/common/export/index.js
  68. 0 170
      src/common/export/js-table2excel.js
  69. 90 0
      src/common/export/setMethods.js
  70. 2 10
      src/components/basisMoudle/Home/hooks/projectTrend.ts
  71. 3 2
      src/components/basisMoudle/layout/header/logo.vue
  72. 263 0
      src/components/businessMoudle/dataManagement/acStatement.vue/dataList.vue
  73. 69 0
      src/components/businessMoudle/dataManagement/acStatement.vue/hooks/index.ts
  74. 346 0
      src/components/businessMoudle/dataManagement/acStatement.vue/hooks/trend.ts
  75. 170 0
      src/components/businessMoudle/dataManagement/acStatement.vue/index.vue
  76. 207 0
      src/components/businessMoudle/dataManagement/acStatement.vue/trend.vue
  77. 23 15
      src/components/capsulationMoudle/downLoadTable.vue
  78. 11 0
      src/router/index.ts

Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/assets/index.193eab92.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/assets/index.5668eb54.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/assets/index.396dcb47.css


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/assets/index.936eabe9.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/assets/index.af38243f.css


+ 2 - 2
dist/index.html

@@ -12,7 +12,7 @@
12 12
 			document.write('<script src="' + src + '"><\/script>');
13 13
 			})();
14 14
     </script>
15
-    <script type="module" crossorigin src="./assets/index.936eabe9.js"></script>
15
+    <script type="module" crossorigin src="./assets/index.5668eb54.js"></script>
16 16
     <link rel="modulepreload" href="./js/@vue/@vue.5bfcce30.js">
17 17
     <link rel="modulepreload" href="./js/vue-router/vue-router.ccba075e.js">
18 18
     <link rel="modulepreload" href="./js/vue-demi/vue-demi.4f3c4c97.js">
@@ -33,7 +33,7 @@
33 33
     <link rel="modulepreload" href="./js/zrender/zrender.fe59a237.js">
34 34
     <link rel="modulepreload" href="./js/echarts/echarts.f2b2ac0c.js">
35 35
     <link rel="stylesheet" href="./assets/element-plus.7d34c13b.css">
36
-    <link rel="stylesheet" href="./assets/index.af38243f.css">
36
+    <link rel="stylesheet" href="./assets/index.f9b8fc74.css">
37 37
   </head>
38 38
   <body>
39 39
     <div id="app"></div>

Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/Home/index.7613df96.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/Home/index.b338f105.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/_dialog/_dialog.06ad7f5f.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/_input/_input.fb81cdd2.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/_select/_select.275fc137.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 3 - 0
dist/js/acStatement.vue/index.31e5b75e.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/adTask/index.1810f4d1.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/adqManage/index.ac6615ca.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/adqManage/index.d42304ef.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/api/api.67259073.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/basisMoudle/error.ca7b6b72.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/basisMoudle/login.0782c651.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/basisMoudle/login.a95a9ffa.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/caret-top/caret-top.80cf6e6c.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/collectClip/index.2c51a6b6.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 7
dist/js/configArea/index.846c8753.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 7 - 0
dist/js/configArea/index.ec1bf995.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/define/define.8e1f4aae.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/echarts/Echarts.8aab55f0.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/export/Export2Excel.4f8dda80.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/file-saver/file-saver.864809c5.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/gdtList/index.6876fbf7.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/gdtList/index.aac2d064.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/index/index.01d9998c.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/index/index.e379fe2a.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/index/index.f748d341.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/layout/index.e762a238.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/layout/index_head.447068a7.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/limitManage/index.742f7320.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/limitManage/index.b24c8a63.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/list/list.4152882d.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/materialBlock/materialBlock.c0f0b92f.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/materialLibrary/index.15385e2a.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/materialLibrary/index.d4b7654a.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/materialTs/materialTs.37d65a02.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/memberManage/index.c29f7d5a.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/memberManage/index.e24915ef.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/menu/index.226f021f.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/miniprogram/index.2491d1c5.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/projectManage/index.32a3da08.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/projectManage/index.cdf4b9d1.js


dist/js/question-filled/question-filled.0e32ad21.js → dist/js/question-filled/question-filled.6b9c8151.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/radioGroup/radioGroup.1cef0455.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/tableInfo/tableInfo.284ce5d3.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/tableInfo/tableInfo.37c7ce9b.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/taskList/index.2c86aa25.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/taskList/index.8f49ca92.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 0 - 1
dist/js/teamManage/index.f6a1dd5f.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 0
dist/js/teamManage/index.fee1a185.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/timeScreen/timeScreen.e59fea90.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/warning/warning.1c682038.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/wechatPage/index.c66f81c8.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 1 - 1
dist/js/weekTime/weekTime.d5ddfa42.js


Dosya farkı çok büyük olduğundan ihmal edildi
+ 3 - 0
dist/js/xlsx/xlsx.71efe0d2.js


+ 79 - 0
package-lock.json

@@ -522,6 +522,11 @@
522 522
       "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
523 523
       "dev": true
524 524
     },
525
+    "adler-32": {
526
+      "version": "1.3.1",
527
+      "resolved": "https://registry.npmjs.org/adler-32/-/adler-32-1.3.1.tgz",
528
+      "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="
529
+    },
525 530
     "ajv": {
526 531
       "version": "6.12.6",
527 532
       "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz",
@@ -650,6 +655,15 @@
650 655
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
651 656
       "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg=="
652 657
     },
658
+    "cfb": {
659
+      "version": "1.2.2",
660
+      "resolved": "https://registry.npmjs.org/cfb/-/cfb-1.2.2.tgz",
661
+      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
662
+      "requires": {
663
+        "adler-32": "~1.3.0",
664
+        "crc-32": "~1.2.0"
665
+      }
666
+    },
653 667
     "chalk": {
654 668
       "version": "4.1.2",
655 669
       "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz",
@@ -737,6 +751,11 @@
737 751
         "wrap-ansi": "^6.2.0"
738 752
       }
739 753
     },
754
+    "codepage": {
755
+      "version": "1.15.0",
756
+      "resolved": "https://registry.npmjs.org/codepage/-/codepage-1.15.0.tgz",
757
+      "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA=="
758
+    },
740 759
     "color-convert": {
741 760
       "version": "1.9.3",
742 761
       "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-1.9.3.tgz",
@@ -772,6 +791,11 @@
772 791
       "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
773 792
       "dev": true
774 793
     },
794
+    "crc-32": {
795
+      "version": "1.2.2",
796
+      "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
797
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
798
+    },
775 799
     "cross-spawn": {
776 800
       "version": "7.0.3",
777 801
       "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -1322,6 +1346,11 @@
1322 1346
         "flat-cache": "^3.0.4"
1323 1347
       }
1324 1348
     },
1349
+    "file-saver": {
1350
+      "version": "2.0.5",
1351
+      "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz",
1352
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
1353
+    },
1325 1354
     "fill-range": {
1326 1355
       "version": "7.0.1",
1327 1356
       "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
@@ -1372,6 +1401,11 @@
1372 1401
         "mime-types": "^2.1.12"
1373 1402
       }
1374 1403
     },
1404
+    "frac": {
1405
+      "version": "1.1.2",
1406
+      "resolved": "https://registry.npmjs.org/frac/-/frac-1.1.2.tgz",
1407
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="
1408
+    },
1375 1409
     "fs": {
1376 1410
       "version": "0.0.1-security",
1377 1411
       "resolved": "https://registry.npmmirror.com/fs/-/fs-0.0.1-security.tgz",
@@ -2010,6 +2044,11 @@
2010 2044
       "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
2011 2045
       "dev": true
2012 2046
     },
2047
+    "raw-loader": {
2048
+      "version": "0.5.1",
2049
+      "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-0.5.1.tgz",
2050
+      "integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q=="
2051
+    },
2013 2052
     "readdirp": {
2014 2053
       "version": "3.6.0",
2015 2054
       "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
@@ -2112,6 +2151,14 @@
2112 2151
         "neo-async": "^2.6.2"
2113 2152
       }
2114 2153
     },
2154
+    "script-loader": {
2155
+      "version": "0.7.2",
2156
+      "resolved": "https://registry.npmjs.org/script-loader/-/script-loader-0.7.2.tgz",
2157
+      "integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==",
2158
+      "requires": {
2159
+        "raw-loader": "~0.5.1"
2160
+      }
2161
+    },
2115 2162
     "scule": {
2116 2163
       "version": "0.2.1",
2117 2164
       "resolved": "https://registry.npmmirror.com/scule/-/scule-0.2.1.tgz",
@@ -2221,6 +2268,14 @@
2221 2268
       "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
2222 2269
       "dev": true
2223 2270
     },
2271
+    "ssf": {
2272
+      "version": "0.11.2",
2273
+      "resolved": "https://registry.npmjs.org/ssf/-/ssf-0.11.2.tgz",
2274
+      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
2275
+      "requires": {
2276
+        "frac": "~1.1.2"
2277
+      }
2278
+    },
2224 2279
     "string-width": {
2225 2280
       "version": "4.2.3",
2226 2281
       "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz",
@@ -2699,6 +2754,16 @@
2699 2754
       "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
2700 2755
       "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ=="
2701 2756
     },
2757
+    "wmf": {
2758
+      "version": "1.0.2",
2759
+      "resolved": "https://registry.npmjs.org/wmf/-/wmf-1.0.2.tgz",
2760
+      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw=="
2761
+    },
2762
+    "word": {
2763
+      "version": "0.3.0",
2764
+      "resolved": "https://registry.npmjs.org/word/-/word-0.3.0.tgz",
2765
+      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="
2766
+    },
2702 2767
     "word-wrap": {
2703 2768
       "version": "1.2.3",
2704 2769
       "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -2744,6 +2809,20 @@
2744 2809
       "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
2745 2810
       "dev": true
2746 2811
     },
2812
+    "xlsx": {
2813
+      "version": "0.18.5",
2814
+      "resolved": "https://registry.npmjs.org/xlsx/-/xlsx-0.18.5.tgz",
2815
+      "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==",
2816
+      "requires": {
2817
+        "adler-32": "~1.3.0",
2818
+        "cfb": "~1.2.1",
2819
+        "codepage": "~1.15.0",
2820
+        "crc-32": "~1.2.1",
2821
+        "ssf": "~0.11.2",
2822
+        "wmf": "~1.0.1",
2823
+        "word": "~0.3.0"
2824
+      }
2825
+    },
2747 2826
     "y18n": {
2748 2827
       "version": "4.0.3",
2749 2828
       "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",

+ 4 - 1
package.json

@@ -14,6 +14,7 @@
14 14
     "axios": "^0.27.2",
15 15
     "echarts": "^5.3.3",
16 16
     "element-plus": "^2.3.8",
17
+    "file-saver": "^2.0.5",
17 18
     "fs": "0.0.1-security",
18 19
     "js-md5": "^0.7.3",
19 20
     "lodash": "^4.17.21",
@@ -22,10 +23,12 @@
22 23
     "pinia-plugin-persist": "^1.0.0",
23 24
     "postcss-pxtorem": "^6.0.0",
24 25
     "qrcode": "^1.5.3",
26
+    "script-loader": "^0.7.2",
25 27
     "vue": "^3.2.25",
26 28
     "vue-clipboard3": "^2.0.0",
27 29
     "vue-router": "^4.1.1",
28
-    "vuedraggable": "^4.1.0"
30
+    "vuedraggable": "^4.1.0",
31
+    "xlsx": "^0.18.5"
29 32
   },
30 33
   "devDependencies": {
31 34
     "@iconify-json/ep": "^1.1.6",

+ 4 - 1
src/api/api.ts

@@ -147,7 +147,10 @@ export enum Api{
147 147
     user_edit = '/api/sys/userEdit',
148 148
     user_dele = '/api/sys/userDel',
149 149
 
150
-
150
+    //数据管理
151
+    report_trend = '/api/report/trend',
152
+    report_ratio = '/api/report/ratio',
153
+    report_accountReportList = '/api/report/accountReportList',
151 154
 
152 155
 
153 156
 

+ 1 - 0
src/assets/style/element/index.scss

@@ -72,4 +72,5 @@
72 72
 .el-popper.is-light {
73 73
   box-shadow: 0 2px 12px 0 rgba(0,0,0,.2);
74 74
   border: none !important;
75
+  // inset-inline: auto !important;
75 76
 }

+ 1 - 1
src/assets/style/index.scss

@@ -70,7 +70,7 @@ $primary-color: #3173FF!important;
70 70
   height:30px!important;
71 71
 }
72 72
 .el-popper{
73
-  max-width: 600px;
73
+  max-width: 700px;
74 74
 }
75 75
 .el-dialog__body{
76 76
   padding: 30px;

+ 258 - 0
src/common/export/Export2Excel.js

@@ -0,0 +1,258 @@
1
+/* eslint-disable */
2
+import FileSaver from 'file-saver';
3
+import * as XLSX from "xlsx"
4
+
5
+function generateArray (table) {
6
+  var out = []
7
+  var rows = table.querySelectorAll("tr")
8
+  var ranges = []
9
+  for (var R = 0; R < rows.length; ++R) {
10
+    var outRow = []
11
+    var row = rows[R]
12
+    var columns = row.querySelectorAll("td")
13
+    for (var C = 0; C < columns.length; ++C) {
14
+      var cell = columns[C]
15
+      var colspan = cell.getAttribute("colspan")
16
+      var rowspan = cell.getAttribute("rowspan")
17
+      var cellValue = cell.innerText
18
+      if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue
19
+
20
+      //Skip ranges
21
+      ranges.forEach(function (range) {
22
+        if (
23
+          R >= range.s.r &&
24
+          R <= range.e.r &&
25
+          outRow.length >= range.s.c &&
26
+          outRow.length <= range.e.c
27
+        ) {
28
+          for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null)
29
+        }
30
+      })
31
+
32
+      //Handle Row Span
33
+      if (rowspan || colspan) {
34
+        rowspan = rowspan || 1
35
+        colspan = colspan || 1
36
+        ranges.push({
37
+          s: {
38
+            r: R,
39
+            c: outRow.length
40
+          },
41
+          e: {
42
+            r: R + rowspan - 1,
43
+            c: outRow.length + colspan - 1
44
+          }
45
+        })
46
+      }
47
+
48
+      //Handle Value
49
+      outRow.push(cellValue !== "" ? cellValue : null)
50
+
51
+      //Handle Colspan
52
+      if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null)
53
+    }
54
+    out.push(outRow)
55
+  }
56
+  return [out, ranges]
57
+}
58
+
59
+function datenum (v, date1904) {
60
+  if (date1904) v += 1462
61
+  var epoch = Date.parse(v)
62
+  return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000)
63
+}
64
+
65
+function sheet_from_array_of_arrays (data, opts) {
66
+  var ws = {}
67
+  var range = {
68
+    s: {
69
+      c: 10000000,
70
+      r: 10000000
71
+    },
72
+    e: {
73
+      c: 0,
74
+      r: 0
75
+    }
76
+  }
77
+  for (var R = 0; R != data.length; ++R) {
78
+    for (var C = 0; C != data[R].length; ++C) {
79
+      if (range.s.r > R) range.s.r = R
80
+      if (range.s.c > C) range.s.c = C
81
+      if (range.e.r < R) range.e.r = R
82
+      if (range.e.c < C) range.e.c = C
83
+      var cell = {
84
+        v: data[R][C]
85
+      }
86
+      if (cell.v == null) continue
87
+      var cell_ref = XLSX.utils.encode_cell({
88
+        c: C,
89
+        r: R
90
+      })
91
+
92
+      if (typeof cell.v === "number") cell.t = "n"
93
+      else if (typeof cell.v === "boolean") cell.t = "b"
94
+      else if (cell.v instanceof Date) {
95
+        cell.t = "n"
96
+        cell.z = XLSX.SSF._table[14]
97
+        cell.v = datenum(cell.v)
98
+      } else cell.t = "s"
99
+
100
+      ws[cell_ref] = cell
101
+    }
102
+  }
103
+  if (range.s.c < 10000000) ws["!ref"] = XLSX.utils.encode_range(range)
104
+  return ws
105
+}
106
+
107
+function Workbook () {
108
+  if (!(this instanceof Workbook)) return new Workbook()
109
+  this.SheetNames = []
110
+  this.Sheets = {}
111
+}
112
+
113
+function s2ab (s) {
114
+  var buf = new ArrayBuffer(s.length)
115
+  var view = new Uint8Array(buf)
116
+  for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff
117
+  return buf
118
+}
119
+
120
+export function export_table_to_excel (id) {
121
+  var theTable = document.getElementById(id)
122
+  var oo = generateArray(theTable)
123
+  var ranges = oo[1]
124
+
125
+  /* original data */
126
+  var data = oo[0]
127
+  var ws_name = "SheetJS"
128
+
129
+  var wb = new Workbook(),
130
+    ws = sheet_from_array_of_arrays(data)
131
+
132
+  /* add ranges to worksheet */
133
+  // ws['!cols'] = ['apple', 'banan'];
134
+  ws["!merges"] = ranges
135
+
136
+  /* add worksheet to workbook */
137
+  wb.SheetNames.push(ws_name)
138
+  wb.Sheets[ws_name] = ws
139
+
140
+  var wbout = XLSX.write(wb, {
141
+    bookType: "xlsx",
142
+    bookSST: false,
143
+    type: "binary"
144
+  })
145
+
146
+  FileSaver.saveAs(
147
+    new Blob([s2ab(wbout)], {
148
+      type: "application/octet-stream"
149
+    }),
150
+    "test.xlsx"
151
+  )
152
+}
153
+
154
+
155
+
156
+//主要修改此函数内的方法
157
+
158
+export function export_json_to_excel ({
159
+  multiHeader = [],
160
+  header,
161
+  data,
162
+  sheetname,
163
+  filename,
164
+  merges = [],
165
+  autoWidth = true,
166
+  bookType = "xlsx"
167
+} = {}) {
168
+  /* original data */
169
+  filename = filename || "excel-list"
170
+  data = [...data]
171
+
172
+  for (var i = 0; i < header.length; i++) {
173
+    data[i].unshift(header[i])
174
+  }
175
+
176
+  // data.unshift(header)
177
+
178
+  for (let i = multiHeader.length - 1; i > -1; i--) {
179
+    data.unshift(multiHeader[i])
180
+  }
181
+
182
+  var ws_name = sheetname
183
+  var wb = new Workbook(),
184
+    ws = []
185
+  for (var j = 0; j < header.length; j++) {
186
+    ws.push(sheet_from_array_of_arrays(data[j]))
187
+  }
188
+
189
+  if (merges.length > 0) {
190
+    if (!ws["!merges"]) ws["!merges"] = []
191
+    merges.forEach(item => {
192
+      ws["!merges"].push(XLSX.utils.decode_range(item))
193
+    })
194
+  }
195
+  // console.log("width", autoWidth)
196
+  if (autoWidth) {
197
+    /*设置worksheet每列的最大宽度*/
198
+    var colWidth = []
199
+    for (var k = 0; k < header.length; k++) {
200
+      colWidth.push(
201
+        data[k].map(row =>
202
+          row.map(val => {
203
+            /*先判断是否为null/undefined*/
204
+            if (val == null) {
205
+              return {
206
+                wch: 10
207
+              }
208
+            } else if (val.toString().charCodeAt(0) > 255) {
209
+              /*再判断是否为中文*/
210
+              return {
211
+                wch: val.toString().length * 2
212
+              }
213
+            } else {
214
+              return {
215
+                wch: val.toString().length
216
+              }
217
+            }
218
+          })
219
+        )
220
+      )
221
+    }
222
+
223
+    /*以第一行为初始值*/
224
+    let result = []
225
+    for (var k = 0; k < colWidth.length; k++) {
226
+      result[k] = colWidth[k][0]
227
+      for (let i = 1; i < colWidth[k].length; i++) {
228
+        for (let j = 0; j < colWidth[k][i].length; j++) {
229
+          if (result[k][j]["wch"] < colWidth[k][i][j]["wch"]) {
230
+            result[k][j]["wch"] = colWidth[k][i][j]["wch"]
231
+          }
232
+        }
233
+      }
234
+    }
235
+    // 分别给sheet表设置宽度
236
+    for (var l = 0; l < result.length; l++) {
237
+      ws[l]["!cols"] = result[l]
238
+    }
239
+  }
240
+
241
+  /* add worksheet to workbook */
242
+  for (var k = 0; k < header.length; k++) {
243
+    wb.SheetNames.push(ws_name[k])
244
+    wb.Sheets[ws_name[k]] = ws[k]
245
+  }
246
+
247
+  var wbout = XLSX.write(wb, {
248
+    bookType: bookType,
249
+    bookSST: false,
250
+    type: "binary"
251
+  })
252
+  FileSaver.saveAs(
253
+    new Blob([s2ab(wbout)], {
254
+      type: "application/octet-stream"
255
+    }),
256
+    `${filename}.${bookType}`
257
+  )
258
+}

+ 25 - 0
src/common/export/index.js

@@ -0,0 +1,25 @@
1
+import { json2excel } from "./setMethods.js"
2
+
3
+export function exportOrder ({ excelDatas, name }) {
4
+  let excelDatas1 = excelDatas
5
+  excelDatas1.forEach((item1) => {
6
+    item1.tableDatas.forEach((item) => {
7
+      for (var i in item) {
8
+        if (i.indexOf('ID') == -1 && i.indexOf('_id') == -1) {
9
+          if (item[i] != '' && item[i] != null) {
10
+            if (!isNaN(item[i])) {
11
+              item[i] = Number(item[i])
12
+            }
13
+          } else {
14
+          }
15
+        }
16
+        if (i.indexOf('date') != -1) {
17
+          if (item[i] && item[i] != '') {
18
+            item[i] = !isNaN(new Date(item[i])) ? new Date(item[i]) : item[i]
19
+          }
20
+        }
21
+      }
22
+    })
23
+  })
24
+  json2excel(excelDatas1, name, true, "xlsx")//   引入的函数
25
+}

+ 0 - 170
src/common/export/js-table2excel.js

@@ -1,170 +0,0 @@
1
-/* eslint-disable */
2
-let idTmr;
3
-const getExplorer = () => {
4
-    let explorer = window.navigator.userAgent;
5
-    //ie
6
-    if (explorer.indexOf("MSIE") >= 0) {
7
-        return 'ie';
8
-    }
9
-    //firefox
10
-
11
-    else if (explorer.indexOf("Firefox") >= 0) {
12
-        return 'Firefox';
13
-    }
14
-    //Chrome
15
-    else if (explorer.indexOf("Chrome") >= 0) {
16
-        return 'Chrome';
17
-    }
18
-    //Opera
19
-    else if (explorer.indexOf("Opera") >= 0) {
20
-        return 'Opera';
21
-    }
22
-    //Safari
23
-    else if (explorer.indexOf("Safari") >= 0) {
24
-        return 'Safari';
25
-    }
26
-}
27
-// 判断浏览器是否为IE
28
-const exportToExcel = (data, name) => {
29
-
30
-    // 判断是否为IE
31
-    if (getExplorer() == 'ie') {
32
-        tableToIE(data, name)
33
-    } else {
34
-        tableToNotIE(data, name)
35
-    }
36
-}
37
-
38
-const Cleanup = () => {
39
-    window.clearInterval(idTmr);
40
-}
41
-
42
-// ie浏览器下执行
43
-const tableToIE = (data, name) => {
44
-    let curTbl = data;
45
-    let oXL = new ActiveXObject("Excel.Application");
46
-
47
-    //创建AX对象excel
48
-    let oWB = oXL.Workbooks.Add();
49
-    //获取workbook对象
50
-    let xlsheet = oWB.Worksheets(1);
51
-    //激活当前sheet
52
-    let sel = document.body.createTextRange();
53
-    sel.moveToElementText(curTbl);
54
-    //把表格中的内容移到TextRange中
55
-    sel.select;
56
-    //全选TextRange中内容
57
-    sel.execCommand("Copy");
58
-    //复制TextRange中内容
59
-    xlsheet.Paste();
60
-    //粘贴到活动的EXCEL中
61
-
62
-    oXL.Visible = true;
63
-    //设置excel可见属性
64
-
65
-    try {
66
-        let fname = oXL.Application.GetSaveAsFilename("Excel.xls", "Excel Spreadsheets (*.xls), *.xls");
67
-    } catch (e) {
68
-        print("Nested catch caught " + e);
69
-    } finally {
70
-        oWB.SaveAs(fname);
71
-
72
-        oWB.Close(savechanges = false);
73
-        //xls.visible = false;
74
-        oXL.Quit();
75
-        oXL = null;
76
-        // 结束excel进程,退出完成
77
-        window.setInterval("Cleanup();", 1);
78
-        idTmr = window.setInterval("Cleanup();", 1);
79
-    }
80
-}
81
-
82
-// 非ie浏览器下执行
83
-const tableToNotIE = (function () {
84
-    // 编码要用utf-8不然默认gbk会出现中文乱码
85
-    const uri = 'data:application/vnd.ms-excel;base64,',
86
-        template = '<html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta charset="UTF-8"><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>{worksheet}</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>{table}</table></body></html>';
87
-
88
-    const base64 = function (s) {
89
-        return window.btoa(unescape(encodeURIComponent(s)));
90
-    }
91
-
92
-    const format = (s, c) => {
93
-        return s.replace(/{(\w+)}/g,
94
-            (m, p) => {
95
-                return c[p];
96
-            })
97
-    }
98
-
99
-    return (table, name) => {
100
-        const ctx = {
101
-            worksheet: name,
102
-            table
103
-        }
104
-        const url = uri + base64(format(template, ctx));
105
-        if (navigator.userAgent.indexOf("Firefox") > -1){
106
-            window.location.href = url
107
-        } else {
108
-            const aLink = document.createElement('a');
109
-            aLink.href = url;
110
-            aLink.download = name || '';
111
-            let event;
112
-            if (window.MouseEvent) {
113
-                event = new MouseEvent('click');
114
-            } else {
115
-                event = document.createEvent('MouseEvents');
116
-                event.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
117
-            }
118
-            aLink.dispatchEvent(event);
119
-        }
120
-    }
121
-})()
122
-
123
-// 导出函数
124
-const table2excel = (column, data, excelName) => {
125
-    const typeMap = {
126
-        image: getImageHtml,
127
-        text: getTextHtml
128
-    }
129
-
130
-    let thead = column.reduce((result, item) => {
131
-        result += `<th>${item.title}</th>`
132
-        return result
133
-    }, '')
134
-
135
-    thead = `<thead><tr>${thead}</tr></thead>`
136
-
137
-    let tbody = data.reduce((result, row) => {
138
-        const temp = column.reduce((tds, col) => {
139
-            let newTds =''
140
-            if((col.hasPercent || row.time == '对比') && row[col.key] && !(col.key=='time' || col.key=='serName')){
141
-                newTds = typeMap[col.type || 'text']((row[col.key]+'%'), col)
142
-            }else{
143
-                newTds = typeMap[col.type || 'text'](row[col.key], col)
144
-            }
145
-            tds += newTds
146
-            // tds += typeMap[col.type || 'text'](row[col.key], col)
147
-            return tds
148
-        }, '')
149
-        result += `<tr>${temp}</tr>`
150
-        return result
151
-    }, '')
152
-
153
-    tbody = `<tbody>${tbody}</tbody>`
154
-
155
-    const table = thead + tbody
156
-
157
-    // 导出表格
158
-    exportToExcel(table, excelName)
159
-
160
-    function getTextHtml(val) {
161
-        return `<td style="text-align: center">${val||val==0 ? val : '-'}</td>`
162
-    }
163
-
164
-    function getImageHtml(val, options) {
165
-        options = Object.assign({width: 40, height: 60}, options)
166
-        return `<td style="width: ${options.width}px; height: ${options.height}px; text-align: center; vertical-align: middle"><img src="${val}" width=${options.width} height=${options.height}></td>`
167
-    }
168
-}
169
-
170
-export default table2excel

+ 90 - 0
src/common/export/setMethods.js

@@ -0,0 +1,90 @@
1
+
2
+ 
3
+/**
4
+ * Parse the time to string
5
+ * @param {(Object|string|number)} time
6
+ * @param {string} cFormat
7
+ * @returns {string}
8
+ */
9
+ export function parseTime (time, cFormat) {
10
+    if (arguments.length === 0) {
11
+      return null
12
+    }
13
+    const format = cFormat || "{y}-{m}-{d} {h}:{i}:{s}"
14
+    let date
15
+    if (typeof time === "object") {
16
+      date = time
17
+    } else {
18
+      if (typeof time === "string" && /^[0-9]+$/.test(time)) {
19
+        time = parseInt(time)
20
+      }
21
+      if (typeof time === "number" && time.toString().length === 10) {
22
+        time = time * 1000
23
+      }
24
+      date = new Date(time)
25
+    }
26
+    const formatObj = {
27
+      y: date.getFullYear(),
28
+      m: date.getMonth() + 1,
29
+      d: date.getDate(),
30
+      h: date.getHours(),
31
+      i: date.getMinutes(),
32
+      s: date.getSeconds(),
33
+      a: date.getDay()
34
+    }
35
+    // eslint-disable-next-line
36
+    const time_str = format.replace(/{(y|m|d|h|i|s|a)+}/g, (result, key) => {
37
+      let value = formatObj[key]
38
+      // Note: getDay() returns 0 on Sunday
39
+      if (key === "a") {
40
+        return ["日", "一", "二", "三", "四", "五", "六"][value]
41
+      }
42
+      if (result.length > 0 && value < 10) {
43
+        value = "0" + value
44
+      }
45
+      return value || 0
46
+    })
47
+    // eslint-disable-next-line
48
+    return time_str
49
+  }
50
+   
51
+  /**
52
+   * Parse the json to excel
53
+   *  tableJson 导出数据 ; filenames导出表的名字; autowidth表格宽度自动 true or false; bookTypes xlsx & csv & txt
54
+   * @param {(Object)} tableJson
55
+   * @param {string} filenames
56
+   * @param {boolean} autowidth
57
+   * @param {string} bookTypes
58
+   */
59
+  export function json2excel (tableJson, filenames, autowidth, bookTypes) {
60
+    import("./Export2Excel").then(excel => {
61
+      var tHeader = []
62
+      var dataArr = []
63
+      var sheetnames = []
64
+      for (var i in tableJson) {
65
+        tHeader.push(tableJson[i].tHeader)
66
+        dataArr.push(formatJson(tableJson[i].filterVal, tableJson[i].tableDatas))
67
+        sheetnames.push(tableJson[i].sheetName)
68
+      }
69
+      excel.export_json_to_excel({
70
+        header: tHeader,
71
+        data: dataArr,
72
+        sheetname: sheetnames,
73
+        filename: filenames,
74
+        autoWidth: autowidth,
75
+        bookType: bookTypes
76
+      })
77
+    })
78
+  }
79
+  // 数据过滤,时间过滤
80
+  function formatJson (filterVal, jsonData) {
81
+    return jsonData.map(v =>
82
+      filterVal.map(j => {
83
+        if (j === "timestamp") {
84
+          return parseTime(v[j])
85
+        } else {
86
+          return v[j]
87
+        }
88
+      })
89
+    )
90
+    }

+ 2 - 10
src/components/basisMoudle/Home/hooks/projectTrend.ts

@@ -21,16 +21,6 @@ export const projectTrendTs = ({
21 21
   })
22 22
 
23 23
 
24
-//   let base = +new Date(1968, 9, 3);
25
-// let oneDay = 24 * 3600 * 1000;
26
-// const date = reactive<any[]>([]);
27
-// let data = [Math.random() * 300];
28
-// for (let i = 1; i < 20000; i++) {
29
-//   var now = new Date((base += oneDay));
30
-//   date.push([now.getFullYear(), now.getMonth() + 1, now.getDate()].join('/'));
31
-//   data.push(Math.round((Math.random() - 0.5) * 20 + data[i - 1]));
32
-// }
33
-
34 24
   const getTrendOption = (yAxis, date, data) => {
35 25
     return {
36 26
       tooltip: {
@@ -92,6 +82,7 @@ export const projectTrendTs = ({
92 82
     dataInfo.data.push({
93 83
       name: dataInfo.trend1_name,
94 84
       type: 'line',
85
+      smooth: true,
95 86
       itemStyle: {
96 87
         color: '#bfdaff'
97 88
       },
@@ -109,6 +100,7 @@ export const projectTrendTs = ({
109 100
       dataInfo.data.push({
110 101
         name: dataInfo.trend2_name,
111 102
         type: 'line',
103
+        smooth: true,
112 104
         yAxisIndex: 1,
113 105
         itemStyle: {
114 106
           color: '#f9b65d'

+ 3 - 2
src/components/basisMoudle/layout/header/logo.vue

@@ -1,7 +1,7 @@
1 1
 <template>
2 2
   <div class="logoBox">
3 3
     <img src="@/assets/img/logo2.jpg" class="logo" @click="goHome" alt="">
4
-    <el-select v-model="teamValue" class="selectTeam" @change="teamSelectChange" placeholder="请选择团队" size="default" v-if="userInfo.userAuth <= 10">
4
+    <el-select v-model="teamValue" class="selectTeam" @change="teamSelectChange" placeholder="请选择团队" size="default" v-if="userInfo.userAuth <= 10 && (route?.name as string)?.indexOf('dataNoTeam') == -1">
5 5
       <el-option v-for="item in teamList" :key="item.id" :label="item.name" :value="String(item.id)" />
6 6
     </el-select>
7 7
   </div>
@@ -11,10 +11,11 @@
11 11
 <script lang="ts" setup>
12 12
 import { getCookie, setCookie } from "@/common/common";
13 13
 import { nextTick, onBeforeMount, ref } from "vue";
14
-import { useRouter } from "vue-router";
14
+import { useRouter, useRoute } from "vue-router";
15 15
 import TeamDialog from "../../teamDialog.vue";
16 16
 
17 17
 const router = useRouter();
18
+const route = useRoute();
18 19
 const teamValue = ref<any>('')
19 20
 const userInfo = ref()
20 21
 const teamList = ref()

+ 263 - 0
src/components/businessMoudle/dataManagement/acStatement.vue/dataList.vue

@@ -0,0 +1,263 @@
1
+<template>
2
+  <div v-loading="loading" class="table_container">
3
+    <div class="tableTop">
4
+      <div class="title">详细数据</div>
5
+      <el-button type="primary" plain @click="exportEvent">导出数据</el-button>
6
+    </div>
7
+    <div>
8
+      <el-table ref="tableAccountRef" :data="tableInfo.tableList"
9
+        :header-cell-style="tableHeaderStyle" style="width: 100%;" border empty-text="暂无数据" :summary-method="getSummaries"
10
+        show-summary max-height="calc(100vh - 280px)">
11
+        <template v-for="item in tableInfo.descol">
12
+          <el-table-column :fixed="item.disabled == 1" :prop="item.key_value"
13
+            :min-width="item.key_value != 'advertiser_status' && item.key_value != 'advertiser_nick' && item.label.length <= 4 ? '120px' : item.label.length <= 8 ? '150px' : '200px'">
14
+            <template #header>
15
+              <div class="flex" :class="[tableInfo.sortKey == item.key_value ? 'active_css' : '']">
16
+                <span :style="{ color: tableInfo.sortKey == item.key_value ? '#3173FF' : '' }">{{ item.label }}</span>
17
+                <el-tooltip v-if="item.tooltip && item.tooltip != item.label" placement="top" effect="dark"
18
+                  :content="item.tooltip"><i-ep-QuestionFilled class="lMar5 c-999 f14 pointer" /></el-tooltip>
19
+                <div v-if="item.if_sort == 1" class="sortBox lMar5 pointer">
20
+                  <div class="sortItem" @click="sortEvent(item.key_value, 'asc')">
21
+                    <el-icon
22
+                      :color="(tableInfo.sortType == 'asc' && tableInfo.sortKey == item.key_value) ? '#3173FF' : ''"><i-ep-CaretTop /></el-icon>
23
+                  </div>
24
+                  <div class="sortItem" @click="sortEvent(item.key_value, 'desc')">
25
+                    <el-icon
26
+                      :color="(tableInfo.sortType == 'desc' && tableInfo.sortKey == item.key_value) ? '#3173FF' : ''"><i-ep-CaretBottom /></el-icon>
27
+                  </div>
28
+                </div>
29
+              </div>
30
+            </template>
31
+            <template #default="scope">
32
+              <!-- 其他 -->
33
+              <div class="cellDiv" :class="tableInfo.sortKey == item.key_value ? 'active_css' : ''">
34
+                <el-tooltip :disabled="!(scope.row[item.key_value] && scope.row[item.key_value].length > 30)"
35
+                  effect="dark" :content="scope.row[item.key_value] + ''">
36
+                  <div class="clampTwo line21" style="flex: 1">
37
+                    {{ scope.row[item.key_value] || scope.row[item.key_value] == 0 ?
38
+                      hasDot(scope.row[item.key_value], 2, false) : '-' }}<span
39
+                      v-if="item.label.indexOf('率') != -1 && (scope.row[item.key_value] || scope.row[item.key_value] == 0)">%</span>
40
+                  </div>
41
+                </el-tooltip>
42
+              </div>
43
+            </template>
44
+          </el-table-column>
45
+        </template>
46
+      </el-table>
47
+      <div class="paginationBox flex" style="justify-content: center" v-if="Number(tableInfo.total) > 0">
48
+        <el-pagination v-model:currentPage="tableInfo.currentPage" v-model:page-size="tableInfo.pageSize" background
49
+          :total="tableInfo.total" @current-change="handleCurrentChange" />
50
+      </div>
51
+    </div>
52
+  </div>
53
+</template>
54
+<script setup lang="ts">
55
+import { nextTick, onMounted, reactive, ref } from "vue";
56
+import { listTs } from "@/components/businessMoudle/gdtList/ts/list";
57
+import { Api } from "@/api/api";
58
+import { ElMessage } from "element-plus";
59
+import { getDay, hasDot } from "@/common/common";
60
+import http from "@/http/http";
61
+import downLoadTable from "@/components/capsulationMoudle/downLoadTable.vue";
62
+import { exportOrder } from "@/common/export/index.js";
63
+const props = defineProps({
64
+  mainPageInfo: {
65
+    type: Object,
66
+    default: ()=>{}
67
+  }
68
+})
69
+
70
+const downLoadTableRef = ref()
71
+const tableAccountRef = ref()
72
+const loading = ref<boolean>(false)
73
+const tableInfo = reactive<any>({
74
+  tableList: [],
75
+  descol: [],
76
+  summary: [],
77
+  sortKey: '',
78
+  currentPage: 1,
79
+  pageSize: 20,
80
+  total: 0,
81
+  totalPages: 0,//共多少页
82
+  sortType: 'desc'
83
+})
84
+
85
+
86
+//排序
87
+const sortEvent = (row: any, order: string) => {
88
+  tableInfo.sortType = order;
89
+  tableInfo.sortKey = row
90
+  init(1)
91
+}
92
+
93
+//分页
94
+const handleCurrentChange = (val) => {
95
+  tableInfo.currentPage = val
96
+  init(val)
97
+}
98
+
99
+//导出
100
+const exportEvent = async () => {
101
+  loading.value = true;
102
+  let params = {
103
+    page: 1,
104
+    pageSize: 1000,
105
+    start: props.mainPageInfo.time[0],
106
+    end: props.mainPageInfo.time[1],
107
+    date_group: props.mainPageInfo.time_type,
108
+    dimension: props.mainPageInfo.data_group,
109
+    keyword: props.mainPageInfo.keyword,
110
+    sys_user_ids: props.mainPageInfo.user_ids,
111
+    account_ids: props.mainPageInfo.account_ids,
112
+    team_ids: props.mainPageInfo.team_ids,
113
+  }
114
+  if (tableInfo.sortKey) {
115
+    params['field'] = tableInfo.sortKey
116
+    params['order'] = tableInfo.sortType
117
+  }
118
+  let res: any = await http.get(Api.report_accountReportList, params)
119
+  loading.value = false;
120
+  if (res && res.errNo == '0') {
121
+    let list = res.rst.data.list;
122
+      let tHeader = res.rst.data?.explain?.map((v) => {
123
+        return v.label;
124
+      })
125
+      let filterVal = res.rst.data?.explain.map((v) => {
126
+        return v.key_value
127
+      })
128
+      let excelDatas = [
129
+        {
130
+          tHeader: tHeader, // sheet表一头部
131
+          filterVal: filterVal, // 表一的数据字段
132
+          tableDatas: list, // 表一的整体json数据
133
+          sheetName: ''// 表一的sheet名字
134
+        }
135
+      ]
136
+      exportOrder({ excelDatas, name: `数据报表(导出时间:${getDay(0)})` })
137
+  } else {
138
+    ElMessage.error(res.errMsg)
139
+  }
140
+}
141
+
142
+//列表
143
+const init = async (page?: any, pageSize?: any) => {
144
+  loading.value = true;
145
+  let params = {
146
+    page: page ? page : tableInfo.currentPage,
147
+    pageSize: pageSize ? pageSize : tableInfo.pageSize,
148
+    start: props.mainPageInfo.time[0],
149
+    end: props.mainPageInfo.time[1],
150
+    date_group: props.mainPageInfo.time_type,
151
+    dimension: props.mainPageInfo.data_group,
152
+    keyword: props.mainPageInfo.keyword,
153
+    sys_user_ids: props.mainPageInfo.user_ids,
154
+    account_ids: props.mainPageInfo.account_ids,
155
+    team_ids: props.mainPageInfo.team_ids,
156
+  }
157
+  if (tableInfo.sortKey) {
158
+    params['field'] = tableInfo.sortKey
159
+    params['order'] = tableInfo.sortType
160
+  }
161
+  let res: any = await http.get(Api.report_accountReportList, params)
162
+  loading.value = false;
163
+  if (res && res.errNo == '0') {
164
+    if(res.rst?.data?.summary?.length > 0){
165
+      tableInfo.summary = res.rst?.data?.summary[0];
166
+    }
167
+    tableInfo.descol = res.rst?.data?.explain;
168
+    tableInfo.tableList = res.rst?.data?.list;
169
+    tableInfo.total = res.rst?.pageInfo.total
170
+    tableInfo.totalPages = res.rst?.pageInfo.total
171
+  } else {
172
+    ElMessage.error(res.errMsg)
173
+  }
174
+}
175
+
176
+/**合计计算 */
177
+const getSummaries = (param) => {
178
+  const { columns, data } = param
179
+  const sums: string[] = []
180
+  columns.forEach((column, index) => {
181
+    if (index === 0) {
182
+      sums[index] = ''
183
+      return
184
+    }
185
+    if (index === 1) {
186
+      sums[index] = '合计'
187
+      return
188
+    }
189
+    if (tableInfo.summary[column.property] && tableInfo.summary[column.property] != 0) {
190
+      let info = tableInfo.descol.filter((v) => {
191
+        return v.key_value == column.property
192
+      })
193
+      if (info.length > 0 && info[0].label?.indexOf('率') != -1) {
194
+        sums[index] = hasDot(tableInfo.summary[column.property], 2, false) + '%'
195
+      } else {
196
+        sums[index] = hasDot(tableInfo.summary[column.property], 2, false)
197
+      }
198
+    } else {
199
+      sums[index] = ''
200
+    }
201
+  })
202
+  return sums
203
+}
204
+
205
+const {
206
+  tableHeaderStyle,
207
+} = listTs()
208
+
209
+// 暴露自己的属性供父组件使用
210
+defineExpose({
211
+  init,
212
+});
213
+</script>
214
+<style lang="scss" scoped>
215
+:deep(.el-table__body-wrapper) {
216
+  order: 1;
217
+}
218
+
219
+:deep(.el-table__footer-wrapper) {
220
+  border-bottom: var(--el-table-border);
221
+  border-top: none;
222
+}
223
+
224
+:deep(.el-table__footer-wrapper tbody td.el-table__cell) {
225
+  background-color: #fafafa;
226
+}
227
+
228
+.el-table th div.cell {
229
+  white-space: nowrap;
230
+  text-overflow: ellipsis;
231
+  overflow: hidden;
232
+  max-width: 600px;
233
+  /* 设置最大宽度,根据需要调整 */
234
+}
235
+.table_container{
236
+  background-color: #fff;
237
+  margin-top: 10px;
238
+  padding: 20px;
239
+}
240
+.sortBox{
241
+  height: 16px;
242
+  margin-left: 2px;
243
+  margin-top: -4px;
244
+  .sortItem{
245
+    width: 8px;
246
+    height: 8px;
247
+    line-height: 8px;
248
+    cursor: pointer;
249
+    color: #999;
250
+  }
251
+}
252
+.tableTop{
253
+  display: flex;
254
+  align-items: center;
255
+  justify-content: space-between;
256
+  padding-bottom: 20px;
257
+  .title{
258
+    font-size: 14px;
259
+    color: #333;
260
+    font-weight: bold;
261
+  }
262
+}
263
+</style>

+ 69 - 0
src/components/businessMoudle/dataManagement/acStatement.vue/hooks/index.ts

@@ -0,0 +1,69 @@
1
+import { Api } from "@/api/api"
2
+import http from "@/http/http"
3
+import { ATeamList } from "@/store/permission"
4
+import { ElMessage } from "element-plus"
5
+import { reactive, ref } from "vue"
6
+import { getDay } from "@/common/common"
7
+
8
+export const ExpIndex = () => {
9
+  const dataListRef = ref()
10
+  const acTrendRef = ref<{change: ()=>void}>()
11
+  const teamRef = ref()
12
+  const acRef = ref()
13
+  const userRef = ref()
14
+  const pageInfo = reactive({
15
+    data_group_list: [
16
+      { name: '账户', val: 'account' },
17
+      { name: '优化师', val: 'user' },
18
+      { name: '团队', val: 'team' },
19
+    ],
20
+    data_group: 'account',
21
+    time_type: 'day',
22
+    time: [ getDay(-7), getDay(0) ],
23
+    keyword: '',
24
+    acList: [],
25
+    userList: [],
26
+    teamList: [],
27
+    user_ids: [],//优化师
28
+    account_ids: [],//账号
29
+    team_ids: [],//团队
30
+  })
31
+
32
+  /**获取账户列表 */
33
+  const init_acList = async () => {
34
+    let res: any = await http.get(Api.account_listToSelect)
35
+    if (res && res.errNo == '0') {
36
+      pageInfo.acList = res.rst
37
+    } else {
38
+      ElMessage.error(res.errMsg)
39
+    }
40
+  }
41
+  /**获取操作人列表 */
42
+  const getUserList = async () => {
43
+    let res: any = await http.get(Api.user_list, {})
44
+    if (res && res.errNo == '0') {
45
+      pageInfo.userList = res.rst
46
+    } else {
47
+      ElMessage.error(res.errMsg)
48
+    }
49
+  }
50
+  /**团队列表 */
51
+  const getTeamList = async () => {
52
+    ATeamList({}).then((res: any) => {
53
+      pageInfo.teamList = res
54
+    }).catch(() => {
55
+    })
56
+  }
57
+
58
+  return {
59
+    dataListRef,
60
+    acTrendRef,
61
+    userRef,
62
+    teamRef,
63
+    acRef,
64
+    pageInfo,
65
+    init_acList,
66
+    getUserList,
67
+    getTeamList
68
+  }
69
+}

+ 346 - 0
src/components/businessMoudle/dataManagement/acStatement.vue/hooks/trend.ts

@@ -0,0 +1,346 @@
1
+import { Api } from "@/api/api"
2
+import http from "@/http/http"
3
+import { ElMessage } from "element-plus"
4
+import { reactive, ref } from "vue"
5
+
6
+export interface IReportTrend {
7
+  st_date: string,
8
+  en_date: string,
9
+  date_group?: string,
10
+  dimension: string,
11
+  keyword?: string,
12
+  user_ids?: any[],
13
+  account_ids?: any[],
14
+  team_ids?: any[],
15
+  top_num: number
16
+}
17
+
18
+export const ExpTrend = () => {
19
+  const isCollapsed = ref(false)
20
+  const trendLoading = ref(false)
21
+  const overviewRef = ref()
22
+  const topNumRef = ref()
23
+  const tabInfo = reactive({
24
+    value: 'trend',
25
+    list: [
26
+      { name: '趋势分析', id: 'trend' },
27
+      { name: '占比分析', id: 'ratio' },
28
+    ]
29
+  })
30
+  const overviewData = reactive<{ list: any[], [key: string]: any }>({
31
+    list: [
32
+      { name: '消耗', key: 'cost', unit: '元' },
33
+      { name: '展示数', key: 'view_count' },
34
+      { name: '千次展示均价(CPM)', key: 'thousand_display_price', unit: '元' },
35
+      { name: '点击数', key: 'valid_click_count' },
36
+      { name: '点击率', key: 'ctr' },
37
+      { name: '点击均价', key: 'cpc' },
38
+      { name: '转化数', key: 'conversions_count' },
39
+      { name: '转化率', key: 'conversions_rate' },
40
+      { name: '转化成本', key: 'conversions_cost', unit: '元' },
41
+      { name: '激活数', key: 'activated_count' },
42
+      { name: '注册数', key: 'app_register_count' },
43
+    ],
44
+    legendArr: [],
45
+    trendDataList: [],
46
+    ratioDataList: []
47
+  })
48
+  const topNumList = reactive([
49
+    { name: 'Top5数据', val: 5 },
50
+    { name: 'Top10数据', val: 10 },
51
+  ])
52
+  const trendInfo = reactive({
53
+    option: {},
54
+    heights: '320px',
55
+  })
56
+  const ratioInfo = reactive({
57
+    option: {},
58
+    heights: '320px',
59
+  })
60
+
61
+  /**数据报表 - 趋势分析 */
62
+  const reportTrendEvent = async (params: IReportTrend) => {
63
+    trendLoading.value = true;
64
+    let res: any = await http.get(Api.report_trend, params)
65
+    trendLoading.value = false;
66
+    if (res && res.errNo == '0') {
67
+      overviewData.trendDataList = res.rst;
68
+      getTrend(params.dimension)
69
+    } else {
70
+      ElMessage.error(res.errMsg)
71
+    }
72
+  }
73
+
74
+  /**数据报表 - 占比分析 */
75
+  const reportRatioEvent = async (params: IReportTrend) => {
76
+    trendLoading.value = true;
77
+    let res: any = await http.get(Api.report_ratio, params)
78
+    trendLoading.value = false;
79
+    if (res && res.errNo == '0') {
80
+      overviewData.ratioDataList = res.rst;
81
+      radiuCharts(params.dimension)
82
+    } else {
83
+      ElMessage.error(res.errMsg)
84
+    }
85
+  }
86
+
87
+  const trendColor = ['#3B9FFF', '#4ECB73', '#F9D336', '#F1637B', '#9760E4', '#EF69FF', '#FCB161', '#62DDFF', '#C7C655', '#318730']
88
+  const getTrendOption = (yAxis, date, data) => {
89
+    return {
90
+      tooltip: {
91
+        trigger: 'axis',
92
+      },
93
+      grid: {
94
+        left: '7%',
95
+        right: '4%',
96
+        bottom: '10%'
97
+      },
98
+      xAxis: {
99
+        type: 'category',
100
+        boundaryGap: false,
101
+        data: date,
102
+        axisTick: {
103
+          show: false
104
+        },
105
+        axisLine: {
106
+          show: true,
107
+          lineStyle: {
108
+            color: '#cccccc'
109
+          }
110
+        },
111
+        axisLabel: {
112
+          color: '#666',
113
+        },
114
+      },
115
+      yAxis: yAxis,
116
+      series: data
117
+    }
118
+  }
119
+  /** 获取曲线数据 */
120
+  const getTrend = (dimension) => {
121
+    let time: any[] = [];
122
+    let dataInfo: any = {
123
+      trend_name: '',
124
+      trend: [],
125
+      data: []
126
+    };
127
+    overviewData.list.forEach((item) => {
128
+      if (item.key == overviewRef.value!.value) {
129
+        dataInfo.trend_name = item.name + (item.unit ? `(${item.unit})` : '')
130
+      }
131
+    });
132
+
133
+    let legendArr: any[] = []
134
+    let yAxis: any[] = []
135
+    yAxis.push({
136
+      name: dataInfo.trend_name,
137
+      type: 'value',
138
+      splitNumber: 4,
139
+      nameTextStyle: {
140
+        align: dataInfo.trend_name.length > 7 ? 'center' : 'right',
141
+      }
142
+    })
143
+
144
+    if (topNumRef.value?.value == 0) {//汇总
145
+      overviewData.trendDataList.forEach((item: any) => {
146
+        time.push(item.time)
147
+        dataInfo.trend.push(item[overviewRef.value!.value])
148
+      });
149
+      dataInfo.data.push({
150
+        name: '汇总',
151
+        type: 'line',
152
+        smooth: true,
153
+        itemStyle: {
154
+          color: trendColor[0]
155
+        },
156
+        data: dataInfo.trend
157
+      })
158
+      legendArr.push({
159
+        name: '汇总',
160
+        color: trendColor[0]
161
+      })
162
+    } else {
163
+      if (overviewData.trendDataList?.length > 0) {
164
+        overviewData.trendDataList[0].data.forEach((item: any) => {
165
+          time.push(item.time)
166
+        });
167
+      }
168
+      let name = '';
169
+      let id = '';
170
+      overviewData.trendDataList.forEach((item: any, index) => {
171
+        if (dimension == 'account') {//数据维度
172
+          name = item.account_name
173
+          id = item.account_id
174
+        }
175
+        if (dimension == 'user') {
176
+          name = item.username
177
+        }
178
+        if (dimension == 'team') {
179
+          name = item.name
180
+        }
181
+        let trendData: any[] = []
182
+        item.data?.forEach((s_item) => {
183
+          trendData.push(s_item[overviewRef.value!.value])
184
+        });
185
+        dataInfo.data.push({
186
+          name: `${name}${id != '' ? `(${id})` : ''}`,
187
+          type: 'line',
188
+          smooth: true,
189
+          itemStyle: {
190
+            color: trendColor[index]
191
+          },
192
+          data: trendData
193
+        })
194
+        legendArr.push({
195
+          name: name,
196
+          id: id,
197
+          color: trendColor[index]
198
+        })
199
+      });
200
+    }
201
+
202
+    overviewData.legendArr = legendArr
203
+
204
+    trendInfo.option = getTrendOption(yAxis, time, dataInfo.data)
205
+  }
206
+  /**占比图 */
207
+  const radiuCharts = (dimension) => {
208
+    let trend_name = ''
209
+    let totalValue = ''
210
+    overviewData.list.forEach((item) => {
211
+      if (item.key == overviewRef.value!.value) {
212
+        trend_name = item.name
213
+      }
214
+    });
215
+    let data: any[] = []
216
+    overviewData.ratioDataList.data.forEach((item) => {
217
+      if (dimension == 'account') {//数据维度
218
+        data.push({
219
+          value: item[`${overviewRef.value!.value}_pro`],
220
+          name: item.account_name,
221
+          id: item.account_id,
222
+        })
223
+      }
224
+      if (dimension == 'user') {
225
+        data.push({
226
+          value: item[`${overviewRef.value!.value}_pro`],
227
+          name: item.username,
228
+        })
229
+      }
230
+      if (dimension == 'team') {
231
+        data.push({
232
+          value: item[`${overviewRef.value!.value}_pro`],
233
+          name: item.name,
234
+        })
235
+      }
236
+    });
237
+    totalValue = overviewData.ratioDataList.total[overviewRef.value!.value];
238
+    let option = {
239
+      title: {
240
+        zlevel: 0,
241
+        text: [`{name|${trend_name}}`, '{value|' + totalValue + '}'].join('\n'),
242
+        top: '38%',
243
+        left: '49.5%',
244
+        textAlign: 'center',
245
+        textStyle: {
246
+            rich: {
247
+                value: {
248
+                    color: '#303133',
249
+                    fontSize: 20,
250
+                    lineHeight: 24,
251
+                },
252
+                name: {
253
+                    color: '#303133',
254
+                    fontSize: 20,
255
+                    lineHeight: 35,
256
+                },
257
+            },
258
+        },
259
+      },
260
+      tooltip: {
261
+        trigger: 'item',
262
+        show: false
263
+      },
264
+      legend: {
265
+        top: '5%',
266
+        left: 'center',
267
+        show: false,
268
+      },
269
+      series: [
270
+        {
271
+          name: trend_name,
272
+          type: 'pie',
273
+          radius: ['40%', '70%'],
274
+          width: 600,
275
+          left: 'center',
276
+          itemStyle: {
277
+            normal: {
278
+              borderRadius: 10,
279
+              borderColor: '#fff',
280
+              borderWidth: 2,
281
+              color: function (colors) {
282
+                return trendColor[colors.dataIndex];
283
+              }
284
+            }
285
+          },
286
+          label: {
287
+            show: true,
288
+            alignTo: 'edge',
289
+            formatter(param) {
290
+              return `{name|${param.name}}\n${param.data.id ? `{id|${param.data.id}}:` : ''}{val|${param.percent}%}`
291
+            },
292
+            minMargin: 5,
293
+            edgeDistance: 10,
294
+            lineHeight: 15,
295
+            rich: {
296
+              name: {
297
+                color: '#888',
298
+              },
299
+            },
300
+          },
301
+          emphasis: {
302
+            label: {
303
+              show: true,
304
+              fontSize: 15,
305
+              fontWeight: 'bold'
306
+            }
307
+          },
308
+          labelLine: {
309
+            length: 15,
310
+            length2: 0,
311
+            maxSurfaceAngle: 80
312
+          },
313
+          labelLayout: function (params) {
314
+            const isLeft = params.labelRect.x < 600;
315
+            const points = params.labelLinePoints;
316
+            // Update the end point.
317
+            points[2][0] = isLeft
318
+              ? params.labelRect.x
319
+              : params.labelRect.x + params.labelRect.width;
320
+            return {
321
+              labelLinePoints: points
322
+            };
323
+          },
324
+          data: data
325
+        }
326
+      ]
327
+    };
328
+    ratioInfo.option = option
329
+  }
330
+
331
+  return {
332
+    isCollapsed,
333
+    tabInfo,
334
+    trendLoading,
335
+    topNumRef,
336
+    overviewRef,
337
+    overviewData,
338
+    topNumList,
339
+    trendInfo,
340
+    ratioInfo,
341
+    getTrend,
342
+    radiuCharts,
343
+    reportTrendEvent,
344
+    reportRatioEvent,
345
+  }
346
+}

+ 170 - 0
src/components/businessMoudle/dataManagement/acStatement.vue/index.vue

@@ -0,0 +1,170 @@
1
+<template>
2
+  <div class="flex_between">
3
+    <div class="page_title">账户报表</div>
4
+    <div class="flex">
5
+      <el-radio-group v-model="pageInfo.time_type" v-if="showDateGroup" @change="init" size="default">
6
+        <el-radio-button label="day">分日</el-radio-button>
7
+        <el-radio-button label="month">分月</el-radio-button>
8
+        <el-radio-button label="total">合计</el-radio-button>
9
+      </el-radio-group>
10
+      <el-date-picker v-model="pageInfo.time" type="daterange" range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期"
11
+        size="default" style="width: 280px;" @change="init" :clearable="false" class="lMar10" />
12
+    </div>
13
+  </div>
14
+  <div class="screen_box">
15
+    <div class="flex">
16
+      <div class="label">数据维度:</div>
17
+      <div class="flex">
18
+        <template v-for="item in pageInfo.data_group_list">
19
+          <div :class="['btn', pageInfo.data_group == item.val ? 'active' : '']"  v-if="item.val == 'team' ? (userInfo?.userAuth <= 10 ? true : false) : true " @click="groupClick(item.val)">{{ item.name }}
20
+          </div>
21
+        </template>
22
+      </div>
23
+    </div>
24
+    <div class="flex tMar15">
25
+      <div class="label">筛选维度:</div>
26
+      <div class="flex flex_1">
27
+        <el-input v-model="pageInfo.keyword" style="width: 200px" placeholder="请输入关键词" clearable @clear="init" @keyup.enter.native="init">
28
+          <template #append>
29
+            <el-button :icon="Search" @click="init"/>
30
+          </template>
31
+        </el-input>
32
+        <div class="label lMar20 rMar10">优化师</div>
33
+        <Select ref="userRef"
34
+              selectWidth="180px"
35
+              @changeEvent="selectChange('userRef')" @clearEvent="selectChange('userRef')"
36
+              :isMultiple="true"
37
+              :filterFlag="true"
38
+              :optObj="{k:'id',la:'username',val:'id'}"
39
+              :options="pageInfo.userList"/>
40
+        <template v-if="userInfo?.userAuth <= 10">
41
+          <div class="label lMar20 rMar10">团队</div>
42
+          <Select ref="teamRef"
43
+                selectWidth="180px"
44
+                @changeEvent="selectChange('teamRef')" @clearEvent="selectChange('teamRef')"
45
+                :isMultiple="true"
46
+                :filterFlag="true"
47
+                :optObj="{k:'id',la:'name',val:'id'}"
48
+                :options="pageInfo.teamList"/>
49
+        </template>
50
+        <div class="label lMar20 rMar10">账户</div>
51
+        <Select ref="acRef"
52
+              selectWidth="180px"
53
+              @changeEvent="selectChange('acRef')" @clearEvent="selectChange('acRef')"
54
+              :isMultiple="true"
55
+              :filterFlag="true"
56
+              :optObj="{k:'account_id',la:'account_id',val:'account_id'}"
57
+              :options="pageInfo.acList"/>
58
+      </div>
59
+    </div>
60
+  </div>
61
+
62
+  <acTrend ref="acTrendRef" :mainPageInfo="pageInfo" @changeTab="(val)=>{val == 'trend' ? showDateGroup = true : showDateGroup = false}"></acTrend>
63
+  <dataList ref="dataListRef" :mainPageInfo="pageInfo"></dataList>
64
+</template>
65
+<script setup lang="ts">
66
+import { nextTick, onBeforeMount, reactive, ref } from 'vue';
67
+import { Search } from '@element-plus/icons-vue'
68
+import { ExpIndex }  from './hooks/index'
69
+import { getCookie } from '@/common/common';
70
+import Select from '@/components/capsulationMoudle/_select.vue'
71
+import acTrend from './trend.vue'
72
+import dataList from './dataList.vue'
73
+
74
+const userInfo = ref()
75
+const showDateGroup = ref(true)
76
+const {
77
+  dataListRef,
78
+  acTrendRef,
79
+  userRef,
80
+  teamRef,
81
+  acRef,
82
+  pageInfo,
83
+  init_acList,
84
+  getUserList,
85
+  getTeamList
86
+} = ExpIndex()
87
+
88
+/**点击数据维度 */
89
+const groupClick = (val) => {
90
+  if(pageInfo.data_group != val){
91
+    pageInfo.data_group = val
92
+    init()
93
+  }
94
+}
95
+
96
+/**点击select筛选 */
97
+const selectChange = (key) => {
98
+  if(key == 'userRef') {
99
+    pageInfo.user_ids = userRef.value?.value;
100
+  }
101
+  if(key == 'teamRef') {
102
+    pageInfo.team_ids = teamRef.value?.value;
103
+  }
104
+  if(key == 'acRef') {
105
+    pageInfo.account_ids = acRef.value?.value;
106
+  }
107
+  init()
108
+}
109
+
110
+const init = () => {
111
+  nextTick(()=>{
112
+    if(acTrendRef.value) {
113
+      acTrendRef.value.change()
114
+    }
115
+    if(dataListRef.value) {
116
+      dataListRef.value.init(1)
117
+    }
118
+  })
119
+}
120
+
121
+onBeforeMount(()=>{
122
+  userInfo.value = JSON.parse(getCookie('userInfo') as string)
123
+  console.log(userInfo.value.userAuth)
124
+  init_acList()
125
+  getUserList()
126
+  if(userInfo.value?.userAuth <= 10){//管理员
127
+    getTeamList()
128
+  }
129
+  init()
130
+})
131
+
132
+</script>
133
+<style lang="scss" scoped>
134
+.page_title {
135
+  font-size: 16px;
136
+  font-weight: bold;
137
+}
138
+
139
+.screen_box {
140
+  background-color: #fff;
141
+  padding: 20px;
142
+  margin-top: 10px;
143
+}
144
+
145
+.btn {
146
+  border: var(--el-border);
147
+  height: 30px;
148
+  line-height: 30px;
149
+  box-sizing: border-box;
150
+  border-radius: 4px;
151
+  padding: 0 15px;
152
+  margin-right: 10px;
153
+  font-size: 13px;
154
+  cursor: pointer;
155
+
156
+  &:hover {
157
+    color: #3173FF;
158
+  }
159
+
160
+  &.active {
161
+    background-color: #3173FF;
162
+    border-color: #3173FF;
163
+    color: #fff;
164
+
165
+    &:hover {
166
+      color: #fff;
167
+    }
168
+  }
169
+}
170
+</style>

+ 207 - 0
src/components/businessMoudle/dataManagement/acStatement.vue/trend.vue

@@ -0,0 +1,207 @@
1
+<template>
2
+  <div class="trend_container" v-loading="trendLoading">
3
+    <div class="flex_between tab_box" style="align-items: flex-start">
4
+      <div class="flex">
5
+        <div :class="['tab', tabInfo.value == item.id ? 'active' : '']" v-for="item in tabInfo.list" @click="tabClick(item.id)">{{ item.name }}</div>
6
+      </div>
7
+      <div class="openOrClose" @click="isCollapsed = !isCollapsed">{{ isCollapsed ? '展开' : '收起' }}</div>
8
+    </div>
9
+    <el-collapse-transition>
10
+      <div v-show="!isCollapsed" style="height: 340px">
11
+        <div class="tMar15 flex_between">
12
+          <Select ref="overviewRef"
13
+                  selectWidth="180px"
14
+                  @changeEvent="overviewChange" @clearEvent="overviewChange"
15
+                  :isMultiple="false"
16
+                  :filterFlag="true"
17
+                  :clearFlag="false"
18
+                  :optObj="{k:'key',la:'name',val:'key'}"
19
+                  :options="overviewData.list"/>
20
+          <Select ref="topNumRef"
21
+                  selectWidth="180px"
22
+                  @changeEvent="change" @clearEvent="change"
23
+                  :isMultiple="false"
24
+                  :filterFlag="true"
25
+                  :clearFlag="false"
26
+                  :optObj="{k:'val',la:'name',val:'val'}"
27
+                  :options="tabInfo.value == 'trend' ? [{ name: '汇总', val: 0 }].concat(topNumList) : topNumList"/>
28
+        </div>
29
+        <div class="flex_start">
30
+          <div class="flex_1">
31
+            <ViewEcharts v-if="tabInfo.value == 'trend'" :options="trendInfo.option" :height="trendInfo.heights"></ViewEcharts>
32
+            <ViewEcharts v-if="tabInfo.value == 'ratio'" :options="ratioInfo.option" :height="ratioInfo.heights"></ViewEcharts>
33
+          </div>
34
+          <div class="legend_box" v-if="tabInfo.value == 'trend'">
35
+            <div class="legend" v-for="item in overviewData.legendArr">
36
+              <div class="icon" :style="{'background': item.color}"></div>
37
+              <div class="account_name flex_1">{{ item.name }}<span v-if="item.id && item.id != ''">({{ item.id }})</span></div>
38
+            </div>
39
+          </div>
40
+        </div>
41
+      </div>
42
+    </el-collapse-transition>
43
+  </div>
44
+  
45
+
46
+</template>
47
+<script setup lang="ts">
48
+import { nextTick, onBeforeMount } from 'vue'
49
+import { ExpTrend } from './hooks/trend'
50
+import Select from '@/components/capsulationMoudle/_select.vue'
51
+import ViewEcharts from '@/common/echarts/Echarts.vue';
52
+
53
+const emit = defineEmits<{
54
+  (event: "changeTab",val:string): void;
55
+}>();
56
+const props = defineProps({
57
+  mainPageInfo: {
58
+    type: Object,
59
+    default: ()=>{}
60
+  }
61
+})
62
+const {
63
+  isCollapsed,
64
+  tabInfo,
65
+  trendLoading,
66
+  topNumRef,
67
+  overviewRef,
68
+  overviewData,
69
+  topNumList,
70
+  trendInfo,
71
+  ratioInfo,
72
+  getTrend,
73
+  radiuCharts,
74
+  reportTrendEvent,
75
+  reportRatioEvent
76
+} = ExpTrend()
77
+
78
+/**点击tab切换 */
79
+const tabClick = (val) => {
80
+  if(isCollapsed.value) {
81
+    isCollapsed.value = false
82
+  }
83
+  nextTick(()=>{
84
+    if(tabInfo.value != val) {
85
+      emit('changeTab', val)
86
+      tabInfo.value = val;
87
+      change()
88
+    }
89
+  })
90
+}
91
+
92
+/**点击数据切换 */
93
+const overviewChange = () => {
94
+  if(tabInfo.value == 'trend'){
95
+    getTrend(props.mainPageInfo.data_group)
96
+  }
97
+  if(tabInfo.value == 'ratio'){//占比分析
98
+    radiuCharts(props.mainPageInfo.data_group)
99
+  }
100
+}
101
+
102
+const change = () => {
103
+  if (overviewRef.value && !overviewRef.value.value) {
104
+    overviewRef.value.value = 'cost'
105
+  }
106
+  if (topNumRef.value && !topNumRef.value.value) {
107
+    topNumRef.value.value = 0
108
+  }
109
+  if(tabInfo.value == 'trend') {
110
+    reportTrendEvent({
111
+      st_date: props.mainPageInfo.time[0],
112
+      en_date: props.mainPageInfo.time[1],
113
+      date_group: props.mainPageInfo.time_type,
114
+      dimension: props.mainPageInfo.data_group,
115
+      top_num: topNumRef.value.value,
116
+      keyword: props.mainPageInfo.keyword,
117
+      user_ids: props.mainPageInfo.user_ids,
118
+      account_ids: props.mainPageInfo.account_ids,
119
+      team_ids: props.mainPageInfo.team_ids,
120
+    })
121
+  }
122
+  if(tabInfo.value == 'ratio'){//占比分析
123
+    if (topNumRef.value && topNumRef.value.value != 5 && topNumRef.value.value != 10) {
124
+      topNumRef.value.value = 5
125
+    }
126
+    reportRatioEvent({
127
+      st_date: props.mainPageInfo.time[0],
128
+      en_date: props.mainPageInfo.time[1],
129
+      // date_group: props.mainPageInfo.time_type,
130
+      dimension: props.mainPageInfo.data_group,
131
+      top_num: topNumRef.value.value,
132
+      keyword: props.mainPageInfo.keyword,
133
+      user_ids: props.mainPageInfo.user_ids,
134
+      account_ids: props.mainPageInfo.account_ids,
135
+      team_ids: props.mainPageInfo.team_ids,
136
+    })
137
+  }
138
+  
139
+}
140
+onBeforeMount(()=>{
141
+  
142
+})
143
+
144
+// 暴露自己的属性供父组件使用
145
+defineExpose({
146
+  change,
147
+});
148
+</script>
149
+<style lang="scss" scoped>
150
+.trend_container{
151
+  background-color: #fff;
152
+  margin-top: 10px;
153
+  padding: 20px;
154
+}
155
+.tab_box{
156
+  border-bottom: 1px solid #f1f1f1;
157
+  .tab{
158
+    font-size: 14px;
159
+    color: #444;
160
+    font-weight: bold;
161
+    margin-right: 20px;
162
+    padding-bottom: 16px;
163
+    position: relative;
164
+    cursor: pointer;
165
+    &.active{
166
+      color: #3173FF;
167
+      &::after{
168
+        content: '';
169
+        width: 40px;
170
+        height: 2px;
171
+        background-color: #3173FF;
172
+        border-radius: 2px;
173
+        position: absolute;
174
+        bottom: 0;
175
+        left: 0;
176
+        right: 0;
177
+        margin: auto;
178
+      }
179
+    }
180
+  }
181
+}
182
+.legend_box{
183
+  min-width: 200px;
184
+  margin-top: 50px;
185
+  height: 260px;
186
+  overflow-y: auto;
187
+}
188
+.legend{
189
+  display: flex;
190
+  align-items: center;
191
+  font-size: 12px;
192
+  line-height: 16px;
193
+  margin-top: 6px;
194
+  .icon{
195
+    width: 12px;
196
+    height: 12px;
197
+    border-radius: 50%;
198
+    margin-right: 6px;
199
+  }
200
+}
201
+.openOrClose{
202
+  color: #3173FF;
203
+  cursor: pointer;
204
+  font-size: 14px;
205
+  line-height: 20px;
206
+}
207
+</style>

+ 23 - 15
src/components/capsulationMoudle/downLoadTable.vue

@@ -1,5 +1,5 @@
1 1
 <template>
2
-  <div class="exportBtn" @click="downLoad">{{exportName}}</div>
2
+  <el-button type="primary" plain @click="downLoad">{{exportName}}</el-button>
3 3
 </template>
4 4
 <script setup lang="ts">
5 5
 import {exportExcle} from "@/common/export/export";
@@ -46,6 +46,22 @@ const downLoad = async ()=>{
46 46
   if(props.haveUpdateFlag){//如果需要分页请求数据
47 47
     emits('updateEvent')
48 48
   }else{
49
+    try {
50
+      await handleCheck()
51
+      showLoading()
52
+      let {tableDataList,descolList,title,listKey,excleName} = props
53
+      await exportExcle(tableDataList,descolList,title,listKey,excleName)
54
+      setTimeout(()=>{
55
+        hideLoading()
56
+      },2000)
57
+    } catch (error) {
58
+      
59
+    }
60
+  }
61
+}
62
+
63
+const downLoadEvent = async ()=>{
64
+  try {
49 65
     await handleCheck()
50 66
     showLoading()
51 67
     let {tableDataList,descolList,title,listKey,excleName} = props
@@ -53,22 +69,14 @@ const downLoad = async ()=>{
53 69
     setTimeout(()=>{
54 70
       hideLoading()
55 71
     },2000)
72
+  } catch (error) {
73
+    
56 74
   }
57 75
 }
76
+// 暴露自己的属性供父组件使用
77
+defineExpose({
78
+  downLoadEvent,
79
+});
58 80
 </script>
59 81
 <style lang="scss" scoped>
60
-.exportBtn{
61
-  width: 80px;
62
-  height: 34px;
63
-  border-radius: 5px;
64
-  cursor: pointer;
65
-  font-size: 14px;
66
-  line-height: 32px;
67
-  color:#fff;
68
-  margin: 0 5px;
69
-  text-align: center;
70
-  background-color: #3173FF;
71
-  border: 1px solid #3173FF;
72
-  margin-left: auto;
73
-}
74 82
 </style>

+ 11 - 0
src/router/index.ts

@@ -27,6 +27,9 @@ const adTask = () => import('@/components/businessMoudle/adTask/index.vue')
27 27
 const menuList = () => import('@/components/businessMoudle/menu/index.vue')
28 28
 const document = () => import('@/components/document/index.vue')
29 29
 
30
+//数据报表
31
+const acStatement = () => import('@/components/businessMoudle/dataManagement/acStatement.vue/index.vue')
32
+
30 33
 const constantRoutes: Array<RouteRecordRaw> = [
31 34
   {
32 35
     path: '/login',
@@ -196,6 +199,14 @@ const constantRoutes: Array<RouteRecordRaw> = [
196 199
         meta: {
197 200
           title: '菜单中心'
198 201
         }
202
+      },
203
+      {
204
+        path: '/acStatement',
205
+        component: acStatement,
206
+        name: 'acStatement dataNoTeam',
207
+        meta: {
208
+          title: '账户列表'
209
+        }
199 210
       }
200 211
     ],
201 212
   },