shaogen1995 пре 2 година
родитељ
комит
acd49e3f86
33 измењених фајлова са 1196 додато и 149 уклоњено
  1. 318 0
      后台/package-lock.json
  2. 1 0
      后台/package.json
  3. BIN
      后台/src/assets/img/layoutLeftMain.jpg
  4. BIN
      后台/src/assets/img/loginBox.jpg
  5. 15 19
      后台/src/assets/styles/base.css
  6. 21 20
      后台/src/assets/styles/base.less
  7. 57 3
      后台/src/pages/A1Stat/index.module.scss
  8. 66 5
      后台/src/pages/A1Stat/index.tsx
  9. 65 0
      后台/src/pages/A2Gather/A2Look/index.module.scss
  10. 78 0
      后台/src/pages/A2Gather/A2Look/index.tsx
  11. 26 3
      后台/src/pages/A2Gather/index.module.scss
  12. 162 5
      后台/src/pages/A2Gather/index.tsx
  13. 26 3
      后台/src/pages/A3Book/index.module.scss
  14. 132 5
      后台/src/pages/A3Book/index.tsx
  15. 5 0
      后台/src/pages/A8Manual/index.module.scss
  16. 14 0
      后台/src/pages/A8Manual/index.tsx
  17. 0 14
      后台/src/pages/A8User/index.tsx
  18. 1 1
      后台/src/pages/A8User/index.module.scss
  19. 14 0
      后台/src/pages/A9User/index.tsx
  20. 27 49
      后台/src/pages/Layout/index.module.scss
  21. 9 2
      后台/src/pages/Layout/index.tsx
  22. 9 0
      后台/src/store/action/A1Stat.ts
  23. 31 0
      后台/src/store/action/A2Gather.ts
  24. 24 0
      后台/src/store/action/A3Book.ts
  25. 27 0
      后台/src/store/reducer/A2Gather.ts
  26. 27 0
      后台/src/store/reducer/A3Book.ts
  27. 4 0
      后台/src/store/reducer/index.ts
  28. 8 12
      后台/src/store/reducer/layout.ts
  29. 16 0
      后台/src/types/api/A2Gather.d.ts
  30. 4 0
      后台/src/types/api/A3Book.d.ts
  31. 2 1
      后台/src/types/index.d.ts
  32. 6 6
      后台/src/utils/http.ts
  33. 1 1
      后台/src/utils/storage.ts

+ 318 - 0
后台/package-lock.json

@@ -21,6 +21,7 @@
         "dayjs": "^1.11.7",
         "echarts": "^5.4.0",
         "js-base64": "^3.7.3",
+        "js-export-excel": "^1.1.4",
         "react": "^18.2.0",
         "react-dnd": "^16.0.1",
         "react-dnd-html5-backend": "^16.0.1",
@@ -4480,6 +4481,21 @@
         "node": ">=8.9"
       }
     },
+    "node_modules/adler-32": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.2.0.tgz",
+      "integrity": "sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==",
+      "dependencies": {
+        "exit-on-epipe": "~1.0.1",
+        "printj": "~1.1.0"
+      },
+      "bin": {
+        "adler32": "bin/adler32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/agent-base": {
       "version": "6.0.2",
       "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
@@ -5164,6 +5180,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/blob.js": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/blob.js/-/blob.js-1.0.1.tgz",
+      "integrity": "sha512-TkPuWPeCHBbN+LWFg7BlXdSh6stRxwmAbuirKfiiHTMmo/uQfKFQMx2jrxVUkueKRiG+Tc7Os1Zn618Yc0MZpg=="
+    },
     "node_modules/bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz",
@@ -5377,6 +5398,26 @@
         "node": ">=4"
       }
     },
+    "node_modules/cfb": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
+      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+      "dependencies": {
+        "adler-32": "~1.3.0",
+        "crc-32": "~1.2.0"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/cfb/node_modules/adler-32": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
+      "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
@@ -5508,6 +5549,26 @@
         "node": ">= 4.0"
       }
     },
+    "node_modules/codepage": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.14.0.tgz",
+      "integrity": "sha512-iz3zJLhlrg37/gYRWgEPkaFTtzmnEv1h+r7NgZum2lFElYQPi0/5bnmuDfODHxfp0INEfnRqyfyeIJDbb7ahRw==",
+      "dependencies": {
+        "commander": "~2.14.1",
+        "exit-on-epipe": "~1.0.1"
+      },
+      "bin": {
+        "codepage": "bin/codepage.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/codepage/node_modules/commander": {
+      "version": "2.14.1",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.14.1.tgz",
+      "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
+    },
     "node_modules/collect-v8-coverage": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@@ -5727,6 +5788,17 @@
         "node": ">=10"
       }
     },
+    "node_modules/crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+      "bin": {
+        "crc32": "bin/crc32.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -7453,6 +7525,14 @@
         "node": ">= 0.8.0"
       }
     },
+    "node_modules/exit-on-epipe": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
+      "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/expect": {
       "version": "27.5.1",
       "resolved": "https://registry.npmmirror.com/expect/-/expect-27.5.1.tgz",
@@ -7620,6 +7700,11 @@
         "webpack": "^4.0.0 || ^5.0.0"
       }
     },
+    "node_modules/file-saver": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-1.3.8.tgz",
+      "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
+    },
     "node_modules/filelist": {
       "version": "1.0.4",
       "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.4.tgz",
@@ -7926,6 +8011,14 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/frac": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz",
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/fraction.js": {
       "version": "4.2.0",
       "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz",
@@ -10887,6 +10980,17 @@
       "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.5.tgz",
       "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA=="
     },
+    "node_modules/js-export-excel": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/js-export-excel/-/js-export-excel-1.1.4.tgz",
+      "integrity": "sha512-19m7e3Gnn4CRfHXoFrLYj4fFfJ/KpvI7HRRn25p4GXYD+AlTV+1oU24NH6S904Ksi44tSx7futxhouOPAQ22oQ==",
+      "dependencies": {
+        "blob.js": "^1.0.1",
+        "file-saver": "^1.3.3",
+        "script-loader": "0.7.2",
+        "xlsx": "0.16.3"
+      }
+    },
     "node_modules/js-sdsl": {
       "version": "4.1.5",
       "resolved": "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.1.5.tgz",
@@ -13147,6 +13251,17 @@
         "node": ">=10"
       }
     },
+    "node_modules/printj": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/printj/-/printj-1.1.2.tgz",
+      "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ==",
+      "bin": {
+        "printj": "bin/printj.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/process-nextick-args": {
       "version": "2.0.1",
       "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -13328,6 +13443,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/raw-loader": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmmirror.com/raw-loader/-/raw-loader-0.5.1.tgz",
+      "integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q=="
+    },
     "node_modules/rc-align": {
       "version": "4.0.15",
       "resolved": "https://registry.npmmirror.com/rc-align/-/rc-align-4.0.15.tgz",
@@ -14869,6 +14989,14 @@
         "node": ">= 10.13.0"
       }
     },
+    "node_modules/script-loader": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmmirror.com/script-loader/-/script-loader-0.7.2.tgz",
+      "integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==",
+      "dependencies": {
+        "raw-loader": "~0.5.1"
+      }
+    },
     "node_modules/scroll-into-view-if-needed": {
       "version": "3.0.6",
       "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.6.tgz",
@@ -15199,6 +15327,17 @@
       "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
       "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
     },
+    "node_modules/ssf": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",
+      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+      "dependencies": {
+        "frac": "~1.1.2"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/stable": {
       "version": "0.1.8",
       "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz",
@@ -16582,6 +16721,22 @@
         "node": ">= 0.4"
       }
     },
+    "node_modules/wmf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz",
+      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/word": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
+      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==",
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
     "node_modules/word-wrap": {
       "version": "1.2.3",
       "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -16956,6 +17111,33 @@
         }
       }
     },
+    "node_modules/xlsx": {
+      "version": "0.16.3",
+      "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.16.3.tgz",
+      "integrity": "sha512-LInZ1OK6vpe+Em8XDZ5gDH3WixARwxI7UWc+3chLeafI6gUwECEgL43k4Tjbs1uRfkxpM7wQFy5DLE0hFBRqRw==",
+      "dependencies": {
+        "adler-32": "~1.2.0",
+        "cfb": "^1.1.4",
+        "codepage": "~1.14.0",
+        "commander": "~2.17.1",
+        "crc-32": "~1.2.0",
+        "exit-on-epipe": "~1.0.1",
+        "ssf": "~0.11.2",
+        "wmf": "~1.0.1",
+        "word": "~0.3.0"
+      },
+      "bin": {
+        "xlsx": "bin/xlsx.njs"
+      },
+      "engines": {
+        "node": ">=0.8"
+      }
+    },
+    "node_modules/xlsx/node_modules/commander": {
+      "version": "2.17.1",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.17.1.tgz",
+      "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
+    },
     "node_modules/xml-name-validator": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
@@ -20309,6 +20491,15 @@
         "regex-parser": "^2.2.11"
       }
     },
+    "adler-32": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.2.0.tgz",
+      "integrity": "sha512-/vUqU/UY4MVeFsg+SsK6c+/05RZXIHZMGJA+PX5JyWI0ZRcBpupnRuPLU/NXXoFwMYCPCoxIfElM2eS+DUXCqQ==",
+      "requires": {
+        "exit-on-epipe": "~1.0.1",
+        "printj": "~1.1.0"
+      }
+    },
     "agent-base": {
       "version": "6.0.2",
       "resolved": "https://registry.npmmirror.com/agent-base/-/agent-base-6.0.2.tgz",
@@ -20856,6 +21047,11 @@
       "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz",
       "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
     },
+    "blob.js": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/blob.js/-/blob.js-1.0.1.tgz",
+      "integrity": "sha512-TkPuWPeCHBbN+LWFg7BlXdSh6stRxwmAbuirKfiiHTMmo/uQfKFQMx2jrxVUkueKRiG+Tc7Os1Zn618Yc0MZpg=="
+    },
     "bluebird": {
       "version": "3.7.2",
       "resolved": "https://registry.npmmirror.com/bluebird/-/bluebird-3.7.2.tgz",
@@ -21034,6 +21230,22 @@
       "resolved": "https://registry.npmmirror.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz",
       "integrity": "sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw=="
     },
+    "cfb": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz",
+      "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==",
+      "requires": {
+        "adler-32": "~1.3.0",
+        "crc-32": "~1.2.0"
+      },
+      "dependencies": {
+        "adler-32": {
+          "version": "1.3.1",
+          "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz",
+          "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A=="
+        }
+      }
+    },
     "chalk": {
       "version": "2.4.2",
       "resolved": "https://registry.npmmirror.com/chalk/-/chalk-2.4.2.tgz",
@@ -21139,6 +21351,22 @@
         "q": "^1.1.2"
       }
     },
+    "codepage": {
+      "version": "1.14.0",
+      "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.14.0.tgz",
+      "integrity": "sha512-iz3zJLhlrg37/gYRWgEPkaFTtzmnEv1h+r7NgZum2lFElYQPi0/5bnmuDfODHxfp0INEfnRqyfyeIJDbb7ahRw==",
+      "requires": {
+        "commander": "~2.14.1",
+        "exit-on-epipe": "~1.0.1"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.14.1",
+          "resolved": "https://registry.npmmirror.com/commander/-/commander-2.14.1.tgz",
+          "integrity": "sha512-+YR16o3rK53SmWHU3rEM3tPAh2rwb1yPcQX5irVn7mb0gXbwuCCrnkbV5+PBfETdfg1vui07nM6PCG1zndcjQw=="
+        }
+      }
+    },
     "collect-v8-coverage": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
@@ -21328,6 +21556,11 @@
         "yaml": "^1.10.0"
       }
     },
+    "crc-32": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz",
+      "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ=="
+    },
     "cross-spawn": {
       "version": "7.0.3",
       "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz",
@@ -22661,6 +22894,11 @@
       "resolved": "https://registry.npmmirror.com/exit/-/exit-0.1.2.tgz",
       "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ=="
     },
+    "exit-on-epipe": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/exit-on-epipe/-/exit-on-epipe-1.0.1.tgz",
+      "integrity": "sha512-h2z5mrROTxce56S+pnvAV890uu7ls7f1kEvVGJbw1OlFH3/mlJ5bkXu0KRyW94v37zzHPiUd55iLn3DA7TjWpw=="
+    },
     "expect": {
       "version": "27.5.1",
       "resolved": "https://registry.npmmirror.com/expect/-/expect-27.5.1.tgz",
@@ -22808,6 +23046,11 @@
         "schema-utils": "^3.0.0"
       }
     },
+    "file-saver": {
+      "version": "1.3.8",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-1.3.8.tgz",
+      "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg=="
+    },
     "filelist": {
       "version": "1.0.4",
       "resolved": "https://registry.npmmirror.com/filelist/-/filelist-1.0.4.tgz",
@@ -23040,6 +23283,11 @@
       "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
       "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="
     },
+    "frac": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz",
+      "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA=="
+    },
     "fraction.js": {
       "version": "4.2.0",
       "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.2.0.tgz",
@@ -25350,6 +25598,17 @@
       "resolved": "https://registry.npmmirror.com/js-base64/-/js-base64-3.7.5.tgz",
       "integrity": "sha512-3MEt5DTINKqfScXKfJFrRbxkrnk2AxPWGBL/ycjz4dK8iqiSJ06UxD8jh8xuh6p10TX4t2+7FsBYVxxQbMg+qA=="
     },
+    "js-export-excel": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmmirror.com/js-export-excel/-/js-export-excel-1.1.4.tgz",
+      "integrity": "sha512-19m7e3Gnn4CRfHXoFrLYj4fFfJ/KpvI7HRRn25p4GXYD+AlTV+1oU24NH6S904Ksi44tSx7futxhouOPAQ22oQ==",
+      "requires": {
+        "blob.js": "^1.0.1",
+        "file-saver": "^1.3.3",
+        "script-loader": "0.7.2",
+        "xlsx": "0.16.3"
+      }
+    },
     "js-sdsl": {
       "version": "4.1.5",
       "resolved": "https://registry.npmmirror.com/js-sdsl/-/js-sdsl-4.1.5.tgz",
@@ -26934,6 +27193,11 @@
         }
       }
     },
+    "printj": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/printj/-/printj-1.1.2.tgz",
+      "integrity": "sha512-zA2SmoLaxZyArQTOPj5LXecR+RagfPSU5Kw1qP+jkWeNlrq+eJZyY2oS68SU1Z/7/myXM4lo9716laOFAVStCQ=="
+    },
     "process-nextick-args": {
       "version": "2.0.1",
       "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -27085,6 +27349,11 @@
         }
       }
     },
+    "raw-loader": {
+      "version": "0.5.1",
+      "resolved": "https://registry.npmmirror.com/raw-loader/-/raw-loader-0.5.1.tgz",
+      "integrity": "sha512-sf7oGoLuaYAScB4VGr0tzetsYlS8EJH6qnTCfQ/WVEa89hALQ4RQfCKt5xCyPQKPDUbVUAIP1QsxAwfAjlDp7Q=="
+    },
     "rc-align": {
       "version": "4.0.15",
       "resolved": "https://registry.npmmirror.com/rc-align/-/rc-align-4.0.15.tgz",
@@ -28194,6 +28463,14 @@
         "ajv-keywords": "^3.5.2"
       }
     },
+    "script-loader": {
+      "version": "0.7.2",
+      "resolved": "https://registry.npmmirror.com/script-loader/-/script-loader-0.7.2.tgz",
+      "integrity": "sha512-UMNLEvgOAQuzK8ji8qIscM3GIrRCWN6MmMXGD4SD5l6cSycgGsCo0tX5xRnfQcoghqct0tjHjcykgI1PyBE2aA==",
+      "requires": {
+        "raw-loader": "~0.5.1"
+      }
+    },
     "scroll-into-view-if-needed": {
       "version": "3.0.6",
       "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.0.6.tgz",
@@ -28478,6 +28755,14 @@
       "resolved": "https://registry.npmmirror.com/sprintf-js/-/sprintf-js-1.0.3.tgz",
       "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="
     },
+    "ssf": {
+      "version": "0.11.2",
+      "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz",
+      "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==",
+      "requires": {
+        "frac": "~1.1.2"
+      }
+    },
     "stable": {
       "version": "0.1.8",
       "resolved": "https://registry.npmmirror.com/stable/-/stable-0.1.8.tgz",
@@ -29564,6 +29849,16 @@
         "is-typed-array": "^1.1.9"
       }
     },
+    "wmf": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz",
+      "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw=="
+    },
+    "word": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz",
+      "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA=="
+    },
     "word-wrap": {
       "version": "1.2.3",
       "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.3.tgz",
@@ -29894,6 +30189,29 @@
       "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==",
       "requires": {}
     },
+    "xlsx": {
+      "version": "0.16.3",
+      "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.16.3.tgz",
+      "integrity": "sha512-LInZ1OK6vpe+Em8XDZ5gDH3WixARwxI7UWc+3chLeafI6gUwECEgL43k4Tjbs1uRfkxpM7wQFy5DLE0hFBRqRw==",
+      "requires": {
+        "adler-32": "~1.2.0",
+        "cfb": "^1.1.4",
+        "codepage": "~1.14.0",
+        "commander": "~2.17.1",
+        "crc-32": "~1.2.0",
+        "exit-on-epipe": "~1.0.1",
+        "ssf": "~0.11.2",
+        "wmf": "~1.0.1",
+        "word": "~0.3.0"
+      },
+      "dependencies": {
+        "commander": {
+          "version": "2.17.1",
+          "resolved": "https://registry.npmmirror.com/commander/-/commander-2.17.1.tgz",
+          "integrity": "sha512-wPMUt6FnH2yzG95SA6mzjQOEKUU3aLaDEmzs1ti+1E9h+CsrZghRlqEM/EJ4KscsQVG8uNN4uVreUeT8+drlgg=="
+        }
+      }
+    },
     "xml-name-validator": {
       "version": "3.0.0",
       "resolved": "https://registry.npmmirror.com/xml-name-validator/-/xml-name-validator-3.0.0.tgz",

+ 1 - 0
后台/package.json

@@ -16,6 +16,7 @@
     "dayjs": "^1.11.7",
     "echarts": "^5.4.0",
     "js-base64": "^3.7.3",
+    "js-export-excel": "^1.1.4",
     "react": "^18.2.0",
     "react-dnd": "^16.0.1",
     "react-dnd-html5-backend": "^16.0.1",

BIN
后台/src/assets/img/layoutLeftMain.jpg


BIN
后台/src/assets/img/loginBox.jpg


+ 15 - 19
后台/src/assets/styles/base.css

@@ -134,6 +134,13 @@ textarea {
 #root .ant-table-cell {
   text-align: center !important;
 }
+#root .ant-table-thead .ant-table-cell {
+  background-color: var(--themeColor);
+  color: #fff;
+}
+#root .ant-picker .ant-picker-input > input {
+  text-align: center;
+}
 [hidden] {
   display: none !important;
 }
@@ -144,14 +151,16 @@ textarea {
   display: none;
 }
 .pageTitle {
-  font-size: 20px;
+  font-size: 18px;
   font-weight: 700;
-  position: relative;
+  position: absolute;
+  z-index: 110;
+  top: -85px;
+  left: -18px;
   padding-left: 40px;
-  height: 50px;
-  line-height: 50px;
-  background-color: #fff;
-  border-radius: 10px;
+  color: #fff;
+  height: 70px;
+  line-height: 70px;
 }
 .pageTitle::before {
   position: absolute;
@@ -181,16 +190,3 @@ textarea {
   border-radius: 10px;
   background: transparent;
 }
-.myCoverBox {
-  pointer-events: none;
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  width: 100%;
-  z-index: 10;
-  background-color: rgba(0, 0, 0, 0.6);
-  color: #fff;
-  height: 30px;
-  line-height: 30px;
-  text-align: center;
-}

+ 21 - 20
后台/src/assets/styles/base.less

@@ -178,6 +178,17 @@ textarea {
   .ant-table-cell {
     text-align: center !important;
   }
+
+  // 表头颜色
+  .ant-table-thead .ant-table-cell {
+    background-color: var(--themeColor);
+    color: #fff;
+  }
+
+  // 时间选择器居中
+  .ant-picker .ant-picker-input>input {
+    text-align: center;
+  }
 }
 
 
@@ -197,15 +208,18 @@ textarea {
 }
 
 // 页面标题
-.pageTitle{
-  font-size: 20px;
+.pageTitle {
+  font-size: 18px;
   font-weight: 700;
-  position: relative;
+  position: absolute;
+  z-index: 110;
+  top: -85px;
+  left: -18px;
   padding-left: 40px;
-  height: 50px;
-  line-height: 50px;
-  background-color: #fff;
-  border-radius: 10px;
+  color: #fff;
+  height: 70px;
+  line-height: 70px;
+
   &::before {
     position: absolute;
     left: 20px;
@@ -238,17 +252,4 @@ textarea {
   -webkit-box-shadow: inset 0 0 5px transparent;
   border-radius: 10px;
   background: transparent;
-}
-.myCoverBox{
-  pointer-events: none;
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  width: 100%;
-  z-index: 10;
-  background-color: rgba(0, 0, 0, 0.6);
-  color: #fff;
-  height: 30px;
-  line-height: 30px;
-  text-align: center;
 }

+ 57 - 3
后台/src/pages/A1Stat/index.module.scss

@@ -1,5 +1,59 @@
-.A1Stat{
-  :global{
-    
+.A1Stat {
+  padding: 30px;
+
+  :global {
+    .A1StatMain {
+      margin-top: 60px;
+      display: flex;
+      justify-content: space-between;
+
+      .row {
+        width: 200px;
+        height: 260px;
+        border: 1px solid #999;
+        border-radius: 4px;
+        text-align: center;
+
+        &>h3 {
+          font-size: 32px;
+          color: var(--themeColor);
+          margin-bottom: 10px;
+        }
+
+        .rowSon {
+          width: 97px;
+          height: 97px;
+          margin: 32px auto 27px;
+          background-size: 100% 100%;
+        }
+
+        .rowSon1 {
+          background-image: url('../../assets/img/icon-1.png');
+        }
+
+        .rowSon2 {
+          background-image: url('../../assets/img/icon-2.png');
+        }
+
+        .rowSon3 {
+          background-image: url('../../assets/img/icon-3.png');
+        }
+
+        .rowSon4 {
+          background-image: url('../../assets/img/icon-4.png');
+        }
+
+        .rowSon5 {
+          background-image: url('../../assets/img/icon-5.png');
+        }
+
+        .rowSon6 {
+          background-image: url('../../assets/img/icon-6.png');
+        }
+      }
+
+
+
+    }
   }
 }

+ 66 - 5
后台/src/pages/A1Stat/index.tsx

@@ -1,12 +1,73 @@
-import React from "react";
+import React, { useCallback, useEffect, useState } from "react";
 import styles from "./index.module.scss";
- function A1Stat() {
-  
+import { DatePicker } from "antd";
+import dayjs from "dayjs";
+import classNames from "classnames";
+const { RangePicker } = DatePicker;
+
+function A1Stat() {
+  const [formData, setFormData] = useState({
+    startTime: dayjs().subtract(30, "day").format("YYYY-MM-DD") + " 00:00:00",
+    endTime: dayjs().format("YYYY-MM-DD") + " 23:59:59",
+  });
+
+  const getListFu = useCallback(() => {
+    console.log("发送请求", formData);
+  }, [formData]);
+
+  useEffect(() => {
+    getListFu();
+  }, [getListFu]);
+
+  // 时间选择器改变
+  const timeChange = useCallback((_: any, dateString: string[]) => {
+    let startTime = "";
+    let endTime = "";
+    if (dateString[0] && dateString[1]) {
+      startTime = dateString[0] + " 00:00:00";
+      endTime = dateString[1] + " 23:59:59";
+    }
+    setFormData({ startTime, endTime });
+  }, []);
+
+  const [data, setData] = useState([
+    { id: 1, name: "收集问卷", pcs: 1341 },
+    { id: 2, name: "产品手册下载量", pcs: 20023 },
+    { id: 3, name: "访客量", pcs: 13123 },
+    { id: 4, name: "线上展厅访问量", pcs: 16446 },
+    { id: 5, name: "导航区域访问量", pcs: 2134 },
+    { id: 6, name: "热点访问量", pcs: 6446 },
+  ]);
+
   return (
     <div className={styles.A1Stat}>
-      <h1>A1Stat</h1>
+      <div className="pageTitle">统计看板</div>
+      {/* 日期选择框 */}
+      <div className="searchBox">
+        <RangePicker
+          style={{ width: 320 }}
+          defaultValue={[
+            dayjs(
+              dayjs().subtract(30, "day").format("YYYY-MM-DD"),
+              "YYYY-MM-DD"
+            ),
+            dayjs(dayjs().format("YYYY-MM-DD"), "YYYY-MM-DD"),
+          ]}
+          onChange={timeChange}
+        />
+      </div>
+      {/* 统计主体 */}
+      <div className="A1StatMain">
+        {data.map((v, i) => (
+          <div className='row' key={v.id}>
+            <div className={classNames('rowSon',`rowSon${i+1}`)}></div>
+            <h3>{v.pcs}</h3>
+            <p>{v.name}</p>
+          </div>
+        ))}
+      </div>
     </div>
-  )
+  );
 }
 
 const MemoA1Stat = React.memo(A1Stat);

+ 65 - 0
后台/src/pages/A2Gather/A2Look/index.module.scss

@@ -0,0 +1,65 @@
+.A2Look {
+  padding: 20px 30px 0;
+  position: absolute;
+  z-index: 11;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: #fff;
+  border-radius: 10px;
+
+  :global {
+
+    .A2LookMain {
+      margin-top: 30px;
+      border: 1px solid #f0f0f0;
+      border-bottom: none;
+      border-radius: 6px;
+      overflow: hidden;
+
+      .row {
+        display: flex;
+        border-bottom: 1px solid #f0f0f0;
+      }
+
+      .tit {
+        width: 200px;
+        height: 75px;
+        line-height: 75px;
+        text-align: center;
+        background-color: var(--themeColor);
+        color: #fff;
+        font-weight: 700;
+      }
+
+      .txt {
+        padding-left: 30px;
+        width: calc(100% - 200px);
+        height: 75px;
+        display: flex;
+        align-items: center;
+        // line-height: 75px;
+      }
+    }
+
+    .tableBox {
+      margin-top: 20px;
+      height: calc(100% - 65px);
+      border: 1px solid #f0f0f0;
+      border-radius: 6px;
+
+      .ant-table-body {
+        height: 605px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+        .ant-table-row {
+          .ant-table-cell {
+            padding: 10px;
+          }
+        }
+      }
+    }
+  }
+}

+ 78 - 0
后台/src/pages/A2Gather/A2Look/index.tsx

@@ -0,0 +1,78 @@
+import React, { useCallback, useEffect, useMemo, useState } from "react";
+import styles from "./index.module.scss";
+import { Button } from "antd";
+import { A2API_getInfoById } from "@/store/action/A2Gather";
+import { A2TableType } from "@/types";
+
+type Props = {
+  id: number;
+  closePage: () => void;
+};
+
+function A2Look({ id, closePage }: Props) {
+  const [info, setInfo] = useState({
+    Name: "",
+    "Job Title": "",
+    "Company Name": "",
+    Email: "",
+    "Should we call you": "",
+    Phone: "",
+    Area: "",
+    Country: "",
+    "Detailed request": "",
+  });
+
+  // 通过id获取详情
+  const getInfoFu = useCallback(async (id: number) => {
+    const res = await A2API_getInfoById(id);
+    if (res.code === 0) {
+      const data: A2TableType = res.data;
+      const obj = {
+        Name: data.name,
+        "Job Title": data.jobTitle,
+        "Company Name": data.companyName,
+        Email: data.email,
+        "Should we call you": data.hasCall ? "Yes" : "No",
+        Phone: data.phone,
+        Area: data.area,
+        Country: data.country,
+        "Detailed request": data.description,
+      };
+      setInfo(obj);
+    }
+  }, []);
+
+  useEffect(() => {
+    getInfoFu(id);
+  }, [getInfoFu, id]);
+
+  const list = useMemo(() => {
+    const arr = [];
+    for (const k in info) {
+      arr.push({ tit: k, txt: Reflect.get(info, k) });
+    }
+    return arr;
+  }, [info]);
+
+  return (
+    <div className={styles.A2Look}>
+      <div className="topBox">
+        <Button onClick={closePage}>返回</Button>
+      </div>
+      <div className="A2LookMain">
+        {list.map((v) => (
+          <div className="row" key={v.tit}>
+            <div className="tit">{v.tit}</div>
+            <div className="txt" title={v.txt}>
+              {v.txt.length >= 200 ? v.txt.substring(0, 200) + "..." : v.txt}
+            </div>
+          </div>
+        ))}
+      </div>
+    </div>
+  );
+}
+
+const MemoA2Look = React.memo(A2Look);
+
+export default MemoA2Look;

+ 26 - 3
后台/src/pages/A2Gather/index.module.scss

@@ -1,5 +1,28 @@
-.A2Gather{
-  :global{
-    
+.A2Gather {
+  padding: 20px 30px 0;
+
+  :global {
+    .searchBox {
+      display: flex;
+      justify-content: space-between;
+    }
+
+    .tableBox {
+      margin-top: 20px;
+      height: calc(100% - 65px);
+      border: 1px solid #f0f0f0;
+      border-radius: 6px;
+      .ant-table-body {
+        height: 605px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+        .ant-table-row {
+          .ant-table-cell {
+            padding: 10px;
+          }
+        }
+      }
+    }
   }
 }

+ 162 - 5
后台/src/pages/A2Gather/index.tsx

@@ -1,12 +1,169 @@
-import React from "react";
+import React, { useCallback, useEffect, useMemo, useState } from "react";
 import styles from "./index.module.scss";
- function A2Gather() {
-  
+import { Button, DatePicker, Table } from "antd";
+import dayjs from "dayjs";
+import { useDispatch, useSelector } from "react-redux";
+import { RootState } from "@/store";
+import { A2TableType } from "@/types";
+
+import ExportJsonExcel from "js-export-excel";
+import { MessageFu } from "@/utils/message";
+import A2Look from "./A2Look";
+import { A2API_getList, A2API_getListToXls } from "@/store/action/A2Gather";
+
+const { RangePicker } = DatePicker;
+function A2Gather() {
+  const dispatch = useDispatch();
+
+  const [formData, setFormData] = useState({
+    startTime: dayjs().subtract(30, "day").format("YYYY-MM-DD") + " 00:00:00",
+    endTime: dayjs().format("YYYY-MM-DD") + " 23:59:59",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  const getListFu = useCallback(() => {
+    // console.log("发送请求", formData);
+    dispatch(A2API_getList(formData));
+  }, [dispatch, formData]);
+
+  useEffect(() => {
+    getListFu();
+  }, [getListFu]);
+
+  // 时间选择器改变
+  const timeChange = useCallback(
+    (_: any, dateString: string[]) => {
+      let startTime = "";
+      let endTime = "";
+      if (dateString[0] && dateString[1]) {
+        startTime = dateString[0] + " 00:00:00";
+        endTime = dateString[1] + " 23:59:59";
+      }
+      setFormData({ ...formData, startTime, endTime });
+    },
+    [formData]
+  );
+
+  // 从仓库获取表格列表信息
+  const tableInfo = useSelector((state: RootState) => state.A2Gather.tableInfo);
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "时间",
+        dataIndex: "createTime",
+      },
+      {
+        title: "名字",
+        dataIndex: "name",
+      },
+      {
+        title: "职位",
+        dataIndex: "jobTitle",
+      },
+      {
+        title: "可否联系",
+        render: (item: A2TableType) => (item.hasCall ? "Yes" : "No"),
+      },
+      {
+        title: "操作",
+        render: (item: A2TableType) => (
+          <Button size="small" type="text" onClick={() => setLookId(item.id)}>
+            查看
+          </Button>
+        ),
+      },
+    ];
+  }, []);
+
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setFormData({ ...formData, pageNum, pageSize });
+    },
+    [formData]
+  );
+
+  // 点击导出
+  const deriveFu = useCallback(async () => {
+    if (tableInfo.list.length === 0)
+      return MessageFu.warning("当前搜索条件没有数据!");
+    const name = dayjs(new Date()).format("YYYYMMDDHHmmss");
+
+    const res = await A2API_getListToXls({
+      ...formData,
+      pageNum: 1,
+      pageSize: 99999,
+    });
+
+    if (res.code === 0) {
+      const option = {
+        fileName: name,
+        datas: [
+          {
+            sheetData: res.data.records.map((v: A2TableType) => {
+              return {
+                ...v,
+                hasCall: v.hasCall ? "Yes" : "No",
+              };
+            }),
+            sheetName: name,
+            sheetFilter: ["createTime", "name", "jobTitle", "hasCall"],
+            sheetHeader: ["时间", "名字", "职位", "可否联系"],
+            columnWidths: [10, 10, 10, 10],
+          },
+        ],
+      };
+
+      const toExcel = new ExportJsonExcel(option); //new
+      toExcel.saveExcel(); //保存
+    }
+  }, [formData, tableInfo.list]);
+
+  // 打开问卷详情页面
+  const [lookId, setLookId] = useState(0);
+
   return (
     <div className={styles.A2Gather}>
-      <h1>A2Gather</h1>
+      <div className="pageTitle">{lookId === 0 ? "收集问卷" : "问卷详情"}</div>
+      <div className="searchBox">
+        <RangePicker
+          style={{ width: 320 }}
+          defaultValue={[
+            dayjs(
+              dayjs().subtract(30, "day").format("YYYY-MM-DD"),
+              "YYYY-MM-DD"
+            ),
+            dayjs(dayjs().format("YYYY-MM-DD"), "YYYY-MM-DD"),
+          ]}
+          onChange={timeChange}
+        />
+        <Button onClick={deriveFu}>导出表格</Button>
+      </div>
+      {/* 表格主体 */}
+      <div className="tableBox">
+        <Table
+          scroll={{ y: 605 }}
+          dataSource={tableInfo.list}
+          columns={columns}
+          rowKey="id"
+          pagination={{
+            showQuickJumper: true,
+            position: ["bottomCenter"],
+            showSizeChanger: true,
+            current: formData.pageNum,
+            pageSize: formData.pageSize,
+            total: tableInfo.total,
+            onChange: paginationChange(),
+          }}
+        />
+      </div>
+      {lookId === 0 ? null : (
+        <A2Look id={lookId} closePage={() => setLookId(0)} />
+      )}
     </div>
-  )
+  );
 }
 
 const MemoA2Gather = React.memo(A2Gather);

+ 26 - 3
后台/src/pages/A3Book/index.module.scss

@@ -1,5 +1,28 @@
-.A3Book{
-  :global{
-    
+.A3Book {
+  padding: 20px 30px 0;
+
+  :global {
+    .searchBox {
+      display: flex;
+      justify-content: space-between;
+    }
+
+    .tableBox {
+      margin-top: 20px;
+      height: calc(100% - 65px);
+      border: 1px solid #f0f0f0;
+      border-radius: 6px;
+      .ant-table-body {
+        height: 605px;
+        overflow-y: auto !important;
+        overflow-y: overlay !important;
+
+        .ant-table-row {
+          .ant-table-cell {
+            padding: 10px;
+          }
+        }
+      }
+    }
   }
 }

+ 132 - 5
后台/src/pages/A3Book/index.tsx

@@ -1,12 +1,139 @@
-import React from "react";
+import React, { useCallback, useEffect, useMemo, useState } from "react";
 import styles from "./index.module.scss";
- function A3Book() {
-  
+import { Button, DatePicker, Table } from "antd";
+import dayjs from "dayjs";
+import { useDispatch, useSelector } from "react-redux";
+import ExportJsonExcel from "js-export-excel";
+import { RootState } from "@/store";
+import { MessageFu } from "@/utils/message";
+import { A3API_getList, A3API_getListToXls } from "@/store/action/A3Book";
+const { RangePicker } = DatePicker;
+function A3Book() {
+  const dispatch = useDispatch();
+
+  const [formData, setFormData] = useState({
+    startTime: dayjs().subtract(30, "day").format("YYYY-MM-DD") + " 00:00:00",
+    endTime: dayjs().format("YYYY-MM-DD") + " 23:59:59",
+    pageSize: 10,
+    pageNum: 1,
+  });
+
+  const getListFu = useCallback(() => {
+    // console.log("发送请求", formData);
+    dispatch(A3API_getList(formData));
+  }, [dispatch, formData]);
+
+  useEffect(() => {
+    getListFu();
+  }, [getListFu]);
+
+  // 时间选择器改变
+  const timeChange = useCallback(
+    (_: any, dateString: string[]) => {
+      let startTime = "";
+      let endTime = "";
+      if (dateString[0] && dateString[1]) {
+        startTime = dateString[0] + " 00:00:00";
+        endTime = dateString[1] + " 23:59:59";
+      }
+      setFormData({ ...formData, startTime, endTime });
+    },
+    [formData]
+  );
+
+  // 从仓库获取表格列表信息
+  const tableInfo = useSelector((state: RootState) => state.A3Book.tableInfo);
+
+  const columns = useMemo(() => {
+    return [
+      {
+        title: "手册名称",
+        dataIndex: "name",
+      },
+      {
+        title: "下载次数",
+        dataIndex: "pcs",
+      },
+    ];
+  }, []);
+
+  // 页码变化
+  const paginationChange = useCallback(
+    () => (pageNum: number, pageSize: number) => {
+      setFormData({ ...formData, pageNum, pageSize });
+    },
+    [formData]
+  );
+
+  // 点击导出
+  const deriveFu = useCallback(async () => {
+    if (tableInfo.list.length === 0)
+      return MessageFu.warning("当前搜索条件没有数据!");
+    const name = dayjs(new Date()).format("YYYYMMDDHHmmss");
+
+    const res = await A3API_getListToXls({
+      ...formData,
+      pageNum: 1,
+      pageSize: 99999,
+    });
+
+    if (res.code === 0) {
+      const option = {
+        fileName: name,
+        datas: [
+          {
+            sheetData: res.data.records,
+            sheetName: name,
+            sheetFilter: ["name", "pcs"],
+            sheetHeader: ["手册名称", "下载次数"],
+            columnWidths: [20, 10],
+          },
+        ],
+      };
+
+      const toExcel = new ExportJsonExcel(option); //new
+      toExcel.saveExcel(); //保存
+    }
+  }, [formData, tableInfo.list]);
+
   return (
     <div className={styles.A3Book}>
-      <h1>A3Book</h1>
+      <div className="pageTitle">产品手册下载量</div>
+      {/* 日期选择框 */}
+      <div className="searchBox">
+        <RangePicker
+          style={{ width: 320 }}
+          defaultValue={[
+            dayjs(
+              dayjs().subtract(30, "day").format("YYYY-MM-DD"),
+              "YYYY-MM-DD"
+            ),
+            dayjs(dayjs().format("YYYY-MM-DD"), "YYYY-MM-DD"),
+          ]}
+          onChange={timeChange}
+        />
+        <Button onClick={deriveFu}>导出表格</Button>
+      </div>
+      {/* 表格主体 */}
+      <div className="tableBox">
+        <Table
+          scroll={{ y: 605 }}
+          dataSource={tableInfo.list}
+          columns={columns}
+          rowKey="id"
+          pagination={{
+            showQuickJumper: true,
+            position: ["bottomCenter"],
+            showSizeChanger: true,
+            current: formData.pageNum,
+            pageSize: formData.pageSize,
+            total: tableInfo.total,
+            onChange: paginationChange(),
+          }}
+        />
+      </div>
     </div>
-  )
+  );
 }
 
 const MemoA3Book = React.memo(A3Book);

+ 5 - 0
后台/src/pages/A8Manual/index.module.scss

@@ -0,0 +1,5 @@
+.A8Manual{
+  :global{
+    
+  }
+}

+ 14 - 0
后台/src/pages/A8Manual/index.tsx

@@ -0,0 +1,14 @@
+import React from "react";
+import styles from "./index.module.scss";
+ function A8Manual() {
+  
+  return (
+    <div className={styles.A8Manual}>
+      <h1>A8Manual</h1>
+    </div>
+  )
+}
+
+const MemoA8Manual = React.memo(A8Manual);
+
+export default MemoA8Manual;

+ 0 - 14
后台/src/pages/A8User/index.tsx

@@ -1,14 +0,0 @@
-import React from "react";
-import styles from "./index.module.scss";
- function A8User() {
-  
-  return (
-    <div className={styles.A8User}>
-      <h1>A8User</h1>
-    </div>
-  )
-}
-
-const MemoA8User = React.memo(A8User);
-
-export default MemoA8User;

+ 1 - 1
后台/src/pages/A8User/index.module.scss

@@ -1,4 +1,4 @@
-.A8User{
+.A9User{
   :global{
     
   }

+ 14 - 0
后台/src/pages/A9User/index.tsx

@@ -0,0 +1,14 @@
+import React from "react";
+import styles from "./index.module.scss";
+ function A9User() {
+  
+  return (
+    <div className={styles.A9User}>
+      <h1>A9User</h1>
+    </div>
+  )
+}
+
+const MemoA9User = React.memo(A9User);
+
+export default MemoA9User;

+ 27 - 49
后台/src/pages/Layout/index.module.scss

@@ -2,6 +2,8 @@
   width: 100%;
   height: 100%;
   display: flex;
+  overflow: hidden;
+  position: relative;
 
   :global {
 
@@ -10,71 +12,45 @@
       z-index: 30;
       width: 220px;
       height: 100%;
-      box-shadow: 0px 0px 5px 3px;
-      background-image: url('../../assets/img/layoutLeftMain.jpg');
-      background-size: 100% 100%;
+      background-color: #234386;
 
       .layoutLeftTop {
-        height: 60px;
-        background-color: var(--themeColor);
-        padding: 10px;
-        padding-left: 20px;
+        height: 70px;
+        background-color: #1f3d7c;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+
+        &>img {
+          width: 180px;
+        }
       }
 
       .layoutLeftMain {
-        height: calc(100% - 60px);
-        padding: 20px;
+        height: calc(100% - 70px);
 
 
         .mainBoxL2Row {
-          padding-left: 60px;
           cursor: pointer;
           height: 50px;
           line-height: 50px;
           font-size: 16px;
           position: relative;
-          margin-top: 25px;
-          border-radius: 8px;
-          color: var(--themeColor);
-
-          .tabImg {
-            z-index: 3;
-            position: absolute;
-            top: 50%;
-            left: 20px;
-            transform: translateY(-50%);
-            width: 20px;
-          }
-
-          .tabImgAc {
-            z-index: 3;
-            position: absolute;
-            top: 50%;
-            left: 20px;
-            transform: translateY(-50%);
-            width: 20px;
-            display: none;
-          }
+          margin-top: 16px;
+          color: #fff;
+          text-align: center;
 
           &:hover {
             color: #fff;
             background-color: var(--themeColor);
-
-            .tabImgAc {
-              display: block;
-            }
           }
         }
 
-
-
         .active {
           color: #fff;
           pointer-events: none;
           background-color: var(--themeColor);
-          .tabImgAc {
-            display: block;
-          }
+
         }
       }
 
@@ -87,8 +63,8 @@
 
 
       .layoutRightTop {
-        height: 60px;
-        background-color: var(--themeColor);
+        height: 70px;
+        background-color: #1f3d7c;
         display: flex;
         justify-content: flex-end;
         position: relative;
@@ -132,7 +108,7 @@
             color: rgb(226, 223, 223);
 
             &>span {
-              background-color: var(--themeColor);
+              background-color: #1f3d7c;
               display: block;
               width: 100%;
               height: 50px;
@@ -170,18 +146,20 @@
       }
 
       .layoutRightMain {
-        height: calc(100% - 60px);
-        padding: 15px 30px 20px;
-        background-image: url('../../assets/img/loginBox.jpg');
-        background-size: 100% 100%;
+        height: calc(100% - 70px);
+        padding: 15px 20px;
+        background-color: #eef0f4;
 
         .mainBoxR {
           width: 100%;
           height: 100%;
-          overflow: hidden;
+          background-color: #fff;
+          border-radius: 10px;
+
           &>div {
             width: 100%;
             height: 100%;
+            position: relative;
           }
         }
       }

+ 9 - 2
后台/src/pages/Layout/index.tsx

@@ -39,7 +39,7 @@ function Layout() {
       },
       {
         id: 300,
-        name: "产品手册",
+        name: "产品手册下载量",
         path: "/book",
         Com: React.lazy(() => import("../A3Book")),
       },
@@ -69,9 +69,16 @@ function Layout() {
       },
       {
         id: 800,
+        name: "产品手册管理",
+        path: "/manual",
+        Com: React.lazy(() => import("../A8Manual")),
+      },
+
+      {
+        id: 900,
         name: "用户管理",
         path: "/user",
-        Com: React.lazy(() => import("../A8User")),
+        Com: React.lazy(() => import("../A9User")),
       },
     ];
   }, []);

+ 9 - 0
后台/src/store/action/A1Stat.ts

@@ -0,0 +1,9 @@
+import http from "@/utils/http";
+// import { AppDispatch } from "..";
+
+/**
+ * 获取统计看板
+ */
+export const A1getNumInfoAPI = (data: any) => {
+  return http.post("admin/login", { ...data });
+};

+ 31 - 0
后台/src/store/action/A2Gather.ts

@@ -0,0 +1,31 @@
+import http from "@/utils/http";
+import { AppDispatch } from "..";
+
+/**
+ * 获取收集问卷列表-页面显示
+ */
+export const A2API_getList = (data: any) => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post("cms/questionnaire/pageList", data);
+    if (res.code === 0) {
+      dispatch({
+        type: "gather/getList",
+        payload: { list: res.data.records, total: res.data.total },
+      });
+    }
+  };
+};
+
+/**
+ * 获取收集问卷列表-导出表格
+ */
+export const A2API_getListToXls = (data: any) => {
+  return http.post("cms/questionnaire/pageList", data);
+};
+
+/**
+ * 获取收集问卷列表-导出表格
+ */
+export const A2API_getInfoById = (id: number) => {
+  return http.get(`cms/questionnaire/detail/${id}`);
+};

+ 24 - 0
后台/src/store/action/A3Book.ts

@@ -0,0 +1,24 @@
+import http from "@/utils/http";
+import { AppDispatch } from "..";
+
+/**
+ * 产品手册下载量列表-页面显示
+ */
+export const A3API_getList = (data: any) => {
+  return async (dispatch: AppDispatch) => {
+    const res = await http.post("cms/report/download/pageList", data);
+    if (res.code === 0) {
+      dispatch({
+        type: "book/getList",
+        payload: { list: res.data.records, total: res.data.total },
+      });
+    }
+  };
+};
+
+/**
+ * 产品手册下载量列表-导出表格
+ */
+export const A3API_getListToXls = (data: any) => {
+  return http.post("cms/report/download/pageList", data);
+};

+ 27 - 0
后台/src/store/reducer/A2Gather.ts

@@ -0,0 +1,27 @@
+import { A2TableType } from "@/types";
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  tableInfo: {
+    list: [] as A2TableType[],
+    total: 0,
+  },
+};
+
+// 定义 action 类型
+type Props = {
+  type: "gather/getList";
+  payload: { list: A2TableType[]; total: number };
+};
+
+// 频道 reducer
+export default function gatherReducer(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case "gather/getList":
+      return { ...state, tableInfo: action.payload };
+    default:
+      return state;
+  }
+}

+ 27 - 0
后台/src/store/reducer/A3Book.ts

@@ -0,0 +1,27 @@
+import { A3TableType } from "@/types/api/A3Book";
+
+// 初始化状态
+const initState = {
+  // 列表数据
+  tableInfo: {
+    list: [] as A3TableType[],
+    total: 0,
+  },
+};
+
+// 定义 action 类型
+type Props = {
+  type: "book/getList";
+  payload: { list: A3TableType[]; total: number };
+};
+
+// 频道 reducer
+export default function reducerFu(state = initState, action: Props) {
+  switch (action.type) {
+    // 获取列表数据
+    case "book/getList":
+      return { ...state, tableInfo: action.payload };
+    default:
+      return state;
+  }
+}

+ 4 - 0
后台/src/store/reducer/index.ts

@@ -1,10 +1,14 @@
 import { combineReducers } from "redux";
 
 import A0layout from "./layout";
+import A2Gather from "./A2Gather";
+import A3Book from "./A3Book";
 
 // 合并 reducer
 const rootReducer = combineReducers({
   A0layout,
+  A2Gather,
+  A3Book,
 });
 
 // 默认导出

+ 8 - 12
后台/src/store/reducer/layout.ts

@@ -1,5 +1,10 @@
 import { MessageType } from "@/utils/message";
 
+type CloseUpFileType = {
+  fu: () => void;
+  state: boolean;
+};
+
 // 初始化状态
 const initState = {
   message: {
@@ -15,21 +20,12 @@ const initState = {
 };
 
 // 定义 action 类型
-type LayoutActionType =
+type Props =
   | { type: "layout/message"; payload: MessageType }
-  | {
-      type: "layout/closeUpFile";
-      payload: {
-        fu: () => void;
-        state: boolean;
-      };
-    };
+  | { type: "layout/closeUpFile"; payload: CloseUpFileType };
 
 // 频道 reducer
-export default function layoutReducer(
-  state = initState,
-  action: LayoutActionType
-) {
+export default function layoutReducer(state = initState, action: Props) {
   switch (action.type) {
     // antd轻提示(兼容360浏览器)
     case "layout/message":

+ 16 - 0
后台/src/types/api/A2Gather.d.ts

@@ -0,0 +1,16 @@
+export type A2TableType = {
+  area: "Asia" | "Europe" | "America" | "Africa" | "Others";
+  companyName: string;
+  country: string;
+  createTime: string;
+  creatorId: number;
+  creatorName: string;
+  description: string;
+  email: string;
+  hasCall: 1 | 0;
+  id: number;
+  jobTitle: "Decision Maker" | "Influencer" | "Employee";
+  name: string;
+  phone: string;
+  updateTime: string;
+};

+ 4 - 0
后台/src/types/api/A3Book.d.ts

@@ -0,0 +1,4 @@
+export type A3TableType ={
+  name:string
+  pcs:number
+}

+ 2 - 1
后台/src/types/index.d.ts

@@ -1,2 +1,3 @@
-// export * from './api/layot'
+export * from './api/A2Gather'
+export * from './api/A2Gather'
 

+ 6 - 6
后台/src/utils/http.ts

@@ -7,10 +7,10 @@ import { domShowFu } from "./domShow";
 // 请求基地址
 export const baseURL =
   // 线下的图片地址需要加上/api/
-  // process.env.NODE_ENV === "development"
-  //   ? "http://192.168.20.55:8042/api/"
-  //   : "";
-  process.env.NODE_ENV === "development" ? "https://bengbubwg.4dage.com" : "";
+  process.env.NODE_ENV === "development"
+    ? "http://192.168.20.55:8045/api/"
+    : "";
+  // process.env.NODE_ENV === "development" ? "https://bengbubwg.4dage.com" : "";
 
 // 处理  类型“AxiosResponse<any, any>”上不存在属性“code”
 declare module "axios" {
@@ -23,10 +23,10 @@ declare module "axios" {
 // 创建 axios 实例
 const http = axios.create({
   // --------线下的地址不用加/api/
-  // baseURL: baseURL,
+  baseURL: baseURL,
 
   // --------打包或线上环境接口需要加上api/
-  baseURL: baseURL + "/api/",
+  // baseURL: baseURL + "/api/",
   timeout: 5000,
 });
 

+ 1 - 1
后台/src/utils/storage.ts

@@ -1,7 +1,7 @@
 // ------------------------------------token的本地存储------------------------------------
 
 // 用户 Token 的本地缓存键名,自己定义
-const TOKEN_KEY = 'BBSBWG_HT_USER_INFO'
+const TOKEN_KEY = 'HDBYSSB_HT_USER_INFO'
 
 /**
  * 从本地缓存中获取 Token 信息