소스 검색

预约登记管理端(暂无报表统计和消息通知接口)

hzj18279462576@163.com 3 년 전
부모
커밋
d43c93cc4d
63개의 변경된 파일10690개의 추가작업 그리고 1개의 파일을 삭제
  1. 24 0
      .gitignore
  2. 3 0
      .vscode/extensions.json
  3. 7 1
      README.md
  4. 17 0
      index.html
  5. 919 0
      package-lock.json
  6. 33 0
      package.json
  7. 22 0
      src/App.vue
  8. 87 0
      src/api/admin.js
  9. 14 0
      src/api/home.js
  10. 110 0
      src/api/vidicons.js
  11. BIN
      src/assets/account.png
  12. BIN
      src/assets/accountA.png
  13. BIN
      src/assets/bg-video.png
  14. BIN
      src/assets/car.png
  15. BIN
      src/assets/carA.png
  16. BIN
      src/assets/carnumber.png
  17. BIN
      src/assets/carnumberA.png
  18. BIN
      src/assets/classes.png
  19. BIN
      src/assets/classesA.png
  20. BIN
      src/assets/import.png
  21. BIN
      src/assets/login.png
  22. BIN
      src/assets/login2.png
  23. BIN
      src/assets/login3.png
  24. BIN
      src/assets/logo.png
  25. BIN
      src/assets/logoBg.jpg
  26. BIN
      src/assets/nanchang.png
  27. BIN
      src/assets/news.png
  28. BIN
      src/assets/nodata.png
  29. BIN
      src/assets/read.png
  30. BIN
      src/assets/staff.png
  31. BIN
      src/assets/staffA.png
  32. BIN
      src/assets/statement.png
  33. BIN
      src/assets/statementA.png
  34. BIN
      src/assets/system.png
  35. 1 0
      src/assets/vite.svg
  36. BIN
      src/assets/waiting.png
  37. BIN
      src/assets/waitingA.png
  38. 42 0
      src/components/HelloWorld.vue
  39. 62 0
      src/layout/index.vue
  40. 350 0
      src/layout/sidebar/Navbar.vue
  41. 183 0
      src/layout/sidebar/SidevarItem.vue
  42. 28 0
      src/main.js
  43. 85 0
      src/router/index.js
  44. 5 0
      src/store/getters.js
  45. 22 0
      src/store/index.js
  46. 30 0
      src/store/modules/user.js
  47. 89 0
      src/style.css
  48. 1 0
      src/style/mixin.scss
  49. 46 0
      src/utils/request.js
  50. 22 0
      src/utils/rsa.js
  51. 709 0
      src/views/account/account.vue
  52. 714 0
      src/views/bus/bus.vue
  53. 822 0
      src/views/busnumber/busnumber.vue
  54. 721 0
      src/views/classes/classes.vue
  55. 295 0
      src/views/login/index.vue
  56. 599 0
      src/views/news/news.vue
  57. 756 0
      src/views/staff/staff.vue
  58. 855 0
      src/views/statement/statement.vue
  59. 983 0
      src/views/vidicons/user/index.vue
  60. 1629 0
      src/views/vidicons/vidicon/index.vue
  61. 302 0
      src/views/waiting/waiting.vue
  62. 22 0
      text.json
  63. 81 0
      vite.config.js

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+dist-ssr
+*.local
+
+# Editor directories and files
+.vscode/*
+!.vscode/extensions.json
+.idea
+.DS_Store
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 3 - 0
.vscode/extensions.json

@@ -0,0 +1,3 @@
+{
+  "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
+}

+ 7 - 1
README.md

@@ -1 +1,7 @@
-#School_bus_manage
+# Vue 3 + Vite
+
+This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
+
+## Recommended IDE Setup
+
+- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).

+ 17 - 0
index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="zh">
+
+<head>
+  <meta charset="UTF-8" />
+  <link rel="icon" type="image/svg+xml" href="./src/assets/vite.svg" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <title>校车预约管理系统</title>
+
+</head>
+
+<body>
+  <div id="app"></div>
+  <script type="module" src="/src/main.js"></script>
+</body>
+
+</html>

+ 919 - 0
package-lock.json

@@ -0,0 +1,919 @@
+{
+  "name": "vite-project",
+  "version": "0.0.0",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@babel/parser": {
+      "version": "7.20.15",
+      "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.20.15.tgz",
+      "integrity": "sha512-DI4a1oZuf8wC+oAJA9RW6ga3Zbe8RZFt7kD9i4qAspz3I/yHet1VvC3DiSy/fsUvv5pvJuNPh0LPOdCcqinDPg=="
+    },
+    "@ctrl/tinycolor": {
+      "version": "3.5.0",
+      "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.5.0.tgz",
+      "integrity": "sha512-tlJpwF40DEQcfR/QF+wNMVyGMaO9FQp6Z1Wahj4Gk3CJQYHwA2xVG7iKDFdW6zuxZY9XWOpGcfNCTsX4McOsOg=="
+    },
+    "@easydarwin/easyplayer": {
+      "version": "5.0.7",
+      "resolved": "https://registry.npmmirror.com/@easydarwin/easyplayer/-/easyplayer-5.0.7.tgz",
+      "integrity": "sha512-cKsvrUwFN1ZmYR+grhTKiOUp6QG1mgu64XGPjeASsAWCc03TygKQBsKCHPndafotjCXFFwuG9g1HatOiNvd3dQ=="
+    },
+    "@element-plus/icons-vue": {
+      "version": "2.0.10",
+      "resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.0.10.tgz",
+      "integrity": "sha512-ygEZ1mwPjcPo/OulhzLE7mtDrQBWI8vZzEWSNB2W/RNCRjoQGwbaK4N8lV4rid7Ts4qvySU3njMN7YCiSlSaTQ=="
+    },
+    "@esbuild/android-arm": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.16.17.tgz",
+      "integrity": "sha512-N9x1CMXVhtWEAMS7pNNONyA14f71VPQN9Cnavj1XQh6T7bskqiLLrSca4O0Vr8Wdcga943eThxnVp3JLnBMYtw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-arm64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.16.17.tgz",
+      "integrity": "sha512-MIGl6p5sc3RDTLLkYL1MyL8BMRN4tLMRCn+yRJJmEDvYZ2M7tmAf80hx1kbNEUX2KJ50RRtxZ4JHLvCfuB6kBg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/android-x64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.16.17.tgz",
+      "integrity": "sha512-a3kTv3m0Ghh4z1DaFEuEDfz3OLONKuFvI4Xqczqx4BqLyuFaFkuaG4j2MtA6fuWEFeC5x9IvqnX7drmRq/fyAQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-arm64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.16.17.tgz",
+      "integrity": "sha512-/2agbUEfmxWHi9ARTX6OQ/KgXnOWfsNlTeLcoV7HSuSTv63E4DqtAc+2XqGw1KHxKMHGZgbVCZge7HXWX9Vn+w==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/darwin-x64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.16.17.tgz",
+      "integrity": "sha512-2By45OBHulkd9Svy5IOCZt376Aa2oOkiE9QWUK9fe6Tb+WDr8hXL3dpqi+DeLiMed8tVXspzsTAvd0jUl96wmg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-arm64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.16.17.tgz",
+      "integrity": "sha512-mt+cxZe1tVx489VTb4mBAOo2aKSnJ33L9fr25JXpqQqzbUIw/yzIzi+NHwAXK2qYV1lEFp4OoVeThGjUbmWmdw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/freebsd-x64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.16.17.tgz",
+      "integrity": "sha512-8ScTdNJl5idAKjH8zGAsN7RuWcyHG3BAvMNpKOBaqqR7EbUhhVHOqXRdL7oZvz8WNHL2pr5+eIT5c65kA6NHug==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.16.17.tgz",
+      "integrity": "sha512-iihzrWbD4gIT7j3caMzKb/RsFFHCwqqbrbH9SqUSRrdXkXaygSZCZg1FybsZz57Ju7N/SHEgPyaR0LZ8Zbe9gQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-arm64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.16.17.tgz",
+      "integrity": "sha512-7S8gJnSlqKGVJunnMCrXHU9Q8Q/tQIxk/xL8BqAP64wchPCTzuM6W3Ra8cIa1HIflAvDnNOt2jaL17vaW+1V0g==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ia32": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.16.17.tgz",
+      "integrity": "sha512-kiX69+wcPAdgl3Lonh1VI7MBr16nktEvOfViszBSxygRQqSpzv7BffMKRPMFwzeJGPxcio0pdD3kYQGpqQ2SSg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-loong64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.16.17.tgz",
+      "integrity": "sha512-dTzNnQwembNDhd654cA4QhbS9uDdXC3TKqMJjgOWsC0yNCbpzfWoXdZvp0mY7HU6nzk5E0zpRGGx3qoQg8T2DQ==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-mips64el": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.16.17.tgz",
+      "integrity": "sha512-ezbDkp2nDl0PfIUn0CsQ30kxfcLTlcx4Foz2kYv8qdC6ia2oX5Q3E/8m6lq84Dj/6b0FrkgD582fJMIfHhJfSw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-ppc64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.16.17.tgz",
+      "integrity": "sha512-dzS678gYD1lJsW73zrFhDApLVdM3cUF2MvAa1D8K8KtcSKdLBPP4zZSLy6LFZ0jYqQdQ29bjAHJDgz0rVbLB3g==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-riscv64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.16.17.tgz",
+      "integrity": "sha512-ylNlVsxuFjZK8DQtNUwiMskh6nT0vI7kYl/4fZgV1llP5d6+HIeL/vmmm3jpuoo8+NuXjQVZxmKuhDApK0/cKw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-s390x": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.16.17.tgz",
+      "integrity": "sha512-gzy7nUTO4UA4oZ2wAMXPNBGTzZFP7mss3aKR2hH+/4UUkCOyqmjXiKpzGrY2TlEUhbbejzXVKKGazYcQTZWA/w==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/linux-x64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.16.17.tgz",
+      "integrity": "sha512-mdPjPxfnmoqhgpiEArqi4egmBAMYvaObgn4poorpUaqmvzzbvqbowRllQ+ZgzGVMGKaPkqUmPDOOFQRUFDmeUw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/netbsd-x64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.16.17.tgz",
+      "integrity": "sha512-/PzmzD/zyAeTUsduZa32bn0ORug+Jd1EGGAUJvqfeixoEISYpGnAezN6lnJoskauoai0Jrs+XSyvDhppCPoKOA==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/openbsd-x64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.16.17.tgz",
+      "integrity": "sha512-2yaWJhvxGEz2RiftSk0UObqJa/b+rIAjnODJgv2GbGGpRwAfpgzyrg1WLK8rqA24mfZa9GvpjLcBBg8JHkoodg==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/sunos-x64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.16.17.tgz",
+      "integrity": "sha512-xtVUiev38tN0R3g8VhRfN7Zl42YCJvyBhRKw1RJjwE1d2emWTVToPLNEQj/5Qxc6lVFATDiy6LjVHYhIPrLxzw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-arm64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.16.17.tgz",
+      "integrity": "sha512-ga8+JqBDHY4b6fQAmOgtJJue36scANy4l/rL97W+0wYmijhxKetzZdKOJI7olaBaMhWt8Pac2McJdZLxXWUEQw==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-ia32": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.16.17.tgz",
+      "integrity": "sha512-WnsKaf46uSSF/sZhwnqE4L/F89AYNMiD4YtEcYekBt9Q7nj0DiId2XH2Ng2PHM54qi5oPrQ8luuzGszqi/veig==",
+      "dev": true,
+      "optional": true
+    },
+    "@esbuild/win32-x64": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.16.17.tgz",
+      "integrity": "sha512-y+EHuSchhL7FjHgvQL/0fnnFmO4T1bhvWANX6gcnqTjtnKWbTvUMCpGnv2+t+31d7RzyEAYAd4u2fnIhHL6N/Q==",
+      "dev": true,
+      "optional": true
+    },
+    "@floating-ui/core": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.2.0.tgz",
+      "integrity": "sha512-GHUXPEhMEmTpnpIfesFA2KAoMJPb1SPQw964tToQwt+BbGXdhqTCWT1rOb0VURGylsxsYxiGMnseJ3IlclVpVA=="
+    },
+    "@floating-ui/dom": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.2.0.tgz",
+      "integrity": "sha512-QXzg57o1cjLz3cGETzKXjI3kx1xyS49DW9l7kV2jw2c8Yftd434t2hllX0sVGn2Q8MtcW/4pNm8bfE1/4n6mng==",
+      "requires": {
+        "@floating-ui/core": "^1.2.0"
+      }
+    },
+    "@jridgewell/gen-mapping": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz",
+      "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==",
+      "requires": {
+        "@jridgewell/set-array": "^1.0.1",
+        "@jridgewell/sourcemap-codec": "^1.4.10",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      }
+    },
+    "@jridgewell/resolve-uri": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
+      "integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
+    },
+    "@jridgewell/set-array": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/set-array/-/set-array-1.1.2.tgz",
+      "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
+    },
+    "@jridgewell/source-map": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/source-map/-/source-map-0.3.2.tgz",
+      "integrity": "sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==",
+      "requires": {
+        "@jridgewell/gen-mapping": "^0.3.0",
+        "@jridgewell/trace-mapping": "^0.3.9"
+      }
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.4.14",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
+      "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
+    },
+    "@jridgewell/trace-mapping": {
+      "version": "0.3.17",
+      "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.17.tgz",
+      "integrity": "sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==",
+      "requires": {
+        "@jridgewell/resolve-uri": "3.1.0",
+        "@jridgewell/sourcemap-codec": "1.4.14"
+      }
+    },
+    "@liveqing/liveplayer-v3": {
+      "version": "3.7.10",
+      "resolved": "https://registry.npmmirror.com/@liveqing/liveplayer-v3/-/liveplayer-v3-3.7.10.tgz",
+      "integrity": "sha512-ZfgEObnH/vZJHotBXY0qY4adXTUnqHtNFsSXB7GBZ/WjOxq13saLG5NgoHjdvHiRyuhNkVzOqHZT4UmQpswoww=="
+    },
+    "@types/lodash": {
+      "version": "4.14.191",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.191.tgz",
+      "integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ=="
+    },
+    "@types/lodash-es": {
+      "version": "4.17.6",
+      "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.6.tgz",
+      "integrity": "sha512-R+zTeVUKDdfoRxpAryaQNRKk3105Rrgx2CFRClIgRGaqDTdjsm8h6IYA8ir584W3ePzkZfst5xIgDwYrlh9HLg==",
+      "requires": {
+        "@types/lodash": "*"
+      }
+    },
+    "@types/web-bluetooth": {
+      "version": "0.0.16",
+      "resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz",
+      "integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ=="
+    },
+    "@vitejs/plugin-vue": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/@vitejs/plugin-vue/-/plugin-vue-4.0.0.tgz",
+      "integrity": "sha512-e0X4jErIxAB5oLtDqbHvHpJe/uWNkdpYV83AOG2xo2tEVSzCzewgJMtREZM30wXnM5ls90hxiOtAuVU6H5JgbA==",
+      "dev": true
+    },
+    "@vue/compiler-core": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
+      "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/shared": "3.2.47",
+        "estree-walker": "^2.0.2",
+        "source-map": "^0.6.1"
+      }
+    },
+    "@vue/compiler-dom": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
+      "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+      "requires": {
+        "@vue/compiler-core": "3.2.47",
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "@vue/compiler-sfc": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
+      "integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.47",
+        "@vue/compiler-dom": "3.2.47",
+        "@vue/compiler-ssr": "3.2.47",
+        "@vue/reactivity-transform": "3.2.47",
+        "@vue/shared": "3.2.47",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7",
+        "postcss": "^8.1.10",
+        "source-map": "^0.6.1"
+      }
+    },
+    "@vue/compiler-ssr": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
+      "integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+      "requires": {
+        "@vue/compiler-dom": "3.2.47",
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "@vue/devtools-api": {
+      "version": "6.5.0",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.5.0.tgz",
+      "integrity": "sha512-o9KfBeaBmCKl10usN4crU53fYtC1r7jJwdGKjPT24t348rHxgfpZ0xL3Xm/gLUYnc0oTp8LAmrxOeLyu6tbk2Q=="
+    },
+    "@vue/reactivity": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity/-/reactivity-3.2.47.tgz",
+      "integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+      "requires": {
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "@vue/reactivity-transform": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
+      "integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+      "requires": {
+        "@babel/parser": "^7.16.4",
+        "@vue/compiler-core": "3.2.47",
+        "@vue/shared": "3.2.47",
+        "estree-walker": "^2.0.2",
+        "magic-string": "^0.25.7"
+      }
+    },
+    "@vue/runtime-core": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
+      "integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+      "requires": {
+        "@vue/reactivity": "3.2.47",
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "@vue/runtime-dom": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
+      "integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+      "requires": {
+        "@vue/runtime-core": "3.2.47",
+        "@vue/shared": "3.2.47",
+        "csstype": "^2.6.8"
+      }
+    },
+    "@vue/server-renderer": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
+      "integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+      "requires": {
+        "@vue/compiler-ssr": "3.2.47",
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "@vue/shared": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/@vue/shared/-/shared-3.2.47.tgz",
+      "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
+    },
+    "@vueuse/core": {
+      "version": "9.12.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.12.0.tgz",
+      "integrity": "sha512-h/Di8Bvf6xRcvS/PvUVheiMYYz3U0tH3X25YxONSaAUBa841ayMwxkuzx/DGUMCW/wHWzD8tRy2zYmOC36r4sg==",
+      "requires": {
+        "@types/web-bluetooth": "^0.0.16",
+        "@vueuse/metadata": "9.12.0",
+        "@vueuse/shared": "9.12.0",
+        "vue-demi": "*"
+      }
+    },
+    "@vueuse/metadata": {
+      "version": "9.12.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.12.0.tgz",
+      "integrity": "sha512-9oJ9MM9lFLlmvxXUqsR1wLt1uF7EVbP5iYaHJYqk+G2PbMjY6EXvZeTjbdO89HgoF5cI6z49o2zT/jD9SVoNpQ=="
+    },
+    "@vueuse/shared": {
+      "version": "9.12.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.12.0.tgz",
+      "integrity": "sha512-TWuJLACQ0BVithVTRbex4Wf1a1VaRuSpVeyEd4vMUWl54PzlE0ciFUshKCXnlLuD0lxIaLK4Ypj3NXYzZh4+SQ==",
+      "requires": {
+        "vue-demi": "*"
+      }
+    },
+    "acorn": {
+      "version": "8.8.2",
+      "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.8.2.tgz",
+      "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw=="
+    },
+    "anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "async-validator": {
+      "version": "4.2.5",
+      "resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz",
+      "integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg=="
+    },
+    "asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "axios": {
+      "version": "1.3.3",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.3.3.tgz",
+      "integrity": "sha512-eYq77dYIFS77AQlhzEL937yUBSepBfPIe8FcgEDN35vMNZKMrs81pgnyrQpwfy4NF4b4XWX1Zgx7yX+25w8QJA==",
+      "requires": {
+        "follow-redirects": "^1.15.0",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "binary-extensions": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.2.0.tgz",
+      "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "buffer-from": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/buffer-from/-/buffer-from-1.1.2.tgz",
+      "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+    },
+    "chokidar": {
+      "version": "3.5.3",
+      "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.5.3.tgz",
+      "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+      "requires": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      }
+    },
+    "combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "requires": {
+        "delayed-stream": "~1.0.0"
+      }
+    },
+    "commander": {
+      "version": "2.20.3",
+      "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
+      "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="
+    },
+    "csstype": {
+      "version": "2.6.21",
+      "resolved": "https://registry.npmmirror.com/csstype/-/csstype-2.6.21.tgz",
+      "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+    },
+    "dayjs": {
+      "version": "1.11.7",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz",
+      "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+    },
+    "deepmerge": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmmirror.com/deepmerge/-/deepmerge-4.3.0.tgz",
+      "integrity": "sha512-z2wJZXrmeHdvYJp/Ux55wIjqo81G5Bp4c+oELTW+7ar6SogWHajt5a9gO3s3IDaGSAXjDk0vlQKN3rms8ab3og=="
+    },
+    "delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
+    },
+    "element-plus": {
+      "version": "2.2.29",
+      "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.2.29.tgz",
+      "integrity": "sha512-g4dcrURrKkR5uUX8n5RVnnqGnimoki9HfqS4yHHG6XwCHBkZGozdq4x+478BzeWUe31h++BO+7dakSx4VnM8RQ==",
+      "requires": {
+        "@ctrl/tinycolor": "^3.4.1",
+        "@element-plus/icons-vue": "^2.0.6",
+        "@floating-ui/dom": "^1.0.1",
+        "@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7",
+        "@types/lodash": "^4.14.182",
+        "@types/lodash-es": "^4.17.6",
+        "@vueuse/core": "^9.1.0",
+        "async-validator": "^4.2.5",
+        "dayjs": "^1.11.3",
+        "escape-html": "^1.0.3",
+        "lodash": "^4.17.21",
+        "lodash-es": "^4.17.21",
+        "lodash-unified": "^1.0.2",
+        "memoize-one": "^6.0.0",
+        "normalize-wheel-es": "^1.2.0"
+      },
+      "dependencies": {
+        "@popperjs/core": {
+          "version": "npm:@sxzz/popperjs-es@2.11.7",
+          "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
+          "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
+        }
+      }
+    },
+    "esbuild": {
+      "version": "0.16.17",
+      "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.16.17.tgz",
+      "integrity": "sha512-G8LEkV0XzDMNwXKgM0Jwu3nY3lSTwSGY6XbxM9cr9+s0T/qSV1q1JVPBGzm3dcjhCic9+emZDmMffkwgPeOeLg==",
+      "dev": true,
+      "requires": {
+        "@esbuild/android-arm": "0.16.17",
+        "@esbuild/android-arm64": "0.16.17",
+        "@esbuild/android-x64": "0.16.17",
+        "@esbuild/darwin-arm64": "0.16.17",
+        "@esbuild/darwin-x64": "0.16.17",
+        "@esbuild/freebsd-arm64": "0.16.17",
+        "@esbuild/freebsd-x64": "0.16.17",
+        "@esbuild/linux-arm": "0.16.17",
+        "@esbuild/linux-arm64": "0.16.17",
+        "@esbuild/linux-ia32": "0.16.17",
+        "@esbuild/linux-loong64": "0.16.17",
+        "@esbuild/linux-mips64el": "0.16.17",
+        "@esbuild/linux-ppc64": "0.16.17",
+        "@esbuild/linux-riscv64": "0.16.17",
+        "@esbuild/linux-s390x": "0.16.17",
+        "@esbuild/linux-x64": "0.16.17",
+        "@esbuild/netbsd-x64": "0.16.17",
+        "@esbuild/openbsd-x64": "0.16.17",
+        "@esbuild/sunos-x64": "0.16.17",
+        "@esbuild/win32-arm64": "0.16.17",
+        "@esbuild/win32-ia32": "0.16.17",
+        "@esbuild/win32-x64": "0.16.17"
+      }
+    },
+    "escape-html": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/escape-html/-/escape-html-1.0.3.tgz",
+      "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
+    },
+    "estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "follow-redirects": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz",
+      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA=="
+    },
+    "form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "optional": true
+    },
+    "function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "requires": {
+        "function-bind": "^1.1.1"
+      }
+    },
+    "hls.js": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmmirror.com/hls.js/-/hls.js-1.3.4.tgz",
+      "integrity": "sha512-iFEwVqtEDk6sKotcTwtJ5OMo/nuDTk9PrpB8FI2J2WYf8EriTVfR4FaK0aNyYtwbYeRSWCXJKlz23xeREdlNYg=="
+    },
+    "immutable": {
+      "version": "4.2.4",
+      "resolved": "https://registry.npmmirror.com/immutable/-/immutable-4.2.4.tgz",
+      "integrity": "sha512-WDxL3Hheb1JkRN3sQkyujNlL/xRjAo3rJtaU5xeufUauG66JdMr32bLj4gF+vWl84DIA3Zxw7tiAjneYzRRw+w=="
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-core-module": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.11.0.tgz",
+      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+      "dev": true,
+      "requires": {
+        "has": "^1.0.3"
+      }
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="
+    },
+    "jsencrypt": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.1.tgz",
+      "integrity": "sha512-dVvV54GdFuJgmEKn+oBiaifDMen4p6o6j/lJh0OVMcouME8sST0bJ7bldIgKBQk4za0zyGn0/pm4vOznR25mLw=="
+    },
+    "klona": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmmirror.com/klona/-/klona-2.0.6.tgz",
+      "integrity": "sha512-dhG34DXATL5hSxJbIexCft8FChFXtmskoZYnoPWjXQuebWYCNkVeV3KkGegCK9CP1oswI/vQibS2GY7Em/sJJA=="
+    },
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "lodash-es": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz",
+      "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
+    },
+    "lodash-unified": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz",
+      "integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ=="
+    },
+    "magic-string": {
+      "version": "0.25.9",
+      "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.25.9.tgz",
+      "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+      "requires": {
+        "sourcemap-codec": "^1.4.8"
+      }
+    },
+    "memoize-one": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
+      "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+    },
+    "mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="
+    },
+    "mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "requires": {
+        "mime-db": "1.52.0"
+      }
+    },
+    "nanoid": {
+      "version": "3.3.4",
+      "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.4.tgz",
+      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw=="
+    },
+    "neo-async": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmmirror.com/neo-async/-/neo-async-2.6.2.tgz",
+      "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
+    },
+    "normalize-wheel-es": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz",
+      "integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw=="
+    },
+    "path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+    },
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
+    },
+    "postcss": {
+      "version": "8.4.21",
+      "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.4.21.tgz",
+      "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+      "requires": {
+        "nanoid": "^3.3.4",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      }
+    },
+    "proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "resolve": {
+      "version": "1.22.1",
+      "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.1.tgz",
+      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+      "dev": true,
+      "requires": {
+        "is-core-module": "^2.9.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      }
+    },
+    "rollup": {
+      "version": "3.14.0",
+      "resolved": "https://registry.npmmirror.com/rollup/-/rollup-3.14.0.tgz",
+      "integrity": "sha512-o23sdgCLcLSe3zIplT9nQ1+r97okuaiR+vmAPZPTDYB7/f3tgWIYNyiQveMsZwshBT0is4eGax/HH83Q7CG+/Q==",
+      "dev": true,
+      "requires": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "sass": {
+      "version": "1.58.0",
+      "resolved": "https://registry.npmmirror.com/sass/-/sass-1.58.0.tgz",
+      "integrity": "sha512-PiMJcP33DdKtZ/1jSjjqVIKihoDc6yWmYr9K/4r3fVVIEDAluD0q7XZiRKrNJcPK3qkLRF/79DND1H5q1LBjgg==",
+      "requires": {
+        "chokidar": ">=3.0.0 <4.0.0",
+        "immutable": "^4.0.0",
+        "source-map-js": ">=0.6.2 <2.0.0"
+      }
+    },
+    "sass-loader": {
+      "version": "13.2.0",
+      "resolved": "https://registry.npmmirror.com/sass-loader/-/sass-loader-13.2.0.tgz",
+      "integrity": "sha512-JWEp48djQA4nbZxmgC02/Wh0eroSUutulROUusYJO9P9zltRbNN80JCBHqRGzjd4cmZCa/r88xgfkjGD0TXsHg==",
+      "requires": {
+        "klona": "^2.0.4",
+        "neo-async": "^2.6.2"
+      }
+    },
+    "shvl": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmmirror.com/shvl/-/shvl-2.0.3.tgz",
+      "integrity": "sha512-V7C6S9Hlol6SzOJPnQ7qzOVEWUQImt3BNmmzh40wObhla3XOYMe4gGiYzLrJd5TFa+cI2f9LKIRJTTKZSTbWgw=="
+    },
+    "source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+    },
+    "source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw=="
+    },
+    "source-map-support": {
+      "version": "0.5.21",
+      "resolved": "https://registry.npmmirror.com/source-map-support/-/source-map-support-0.5.21.tgz",
+      "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+      "requires": {
+        "buffer-from": "^1.0.0",
+        "source-map": "^0.6.0"
+      }
+    },
+    "sourcemap-codec": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmmirror.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA=="
+    },
+    "supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true
+    },
+    "terser": {
+      "version": "5.16.5",
+      "resolved": "https://registry.npmmirror.com/terser/-/terser-5.16.5.tgz",
+      "integrity": "sha512-qcwfg4+RZa3YvlFh0qjifnzBHjKGNbtDo9yivMqMFDy9Q6FSaQWSB/j1xKhsoUFJIqDOM3TsN6D5xbrMrFcHbg==",
+      "requires": {
+        "@jridgewell/source-map": "^0.3.2",
+        "acorn": "^8.5.0",
+        "commander": "^2.20.0",
+        "source-map-support": "~0.5.20"
+      }
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "vite": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmmirror.com/vite/-/vite-4.1.1.tgz",
+      "integrity": "sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==",
+      "dev": true,
+      "requires": {
+        "esbuild": "^0.16.14",
+        "fsevents": "~2.3.2",
+        "postcss": "^8.4.21",
+        "resolve": "^1.22.1",
+        "rollup": "^3.10.0"
+      }
+    },
+    "vue": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmmirror.com/vue/-/vue-3.2.47.tgz",
+      "integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+      "requires": {
+        "@vue/compiler-dom": "3.2.47",
+        "@vue/compiler-sfc": "3.2.47",
+        "@vue/runtime-dom": "3.2.47",
+        "@vue/server-renderer": "3.2.47",
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "vue-demi": {
+      "version": "0.13.11",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.13.11.tgz",
+      "integrity": "sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A=="
+    },
+    "vue-router": {
+      "version": "4.0.10",
+      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.0.10.tgz",
+      "integrity": "sha512-YbPf6QnZpyyWfnk7CUt2Bme+vo7TLfg1nGZNkvYqKYh4vLaFw6Gn8bPGdmt5m4qrGnKoXLqc4htAsd3dIukICA==",
+      "requires": {
+        "@vue/devtools-api": "^6.0.0-beta.14"
+      }
+    },
+    "vuex": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/vuex/-/vuex-4.0.2.tgz",
+      "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
+      "requires": {
+        "@vue/devtools-api": "^6.0.0-beta.11"
+      }
+    },
+    "vuex-persistedstate": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmmirror.com/vuex-persistedstate/-/vuex-persistedstate-4.1.0.tgz",
+      "integrity": "sha512-3SkEj4NqwM69ikJdFVw6gObeB0NHyspRYMYkR/EbhR0hbvAKyR5gksVhtAfY1UYuWUOCCA0QNGwv9pOwdj+XUQ==",
+      "requires": {
+        "deepmerge": "^4.2.2",
+        "shvl": "^2.0.3"
+      }
+    }
+  }
+}

+ 33 - 0
package.json

@@ -0,0 +1,33 @@
+{
+  "name": "vite-project",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@easydarwin/easyplayer": "^5.0.7",
+    "@element-plus/icons-vue": "^2.0.10",
+    "@liveqing/liveplayer-v3": "^3.7.10",
+    "axios": "^1.3.2",
+    "dayjs": "^1.11.7",
+    "element-plus": "^2.2.29",
+    "hls.js": "^1.3.4",
+    "jsencrypt": "^3.3.1",
+    "lodash": "^4.17.21",
+    "sass": "^1.58.0",
+    "sass-loader": "^13.2.0",
+    "terser": "^5.16.5",
+    "vue": "^3.2.45",
+    "vue-router": "^4.0.10",
+    "vuex": "^4.0.2",
+    "vuex-persistedstate": "^4.1.0"
+  },
+  "devDependencies": {
+    "@vitejs/plugin-vue": "^4.0.0",
+    "vite": "^4.1.0"
+  }
+}

+ 22 - 0
src/App.vue

@@ -0,0 +1,22 @@
+<script setup>
+import { useRouter } from "vue-router";
+import { useStore } from "vuex";
+const store = useStore();
+const router = useRouter();
+</script>
+
+<template>
+  <router-view></router-view>
+</template>
+
+<style lang="scss">
+#app {
+  max-width: 1920px;
+  width: 1920px;
+  min-height: 100vh;
+  max-height: 1080px;
+  margin: 0;
+  padding: 0;
+  text-align: unset;
+}
+</style>

+ 87 - 0
src/api/admin.js

@@ -0,0 +1,87 @@
+import request from "@/utils/request";
+const requests = '/api'// 线上
+// const requests = '/video/transcoding/transcoding'//线下
+export default {
+    // 登录账号
+    // async adminLogin(data) {
+    //     let requestData = await request({
+    //         url: `${requests}/carBook/adminlogin.action`,
+    //         method: "POST",
+    //         // headers: { "Content-Type": "application/json;charset=utf-8" },
+    //         data: data,
+    //     });
+    //     return requestData;
+    // },
+
+    // 账号列表
+    // async adminList(params) {
+    //     let requestData = await request({
+    //         url: `${requests}/carBook/adminlist.action`,
+    //         method: "GET",
+    //         headers: { "Content-Type": "multipart/form-data" },
+    //         params,
+    //     });
+    //     return requestData;
+    // },
+
+    // 新增管理员
+    async adminAdd(data) {
+        let requestData = await request({
+            url: `${requests}/carBook/admininsert.action`,
+            method: "POST",
+            data,
+        });
+        return requestData;
+    },
+    // 修改管理员
+    async adminUpdate(data) {
+        let requestData = await request({
+            url: `${requests}/carBook/adminupdate.action`,
+            method: "POST",
+            data,
+        });
+        return requestData;
+    },
+    // 修改密码
+    // async updatePassword(data) {
+    //     let requestData = await request({
+    //         url: `${requests}/videoAdmin/updatePassword`,
+    //         method: "PUT",
+    //         data,
+    //     });
+    //     return requestData;
+    // },
+    // 删除管理员
+    async adminDel(data) {
+        let requestData = await request({
+            url: `${requests}/carBook/admindel.action`,
+            method: "POST",
+            data,
+        });
+        return requestData;
+    },
+
+    // 导出管理员信息
+    async download(params) {
+        let requestData = await request({
+            url: `${requests}/carBook/admintoExcel.action`,
+            method: "GET",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            responseType: "blob",
+            params,
+        });
+        return requestData;
+    },
+
+    // 导入监控信息
+    // async importByExcel(data) {
+    //     let requestData = await request({
+    //         // url: "/api/video/videoAdmin/importByExcel",
+    //         url: `${requests}/camera/importByExcel`,
+    //         method: "POST",
+    //         headers: { "Content-Type": "multipart/form-data" },
+    //         data: data,
+    //     });
+    //     return requestData;
+    // },
+};

+ 14 - 0
src/api/home.js

@@ -0,0 +1,14 @@
+import request from "@/utils/request";
+// const request='/api'
+export default {
+    /*搜索*/
+
+    async getHomeList(data) {
+        let requestData = await request({
+            url: "/api",
+            method: "POST",
+            data: data,
+        });
+        return requestData;
+    }
+}

+ 110 - 0
src/api/vidicons.js

@@ -0,0 +1,110 @@
+import request from "@/utils/request";
+const requests = '/video/transcoding'// 线上
+// const requests = '/video/transcoding/transcoding'// 线下
+export default {
+    // 添加监控信息
+    async addCamera(data) {
+        let requestData = await request({
+            url: `${requests}/camera/addCamera`,
+            method: "POST",
+            headers: { "Content-Type": "application/json; charset=utf-8" },
+            data: data,
+        });
+        return requestData;
+    },
+
+    // 删除监控信息
+    async delCamera(data) {
+        let requestData = await request({
+            url: `${requests}/camera/delete`,
+            method: "DELETE",
+            data
+        });
+        return requestData;
+    },
+
+    // 修改监控信息
+    async updateCamera(data) {
+        let requestData = await request({
+            url: `${requests}/camera/updateCamera`,
+            method: "PUT",
+            headers: { "Content-Type": "application/json; charset=utf-8" },
+            data,
+        });
+        return requestData;
+    },
+
+    // 监控信息列表
+    async queryCamera(data) {
+        let requestData = await request({
+            url: `${requests}/camera/queryCamera`,
+            method: "POST",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            params: data,
+        });
+        return requestData;
+    },
+    // 导出监控信息
+    async download(params) {
+        let requestData = await request({
+            url: `${requests}/camera/download`,
+            method: "GET",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            responseType: "blob",
+            params,
+        });
+        return requestData;
+    },
+    // 导入监控信息
+    async importByExcel(data) {
+        let requestData = await request({
+            url: `${requests}/camera/importByExcel`,
+            method: "POST",
+            headers: { "Content-Type": "multipart/form-data" },
+            data,
+        });
+        return requestData;
+    },
+
+    // 使用Stream推拉流
+    async addStreamSource(data) {
+        let requestData = await request({
+            url: `${requests}/stream/addStreamSource`,
+            method: "POST",
+            headers: { "Content-Type": "application/json; charset=utf-8" },
+            data,
+        });
+        return requestData;
+    },
+
+    // 关闭Stream推拉流
+    async delStreamSource(data) {
+        let requestData = await request({
+            url: `${requests}/stream/delStreamSource`,
+            method: "POST",
+            headers: { "Content-Type": "application/json; charset=utf-8" },
+            data,
+        });
+        return requestData;
+    },
+    // 更新输出链接
+    async updateCameraLink(params) {
+        let requestData = await request({
+            url: `${requests}/camera/updateCameraLink`,
+            method: "GET",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            params,
+        });
+        return requestData;
+    },
+    // 关闭当前播放回写剩下的播放链接、
+    async delAndUpdateLink(params) {
+        let requestData = await request({
+            url: `${requests}/stream/delAndUpdateLink`,
+            method: "GET",
+            headers: { "Content-Type": "application/x-www-form-urlencoded;charset=utf-8" },
+            params,
+        });
+        return requestData;
+    },
+};

BIN
src/assets/account.png


BIN
src/assets/accountA.png


BIN
src/assets/bg-video.png


BIN
src/assets/car.png


BIN
src/assets/carA.png


BIN
src/assets/carnumber.png


BIN
src/assets/carnumberA.png


BIN
src/assets/classes.png


BIN
src/assets/classesA.png


BIN
src/assets/import.png


BIN
src/assets/login.png


BIN
src/assets/login2.png


BIN
src/assets/login3.png


BIN
src/assets/logo.png


BIN
src/assets/logoBg.jpg


BIN
src/assets/nanchang.png


BIN
src/assets/news.png


BIN
src/assets/nodata.png


BIN
src/assets/read.png


BIN
src/assets/staff.png


BIN
src/assets/staffA.png


BIN
src/assets/statement.png


BIN
src/assets/statementA.png


BIN
src/assets/system.png


파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1 - 0
src/assets/vite.svg


BIN
src/assets/waiting.png


BIN
src/assets/waitingA.png


+ 42 - 0
src/components/HelloWorld.vue

@@ -0,0 +1,42 @@
+<template>
+  <h1>{{ msg }}</h1>
+
+  <div class="card">
+    <button type="button" @click="count++">count is {{ count }}</button>
+    <p>
+      Edit
+      <code>components/HelloWorld.vue</code> to test HMR
+    </p>
+  </div>
+
+  <p>
+    Check out
+    <a href="https://vuejs.org/guide/quick-start.html#local" target="_blank">create-vue</a
+    >, the official Vue + Vite starter
+  </p>
+  <p>
+    Install
+    <a href="https://github.com/johnsoncodehk/volar" target="_blank">Volar</a>
+    in your IDE for a better DX
+  </p>
+  <p class="read-the-docs">Click on the Vite and Vue logos to learn more</p>
+  <div class="ul">
+    <div class="li">文字</div>
+  </div>
+</template>
+
+<script setup>
+import { ref } from "vue";
+
+defineProps({
+  msg: String,
+});
+
+const count = ref(0);
+</script>
+
+<style scoped lang="scss">
+.read-the-docs {
+  color: #888;
+}
+</style>

+ 62 - 0
src/layout/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div class="body-box">
+    <SidevarItem></SidevarItem>
+    <div class="content">
+      <Navbar></Navbar>
+      <router-view></router-view>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount, onMounted, watch } from "vue";
+import Navbar from "./sidebar/Navbar.vue";
+import SidevarItem from "./sidebar/SidevarItem.vue";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+const store = useStore();
+const router = useRouter();
+
+onBeforeMount(() => {});
+</script>
+
+<style scoped lang="scss">
+.body-box {
+  display: flex;
+  .content {
+    // min-width: 1740px;
+    max-width: calc(1920px - 78px);
+    width: 100%;
+    height: calc(100vh);
+    background-color: rgba(238, 238, 238, 1);
+    :deep(.el-dialog__header .el-dialog__headerbtn) {
+      outline: none;
+    }
+  }
+}
+</style>
+<style lang="scss">
+.is-message-box {
+  .el-message-box__header {
+    .el-message-box__headerbtn {
+      outline: none;
+    }
+  }
+  .el-message-box__btns {
+    .el-button--primary {
+      background-color: rgba(61, 81, 232, 1);
+      border: 0.5px solid rgba(61, 81, 232, 1);
+    }
+    .el-button--primary:hover {
+      background-color: rgb(119, 133, 239);
+    }
+  }
+}
+.el-dialog__body {
+  .el-input {
+    .el-input__suffix-inner {
+      color: rgba(61, 81, 232, 1);
+    }
+  }
+}
+</style>

+ 350 - 0
src/layout/sidebar/Navbar.vue

@@ -0,0 +1,350 @@
+<template>
+  <el-menu
+    :default-active="activeIndex"
+    class="el-menu-demo"
+    mode="horizontal"
+    background-color=""
+    text-color="#000"
+    active-text-color="#4392f7"
+    @select="handleSelect"
+  >
+    <el-icon
+      :size="20"
+      v-show="!menuclose"
+      class="fold"
+      @click="SpreadMenu(true)"
+      ><Fold
+    /></el-icon>
+    <el-icon
+      :size="20"
+      v-show="menuclose"
+      class="fold"
+      @click="SpreadMenu(false)"
+      ><Expand
+    /></el-icon>
+    <div class="logo">
+      <img src="@/assets/nanchang.png" style="width: 30px; height: 30px" />
+      <span>校车预约管理系统</span>
+    </div>
+    <div class="login">
+      <span class="dateTime">{{ dateTime }}</span>
+      <img
+        src="@/assets/news.png"
+        @click="newsClick"
+        style="width: 20px; height: 20px; padding: 0 20px 0 0; cursor: pointer"
+        alt=""
+      />
+      <div class="flex flex-wrap items-center" style="cursor: pointer">
+        <el-dropdown :hide-on-click="false" trigger="click">
+          <el-avatar :size="30">
+            <img
+              src="https://cube.elemecdn.com/0/88/03b0d39583f48206768a7534e55bcpng.png"
+            />
+          </el-avatar>
+          <template #dropdown>
+            <el-dropdown-menu>
+              <el-dropdown-item @click="uppassword">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <Lock />
+                </el-icon>
+                <span>修改密码</span>
+              </el-dropdown-item>
+              <el-dialog
+                :append-to-body="true"
+                custom-class="pw"
+                v-model="uppasswordShow"
+                title="修改密码"
+                width="30%"
+                :before-close="uppasswordClose"
+                align-center
+              >
+                <el-form
+                  ref="ruleFormRef"
+                  :model="ruleForm"
+                  :rules="rules"
+                  label-width="100px"
+                  class="demo-ruleForm"
+                  :size="formSize"
+                  label-position="left"
+                  status-icon
+                >
+                  <el-form-item label="原密码" prop="oldpass">
+                    <el-input
+                      v-model="ruleForm.oldpass"
+                      placeholder="请输入原密码"
+                    />
+                  </el-form-item>
+                  <el-form-item label="新密码" prop="nowpass">
+                    <el-input
+                      v-model="ruleForm.nowpass"
+                      placeholder="请输入新密码"
+                    />
+                  </el-form-item>
+                  <el-form-item
+                    label="确定新密码"
+                    prop="correctpass"
+                    style="
+                      padding-bottom: 40px;
+                      border-bottom: 1px solid rgba(230, 230, 230, 1);
+                    "
+                  >
+                    <el-input
+                      v-model="ruleForm.correctpass"
+                      placeholder="请再次输入新密码"
+                    />
+                  </el-form-item>
+
+                  <el-form-item class="options">
+                    <el-button
+                      class="queding"
+                      type="primary"
+                      @click="submitForm(ruleFormRef)"
+                    >
+                      确定
+                    </el-button>
+                    <el-button class="congzhi" @click="resetForm(ruleFormRef)"
+                      >重置</el-button
+                    >
+                  </el-form-item>
+                </el-form>
+                <!-- <template #footer>
+                  <span class="dialog-footer">
+                    <el-button @click="dialogVisible = false">取消</el-button>
+                    <el-button type="primary" @click="dialogVisible = false">
+                      确定
+                    </el-button>
+                  </span>
+                </template> -->
+              </el-dialog>
+              <el-dropdown-item @click="loginOut">
+                <el-icon class="switchButton" :size="20" color="#000">
+                  <SwitchButton />
+                </el-icon>
+                <span>退出登录</span>
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </template>
+        </el-dropdown>
+      </div>
+      <span class="name">admin</span>
+    </div>
+  </el-menu>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount, onMounted, watch } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { useStore } from "vuex";
+import { useRouter } from "vue-router";
+import { dayjs } from "element-plus";
+
+const store = useStore();
+const router = useRouter();
+const activeIndex = ref(); // 路由路径
+const loginOutDialogVisible = ref(false); // 退出按钮
+const menuclose = ref(false); // 左边状态栏展开按钮状态
+const uppasswordShow = ref(false); // 修改密码展示框
+const ruleForm = reactive({
+  oldpass: "",
+  nowpass: "",
+  correctpass: "",
+});
+const ruleFormRef = ref();
+// 表单验证
+const rules = reactive({
+  oldpass: [
+    {
+      required: true,
+      message: "密码不能为空",
+      trigger: "blur",
+    },
+  ],
+  nowpass: [
+    {
+      required: true,
+      message: "新密码不能为空",
+      trigger: "blur",
+    },
+  ],
+  correctpass: [
+    {
+      required: true,
+      message: "确定新密码是否正确",
+      trigger: "blur",
+    },
+  ],
+  // desc: [{ required: true, message: "Please input activity form", trigger: "blur" }],
+});
+
+// 点击消息通知
+const newsClick = () => {
+  store.commit("indexUp", 0);
+  router.push({
+    path: `/schoolBus/news`,
+  });
+};
+const dateTime = ref(
+  dayjs().format("YYYY-MM-DD") + ` 星期四 ` + dayjs().format("HH:mm:ss")
+); // 时间
+
+// 选择菜单
+const handleSelect = (key, keyPath) => {
+  store.commit("navbarUpdata", key);
+  activeIndex.value = key;
+};
+
+// 展开按钮
+const SpreadMenu = (flag) => {
+  store.commit("menuClose", flag);
+  menuclose.value = flag;
+};
+// 修改密码
+const uppassword = () => {
+  uppasswordShow.value = true;
+};
+const uppasswordClose = () => {
+  uppasswordShow.value = false;
+  // ruleForm.oldpass = "";
+  // ruleForm.nowpass = "";
+  // ruleForm.correctpass = "";
+  ruleFormRef.value.resetFields();
+};
+// 提交修改密码表单
+const submitForm = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      // console.log("submit!");
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+// 重置表单
+const resetForm = (formEl) => {
+  // console.log("重置表单");
+  if (!formEl) return;
+  formEl.resetFields();
+};
+
+const loginOut = () => {
+  ElMessageBox.confirm("是否退出登录?", "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(() => {
+      sessionStorage.removeItem("token");
+      router.push({
+        path: `/login`,
+      });
+      ElMessage({
+        type: "success",
+        message: "退出成功",
+      });
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消登录",
+      });
+    });
+};
+
+onBeforeMount(() => {
+  activeIndex.value = store.state.user.navbar;
+  // handleSelect("vidicons");
+  setInterval(() => {
+    var week = ["日", "一", "二", "三", "四", "五", "六"];
+    var datas = dayjs().day();
+    dateTime.value =
+      dayjs().format("YYYY-MM-DD") +
+      ` 星期${week[datas]} ` +
+      dayjs().format("HH:mm:ss");
+  }, 1000);
+});
+</script>
+
+<style scoped lang="scss">
+.el-menu--horizontal {
+  border-bottom: none;
+  display: flex;
+  align-items: center;
+  height: 65px;
+  // width: 100%;
+  background-color: #fff;
+  box-shadow: 5px 5px 10px 0px rgba(213, 228, 252, 1);
+  .logo {
+    left: 90px;
+    display: flex;
+    align-items: center;
+    span {
+      margin-left: 10px;
+      font-size: 24px;
+      font-weight: 600;
+    }
+  }
+  .login {
+    color: #000;
+    display: flex;
+    align-items: center;
+    position: absolute;
+    right: 10px;
+    top: 17px;
+
+    .dateTime {
+      margin-right: 20px;
+    }
+    .name {
+      height: 20px;
+      line-height: 20px;
+      text-decoration: underline;
+      padding: 0 10px;
+      margin: 0 10px;
+      border-left: 2px solid #ccc;
+    }
+    .switchButton {
+      cursor: pointer;
+    }
+  }
+  .login:focus {
+    outline: none;
+  }
+  .fold {
+    width: 46px;
+    height: 65px;
+  }
+  .fold:hover {
+    cursor: pointer;
+    background-color: rgba(67, 146, 249, 0.1);
+  }
+}
+</style>
+<style lang="scss">
+.el-overlay {
+  .el-overlay-dialog {
+    .pw {
+      border-radius: 11px;
+
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+      .el-dialog__body {
+        padding: 30px 20px 10px 20px;
+        .el-input {
+          width: 200px;
+        }
+        .options {
+          margin-left: 170px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 183 - 0
src/layout/sidebar/SidevarItem.vue

@@ -0,0 +1,183 @@
+<template>
+  <div class="box-item">
+    <el-menu
+      class="el-menu-vertical-demo"
+      text-color="#fff"
+      active-color="#000"
+      :default-active="activeIndex"
+      :collapse="menuClose"
+      @select="handleSelect"
+    >
+      <!-- <div class="logo">logo</div> -->
+      <div class="logo el-menu-item">
+        <el-icon :size="20"
+          ><img src="@/assets/logo.png" style="width: 30px; height: 24px"
+        /></el-icon>
+      </div>
+
+      <el-menu-item :index="1">
+        <img v-if="activeIndex == 1" src="@/assets/statementA.png" alt="" />
+        <img v-else src="@/assets/statement.png " alt="" />
+        <span>报表统计</span>
+      </el-menu-item>
+      <el-menu-item :index="2">
+        <img v-if="activeIndex == 2" src="@/assets/staffA.png" alt="" />
+        <img v-else src="@/assets/staff.png" alt="" />
+        <span>员工管理</span>
+      </el-menu-item>
+      <el-menu-item :index="3">
+        <img v-if="activeIndex == 3" src="@/assets/accountA.png" alt="" />
+        <img v-else src="@/assets/account.png" alt="" />
+        <span>账号管理</span>
+      </el-menu-item>
+      <el-menu-item :index="4">
+        <img v-if="activeIndex == 4" src="@/assets/carA.png" alt="" />
+        <img v-else src="@/assets/car.png" alt="" />
+        <span>车辆管理</span>
+      </el-menu-item>
+      <el-menu-item :index="5">
+        <img v-if="activeIndex == 5" src="@/assets/classesA.png" alt="" />
+        <img v-else src="@/assets/classes.png" alt="" />
+        <span>班次管理</span>
+      </el-menu-item>
+      <el-menu-item :index="6">
+        <img v-if="activeIndex == 6" src="@/assets/carnumberA.png" alt="" />
+        <img v-else src="@/assets/carnumber.png" alt="" />
+        <span>车次管理</span>
+      </el-menu-item>
+      <el-menu-item :index="7">
+        <img v-if="activeIndex == 7" src="@/assets/waitingA.png" alt="" />
+        <img v-else src="@/assets/waiting.png" alt="" />
+        <span>候补设置</span>
+      </el-menu-item>
+    </el-menu>
+  </div>
+</template>
+
+<script setup>
+import { ref, onBeforeMount, onMounted, watch, reactive } from "vue";
+import { useStore } from "vuex";
+import {
+  Document,
+  Menu as IconMenu,
+  Location,
+  Setting,
+} from "@element-plus/icons-vue";
+import { useRouter } from "vue-router";
+
+const store = useStore();
+const router = useRouter();
+
+const menuClose = ref(false);
+const activeIndex = ref(1);
+const acitveItems = reactive({ list: [] });
+
+watch(
+  () => store.state.user.collapse,
+  (newValue, oldValue) => {
+    menuClose.value = newValue;
+  }
+);
+watch(
+  () => store.state.user.activeIndex,
+  (newValue, oldValue) => {
+    activeIndex.value = newValue;
+  }
+);
+
+const handleSelect = (key, keyPath) => {
+  // console.log(key, keyPath);
+  store.commit("indexUp", key);
+  activeIndex.value = key;
+  sessionStorage.setItem("sidevarItem", key);
+  if (activeIndex.value == 1) {
+    router.push({
+      path: `/schoolBus/statement`,
+    });
+  }
+  if (activeIndex.value == 2) {
+    router.push({
+      path: `/schoolBus/staff`,
+    });
+  }
+  if (activeIndex.value == 3) {
+    router.push({
+      path: `/schoolBus/account`,
+    });
+  }
+  if (activeIndex.value == 4) {
+    router.push({
+      path: `/schoolBus/bus`,
+    });
+  }
+  if (activeIndex.value == 5) {
+    router.push({
+      path: `/schoolBus/classes`,
+    });
+  }
+  if (activeIndex.value == 6) {
+    router.push({
+      path: `/schoolBus/busnumber`,
+    });
+  }
+  if (activeIndex.value == 7) {
+    router.push({
+      path: `/schoolBus/waiting`,
+    });
+  }
+};
+onBeforeMount(() => {
+  // console.log("切换", Number(sessionStorage.getItem("sidevarItem")));
+  store.commit("indexUp", Number(sessionStorage.getItem("sidevarItem")));
+});
+</script>
+
+<style lang="scss" scoped>
+.box-item {
+  // width: 160px;
+}
+.el-menu-vertical-demo:not(.el-menu--collapse) {
+  width: 200px;
+  // min-height: 400px;
+}
+.el-menu {
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(
+    0deg,
+    rgba(134, 132, 255, 1) 0%,
+    rgba(60, 80, 232, 1) 100%
+  );
+  box-shadow: 0px 0px 20px 0px rgba(0, 0, 0, 0.16);
+
+  .logo {
+    height: 65px;
+    color: rgb(255, 255, 255);
+    cursor: default;
+    display: flex;
+    justify-content: space-around;
+    align-items: center;
+  }
+  .logo:hover {
+    background-color: transparent !important;
+  }
+  .el-menu-item {
+    // border-left: 3px solid rgba(248, 220, 38);
+    padding: 0 10px;
+  }
+  .el-menu-item:hover {
+    background-color: #ccc;
+  }
+  .el-menu-item img {
+    width: 18px;
+    height: 18px;
+    margin: 0 20px;
+  }
+}
+
+.el-menu-item.is-active {
+  box-sizing: border-box;
+  background-color: #fff;
+  color: #000;
+}
+</style>

+ 28 - 0
src/main.js

@@ -0,0 +1,28 @@
+import { createApp } from 'vue'
+import './style.css'
+import ElementPlus from 'element-plus'
+import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
+import 'element-plus/dist/index.css'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+import App from './App.vue'
+
+
+import router from "@/router";
+import store from '@/store'
+
+
+
+const app = createApp(App)
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+    app.component(key, component)
+}
+
+app.use(ElementPlus, {
+    locale: zhCn,
+})
+app.use(router)
+app.use(store)
+
+
+app.mount('#app')
+app.config.devtools = true;

+ 85 - 0
src/router/index.js

@@ -0,0 +1,85 @@
+import { createRouter, createWebHistory, createWebHashHistory } from "vue-router";
+import Layout from "@/layout/index.vue";
+const router = createRouter({
+    history: createWebHashHistory(""),
+    routes: [
+        {
+            path: "/login",
+            component: () => import("@/views/login/index.vue"),
+        },
+        {
+            path: "/",
+            redirect: "/login",
+        },
+        {
+            path: "/schoolBus",
+            component: Layout,
+            children: [
+                {
+                    path: "user",
+                    component: () => import("@/views/vidicons/user/index.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "vidicon",
+                    component: () => import("@/views/vidicons/vidicon/index.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "news",
+                    component: () => import("@/views/news/news.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "statement",
+                    component: () => import("@/views/statement/statement.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "staff",
+                    component: () => import("@/views/staff/staff.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "account",
+                    component: () => import("@/views/account/account.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "bus",
+                    component: () => import("@/views/bus/bus.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "classes",
+                    component: () => import("@/views/classes/classes.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "busnumber",
+                    component: () => import("@/views/busnumber/busnumber.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+                {
+                    path: "waiting",
+                    component: () => import("@/views/waiting/waiting.vue"),
+                    meta: { title: "Dashboard", icon: "dashboard" },
+                },
+            ],
+        },
+        
+    ],
+});
+export default router;
+router.beforeEach((to, from, next) => {
+    // 判断 如果cook是否存在,可以进去
+    if (sessionStorage.getItem("token")) {
+        next(); //放行
+    } else {
+        if (to.path == "/login") {
+            next();
+        } else {
+            next(`/login`);
+        }
+    }
+});

+ 5 - 0
src/store/getters.js

@@ -0,0 +1,5 @@
+//store/getters
+const getters = {
+    age: state => state.user.age //导出age
+}
+export default getters;

+ 22 - 0
src/store/index.js

@@ -0,0 +1,22 @@
+//store/index.js
+
+import createPersistedstate from 'vuex-persistedstate' //持久化插件
+import { createStore } from 'vuex'; //引入vuex
+import user from './modules/user'; //引入modules的方法;
+import getters from './getters' //引入getters
+const store = createStore({
+    modules: {
+        user
+    },
+    getters,
+    // plugins: [
+    //     createPersistedstate({
+    //         // key: 'userStore', //存储持久状态的键。(默认:vuex)
+    //         paths: ['user'], //部分持续状态的任何路径的数组。如果不加,默认所有。
+    //         storage: window.sessionStorage //默认存储到localStorage,想要存储到sessionStorage
+    //     })
+    // ]
+
+})
+
+export default store;

+ 30 - 0
src/store/modules/user.js

@@ -0,0 +1,30 @@
+//store/modules/user.js
+// 声明变量
+const state = {
+    collapse: false,
+    activeIndex:1
+};
+
+// 修改变量(state不能直接赋值修改,只能通过mutations)
+const mutations = {
+    menuClose: (state, value) => {
+        state.collapse = value;
+    },
+    indexUp: (state, value) => {
+        state.activeIndex = value;
+    },
+};
+// mutations的值由actions传入(异步)
+const actions = {
+    addAsync: ({ commit }) => {
+        setTimeout(() => {
+            commit("add");
+        }, 1000);
+    },
+};
+
+export default {
+    state,
+    mutations,
+    actions,
+};

+ 89 - 0
src/style.css

@@ -0,0 +1,89 @@
+:root {
+  font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
+  line-height: 1.5;
+  font-weight: 400;
+
+  color-scheme: light dark;
+  color: rgba(255, 255, 255, 0.87);
+  background-color: #242424;
+
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  -webkit-text-size-adjust: 100%;
+}
+
+a {
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
+}
+a:hover {
+  color: #535bf2;
+}
+
+a {
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
+}
+a:hover {
+  color: #535bf2;
+}
+
+body {
+  margin: 0;
+  display: flex;
+  place-items: center;
+  min-width: 320px;
+  min-height: 100vh;
+}
+
+h1 {
+  font-size: 3.2em;
+  line-height: 1.1;
+}
+
+button {
+  border-radius: 8px;
+  border: 1px solid transparent;
+  padding: 0.6em 1.2em;
+  font-size: 1em;
+  font-weight: 500;
+  font-family: inherit;
+  background-color: #1a1a1a;
+  cursor: pointer;
+  transition: border-color 0.25s;
+}
+button:hover {
+  border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+  outline: 4px auto -webkit-focus-ring-color;
+}
+
+.card {
+  padding: 2em;
+}
+
+#app {
+  max-width: 1280px;
+  margin: 0 auto;
+  padding: 2rem;
+  text-align: center;
+}
+
+@media (prefers-color-scheme: light) {
+  :root {
+    color: #213547;
+    background-color: #ffffff;
+  }
+  a:hover {
+    color: #747bff;
+  }
+  button {
+    background-color: #f9f9f9;
+  }
+}

+ 1 - 0
src/style/mixin.scss

@@ -0,0 +1 @@
+$default: red

+ 46 - 0
src/utils/request.js

@@ -0,0 +1,46 @@
+import axios from "axios";
+const service = axios.create({
+    baseURL: import.meta.env.VITE_BASE_API, //接口统一域名
+    timeout: 6000, //设置超时
+   
+});
+
+// request拦截器
+service.interceptors.request.use(
+    (config) => {
+        config.data = JSON.stringify(config.data); //数据转化,也可以使用qs转换
+        // config.headers = {
+        //     'Content-Type': 'application/json' //配置请求头
+        // }
+        // config.headers = { 'Content-Type': 'application/x-www-form-urlencoded' } //配置请求头
+        //如有需要:注意使用token的时候需要引入cookie方法或者用本地localStorage等方法,推荐js-cookie
+        // const token = sessionStorage.getItem("token"); //这里取token之前,你肯定需要先拿到token,存一下
+        // if (token) {
+        //     // config.params = { 'admin_token': token } //如果要求携带在参数中
+        //     config.headers["token"] = token; //如果要求携带在请求头中
+        // }
+        return config;
+    },
+    (error) => {
+        Promise.reject(error);
+    }
+);
+// 响应拦截器
+service.interceptors.response.use(
+    (response) => {
+        const res = response.data;
+
+        return response.data;
+    },
+    (error) => {
+        /* Message({
+                      message: '服务器调用错误',
+                      type: 'error',
+                      duration: 5 * 1000
+                    }) */
+        console.log(error);
+
+        return Promise.reject(error);
+    }
+);
+export default service;

+ 22 - 0
src/utils/rsa.js

@@ -0,0 +1,22 @@
+import { JSEncrypt } from 'jsencrypt';
+let publicKey = 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMOcPB06u5yKyQsPjfVWiWgbEIrd14kiXNNihciaVKb6HnkQvq7zpQuZ80WEX94spnUMI3iOAl/GmIvHrpGwcbB4hJbznm+PajiwnUSPuCCXA68YJF640cJKb/8KeM7WVz69OFkIEPHhVxOy4FFF5QWe/kt6zOZ19HmE+ak+5x/QIDAQAB'
+let privateKey = '这里是封装的私钥'
+//加密方法
+export function RSAencrypt(pas) {
+    //实例化jsEncrypt对象
+    let jse = new JSEncrypt();
+    //设置公钥
+    jse.setPublicKey(publicKey);
+    console.log('加密:' + jse.encrypt(pas))
+    return jse.encrypt(pas);
+}
+
+//解密方法
+export function RSAdecrypt(pas) {
+    let jse = new JSEncrypt();
+    // 私钥
+    jse.setPrivateKey(privateKey)
+    console.log('解密:' + jse.decrypt(pas))
+    return jse.decrypt(pas);
+}
+

+ 709 - 0
src/views/account/account.vue

@@ -0,0 +1,709 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">账号管理</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>账号 : </span>
+          <el-input
+            clearable
+            v-model="searchInput.carnumber"
+            class="w-50 m-2"
+            placeholder="请输入账号"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>创建时间 : </span>
+          <el-date-picker
+            v-model="searchInput.createTime"
+            type="date"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            :prefix-icon="Calendar"
+            placeholder="请选择时间"
+          />
+        </div>
+
+        <el-button
+          style="margin-left: 20px"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchBtn"
+          ><el-icon><Search /></el-icon> <span>查询</span></el-button
+        >
+        <el-button
+          style="margin-left: 20px !important"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchRefresh"
+          ><el-icon><Refresh /></el-icon><span>重置</span></el-button
+        >
+      </div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button type="primary" color="rgba(61, 81, 232, 1)" @click="addlist"
+          ><el-icon><CirclePlus /></el-icon><span>新增账号</span></el-button
+        >
+        <el-button
+          color="rgba(61, 81, 232, 1)"
+          class="import"
+          type="primary"
+          @click="importExcel"
+          ><img
+            src="@/assets/import.png"
+            style="width: 14px; height: 14px; margin-right: 4px"
+            alt=""
+          />
+          <span>导出表单</span></el-button
+        >
+      </div>
+    </div>
+    <div class="footer">
+      <el-table
+        :row-class-name="tableRowClassName"
+        :data="tableData.list"
+        @selection-change="handleSelectionChange"
+        style="width: 100%"
+        :header-cell-style="{
+          background: 'rgba(240, 243, 247, 1)',
+          height: '50px',
+          border: 0,
+        }"
+      >
+        <!-- <el-table-column align="center" type="selection" width="80" /> -->
+        <el-table-column width="150" align="center " type="index" index="1" />
+        <el-table-column align="center" prop="name" label="姓名" />
+        <el-table-column align="center" prop="number" label="账号" />
+        <el-table-column
+          align="center"
+          prop="create_time"
+          label="创建时间"
+          width="250"
+        />
+
+        <el-table-column align="center" label="操作" width="220">
+          <template #default="scope">
+            <el-button link type="primary" @click="edit(scope.row)"
+              ><div class="look">编辑</div></el-button
+            >
+            <el-button link type="primary" @click="del(scope.row)"
+              ><div class="look">删除</div></el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 添加车辆弹窗 -->
+      <el-dialog
+        class="addStaff"
+        v-model="addDialogVisible"
+        :close-on-click-modal="false"
+        :close-on-press-escape="false"
+        :title="dialongTitle"
+        align-center
+        width="609"
+        :before-close="cancelAdd"
+      >
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          label-width="100px"
+          class="demo-ruleForm"
+          :size="formSize"
+          label-position="left"
+          status-icon
+        >
+          <el-form-item label="账号 :" prop="account">
+            <el-input
+              v-model="ruleForm.account"
+              placeholder="请输入账号"
+              clearable
+            />
+          </el-form-item>
+          <el-form-item label="密码 :" prop="pass">
+            <el-input
+              v-model="ruleForm.pass"
+              placeholder="请输入密码"
+              clearable
+            />
+          </el-form-item>
+          <el-form-item
+            label="昵称 :"
+            prop="name"
+            style="
+              padding-bottom: 40px;
+              border-bottom: 1px solid rgba(230, 230, 230, 1);
+            "
+          >
+            <el-input
+              v-model="ruleForm.name"
+              placeholder="请输入昵称"
+              clearable
+            />
+          </el-form-item>
+
+          <el-form-item class="options">
+            <el-button class="congzhi" @click="cancelAdd()">取消</el-button>
+            <el-button
+              color="rgba(61, 81, 232, 1)"
+              class="queding"
+              type="primary"
+              @click="submitAdd(ruleFormRef)"
+            >
+              确定
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+    </div>
+
+    <!-- 分页组件 -->
+    <el-pagination
+      background
+      :current-page="currentPage"
+      :page-size="pageSize"
+      layout="total, prev, pager, next, jumper, slot"
+      :total="total"
+      @update:current-page="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, onBeforeMount, onUnmounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import vidiconsApi from "@/api/vidicons.js";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import axios from "axios";
+import admin from "@/api/admin.js";
+const router = useRouter();
+// 表格数据
+const tableData = reactive({ list: [] });
+const activeIndex = ref(); // 默认跳转路由
+const dialongTitle = ref("新增账号"); // 弹窗标题
+
+const searchInput = reactive({
+  carnumber: "",
+  createTime: "",
+}); // 搜索按钮数据
+5;
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectData = reactive({ list: [] }); // 多选框选择的数据
+
+const addDialogVisible = ref(false); // 控制添加员工弹窗
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  name: "",
+  account: "",
+  pass: "",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  account: [
+    { required: true, message: "账号不能为空", trigger: "blur" },
+    // { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" },
+  ],
+  pass: [
+    {
+      required: true,
+      message: "密码不能为空",
+      trigger: "blur",
+    },
+  ],
+  name: [
+    {
+      required: true,
+      message: "昵称不能为空",
+      trigger: "blur",
+    },
+  ],
+
+  // desc: [{ required: true, message: "Please input activity form", trigger: "blur" }],
+});
+// 获取账户列表
+const getList = async () => {
+  // let res = await vidiconsApi.queryCamera(data);
+  let data = new FormData();
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  data.set("number", searchInput.carnumber);
+  data.set("create_time", searchInput.createTime); //前面的key记得对应!
+  data.set("page", currentPage.value);
+  data.set("rows", pageSize.value); //前面的key记得对应!
+  // let res = await adminApi.adminLogin(data);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/adminlist.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  if (res.data.code == 200) {
+    // ElMessage({
+    //   type: "success",
+    //   showClose: true,
+    //   message: "请求数据成功",
+    //   center: true,
+    // });
+    tableData.list = res.data.rows;
+    currentPage.value = res.data.currentPage;
+    total.value = res.data.total;
+  } else {
+    tableData.list = res.data.rows;
+    currentPage.value = 1;
+    total.value = res.data.total;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: "暂无数据",
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+
+// 重置搜索
+const searchRefresh = lodash.debounce(async () => {
+  searchInput.carnumber = "";
+  searchInput.createTime = "";
+  currentPage.value = 1;
+  getList();
+}, 300);
+
+//导出功能
+const importExcel = async () => {
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  let data = new FormData();
+  data.set("number", searchInput.carnumber);
+  data.set("create_time", searchInput.createTime);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/admintoExcel.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  // console.log(res, "导出账号");
+  if (res.data.code == 200) {
+    const elt = document.createElement("a");
+    elt.setAttribute(
+      "href",
+      "https://chtech.ncjti.edu.cn/carstop" + res.data.downurl
+    );
+    elt.setAttribute("download", "file.png");
+    elt.style.display = "none";
+    document.body.appendChild(elt);
+    elt.click();
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+// 添加员工
+const addlist = () => {
+  dialongTitle.value = "新增账号";
+  addDialogVisible.value = true;
+  ruleForm.name = "";
+  ruleForm.account = "";
+  ruleForm.pass = "";
+};
+// 取消添加员工
+const cancelAdd = () => {
+  addDialogVisible.value = false;
+  ruleFormRef.value.resetFields();
+};
+// 确认添加员工
+const submitAdd = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      if (dialongTitle.value == "新增账号") {
+        let data = {
+          name: ruleForm.name,
+          number: ruleForm.account,
+          password: ruleForm.pass,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/admininsert.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "添加账号");
+        if (res.data.code == 200) {
+          getList();
+
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      } else {
+        let data = {
+          name: ruleForm.name,
+          number: ruleForm.account,
+          password: ruleForm.pass,
+          id: ruleForm.id,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/adminupdate.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "修改账号");
+        if (res.data.code == 200) {
+          getList();
+
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+//编辑按钮
+const edit = (row) => {
+  dialongTitle.value = "编辑信息";
+  addDialogVisible.value = true;
+  console.log(row);
+  ruleForm.pass = row.password;
+  ruleForm.name = row.name;
+  ruleForm.account = row.number;
+  ruleForm.id = row.id;
+};
+//删除按钮
+const del = (row) => {
+  console.log(row);
+  ElMessageBox.confirm(`是否删除 ${row.number} 账号?`, "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let data = new FormData();
+      data.set("id", row.id);
+      // let res = await adminApi.adminLogin(data);
+      let res = await axios({
+        method: "post",
+        url: "/api/carBook/admindel.action",
+        headers: {
+          token: sessionStorage.getItem("token"),
+        },
+        data: data,
+      });
+      if (res.data.code == 200) {
+        if (tableData.list.length == 1 && currentPage.value != 1) {
+          currentPage.value = currentPage.value - 1;
+        }
+        getList();
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      }
+      console.log(res);
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消删除",
+      });
+    });
+};
+
+// 多选框功能
+// const handleSelectionChange = (val) => {
+//   console.log(val);
+//   selectData.list = val;
+// };
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList();
+};
+
+onBeforeMount(() => {
+  getList();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 89%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+      .search {
+        margin-left: 0 !important;
+      }
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 550px;
+    margin: 10px auto 30px;
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+      }
+      :deep(.el-table__row) {
+        height: 50px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: rgba(240, 243, 247, 1);
+      }
+      :deep(.look) {
+        padding: 5px 10px;
+        border-radius: 45px;
+        border: 0.74px solid rgba(30, 125, 251, 1);
+      }
+    }
+
+    // 添加员工弹窗样式
+    :deep(.addStaff) {
+      //   height: 420px;
+      border-radius: 11px;
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+      .el-dialog__body {
+        padding: 30px 20px 10px 20px;
+        .el-input {
+          width: 200px;
+          .el-input__suffix-inner {
+            color: rgba(61, 81, 232, 1);
+          }
+        }
+
+        .options {
+          margin-left: 320px;
+        }
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+    :deep(.el-input__wrapper) {
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      box-shadow: none;
+    }
+    :deep(.el-pager li) {
+      margin: 0 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.el-pager li.is-active) {
+      background-color: rgba(30, 125, 251, 1);
+    }
+    :deep(.btn-prev) {
+      margin-right: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.btn-next) {
+      margin-left: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

+ 714 - 0
src/views/bus/bus.vue

@@ -0,0 +1,714 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">车辆管理</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>车牌 : </span>
+          <el-input
+            clearable
+            v-model="searchInput.carnumber"
+            class="w-50 m-2"
+            placeholder="请输入车牌号"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>创建时间 : </span>
+          <el-date-picker
+            v-model="searchInput.createTime"
+            type="date"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            :prefix-icon="Calendar"
+            placeholder="请选择时间"
+          />
+        </div>
+
+        <el-button
+          style="margin-left: 20px"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchBtn"
+          ><el-icon><Search /></el-icon> <span>查询</span></el-button
+        >
+        <el-button
+          style="margin-left: 20px !important"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchRefresh"
+          ><el-icon><Refresh /></el-icon><span>重置</span></el-button
+        >
+      </div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button type="primary" color="rgba(61, 81, 232, 1)" @click="addlist"
+          ><el-icon><CirclePlus /></el-icon><span>新增车辆</span></el-button
+        >
+        <el-button
+          color="rgba(61, 81, 232, 1)"
+          class="import"
+          type="primary"
+          @click="importExcel"
+          ><img
+            src="@/assets/import.png"
+            style="width: 14px; height: 14px; margin-right: 4px"
+            alt=""
+          />
+          <span>导出表单</span></el-button
+        >
+      </div>
+    </div>
+    <div class="footer">
+      <el-table
+        :row-class-name="tableRowClassName"
+        :data="tableData.list"
+        @selection-change="handleSelectionChange"
+        style="width: 100%"
+        :header-cell-style="{
+          background: 'rgba(240, 243, 247, 1)',
+          height: '50px',
+          border: 0,
+        }"
+      >
+        <!-- <el-table-column align="center" type="selection" width="80" /> -->
+        <el-table-column
+          width="150"
+          align="center "
+          prop="device_number"
+          label="序列号"
+        />
+        <el-table-column align="center" prop="car_number" label="车牌" />
+        <el-table-column
+          align="center"
+          prop="contain"
+          label="容量"
+          width="200"
+        />
+
+        <el-table-column
+          align="center"
+          prop="create_time"
+          label="创建时间"
+          width="250"
+        />
+
+        <el-table-column align="center" label="操作" width="220">
+          <template #default="scope">
+            <el-button link type="primary" @click="edit(scope.row)"
+              ><div class="look">编辑</div></el-button
+            >
+            <el-button link type="primary" @click="del(scope.row)"
+              ><div class="look">删除</div></el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 添加车辆弹窗 -->
+      <el-dialog
+        class="addStaff"
+        v-model="addDialogVisible"
+        :close-on-click-modal="false"
+        :close-on-press-escape="false"
+        :title="dialongTitle"
+        align-center
+        width="609"
+        :before-close="handleVideoClose"
+      >
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          label-width="100px"
+          class="demo-ruleForm"
+          :size="formSize"
+          label-position="left"
+          status-icon
+        >
+          <el-form-item label="序列号 :" prop="serial">
+            <el-input
+              clearable
+              v-model="ruleForm.serial"
+              placeholder="请输入序列号"
+            />
+          </el-form-item>
+          <el-form-item label="车牌 :" prop="buslicence">
+            <el-input
+              clearable
+              v-model="ruleForm.buslicence"
+              placeholder="请输入车牌"
+            />
+          </el-form-item>
+          <el-form-item
+            label="容量 :"
+            prop="volume"
+            style="
+              padding-bottom: 40px;
+              border-bottom: 1px solid rgba(230, 230, 230, 1);
+            "
+          >
+            <el-input
+              clearable
+              v-model="ruleForm.volume"
+              placeholder="请输入容量"
+            />
+          </el-form-item>
+
+          <el-form-item class="options">
+            <el-button class="congzhi" @click="cancelAdd(ruleFormRef)"
+              >取消</el-button
+            >
+            <el-button
+              color="rgba(61, 81, 232, 1)"
+              class="queding"
+              type="primary"
+              @click="submitAdd(ruleFormRef)"
+            >
+              确定
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+    </div>
+
+    <!-- 分页组件 -->
+    <el-pagination
+      background
+      :current-page="currentPage"
+      :page-size="pageSize"
+      layout="total, prev, pager, next, jumper, slot"
+      :total="total"
+      @update:current-page="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, onBeforeMount, onUnmounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import vidiconsApi from "@/api/vidicons.js";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import axios from "axios";
+const router = useRouter();
+// 表格数据
+const tableData = reactive({ list: [] });
+const activeIndex = ref(); // 默认跳转路由
+const dialongTitle = ref("新增车辆"); // 弹窗标题
+
+const searchInput = reactive({
+  carnumber: "",
+  createTime: "",
+}); // 搜索按钮数据
+5;
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectData = reactive({ list: [] }); // 多选框选择的数据
+
+const addDialogVisible = ref(false); // 控制添加员工弹窗
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  serial: "",
+  buslicence: "",
+  volume: "",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  serial: [
+    { required: true, message: "序列号不能为空", trigger: "blur" },
+    // { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" },
+  ],
+  buslicence: [
+    { required: true, message: "请输入车牌号", trigger: "blur" },
+    {
+      pattern:
+        /^[京津沪渝冀豫云辽黑湘皖鲁新苏浙赣鄂桂甘晋蒙陕吉闽贵粤青藏川宁琼使领A-Z]{1}[A-Z]{1}[A-Z0-9]{4}[A-Z0-9挂学警港澳]{1}$/,
+      message: "请输入合法车牌号",
+      trigger: "blur",
+    },
+  ],
+  volume: [
+    {
+      required: true,
+      message: "容量不能为空",
+      trigger: "blur",
+    },
+  ],
+
+  // desc: [{ required: true, message: "Please input activity form", trigger: "blur" }],
+});
+// 查看车辆管理列表
+const getList = async () => {
+  let data = new FormData();
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  data.set("car_number", searchInput.carnumber);
+  data.set("create_time", searchInput.createTime); //前面的key记得对应!
+  data.set("page", currentPage.value);
+  data.set("rows", pageSize.value); //前面的key记得对应!
+  // let res = await adminApi.adminLogin(data);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/cinfolist.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  console.log(res);
+  if (res.data.code == 200) {
+    // ElMessage({
+    //   type: "success",
+    //   showClose: true,
+    //   message: "请求数据成功",
+    //   center: true,
+    // });
+    tableData.list = res.data.rows;
+    currentPage.value = res.data.currentPage;
+    total.value = res.data.total;
+  } else {
+    tableData.list = res.data.rows;
+    currentPage.value = 1;
+    total.value = res.data.total;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: "暂无数据",
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+// 重置搜索
+const searchRefresh = lodash.debounce(async () => {
+  searchInput.carnumber = "";
+  searchInput.createTime = "";
+  currentPage.value = 1;
+  getList();
+}, 300);
+//新增车辆
+const addlist = () => {
+  dialongTitle.value = "新增车辆";
+  addDialogVisible.value = true;
+  ruleForm.serial = "";
+  ruleForm.buslicence = "";
+  ruleForm.volume = "";
+};
+//编辑按钮
+const edit = (row) => {
+  dialongTitle.value = "编辑车辆";
+  addDialogVisible.value = true;
+  ruleForm.serial = row.device_number;
+  ruleForm.buslicence = row.car_number;
+  ruleForm.volume = row.contain;
+  ruleForm.id = row.id;
+};
+// 取消新增车辆
+const cancelAdd = () => {
+  addDialogVisible.value = false;
+  ruleFormRef.value.resetFields();
+};
+// 确认新增车辆
+const submitAdd = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      if (dialongTitle.value == "新增车辆") {
+        let data = {
+          device_number: ruleForm.serial,
+          car_number: ruleForm.buslicence,
+          contain: ruleForm.volume,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/cinfoinsert.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "添加账号");
+        if (res.data.code == 200) {
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      } else {
+        let data = {
+          device_number: ruleForm.serial,
+          car_number: ruleForm.buslicence,
+          contain: ruleForm.volume,
+          id: ruleForm.id,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/cinfoupdate.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "修改账号");
+        if (res.data.code == 200) {
+          getList();
+
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+
+//删除按钮
+const del = (row) => {
+  ElMessageBox.confirm(`是否删除 ${row.car_number} 车?`, "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let data = new FormData();
+      data.set("id", row.id);
+      // let res = await adminApi.adminLogin(data);
+      let res = await axios({
+        method: "post",
+        url: "/api/carBook/cinfodel.action",
+        headers: {
+          token: sessionStorage.getItem("token"),
+        },
+        data: data,
+      });
+      if (res.data.code == 200) {
+        if (tableData.list.length == 1 && currentPage.value != 1) {
+          currentPage.value = currentPage.value - 1;
+        }
+        getList();
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      }
+      console.log(res);
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消删除",
+      });
+    });
+};
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+//导出功能
+const importExcel = async () => {
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  let data = new FormData();
+  data.set("car_number", searchInput.carnumber);
+  data.set("create_time", searchInput.createTime);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/cinfotoExcel.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  // console.log(res, "导出账号");
+  if (res.data.code == 200) {
+    const elt = document.createElement("a");
+    elt.setAttribute(
+      "href",
+      "https://chtech.ncjti.edu.cn/carstop" + res.data.downurl
+    );
+    elt.setAttribute("download", "file.png");
+    elt.style.display = "none";
+    document.body.appendChild(elt);
+    elt.click();
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList();
+};
+
+onBeforeMount(() => {
+  getList();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 89%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+      .search {
+        margin-left: 0 !important;
+      }
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 550px;
+    margin: 10px auto 30px;
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+      }
+      :deep(.el-table__row) {
+        height: 50px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: rgba(240, 243, 247, 1);
+      }
+      :deep(.look) {
+        padding: 5px 10px;
+        border-radius: 45px;
+        border: 0.74px solid rgba(30, 125, 251, 1);
+      }
+    }
+
+    // 添加员工弹窗样式
+    :deep(.addStaff) {
+      //   height: 420px;
+      border-radius: 11px;
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+      .el-dialog__body {
+        padding: 30px 20px 10px 20px;
+        .el-input {
+          width: 200px;
+        }
+        .options {
+          margin-left: 320px;
+        }
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+    :deep(.el-input__wrapper) {
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      box-shadow: none;
+    }
+    :deep(.el-pager li) {
+      margin: 0 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.el-pager li.is-active) {
+      background-color: rgba(30, 125, 251, 1);
+    }
+    :deep(.btn-prev) {
+      margin-right: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.btn-next) {
+      margin-left: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

+ 822 - 0
src/views/busnumber/busnumber.vue

@@ -0,0 +1,822 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">车次管理</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>车牌 : </span>
+          <el-input
+            clearable
+            v-model="searchInput.carnumber"
+            class="w-50 m-2"
+            placeholder="请输入车牌号"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>创建时间 : </span>
+          <el-date-picker
+            v-model="searchInput.createTime"
+            type="date"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            :prefix-icon="Calendar"
+            placeholder="请选择时间"
+          />
+        </div>
+
+        <el-button
+          style="margin-left: 20px"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchBtn"
+          ><el-icon><Search /></el-icon> <span>查询</span></el-button
+        >
+        <el-button
+          style="margin-left: 20px !important"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchRefresh"
+          ><el-icon><Refresh /></el-icon><span>重置</span></el-button
+        >
+      </div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button type="primary" color="rgba(61, 81, 232, 1)" @click="addlist"
+          ><el-icon><CirclePlus /></el-icon><span>新增车次</span></el-button
+        >
+        <!-- <el-button
+          color="rgba(61, 81, 232, 1)"
+          class="import"
+          type="primary"
+          @click="resetInput"
+          ><img
+            src="@/assets/import.png"
+            style="width: 14px; height: 14px; margin-right: 4px"
+            alt=""
+          />
+          <span>导出表单</span></el-button
+        > -->
+      </div>
+    </div>
+    <div class="footer">
+      <el-table
+        :row-class-name="tableRowClassName"
+        :data="tableData.list"
+        @selection-change="handleSelectionChange"
+        style="width: 100%"
+        :header-cell-style="{
+          background: 'rgba(240, 243, 247, 1)',
+          height: '50px',
+          border: 0,
+        }"
+      >
+        <!-- <el-table-column align="center" type="selection" width="80" /> -->
+        <el-table-column
+          align="center"
+          prop="car_number"
+          width="180"
+          label="车牌"
+        />
+        <el-table-column
+          align="center"
+          width="100"
+          prop="contain"
+          label="容量"
+        />
+        <el-table-column align="center " prop="ci_time" label="发车时间" />
+        <el-table-column align="center" prop="route" label="路线" width="200" />
+
+        <el-table-column
+          align="center"
+          prop="create_time"
+          width="250"
+          label="创建时间"
+        />
+        <el-table-column
+          align="center"
+          prop="ci_time"
+          label="截止时间"
+          width="120"
+        />
+        <el-table-column
+          align="center"
+          width="250"
+          prop="work_duration"
+          label="生效日期"
+        />
+        <el-table-column
+          align="center"
+          prop="notice_time"
+          label="提前通知时间"
+          width="100"
+        />
+        <el-table-column align="center" label="操作" width="220">
+          <template #default="scope">
+            <el-button link type="primary" @click="edit(scope.row)"
+              ><div class="look">编辑</div></el-button
+            >
+            <el-button link type="primary" @click="del(scope.row)"
+              ><div class="look">删除</div></el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 添加车辆弹窗 -->
+      <el-dialog
+        class="addStaff"
+        v-model="addDialogVisible"
+        :close-on-click-modal="false"
+        :close-on-press-escape="false"
+        :title="dialongTitle"
+        align-center
+        width="609"
+        :before-close="cancelAdd"
+      >
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          label-width="100px"
+          class="demo-ruleForm"
+          :size="formSize"
+          label-position="left"
+          status-icon
+        >
+          <el-form-item label="车牌 :" prop="carnumber">
+            <el-select
+              v-model="ruleForm.carnumber"
+              class="m-2"
+              placeholder="请输入车牌"
+            >
+              <el-option label="赣A11111" value="赣A11111" />
+              <el-option label="赣A22222" value="赣A22222" />
+              <el-option label="赣A33333" value="赣A33333" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="容量 :" prop="contain">
+            <el-input
+              clearable
+              v-model="ruleForm.contain"
+              placeholder="请输入容量"
+            />
+          </el-form-item>
+          <el-form-item label="生效日期 :" prop="workduration">
+            <el-date-picker
+              v-model="ruleForm.workduration"
+              type="daterange"
+              format="YYYY-MM-DD"
+              value-format="YYYY-MM-DD"
+              :prefix-icon="Calendar"
+              start-placeholder="开始时间"
+              end-placeholder="结束时间"
+            />
+          </el-form-item>
+          <el-form-item label="发车时间 :" prop="startytime">
+            <!-- <el-time-picker
+              v-model="ruleForm.startytime"
+              format="HH:mm"
+              value-format="HH:mm"
+              placeholder="请选择发车时间"
+            /> -->
+            <el-select
+              v-model="ruleForm.startytime"
+              class="m-2"
+              placeholder="请选择发车时间"
+            >
+              <el-option label="14:30" value="14:30" />
+              <el-option label="16:30" value="16:30" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="路线 :" prop="route">
+            <el-select
+              v-model="ruleForm.route"
+              class="m-2"
+              placeholder="请选择路线"
+            >
+              <el-option label="墨轩湖-黄家湖" value="墨轩湖-黄家湖" />
+              <el-option label="南昌校区" value="南昌校区" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="截止时间 :" prop="citime">
+            <el-select
+              v-model="ruleForm.citime"
+              class="m-2"
+              placeholder="请选择截止时间"
+            >
+              <el-option label="16:30" value="16:30" />
+              <el-option label="18:30" value="18:30" />
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            label="提前通知时间 :"
+            prop="noticetime"
+            type="Number"
+            style="
+              padding-bottom: 40px;
+              border-bottom: 1px solid rgba(230, 230, 230, 1);
+            "
+          >
+            <div class="informTime">
+              <el-input v-model="ruleForm.noticetime" clearable />
+              <span> 分钟</span>
+            </div>
+          </el-form-item>
+
+          <el-form-item class="options">
+            <el-button class="congzhi" @click="cancelAdd(ruleFormRef)"
+              >取消</el-button
+            >
+            <el-button
+              color="rgba(61, 81, 232, 1)"
+              class="queding"
+              type="primary"
+              @click="submitAdd(ruleFormRef)"
+            >
+              确定
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+    </div>
+
+    <!-- 分页组件 -->
+    <el-pagination
+      background
+      :current-page="currentPage"
+      :page-size="pageSize"
+      layout="total, prev, pager, next, jumper, slot"
+      :total="total"
+      @update:current-page="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, onBeforeMount, onUnmounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import vidiconsApi from "@/api/vidicons.js";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import axios from "axios";
+const router = useRouter();
+// 表格数据
+const tableData = reactive({ list: [] });
+const activeIndex = ref(); // 默认跳转路由
+const dialongTitle = ref("新增车次"); // 弹窗标题
+
+const searchInput = reactive({
+  carnumber: "",
+  createTime: "",
+  route: "",
+  citime: "",
+}); // 搜索按钮数据
+5;
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectData = reactive({ list: [] }); // 多选框选择的数据
+
+const addDialogVisible = ref(false); // 控制添加员工弹窗
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  carnumber: "",
+  contain: "",
+  citime: "",
+  route: "",
+  startytime: "",
+  workduration: "",
+  noticetime: "10",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  workduration: [
+    { required: true, message: "生效日期不能为空", trigger: "blur" },
+    // { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" },
+  ],
+  carnumber: [
+    {
+      required: true,
+      message: "车牌号不能为空",
+      trigger: "change",
+    },
+  ],
+  contain: [
+    {
+      required: true,
+      message: "容量不能为空",
+      trigger: "blur",
+    },
+  ],
+  startytime: [
+    {
+      required: true,
+      message: "发车时间不能为空",
+      trigger: "change",
+    },
+  ],
+  route: [
+    {
+      required: true,
+      message: "路线不能为空",
+      trigger: "change",
+    },
+  ],
+  citime: [
+    {
+      required: true,
+      message: "截止时间不能为空",
+      trigger: "change",
+    },
+  ],
+  noticetime: [
+    {
+      required: true,
+      message: "提前通知时间不能为空",
+      trigger: "blur",
+    },
+    {
+      pattern: /^[1-720]*$/,
+      message: "请填写正确的时间 (整数)",
+      trigger: "blur",
+    },
+  ],
+});
+// 获取车次信息
+const getList = async () => {
+  let data = new FormData();
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  data.set("car_number", searchInput.carnumber);
+  data.set("ci_time", searchInput.citime);
+  data.set("route", searchInput.route);
+  data.set("create_time", searchInput.createTime);
+  data.set("page", currentPage.value);
+  data.set("rows", pageSize.value);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/cclist.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  console.log(res);
+  if (res.data.code == 200) {
+    // ElMessage({
+    //   type: "success",
+    //   showClose: true,
+    //   message: "请求数据成功",
+    //   center: true,
+    // });
+    tableData.list = res.data.rows;
+    currentPage.value = res.data.currentPage;
+    total.value = res.data.total;
+  } else {
+    tableData.list = res.data.rows;
+    currentPage.value = 1;
+    total.value = res.data.total;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: "暂无数据",
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+
+// 添加车次
+const addlist = () => {
+  dialongTitle.value = "新增车次";
+  addDialogVisible.value = true;
+  ruleForm.carnumber = "";
+  ruleForm.contain = "";
+  ruleForm.citime = "";
+  ruleForm.route = "";
+  ruleForm.startytime = "";
+  ruleForm.workduration = "";
+  ruleForm.noticetime = "";
+};
+//编辑按钮
+const edit = (row) => {
+  dialongTitle.value = "编辑车次";
+  addDialogVisible.value = true;
+  ruleForm.carnumber = row.car_number;
+  ruleForm.contain = row.contain;
+  ruleForm.citime = row.ci_time;
+  ruleForm.route = row.route;
+  ruleForm.startytime = row.starty_time;
+  ruleForm.workduration = row.work_duration.split("~");
+  ruleForm.noticetime = row.notice_time;
+  ruleForm.id = row.id;
+};
+// 取消添加车次
+const cancelAdd = () => {
+  addDialogVisible.value = false;
+  ruleFormRef.value.resetFields();
+};
+// 重置搜索
+const searchRefresh = lodash.debounce(async () => {
+  searchInput.carnumber = "";
+  searchInput.createTime = "";
+  currentPage.value = 1;
+  getList();
+}, 300);
+// 确认新增车次
+const submitAdd = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      if (dialongTitle.value == "新增车次") {
+        let data = {
+          car_number: ruleForm.carnumber,
+          contain: ruleForm.contain,
+          ci_time: ruleForm.citime,
+          route: ruleForm.route,
+          starty_time: ruleForm.startytime,
+          work_duration:
+            ruleForm.workduration[0] + "~" + ruleForm.workduration[1],
+          notice_time: ruleForm.noticetime,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/ccinsert.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "添加账号");
+        if (res.data.code == 200) {
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      } else {
+        let data = {
+          car_number: ruleForm.carnumber,
+          contain: ruleForm.contain,
+          ci_time: ruleForm.citime,
+          route: ruleForm.route,
+          starty_time: ruleForm.startytime,
+          work_duration:
+            ruleForm.workduration[0] + "~" + ruleForm.workduration[1],
+          notice_time: ruleForm.noticetime,
+          id: ruleForm.id,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/ccupdate.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "修改账号");
+        if (res.data.code == 200) {
+          getList();
+
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+
+//删除按钮
+const del = (row) => {
+  ElMessageBox.confirm(`是否删除 ${row.car_number} 车次?`, "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let data = new FormData();
+      data.set("id", row.id);
+      // let res = await adminApi.adminLogin(data);
+      let res = await axios({
+        method: "post",
+        url: "/api/carBook/ccdel.action",
+        headers: {
+          token: sessionStorage.getItem("token"),
+        },
+        data: data,
+      });
+      if (res.data.code == 200) {
+        if (tableData.list.length == 1 && currentPage.value != 1) {
+          currentPage.value = currentPage.value - 1;
+        }
+        getList();
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      }
+      console.log(res);
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消删除",
+      });
+    });
+};
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList(value);
+};
+
+onBeforeMount(() => {
+  getList(1);
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 89%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+      .search {
+        margin-left: 0 !important;
+      }
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 550px;
+    margin: 10px auto 30px;
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+      }
+      :deep(.el-table__row) {
+        height: 50px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: rgba(240, 243, 247, 1);
+      }
+      :deep(.look) {
+        padding: 5px 10px;
+        border-radius: 45px;
+        border: 0.74px solid rgba(30, 125, 251, 1);
+      }
+    }
+
+    // 添加员工弹窗样式
+    :deep(.addStaff) {
+      //   height: 420px;
+      border-radius: 11px;
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+      .el-dialog__body {
+        padding: 30px 20px 10px 20px;
+        .el-form-item {
+          label {
+            width: 120px !important;
+          }
+          .el-form-item__label {
+            width: 120px !important;
+          }
+          .informTime {
+            display: flex;
+            align-items: center;
+            .el-input {
+              width: 120px;
+              margin-right: 10px;
+              .el-input__inner {
+                text-align: center;
+              }
+            }
+          }
+        }
+        .el-form-item__content {
+          flex: none;
+          .el-input {
+            width: 370px;
+          }
+        }
+        .el-input {
+          width: 200px;
+        }
+        .options {
+          margin-left: 320px;
+        }
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+    :deep(.el-input__wrapper) {
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      box-shadow: none;
+    }
+    :deep(.el-pager li) {
+      margin: 0 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.el-pager li.is-active) {
+      background-color: rgba(30, 125, 251, 1);
+    }
+    :deep(.btn-prev) {
+      margin-right: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.btn-next) {
+      margin-left: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

+ 721 - 0
src/views/classes/classes.vue

@@ -0,0 +1,721 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">班次管理</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>路线 : </span>
+          <el-input
+            clearable
+            v-model="searchInput.route"
+            class="w-50 m-2"
+            placeholder="请输入路线"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>发车时间 : </span>
+          <el-input
+            clearable
+            v-model="searchInput.startTime"
+            class="w-50 m-2"
+            placeholder="请输入发车时间"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>创建时间 : </span>
+          <el-date-picker
+            v-model="searchInput.createTime"
+            type="date"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            :prefix-icon="Calendar"
+            placeholder="请选择日期"
+          />
+        </div>
+
+        <el-button
+          style="margin-left: 20px"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchBtn"
+          ><el-icon><Search /></el-icon> <span>查询</span></el-button
+        >
+        <el-button
+          style="margin-left: 20px !important"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchRefresh"
+          ><el-icon><Refresh /></el-icon><span>重置</span></el-button
+        >
+      </div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button type="primary" color="rgba(61, 81, 232, 1)" @click="addlist"
+          ><el-icon><CirclePlus /></el-icon><span>新增班次</span></el-button
+        >
+        <el-button
+          color="rgba(61, 81, 232, 1)"
+          class="import"
+          type="primary"
+          @click="importExcel"
+          ><img
+            src="@/assets/import.png"
+            style="width: 14px; height: 14px; margin-right: 4px"
+            alt=""
+          />
+          <span>导出表单</span></el-button
+        >
+      </div>
+    </div>
+    <div class="footer">
+      <el-table
+        :row-class-name="tableRowClassName"
+        :data="tableData.list"
+        @selection-change="handleSelectionChange"
+        style="width: 100%"
+        :header-cell-style="{
+          background: 'rgba(240, 243, 247, 1)',
+          height: '50px',
+          border: 0,
+        }"
+      >
+        <!-- <el-table-column align="center" type="selection" width="80" /> -->
+        <!-- <el-table-column
+          type="index"
+          width="80"
+          align="center"
+          label="序号"
+          index="1"
+        /> -->
+        <el-table-column
+          align="center"
+          prop="start_time"
+          label="发车时间"
+          width="150"
+        />
+        <el-table-column align="center" prop="route" label="路线" />
+        <el-table-column
+          align="center"
+          prop="create_time"
+          label="创建时间"
+          width="250"
+        />
+
+        <el-table-column align="center" label="操作" width="220">
+          <template #default="scope">
+            <el-button link type="primary" @click="edit(scope.row)"
+              ><div class="look">编辑</div></el-button
+            >
+            <el-button link type="primary" @click="del(scope.row)"
+              ><div class="look">删除</div></el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 添加班次弹窗 -->
+      <el-dialog
+        class="addStaff"
+        v-model="addDialogVisible"
+        :close-on-click-modal="false"
+        :close-on-press-escape="false"
+        :title="dialongTitle"
+        align-center
+        width="609"
+        :before-close="cancelAdd"
+      >
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          label-width="100px"
+          class="demo-ruleForm"
+          :size="formSize"
+          label-position="left"
+          status-icon
+        >
+          <el-form-item label="发车时间 :" prop="startTime">
+            <!-- <el-date-picker
+              v-model="ruleForm.cartime"
+              type="datetime"
+              format="YYYY-MM-DD HH:mm:ss"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              :prefix-icon="Calendar"
+              placeholder="请选择时间"
+            /> -->
+            <el-select
+              v-model="ruleForm.startTime"
+              class="m-2"
+              placeholder="请选择时间"
+            >
+              <el-option label="15:30" value="15:30" />
+              <el-option label="16:30" value="16:30" />
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            label="路线 :"
+            prop="route"
+            style="
+              padding-bottom: 40px;
+              border-bottom: 1px solid rgba(230, 230, 230, 1);
+            "
+          >
+            <el-select
+              v-model="ruleForm.route"
+              class="m-2"
+              placeholder="请选择路线"
+            >
+              <el-option label="墨轩湖-黄家湖" value="墨轩湖-黄家湖" />
+              <el-option label="南昌校区" value="南昌校区" />
+            </el-select>
+          </el-form-item>
+          <el-form-item class="options">
+            <el-button class="congzhi" @click="cancelAdd(ruleFormRef)"
+              >取消</el-button
+            >
+            <el-button
+              color="rgba(61, 81, 232, 1)"
+              class="queding"
+              type="primary"
+              @click="submitAdd(ruleFormRef)"
+            >
+              确定
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+    </div>
+
+    <!-- 分页组件 -->
+    <el-pagination
+      background
+      :current-page="currentPage"
+      :page-size="pageSize"
+      layout="total, prev, pager, next, jumper, slot"
+      :total="total"
+      @update:current-page="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, onBeforeMount, onUnmounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import vidiconsApi from "@/api/vidicons.js";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import axios from "axios";
+
+const router = useRouter();
+// 表格数据
+const tableData = reactive({ list: [] });
+const activeIndex = ref(); // 默认跳转路由
+const dialongTitle = ref("新增班次"); // 弹窗标题
+
+const searchInput = reactive({
+  startTime: "",
+  route: "",
+  createTime: "",
+}); // 搜索按钮数据
+5;
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectData = reactive({ list: [] }); // 多选框选择的数据
+
+const addDialogVisible = ref(false); // 控制添加员工弹窗
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  route: "南昌校区",
+  startTime: "16:30",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  startTime: [
+    { required: true, message: "时间不能为空", trigger: "blur" },
+    // { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" },
+  ],
+  route: [
+    {
+      required: true,
+      message: "路线不能为空",
+      trigger: "blur",
+    },
+  ],
+
+  // desc: [{ required: true, message: "Please input activity form", trigger: "blur" }],
+});
+// 获取摄像头列表
+const getList = async () => {
+  let data = new FormData();
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  data.set("route", searchInput.route);
+  data.set("start_time", searchInput.startTime);
+  data.set("create_time", searchInput.createTime); //前面的key记得对应!
+  data.set("page", currentPage.value);
+  data.set("rows", pageSize.value); //前面的key记得对应!
+  // let res = await adminApi.adminLogin(data);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/bclist.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  console.log(res);
+  if (res.data.code == 200) {
+    // ElMessage({
+    //   type: "success",
+    //   showClose: true,
+    //   message: "请求数据成功",
+    //   center: true,
+    // });
+    tableData.list = res.data.rows;
+    currentPage.value = res.data.currentPage;
+    total.value = res.data.total;
+  } else {
+    tableData.list = res.data.rows;
+    currentPage.value = 1;
+    total.value = res.data.total;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: "暂无数据",
+      center: true,
+    });
+  }
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+// 重置搜索
+const searchRefresh = lodash.debounce(async () => {
+  searchInput.startTime = "";
+  searchInput.createTime = "";
+  searchInput.route = "";
+  currentPage.value = 1;
+  getList();
+}, 300);
+// 添加班次
+const addlist = () => {
+  dialongTitle.value = "新增班次";
+  addDialogVisible.value = true;
+  ruleForm.startTime = "";
+  ruleForm.route = "";
+};
+//编辑按钮
+const edit = (row) => {
+  dialongTitle.value = "编辑班次";
+  addDialogVisible.value = true;
+  ruleForm.startTime = row.start_time;
+  ruleForm.route = row.route;
+  ruleForm.id = row.id;
+};
+// 取消添加班次
+const cancelAdd = () => {
+  addDialogVisible.value = false;
+  ruleFormRef.value.resetFields();
+};
+// 确认添加班次
+const submitAdd = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      if (dialongTitle.value == "新增班次") {
+        let data = {
+          start_time: ruleForm.startTime,
+          route: ruleForm.route,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/bcinsert.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "添加账号");
+        if (res.data.code == 200) {
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      } else {
+        let data = {
+          start_time: ruleForm.startTime,
+          route: ruleForm.route,
+          id: ruleForm.id,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/bcupdate.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "修改账号");
+        if (res.data.code == 200) {
+          getList();
+
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+
+//删除按钮
+const del = (row) => {
+  ElMessageBox.confirm(`是否删除 ${row.start_time} 这趟车 ?`, "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let data = new FormData();
+      data.set("id", row.id);
+      // let res = await adminApi.adminLogin(data);
+      let res = await axios({
+        method: "post",
+        url: "/api/carBook/bcdel.action",
+        headers: {
+          token: sessionStorage.getItem("token"),
+        },
+        data: data,
+      });
+      if (res.data.code == 200) {
+        if (tableData.list.length == 1 && currentPage.value != 1) {
+          currentPage.value = currentPage.value - 1;
+        }
+        getList();
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      }
+      console.log(res);
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消删除",
+      });
+    });
+};
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+//导出功能
+const importExcel = async () => {
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  let data = new FormData();
+  data.set("route", searchInput.route);
+  data.set("start_time", searchInput.startTime);
+  data.set("create_time", searchInput.createTime);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/bctoExcel.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  // console.log(res, "导出账号");
+  if (res.data.code == 200) {
+    const elt = document.createElement("a");
+    elt.setAttribute(
+      "href",
+      "https://chtech.ncjti.edu.cn/carstop" + res.data.downurl
+    );
+    elt.setAttribute("download", "file.png");
+    elt.style.display = "none";
+    document.body.appendChild(elt);
+    elt.click();
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList(value);
+};
+
+onBeforeMount(() => {
+  getList(1);
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 89%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+      .search {
+        margin-left: 0 !important;
+      }
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 550px;
+    margin: 10px auto 30px;
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+      }
+      :deep(.el-table__row) {
+        height: 50px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: rgba(240, 243, 247, 1);
+      }
+      :deep(.look) {
+        padding: 5px 10px;
+        border-radius: 45px;
+        border: 0.74px solid rgba(30, 125, 251, 1);
+      }
+    }
+
+    // 添加员工弹窗样式
+    :deep(.addStaff) {
+      //   height: 420px;
+      border-radius: 11px;
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+      .el-dialog__body {
+        padding: 30px 20px 10px 20px;
+        .el-input {
+          width: 200px;
+        }
+        .options {
+          margin-left: 320px;
+        }
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+    :deep(.el-input__wrapper) {
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      box-shadow: none;
+    }
+    :deep(.el-pager li) {
+      margin: 0 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.el-pager li.is-active) {
+      background-color: rgba(30, 125, 251, 1);
+    }
+    :deep(.btn-prev) {
+      margin-right: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.btn-next) {
+      margin-left: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

+ 295 - 0
src/views/login/index.vue

@@ -0,0 +1,295 @@
+<template>
+  <div class="box">
+    <!-- <div class="middle"></div> -->
+    <div class="left">
+      <div class="loginForm">
+        <div
+          style="
+            display: flex;
+            align-items: center;
+            justify-content: center;
+            margin-top: 104px;
+            margin-bottom: 121px;
+          "
+        >
+          <img
+            src="@/assets/nanchang.png"
+            alt=""
+            style="width: 56px; height: 56px"
+          /><span
+            style="
+              font-size: 38px;
+              font-weight: 500;
+              letter-spacing: 0px;
+              color: rgba(0, 0, 0, 1);
+            "
+            >视频管理系统</span
+          >
+        </div>
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          status-icon
+          :rules="rules"
+          label-width="120px"
+          class="demo-ruleForm"
+        >
+          <el-form-item label="" prop="user">
+            <el-input
+              :prefix-icon="User"
+              v-model="ruleForm.user"
+              placeholder="请输入登录名"
+              autocomplete="off"
+            />
+          </el-form-item>
+          <el-form-item label="" prop="pass">
+            <el-input
+              :prefix-icon="Lock"
+              show-password
+              v-model="ruleForm.pass"
+              type="password"
+              autocomplete="off"
+              placeholder="请输入登录密码"
+            />
+          </el-form-item>
+          <el-checkbox v-model="checked" class="remeberPwd el-form-item"
+            >记住密码</el-checkbox
+          >
+          <el-form-item>
+            <el-button
+              :loading="loading"
+              type="primary"
+              @click="submitForm(ruleFormRef)"
+              >登录</el-button
+            >
+          </el-form-item>
+        </el-form>
+      </div>
+      <div class="rLogin">
+        <img src="@/assets/login3.png" alt="" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, onUnmounted } from "vue";
+import { User, Lock } from "@element-plus/icons-vue";
+import { useRouter } from "vue-router";
+import { ElMessage } from "element-plus";
+import { JSEncrypt } from "jsencrypt"; // 加密密码
+import adminApi from "@/api/admin.js";
+import { useStore } from "vuex";
+import axios from "axios";
+const router = useRouter();
+
+const store = useStore();
+const loading = ref(false);
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  user: "",
+  pass: "",
+});
+const checked = ref(false); // 记住密码
+
+const rules = reactive({
+  user: [{ required: true, message: "请填写用户名", trigger: "blur" }],
+  pass: [{ required: true, message: "请填写密码", trigger: "blur" }],
+});
+
+const submitForm = (formEl) => {
+  if (!formEl) return;
+  formEl.validate(async (valid) => {
+    if (valid) {
+      loading.value = true;
+      // let publicKey =
+      //   "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMOcPB06u5yKyQsPjfVWiWgbEIrd14kiXNNihciaVKb6HnkQvq7zpQuZ80WEX94spnUMI3iOAl/GmIvHrpGwcbB4hJbznm+PajiwnUSPuCCXA68YJF640cJKb/8KeM7WVz69OFkIEPHhVxOy4FFF5QWe/kt6zOZ19HmE+ak+5x/QIDAQAB";
+      // let encryptor = new JSEncrypt(); // 新建JSEncrypt对象
+      // encryptor.setPublicKey(publicKey); // 设置公钥
+      // let rsaPassWord = encryptor.encrypt(ruleForm.pass); // 对密码进行加密
+      // console.log(rsaPassWord);
+      // let logincontent = {
+      //   number: ruleForm.user,
+      //   password: ruleForm.pass,
+      // };
+      let data = new FormData();
+      data.set("number", ruleForm.user);
+      data.set("password", ruleForm.pass); //前面的key记得对应!
+      // let res = await adminApi.adminLogin(data);
+      let res = await axios({
+        method: "post",
+        url: "/api/carBook/adminlogin.action",
+        data: data,
+      });
+
+      console.log(res);
+      if (res.data.code == 200) {
+        if (checked.value) {
+          localStorage.setItem("user", ruleForm.user);
+          localStorage.setItem("pass", ruleForm.pass);
+        } else {
+          localStorage.removeItem("user", ruleForm.user);
+          localStorage.removeItem("pass", ruleForm.pass);
+        }
+        sessionStorage.setItem("token", res.data.access_token);
+        sessionStorage.setItem("sidevarItem", 1);
+        store.commit("indexUp", 1);
+        router.push({
+          path: `/schoolBus/statement`,
+        });
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: "登录成功",
+          center: true,
+        });
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: "登录失败",
+          center: true,
+        });
+        loading.value = false;
+      }
+    } else {
+      return false;
+    }
+  });
+};
+const Enters = (e) => {
+  console.log("按键:", e.key);
+  if (e.key == "Enter") {
+    submitForm(ruleFormRef.value);
+  }
+};
+onMounted(() => {
+  if (localStorage.getItem("pass") && localStorage.getItem("user")) {
+    ruleForm.user = localStorage.getItem("user");
+    ruleForm.pass = localStorage.getItem("pass");
+    checked.value = true;
+  }
+  document.addEventListener("keyup", Enters);
+});
+onUnmounted(() => {
+  document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style lang="scss" scoped>
+.box {
+  width: 100%;
+  height: 100vh;
+  background: url(@/assets/login.png);
+  background-size: cover;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  // .middle {
+  //   width: 1180px;
+  //   height: 740px;
+  //   position: absolute;
+
+  // }
+  .left {
+    box-shadow: 39px 30px 58px 0px rgba(8, 53, 115, 0.55);
+
+    width: 1180px;
+    height: 740px;
+    border-radius: 18px;
+    display: flex;
+    align-items: center;
+    background-image: url(@/assets/login2.png);
+    background-size: 110% 116%;
+    background-position: -19px -28px;
+    .loginForm {
+      width: 497px;
+      height: 740px;
+      background-color: #fff;
+      border-radius: 18px 0 0 18px;
+
+      .el-form {
+        width: 500px;
+        display: flex;
+        flex-direction: column;
+        justify-content: center;
+        align-items: center;
+        .el-form-item {
+          width: 420px;
+          height: 60px;
+          // margin-bottom: 40px;
+
+          :deep(.el-form-item__content) {
+            margin-left: 0 !important;
+
+            .el-input {
+              height: 60px;
+
+              .el-input__wrapper {
+                border-radius: 12px;
+                .el-input__prefix {
+                  margin: 0 10px 0 12px;
+                }
+              }
+
+              .el-icon {
+                width: 24px;
+                height: 24px;
+                svg {
+                  width: 24px;
+                  height: 24px;
+                }
+              }
+              .el-input__inner {
+                height: 100%;
+                font-size: 24px;
+              }
+            }
+            .el-form-item__error {
+              font-size: 16px;
+            }
+            .el-button {
+              width: 100%;
+              height: 60px;
+              font-size: 24px;
+              border-radius: 12px;
+            }
+          }
+
+          .el-button--primary {
+            width: 380px;
+          }
+        }
+        .remeberPwd {
+          height: 30px;
+          margin-bottom: 30px;
+          :deep(.el-checkbox__input) {
+            .el-checkbox__inner {
+              width: 18px;
+              height: 18px;
+            }
+            .el-checkbox__inner::after {
+              width: 6px;
+              height: 12px;
+              top: -1px;
+            }
+          }
+          :deep(.el-checkbox__label) {
+            font-size: 18px;
+          }
+        }
+      }
+    }
+    .rLogin {
+      width: 683px;
+      height: 740px;
+      border-radius: 0 18px 18px 0;
+      display: flex;
+      img {
+        width: 683px;
+        align-self: flex-end;
+      }
+    }
+  }
+}
+</style>

+ 599 - 0
src/views/news/news.vue

@@ -0,0 +1,599 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">消息通知</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>创建时间 : </span>
+          <el-date-picker
+            v-model="searchInput.createTime"
+            type="datetimerange"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          />
+        </div>
+        <div class="condition">
+          <span>状态 : </span>
+          <el-select
+            style="width: 120px"
+            v-model="searchInput.statu"
+            class="m-2"
+            placeholder="请选择状态"
+          >
+            <el-option label="全部" value="0" />
+            <el-option label="已读" value="1" />
+            <el-option label="未读" value="2" />
+          </el-select>
+        </div>
+
+        <el-button
+          style="margin-left: 20px"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchBtn"
+          ><el-icon><Search /></el-icon> <span>搜索</span></el-button
+        >
+      </div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button type="primary" color="rgba(61, 81, 232, 1)" @click="addlist"
+          ><el-icon><RefreshRight /></el-icon><span>刷新</span></el-button
+        >
+        <el-button type="danger" @click="allDel" color="rgba(61, 81, 232, 1)">
+          <img
+            src="@/assets/read.png"
+            style="width: 14px; height: 14px; margin-right: 4px"
+            alt=""
+          />
+          <span>一键已读</span></el-button
+        >
+      </div>
+
+      <!-- 编辑/新增摄像头IP -->
+      <el-dialog
+        v-model="centerDialogVisible"
+        :title="dialongTitle"
+        width="40%"
+        align-center
+        :before-close="editClose"
+      >
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          label-width="120px"
+          class="demo-ruleForm"
+          :size="formSize"
+          status-icon
+        >
+          <el-form-item label="厂商" prop="count">
+            <el-input v-model="ruleForm.count" placeholder="请输入厂商" />
+          </el-form-item>
+          <el-form-item label="IP地址" prop="name">
+            <el-input v-model="ruleForm.name" placeholder="请输入IP地址" />
+          </el-form-item>
+          <el-form-item label="状态" prop="region">
+            <el-select v-model="ruleForm.region" placeholder="请选择状态">
+              <el-option label="在线" value="1" />
+              <el-option label="离线" value="0" />
+            </el-select>
+          </el-form-item>
+          <el-form-item
+            label="创建人"
+            prop="username"
+            v-show="dialongTitle == '新增摄像头IP'"
+          >
+            <el-input
+              v-model="ruleForm.username"
+              placeholder="请输入创建人姓名"
+            />
+          </el-form-item>
+          <el-form-item label="摄像头密码" prop="pass">
+            <el-input v-model="ruleForm.pass" placeholder="请输入摄像头密码" />
+          </el-form-item>
+          <el-form-item
+            label="最后更新人"
+            prop="username"
+            v-show="dialongTitle == '修改摄像头IP'"
+          >
+            <el-input
+              v-model="ruleForm.username"
+              placeholder="请输入更新人姓名"
+            />
+          </el-form-item>
+          <el-form-item label="设备型号" prop="date1">
+            <el-input v-model="ruleForm.date1" placeholder="请输入设备型号" />
+          </el-form-item>
+          <el-form-item label="协议" prop="types">
+            <el-input
+              v-model="ruleForm.types"
+              placeholder="请输入协议"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="安装位置" prop="date2">
+            <el-input v-model="ruleForm.date2" placeholder="请输入安装位置" />
+          </el-form-item>
+
+          <el-form-item label="摄像机编码" prop="resource">
+            <el-input
+              v-model="ruleForm.resource"
+              placeholder="请输入摄像机编码"
+            >
+            </el-input>
+          </el-form-item>
+          <el-form-item label="备注" prop="desc">
+            <el-input
+              v-model="ruleForm.desc"
+              type="textarea"
+              placeholder="请输入备注"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="submitForm(ruleFormRef)">
+              确定
+            </el-button>
+            <el-button @click="resetForm(ruleFormRef)">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+    </div>
+    <div class="footer">
+      <el-table
+        :row-class-name="tableRowClassName"
+        :data="tableData.list"
+        @selection-change="handleSelectionChange"
+        style="width: 100%"
+        :header-cell-style="{
+          background: 'rgba(240, 243, 247, 1)',
+          height: '50px',
+          border: 0,
+        }"
+      >
+        <el-table-column align="center" type="selection" width="80" />
+        <el-table-column
+          align="center"
+          prop="createTime"
+          label="创建时间"
+          width="250"
+        />
+        <el-table-column align="center" prop="lastUpdateTime" label="消息" />
+        <el-table-column align="center" prop="statu" label="状态" width="200" />
+        <el-table-column align="center" label="操作" width="220">
+          <template #default="scope">
+            <el-button link type="primary" @click="look(scope.row)"
+              ><div class="look">查看</div></el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 分页组件 -->
+    <el-pagination
+      background
+      :current-page="currentPage"
+      :page-size="pageSize"
+      layout="total, prev, pager, next, jumper"
+      :total="total"
+      @update:current-page="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, onBeforeMount, onUnmounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import vidiconsApi from "@/api/vidicons.js";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+const router = useRouter();
+// 表格数据
+const tableData = reactive({ list: [] });
+const activeIndex = ref(); // 默认跳转路由
+const dialongTitle = ref(""); // 弹窗标题
+
+const searchInput = reactive({
+  statu: "",
+  createTime: "",
+}); // 搜索按钮数据
+5;
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectData = reactive({ list: [] }); // 多选框选择的数据
+
+const centerDialogVisible = ref(false); // 控制新增或修改弹窗开关
+const videoDialogVisible = ref(false); // 控制播放弹窗开关
+
+const urlLink = ref(); // 输出链接
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  name: "",
+  region: "",
+  count: "",
+  date1: "",
+  date2: "",
+  types: [],
+  resource: "",
+  desc: "",
+  username: "",
+  pass: "",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  region: [
+    {
+      required: true,
+      message: "状态不能为空",
+      trigger: "blur",
+    },
+  ],
+  name: [
+    { required: true, message: "IP地址不能为空", trigger: "blur" },
+    // { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" },
+  ],
+  count: [
+    {
+      required: true,
+      message: "厂商不能为空",
+      trigger: "blur",
+    },
+  ],
+  date1: [
+    {
+      required: true,
+      message: "设备型号不能为空",
+      trigger: "blur",
+    },
+  ],
+  date2: [
+    {
+      required: true,
+      message: "安装位置不能为空",
+      trigger: "blur",
+    },
+  ],
+  types: [
+    {
+      required: true,
+      message: "协议不能为空",
+      trigger: "blur",
+    },
+  ],
+  username: [
+    {
+      required: true,
+      message: "更新人员姓名不能为空",
+      trigger: "blur",
+    },
+  ],
+  pass: [
+    {
+      required: true,
+      message: "密码不能为空",
+      trigger: "blur",
+    },
+  ],
+  resource: [
+    {
+      required: true,
+      message: "摄像机编码不能为空",
+      trigger: "blur",
+    },
+  ],
+  // desc: [{ required: true, message: "Please input activity form", trigger: "blur" }],
+});
+// 获取摄像头列表
+const getList = async (curPage) => {
+  let createTimeStart = "";
+  let createTimeEnd = "";
+  if (searchInput.createTime) {
+    createTimeStart = searchInput.createTime[0];
+    createTimeEnd = searchInput.createTime[1];
+  }
+  let lastUpdateTimeStart = "";
+  let lastUpdateTimeEnd = "";
+  if (searchInput.lastUpdateTime) {
+    lastUpdateTimeStart = searchInput.lastUpdateTime[0];
+    lastUpdateTimeEnd = searchInput.lastUpdateTime[1];
+  }
+  let data = {
+    vendor: searchInput.vendor,
+    ip: searchInput.ip,
+    statu: searchInput.statu,
+    agreement: searchInput.agreement,
+    installationSite: searchInput.installationSite,
+    outputLink: searchInput.outputLink,
+    cameraCoding: searchInput.cameraCoding,
+    remark: searchInput.remark,
+    creater: searchInput.creater,
+    lastUpdater: searchInput.lastUpdater,
+    createTimeStart,
+    createTimeEnd,
+    lastUpdateTimeStart,
+    lastUpdateTimeEnd,
+    pageSize: 10,
+    curPage,
+  };
+  let res = await vidiconsApi.queryCamera(data);
+  res.data.list.forEach((item) => {
+    if (item.statu == 1) {
+      item.statu = "在线";
+    } else {
+      item.statu = "离线";
+    }
+  });
+  tableData.list = res.data.list;
+  total.value = res.data.totalCount;
+  // console.log(res);
+};
+
+// 路由
+const handleSelect = (key, keyPath) => {
+  router.push({
+    path: `/vidicons/${key}`,
+  });
+};
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  let createTimeStart = "";
+  let createTimeEnd = "";
+  if (searchInput.createTime) {
+    createTimeStart = searchInput.createTime[0];
+    createTimeEnd = searchInput.createTime[1];
+  }
+  let lastUpdateTimeStart = "";
+  let lastUpdateTimeEnd = "";
+  if (searchInput.lastUpdateTime) {
+    lastUpdateTimeStart = searchInput.lastUpdateTime[0];
+    lastUpdateTimeEnd = searchInput.lastUpdateTime[1];
+  }
+  let data = {
+    vendor: searchInput.vendor,
+    ip: searchInput.ip,
+    statu: searchInput.statu,
+    agreement: searchInput.agreement,
+    installationSite: searchInput.installationSite,
+    outputLink: searchInput.outputLink,
+    cameraCoding: searchInput.cameraCoding,
+    remark: searchInput.remark,
+    creater: searchInput.creater,
+    lastUpdater: searchInput.lastUpdater,
+    createTimeStart,
+    createTimeEnd,
+    lastUpdateTimeStart,
+    lastUpdateTimeEnd,
+    pageSize: 10,
+    curPage: 1,
+  };
+  let res = await vidiconsApi.queryCamera(data);
+  if (res.success) {
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: "搜索成功",
+      center: true,
+    });
+    // console.log(res.data.list);
+    res.data.list.forEach((item) => {
+      if (item.statu == 1) {
+        item.statu = "在线";
+      } else {
+        item.statu = "离线";
+      }
+    });
+    currentPage.value = 1;
+    tableData.list = res.data.list;
+    total.value = res.data.totalCount;
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: "搜索失败",
+      center: true,
+    });
+  }
+}, 300);
+
+// 查看按钮
+const look = (row) => {
+  console.log(row);
+};
+
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList(value);
+};
+
+onBeforeMount(() => {
+  getList(1);
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 89%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 550px;
+    margin: 10px auto 30px;
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+      }
+      :deep(.el-table__row) {
+        height: 50px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: rgba(240, 243, 247, 1);
+      }
+      :deep(.look) {
+        padding: 5px 14px;
+        border-radius: 45px;
+        border: 0.74px solid rgba(30, 125, 251, 1);
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+    :deep(.el-input__wrapper) {
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      box-shadow: none;
+    }
+    :deep(.el-pager li) {
+      margin: 0 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.el-pager li.is-active) {
+      background-color: rgba(30, 125, 251, 1);
+    }
+    :deep(.btn-prev) {
+      margin-right: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.btn-next) {
+      margin-left: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

+ 756 - 0
src/views/staff/staff.vue

@@ -0,0 +1,756 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">员工管理</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>姓名 : </span>
+          <el-input
+            clearable
+            v-model="searchInput.name"
+            class="w-50 m-2"
+            placeholder="请输入姓名"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>手机号码 : </span>
+          <el-input
+            clearable
+            v-model="searchInput.phone"
+            class="w-50 m-2"
+            placeholder="请输入手机号码"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>创建时间 : </span>
+          <el-date-picker
+            v-model="searchInput.createTime"
+            type="date"
+            format="YYYY-MM-DD"
+            value-format="YYYY-MM-DD"
+            :prefix-icon="Calendar"
+            placeholder="请选择日期"
+          />
+        </div>
+
+        <el-button
+          style="margin-left: 20px"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchBtn"
+          ><el-icon><Search /></el-icon> <span>查询</span></el-button
+        >
+        <el-button
+          style="margin-left: 20px !important"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="searchRefresh"
+          ><el-icon><Refresh /></el-icon><span>重置</span></el-button
+        >
+      </div>
+      <!-- 按钮列表 -->
+      <div class="gongneng">
+        <el-button type="primary" color="rgba(61, 81, 232, 1)" @click="addlist"
+          ><el-icon><CirclePlus /></el-icon><span>新增员工</span></el-button
+        >
+        <el-button
+          color="rgba(61, 81, 232, 1)"
+          class="import"
+          type="primary"
+          @click="importExcel"
+          ><img
+            src="@/assets/import.png"
+            style="width: 14px; height: 14px; margin-right: 4px"
+            alt=""
+          />
+          <span>导出表单</span></el-button
+        >
+      </div>
+    </div>
+    <div class="footer">
+      <el-table
+        :row-class-name="tableRowClassName"
+        :data="tableData.list"
+        @selection-change="handleSelectionChange"
+        style="width: 100%"
+        :header-cell-style="{
+          background: 'rgba(240, 243, 247, 1)',
+          height: '50px',
+          border: 0,
+        }"
+      >
+        <!-- <el-table-column align="center" type="selection" width="80" /> -->
+        <el-table-column
+          type="index"
+          width="80"
+          align="center"
+          label="序号"
+          index="1"
+        />
+        <el-table-column align="center" prop="user_name" label="姓名" />
+        <el-table-column
+          align="center"
+          prop="user_zz"
+          label="职位"
+          width="200"
+        />
+        <el-table-column
+          align="center"
+          prop="user_phone"
+          label="电话号码"
+          width="200"
+        />
+        <el-table-column
+          align="center"
+          prop="create_time"
+          label="创建时间"
+          width="250"
+        />
+
+        <el-table-column align="center" label="操作" width="220">
+          <template #default="scope">
+            <el-button link type="primary" @click="edit(scope.row)"
+              ><div class="look">编辑</div></el-button
+            >
+            <el-button link type="primary" @click="del(scope.row)"
+              ><div class="look">删除</div></el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 添加员工弹窗 -->
+      <el-dialog
+        class="addStaff"
+        v-model="addDialogVisible"
+        :close-on-click-modal="false"
+        :close-on-press-escape="false"
+        :title="dialongTitle"
+        align-center
+        width="609"
+        :before-close="cancelAdd"
+      >
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          label-width="100px"
+          class="demo-ruleForm"
+          :size="formSize"
+          label-position="left"
+          status-icon
+        >
+          <el-form-item label="姓名 :" prop="name">
+            <el-input
+              v-model="ruleForm.name"
+              placeholder="请输入姓名"
+              clearable
+            />
+          </el-form-item>
+          <el-form-item
+            label="卡号 :"
+            prop="cardnumber"
+            v-if="dialongTitle == '新增员工'"
+          >
+            <el-input
+              v-model="ruleForm.cardnumber"
+              placeholder="请输入卡号"
+              clearable
+            />
+          </el-form-item>
+          <el-form-item label="手机号码 :" prop="phone">
+            <el-input
+              v-model="ruleForm.phone"
+              placeholder="请输入手机号码"
+              clearable
+            />
+          </el-form-item>
+          <el-form-item
+            label="职位 :"
+            prop="post"
+            style="
+              padding-bottom: 40px;
+              border-bottom: 1px solid rgba(230, 230, 230, 1);
+            "
+          >
+            <el-select
+              v-model="ruleForm.post"
+              class="m-2"
+              placeholder="请选择职位"
+            >
+              <el-option label="教师" value="教师" />
+              <el-option label="学生" value="学生" />
+              <el-option label="司机" value="司机" />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item class="options">
+            <el-button class="congzhi" @click="cancelAdd(ruleFormRef)"
+              >取消</el-button
+            >
+            <el-button
+              color="rgba(61, 81, 232, 1)"
+              class="queding"
+              type="primary"
+              @click="submitAdd(ruleFormRef)"
+            >
+              确定
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+    </div>
+
+    <!-- 分页组件 -->
+    <el-pagination
+      background
+      :current-page="currentPage"
+      :page-size="pageSize"
+      layout="total, prev, pager, next, jumper, slot"
+      :total="total"
+      @update:current-page="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, onBeforeMount, onUnmounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { Calendar } from "@element-plus/icons-vue";
+import vidiconsApi from "@/api/vidicons.js";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import axios from "axios";
+const router = useRouter();
+// 表格数据
+const tableData = reactive({ list: [] });
+const activeIndex = ref(); // 默认跳转路由
+const dialongTitle = ref("新增员工"); // 弹窗标题
+
+const searchInput = reactive({
+  name: "",
+  phone: "",
+  createTime: "",
+}); // 搜索按钮数据
+
+const currentPage = ref(1); // 当前页
+const pageSize = ref(10);
+const total = ref(); // 当前总数
+const selectData = reactive({ list: [] }); // 多选框选择的数据
+
+const addDialogVisible = ref(false); // 控制添加员工弹窗
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+const ruleForm = reactive({
+  name: "",
+  phone: "",
+  post: "0",
+  cardnumber: "",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  name: [
+    { required: true, message: "姓名不能为空", trigger: "blur" },
+    // { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" },
+  ],
+  cardnumber: [
+    { required: true, message: "卡号不能为空", trigger: "blur" },
+    // { min: 3, max: 5, message: "Length should be 3 to 5", trigger: "blur" },
+  ],
+  phone: [
+    { required: true, message: "请输入手机号", trigger: "blur" },
+    {
+      pattern: /^((0\d{2,3}-\d{7,8})|(1[3584]\d{9}))$/,
+      message: "请输入合法手机号/电话号",
+      trigger: "blur",
+    },
+  ],
+  post: [
+    {
+      required: true,
+      message: "职位不能为空",
+      trigger: "change",
+    },
+  ],
+
+  // desc: [{ required: true, message: "Please input activity form", trigger: "blur" }],
+});
+// 获取摄像头列表
+const getList = async () => {
+  let data = new FormData();
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  data.set("user_name", searchInput.name);
+  data.set("user_phone", searchInput.phone);
+  data.set("create_time", searchInput.createTime); //前面的key记得对应!
+  data.set("page", currentPage.value);
+  data.set("rows", pageSize.value); //前面的key记得对应!
+  // let res = await adminApi.adminLogin(data);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/userlist.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  console.log(res);
+  if (res.data.code == 200) {
+    // ElMessage({
+    //   type: "success",
+    //   showClose: true,
+    //   message: "请求数据成功",
+    //   center: true,
+    // });
+    tableData.list = res.data.rows;
+    currentPage.value = res.data.currentPage;
+    total.value = res.data.total;
+  } else {
+    tableData.list = res.data.rows;
+    currentPage.value = 1;
+    total.value = res.data.total;
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: "暂无数据",
+      center: true,
+    });
+  }
+  // console.log(res);
+};
+
+// 搜索功能
+const searchBtn = lodash.debounce(async () => {
+  getList();
+}, 300);
+// 重置搜索
+const searchRefresh = lodash.debounce(async () => {
+  searchInput.name = "";
+  searchInput.phone = "";
+  searchInput.createTime = "";
+  currentPage.value = 1;
+  getList();
+}, 300);
+// 添加员工
+const addlist = () => {
+  dialongTitle.value = "新增员工";
+  addDialogVisible.value = true;
+  ruleForm.name = "";
+  ruleForm.phone = "";
+  ruleForm.post = "";
+  ruleForm.cardnumber = "";
+};
+//编辑按钮
+const edit = (row) => {
+  dialongTitle.value = "编辑员工";
+  addDialogVisible.value = true;
+  console.log(row);
+  ruleForm.name = row.user_name;
+  ruleForm.phone = row.user_phone;
+  ruleForm.post = row.user_zz;
+  ruleForm.id = row.id;
+};
+// 取消添加员工
+const cancelAdd = () => {
+  addDialogVisible.value = false;
+  ruleFormRef.value.resetFields();
+};
+// 确认添加员工
+const submitAdd = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate(async (valid, fields) => {
+    if (valid) {
+      if (dialongTitle.value == "新增员工") {
+        let data = {
+          user_name: ruleForm.name,
+          user_phone: ruleForm.phone,
+          user_zz: ruleForm.post,
+          card_number: ruleForm.cardnumber,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/userinsert.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "添加账号");
+        if (res.data.code == 200) {
+          getList();
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      } else {
+        let data = {
+          user_name: ruleForm.name,
+          user_phone: ruleForm.phone,
+          user_zz: ruleForm.post,
+          id: ruleForm.id,
+        };
+        // let res = await admin.adminAdd(data);
+        let res = await axios({
+          method: "post",
+          url: "/api/carBook/userupdate.action",
+          headers: {
+            "Content-Type": "application/json;charset=utf-8",
+            token: sessionStorage.getItem("token"),
+          },
+          data: data,
+        });
+        // console.log(res, "修改账号");
+        if (res.data.code == 200) {
+          getList();
+
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+          addDialogVisible.value = false;
+          ruleFormRef.value.resetFields();
+        } else {
+          ElMessage({
+            type: "error",
+            showClose: true,
+            message: res.data.message,
+            center: true,
+          });
+        }
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+
+//删除按钮
+const del = (row) => {
+  ElMessageBox.confirm(`是否删除 ${row.user_name} 员工?`, "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let data = new FormData();
+      data.set("id", row.id);
+      // let res = await adminApi.adminLogin(data);
+      let res = await axios({
+        method: "post",
+        url: "/api/carBook/userdel.action",
+        headers: {
+          token: sessionStorage.getItem("token"),
+        },
+        data: data,
+      });
+      if (res.data.code == 200) {
+        if (tableData.list.length == 1 && currentPage.value != 1) {
+          currentPage.value = currentPage.value - 1;
+        }
+        getList();
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      } else {
+        ElMessage({
+          type: "error",
+          showClose: true,
+          message: res.data.message,
+          center: true,
+        });
+      }
+      console.log(res);
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "已取消删除",
+      });
+    });
+};
+//导出功能
+const importExcel = async () => {
+  if (searchInput.createTime == null) {
+    searchInput.createTime = "";
+  }
+  let data = new FormData();
+  data.set("user_name", searchInput.name);
+  data.set("user_phone", searchInput.phone);
+  data.set("create_time", searchInput.createTime);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/usertoExcel.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  // console.log(res, "导出账号");
+  if (res.data.code == 200) {
+    const elt = document.createElement("a");
+    elt.setAttribute(
+      "href",
+      "https://chtech.ncjti.edu.cn/carstop" + res.data.downurl
+    );
+    elt.setAttribute("download", "file.png");
+    elt.style.display = "none";
+    document.body.appendChild(elt);
+    elt.click();
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 分页
+const handleCurrentChange = (value) => {
+  // console.log(value);
+  currentPage.value = value;
+  getList();
+};
+
+onBeforeMount(() => {
+  getList();
+});
+onUnmounted(() => {
+  // document.removeEventListener("keyup", Enters);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 89%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+      .search {
+        margin-left: 0 !important;
+      }
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 550px;
+    margin: 10px auto 30px;
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+      }
+      :deep(.el-table__row) {
+        height: 50px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: rgba(240, 243, 247, 1);
+      }
+      :deep(.look) {
+        padding: 5px 10px;
+        border-radius: 45px;
+        border: 0.74px solid rgba(30, 125, 251, 1);
+      }
+    }
+
+    // 添加员工弹窗样式
+    :deep(.addStaff) {
+      //   height: 420px;
+      border-radius: 11px;
+      .el-dialog__header {
+        border-radius: 11px 11px 0 0;
+        background: rgba(237, 241, 245, 1);
+        font-weight: 600;
+        margin: 0;
+        .el-dialog__headerbtn {
+          outline: none;
+        }
+      }
+      .el-dialog__body {
+        padding: 30px 20px 10px 20px;
+        .el-input {
+          width: 200px;
+        }
+        .options {
+          margin-left: 320px;
+        }
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+    :deep(.el-input__wrapper) {
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      box-shadow: none;
+    }
+    :deep(.el-pager li) {
+      margin: 0 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.el-pager li.is-active) {
+      background-color: rgba(30, 125, 251, 1);
+    }
+    :deep(.btn-prev) {
+      margin-right: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.btn-next) {
+      margin-left: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

+ 855 - 0
src/views/statement/statement.vue

@@ -0,0 +1,855 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><UserFilled /></el-icon> -->
+      <span class="cameratxt">报表统计</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>车牌号 : </span>
+          <el-input
+            clearable
+            v-model="searchData.input1"
+            class="w-50 m-2"
+            placeholder="请输入车牌号"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>汇总日期 : </span>
+          <el-date-picker
+            v-model="searchData.input5"
+            type="datetimerange"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          />
+        </div>
+        <el-button
+          style="margin-left: 20px"
+          color="rgba(61, 81, 232, 1)"
+          type="primary"
+          class="search"
+          @click="search"
+          ><el-icon><Search /></el-icon><span>搜索</span></el-button
+        >
+      </div>
+      <el-button
+        color="rgba(61, 81, 232, 1)"
+        class="import"
+        type="primary"
+        @click="resetInput"
+        ><img
+          src="@/assets/import.png"
+          style="width: 14px; height: 14px; margin-right: 4px"
+          alt=""
+        />
+        <span>导出表单</span></el-button
+      >
+      <!-- 编辑/新增管理员 -->
+      <el-dialog
+        v-model="centerDialogVisible"
+        :title="dialongTitle"
+        width="30%"
+        align-center
+        :before-close="editClose"
+      >
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          label-width="120px"
+          class="demo-ruleForm"
+          :size="formSize"
+          status-icon
+        >
+          <el-form-item label="姓名" prop="name">
+            <el-input v-model="ruleForm.name" placeholder="请输入姓名" />
+          </el-form-item>
+          <el-form-item label="性别" prop="region">
+            <el-select
+              v-model="ruleForm.region"
+              style="width: 180px"
+              placeholder="请选择性别"
+            >
+              <el-option label="男" value="1" />
+              <el-option label="女" value="0" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="账号" prop="date1">
+            <el-input v-model="ruleForm.date1" placeholder="请输入账号" />
+          </el-form-item>
+          <el-form-item
+            label="密码"
+            prop="date2"
+            v-show="dialongTitle == '新增用户'"
+          >
+            <el-input v-model="ruleForm.date2" placeholder="请输入密码" />
+          </el-form-item>
+          <el-form-item label="联系方式" prop="phone">
+            <el-input
+              v-model="ruleForm.phone"
+              placeholder="请输入联系方式"
+            ></el-input>
+          </el-form-item>
+          <el-form-item label="类别" prop="resource">
+            <el-select v-model="ruleForm.resource" placeholder="请选择类别">
+              <el-option label="超级管理员" value="1" />
+              <el-option label="管理员" value="2" />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="submitForm(ruleFormRef)">
+              确定
+            </el-button>
+            <el-button @click="resetForm(ruleFormRef)">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+      <!-- 导入用户按钮 -->
+      <el-dialog
+        v-model="importByExcelDialog"
+        title="导入用户信息"
+        width="45%"
+        align-center
+        custom-class="importExecl"
+      >
+        <el-steps :active="active" align-center finish-status="success">
+          <el-step title="下载模板 1" :icon="Pointer" />
+          <el-step title="执行上传 2" :icon="Upload" />
+          <el-step title="导入完成 3" :icon="Select" />
+        </el-steps>
+        <div class="cont">
+          <div class="download">
+            <el-icon :size="30"><Pointer /></el-icon
+            ><span>请按照数据模板导入数据</span>
+          </div>
+          <el-button type="primary">
+            <a href="/video/videoManage/userProfile.xlsx" style="color: #fff"
+              >下载模板</a
+            >
+          </el-button>
+          <div class="upload">
+            <div class="download">
+              <el-icon :size="30"><Upload /></el-icon
+              ><span>将文件上传(用户上传的excel文件需要符合规定的格式)</span>
+            </div>
+            <el-upload
+              v-model:file-list="fileList"
+              class="upload-demo"
+              action="none"
+              :limit="1"
+              :multiple="false"
+              :auto-upload="false"
+              :on-change="changeUpload"
+              :before-remove="beforeRemove"
+            >
+              <el-button type="primary">选择文件</el-button>
+              <template #tip>
+                <div class="el-upload__tip">仅允许上传一个文件</div>
+              </template>
+            </el-upload>
+          </div>
+          <div class="accomplish">
+            <el-button
+              type="primary"
+              :disabled="active == 1"
+              style="margin-top: 12px"
+              @click="next"
+              >完成导入</el-button
+            >
+          </div>
+        </div>
+      </el-dialog>
+    </div>
+    <div class="footerMenu">
+      <div
+        class="menuList"
+        @click="collectMenu"
+        :class="footerMenuIndex == 0 ? 'menuListActive' : ''"
+      >
+        汇总
+      </div>
+      <div
+        class="menuList"
+        @click="detailMenu"
+        :class="footerMenuIndex == 1 ? 'menuListActive' : ''"
+      >
+        明细列表
+      </div>
+    </div>
+    <div class="MingXi" v-if="footerMenuIndex == 0">
+      <div class="footer">
+        <el-table
+          :row-class-name="tableRowClassName"
+          :data="tableData.list"
+          style="width: 100%"
+          :header-cell-style="{
+            background: 'rgba(240, 243, 247, 1)',
+            border: 0,
+          }"
+          @selection-change="handleSelectionChange"
+        >
+          <el-table-column align="center" type="selection" width="80" />
+          <el-table-column
+            align="center"
+            width="200"
+            prop="createTime"
+            label="创建时间"
+          />
+          <el-table-column
+            align="center"
+            prop="name"
+            label="发车时间"
+            width="100"
+          />
+          <el-table-column
+            align="center"
+            width="120"
+            prop="sex"
+            label="已预约通行人数"
+          />
+          <el-table-column
+            align="center"
+            prop="username"
+            width="120"
+            label="未预约通行人数"
+          />
+          <el-table-column align="center" prop="adminType" label="车牌号" />
+          <el-table-column align="center" prop="phone" label="容量" />
+          <el-table-column align="center" prop="phone" label="状态" />
+          <el-table-column align="center" label="操作">
+            <template #default="scope">
+              <el-button link type="primary" @click="look(scope.row)"
+                ><div class="look">查看</div></el-button
+              >
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+      <el-pagination
+        background
+        v-model:page-size="pageSize"
+        v-model:current-page="currentPage"
+        layout="total, prev, pager, next, jumper"
+        :total="total"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+    <div class="MingXi" v-else>
+      <div class="footer">
+        <el-table
+          :row-class-name="tableRowClassName"
+          :data="tableData.list"
+          style="width: 100%"
+          :header-cell-style="{
+            background: 'rgba(240, 243, 247, 1)',
+            border: 0,
+          }"
+          @selection-change="handleSelectionChange"
+        >
+          <el-table-column align="center" type="selection" width="80" />
+          <el-table-column
+            align="center"
+            width="200"
+            prop="createTime"
+            label="下单时间"
+          />
+          <el-table-column
+            align="center"
+            prop="name"
+            label="发车时间"
+            width="100"
+          />
+          <el-table-column
+            align="center"
+            width="120"
+            prop="sex"
+            label="预约人"
+          />
+          <el-table-column align="center" width="120" prop="sex" label="身份" />
+          <el-table-column
+            align="center"
+            width="120"
+            prop="sex"
+            label="手机号码"
+          />
+
+          <el-table-column align="center" prop="adminType" label="车牌号" />
+          <el-table-column
+            align="center"
+            prop="phone"
+            label="容量"
+            width="150"
+          />
+
+          <el-table-column
+            align="center"
+            prop="username"
+            width="120"
+            label="变更信息"
+          />
+
+          <el-table-column
+            align="center"
+            width="200"
+            prop="createTime"
+            label="通行时间"
+          />
+          <el-table-column
+            align="center"
+            width="120"
+            prop="phone"
+            label="状态"
+          />
+          <!-- <el-table-column align="center" label="操作">
+            <template #default="scope">
+              <el-button link type="primary" @click="look(scope.row)"
+                ><div class="look">查看</div></el-button
+              >
+            </template>
+          </el-table-column> -->
+        </el-table>
+      </div>
+      <el-pagination
+        background
+        v-model:page-size="pageSize"
+        v-model:current-page="currentPage"
+        layout="total, prev, pager, next, jumper"
+        :total="total"
+        @current-change="handleCurrentChange"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount } from "vue";
+import { useRouter } from "vue-router";
+import { Select, Pointer, Upload } from "@element-plus/icons-vue";
+import { JSEncrypt } from "jsencrypt"; // 加密密码
+import { ElMessage, ElMessageBox } from "element-plus";
+import { dayjs } from "element-plus";
+import axios from "axios";
+import lodash from "lodash";
+import adminApi from "@/api/admin.js";
+const router = useRouter();
+
+// 搜索框数据
+const searchData = reactive({
+  input1: "",
+  input2: "",
+  input3: "",
+  input4: "",
+  input5: "",
+});
+// 表格数据
+const tableData = reactive({ list: [] });
+const dialongTitle = ref(""); // 弹窗标题
+const pageSize = ref(13);
+const currentPage = ref(1); // 当前页
+const total = ref(); // 当前总数
+const selectData = reactive({ list: [] }); // 多选框选择的数据
+
+const footerMenuIndex = ref(1); // 控制汇总和明细列表
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+
+const ruleForm = reactive({
+  name: "",
+  region: "",
+  date1: "",
+  date2: "",
+  phone: "",
+  resource: "",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  region: [
+    {
+      required: true,
+      message: "请选择性别",
+      trigger: "blur",
+    },
+  ],
+  name: [
+    {
+      required: true,
+      message: "请输入姓名",
+      trigger: "blur",
+    },
+  ],
+  date1: [
+    {
+      required: true,
+      message: "请输入账号",
+      trigger: "blur",
+    },
+  ],
+  date2: [
+    {
+      required: true,
+      message: "请输入密码",
+      trigger: "blur",
+    },
+  ],
+  phone: [
+    {
+      required: true,
+      message: "请输入电话号码",
+      trigger: "blur",
+    },
+    {
+      validator: function (rule, value, callback) {
+        if (/^1[34578]\d{9}$/.test(value) == false) {
+          callback(new Error("请输入正确的手机号"));
+        } else {
+          callback();
+        }
+      },
+      trigger: "blur",
+    },
+  ],
+  resource: [
+    {
+      required: true,
+      message: "请选择类别",
+      trigger: "blur",
+    },
+  ],
+});
+
+// 获取管理员列表
+const getList = (curPage) => {};
+
+// 搜索按钮
+const search = lodash.debounce(() => {
+  let startTime = "";
+  let endTime = "";
+  if (searchData.input5 == []) {
+    startTime = "";
+    endTime = "";
+  } else {
+    startTime = searchData.input5[0];
+    endTime = searchData.input5[1];
+  }
+
+  let data = {
+    pageSize: 13,
+    curPage: 1,
+    name: searchData.input1,
+    username: searchData.input2,
+    phone: searchData.input3,
+    adminType: searchData.input4,
+    startTime,
+    endTime,
+  };
+  adminApi
+    .adminList(data)
+    .then((res) => {
+      console.log(res);
+      total.value = res.data.totalCount;
+      res.data.list.forEach((item) => {
+        if (item.sex == 0) {
+          item.sex = "女";
+        } else {
+          item.sex = "男";
+        }
+        if (item.adminType == "1") {
+          item.adminType = "超级管理员";
+        } else {
+          item.adminType = "管理员";
+        }
+      });
+      ElMessage({
+        type: "success",
+        showClose: true,
+        message: "搜索成功",
+        center: true,
+      });
+      currentPage.value = 1;
+      tableData.list = res.data.list;
+    })
+    .catch((err) => {
+      console.log(err);
+      ElMessage({
+        type: "error",
+        showClose: true,
+        message: "搜索失败",
+        center: true,
+      });
+    });
+}, 300);
+// 重置搜索框
+const resetInput = lodash.debounce(() => {
+  searchData.input1 = "";
+  searchData.input2 = "";
+  searchData.input3 = "";
+  searchData.input4 = "";
+  searchData.input5 = "";
+  currentPage.value = 1;
+  getList(1);
+}, 300);
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+
+// 汇总按钮
+const collectMenu = () => {
+  footerMenuIndex.value = 0;
+};
+//明细列表按钮
+const detailMenu = () => {
+  footerMenuIndex.value = 1;
+};
+
+// 查看按钮
+const look = (row) => {
+  console.log(row);
+};
+// 删除多选IP
+const allDel = () => {
+  console.log(selectData.list);
+  ElMessageBox.confirm("此操作将永久删除所选用户,是否继续?", "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let idIP = [];
+      selectData.list.forEach((item) => {
+        idIP.push(item.id);
+      });
+      console.log(idIP);
+      let res = await adminApi.adminDel(idIP);
+      if (res.success) {
+        if (
+          tableData.list.length - selectData.list.length == 0 &&
+          currentPage.value != 1
+        ) {
+          currentPage.value = currentPage.value - 1;
+        }
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: "删除成功",
+          center: true,
+        });
+        getList(currentPage.value);
+      } else {
+        ElMessage({
+          type: "warning",
+          showClose: true,
+          message: "删除失败",
+          center: true,
+        });
+      }
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "取消删除",
+      });
+    });
+};
+
+// 重置表单
+const resetForm = (formEl) => {
+  formEl.resetFields();
+  console.log("重置表单");
+  ruleForm.phone = "";
+  ruleForm.date1 = "";
+  ruleForm.resource = "";
+  // ruleForm.password=,
+  ruleForm.region = "";
+  ruleForm.name = "";
+  ruleForm.date2 = "";
+  // if (!formEl) return;
+
+  // formEl.resetFields();
+};
+
+// 导出按钮
+const downLoad = lodash.debounce(() => {
+  let startTime = searchData.input5[0];
+  let endTime = searchData.input5[1];
+  let data = {
+    name: searchData.input1,
+    username: searchData.input2,
+    phone: searchData.input3,
+    adminType: searchData.input4,
+    startTime,
+    endTime,
+  };
+  adminApi
+    .download(data)
+    .then((res) => {
+      let name = "用户管理列表";
+      var content = res;
+      // var data = new Blob([content], { type: "application/octet-stream;charset=utf-8" });
+      var data = new Blob([content], {
+        type: "application/vnd.ms-excel;charset=utf-8",
+      });
+      var downloadUrl = window.URL.createObjectURL(data);
+      var anchor = document.createElement("a");
+      anchor.href = downloadUrl;
+      anchor.download = name + ".xlsx";
+      anchor.click();
+      window.URL.revokeObjectURL(data);
+      console.log(res);
+      ElMessage({
+        type: "success",
+        showClose: true,
+        message: "导出成功",
+        center: true,
+      });
+    })
+    .catch((err) => {
+      console.log(err);
+      ElMessage({
+        type: "error",
+        showClose: true,
+        message: "导出失败",
+        center: true,
+      });
+    });
+}, 300);
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 分页
+const handleCurrentChange = (value) => {
+  currentPage.value = value;
+  getList(value);
+};
+onBeforeMount(() => {
+  getList(1);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 89%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+    justify-content: space-between;
+    color: #000;
+    border-bottom: 1px solid rgb(231, 231, 231);
+    padding: 10px 0;
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      //   width: 100%;
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .import {
+      margin: 10px 0;
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footerMenu {
+    width: 96%;
+    margin: 10px auto 0;
+    display: flex;
+    align-items: center;
+    // height: 40px;
+    .menuList {
+      padding: 5px 12px;
+      border-bottom: 2px solid transparent;
+      //   color: rgba(61, 81, 232, 1);
+      color: rgba(128, 128, 128, 1);
+      cursor: pointer;
+      //   font-size: 16px;
+      font-weight: 600;
+    }
+    .menuListActive {
+      border-bottom: 2px solid rgba(61, 81, 232, 1);
+      color: rgba(61, 81, 232, 1);
+      font-size: 17px;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 562px;
+    margin: 20px auto 30px;
+    .el-table--fit {
+      height: 100%;
+
+      :deep(.el-table__row) {
+        height: 40px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+      :deep(.look) {
+        padding: 3px 10px;
+        border-radius: 40px;
+        border: 0.74px solid rgba(30, 125, 251, 1);
+      }
+      // 输出链接弹窗样式
+      :deep(.link .el-dialog__header) {
+        text-align: left;
+        font-size: 20px;
+        font-weight: 600;
+      }
+      :deep(.link .el-dialog__body) {
+        padding: 10px 20px;
+        height: 100px;
+        margin-bottom: 10px;
+      }
+      .link {
+        .linkcontent {
+          text-align: left;
+          height: 100px;
+          border: 1px solid #ccc;
+          p {
+            margin: 5px 10px;
+          }
+        }
+      }
+
+      :deep(.playVideo .el-dialog__header) {
+        text-align: left;
+        font-size: 20px;
+        font-weight: 600;
+        background-color: #ccc;
+        margin-right: 0;
+      }
+      :deep(.playVideo .el-dialog__body) {
+        height: 600px;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: rgba(240, 243, 247, 1);
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+    :deep(.el-input__wrapper) {
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      box-shadow: none;
+    }
+    :deep(.el-pager li) {
+      margin: 0 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.el-pager li.is-active) {
+      background-color: rgba(30, 125, 251, 1);
+    }
+    :deep(.btn-prev) {
+      margin-right: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+    :deep(.btn-next) {
+      margin-left: 5px;
+      border: 1px solid rgba(0, 0, 0, 1);
+      border-radius: 5px;
+      background-color: transparent;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

+ 983 - 0
src/views/vidicons/user/index.vue

@@ -0,0 +1,983 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <el-icon :size="23" class="camera"><UserFilled /></el-icon>
+      <span class="cameratxt">用户管理</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>姓名 </span>
+          <el-input
+            clearable
+            v-model="searchData.input1"
+            class="w-50 m-2"
+            placeholder="请输入姓名"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>账号 </span>
+          <el-input
+            clearable
+            v-model="searchData.input2"
+            class="w-50 m-2"
+            placeholder="请输入账号"
+            style="width: 150px"
+          />
+        </div>
+        <div class="condition">
+          <span>联系方式 </span>
+          <el-input
+            clearable
+            v-model="searchData.input3"
+            class="w-50 m-2"
+            placeholder="请输入联系方式"
+            style="width: 180px"
+          />
+        </div>
+        <div class="condition">
+          <span>角色 </span>
+          <el-select
+            style="width: 120px"
+            v-model="searchData.input4"
+            class="m-2"
+            placeholder="请选择角色"
+          >
+            <el-option label="管理员" value="2" />
+            <el-option label="超级管理员" value="1" />
+          </el-select>
+        </div>
+
+        <div class="condition">
+          <span>创建时间 </span>
+          <el-date-picker
+            v-model="searchData.input5"
+            type="datetimerange"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="YYYY-MM-DD HH:mm:ss"
+          />
+        </div>
+        <el-button type="primary" class="search" @click="search">搜索</el-button>
+        <el-button type="primary" plain @click="resetInput">重置</el-button>
+      </div>
+
+      <!-- 按钮列表 -->
+
+      <div class="gongneng">
+        <el-button type="primary" plain @click="addlist">新增</el-button>
+        <el-button type="success" plain @click="importExcel">导入</el-button>
+        <el-button type="warning" plain @click="downLoad">导出</el-button>
+        <el-button type="danger" plain @click="allDel">删除</el-button>
+      </div>
+
+      <!-- 编辑/新增管理员 -->
+      <el-dialog
+        v-model="centerDialogVisible"
+        :title="dialongTitle"
+        width="30%"
+        align-center
+        :before-close="editClose"
+      >
+        <el-form
+          ref="ruleFormRef"
+          :model="ruleForm"
+          :rules="rules"
+          label-width="120px"
+          class="demo-ruleForm"
+          :size="formSize"
+          status-icon
+        >
+          <el-form-item label="姓名" prop="name">
+            <el-input v-model="ruleForm.name" placeholder="请输入姓名" />
+          </el-form-item>
+          <el-form-item label="性别" prop="region">
+            <el-select
+              v-model="ruleForm.region"
+              style="width: 180px"
+              placeholder="请选择性别"
+            >
+              <el-option label="男" value="1" />
+              <el-option label="女" value="0" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="账号" prop="date1">
+            <el-input v-model="ruleForm.date1" placeholder="请输入账号" />
+          </el-form-item>
+          <el-form-item label="密码" prop="date2" v-show="dialongTitle == '新增用户'">
+            <el-input v-model="ruleForm.date2" placeholder="请输入密码" />
+          </el-form-item>
+          <el-form-item label="联系方式" prop="phone">
+            <el-input v-model="ruleForm.phone" placeholder="请输入联系方式"></el-input>
+          </el-form-item>
+          <el-form-item label="类别" prop="resource">
+            <el-select v-model="ruleForm.resource" placeholder="请选择类别">
+              <el-option label="超级管理员" value="1" />
+              <el-option label="管理员" value="2" />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" @click="submitForm(ruleFormRef)"> 确定 </el-button>
+            <el-button @click="resetForm(ruleFormRef)">重置</el-button>
+          </el-form-item>
+        </el-form>
+      </el-dialog>
+      <!-- 导入用户按钮 -->
+      <el-dialog
+        v-model="importByExcelDialog"
+        title="导入用户信息"
+        width="45%"
+        align-center
+        custom-class="importExecl"
+      >
+        <el-steps :active="active" align-center finish-status="success">
+          <el-step title="下载模板 1" :icon="Pointer" />
+          <el-step title="执行上传 2" :icon="Upload" />
+          <el-step title="导入完成 3" :icon="Select" />
+        </el-steps>
+        <div class="cont">
+          <div class="download">
+            <el-icon :size="30"><Pointer /></el-icon><span>请按照数据模板导入数据</span>
+          </div>
+          <el-button type="primary">
+            <a href="/video/videoManage/userProfile.xlsx" style="color: #fff">下载模板</a>
+          </el-button>
+          <div class="upload">
+            <div class="download">
+              <el-icon :size="30"><Upload /></el-icon
+              ><span>将文件上传(用户上传的excel文件需要符合规定的格式)</span>
+            </div>
+            <el-upload
+              v-model:file-list="fileList"
+              class="upload-demo"
+              action="none"
+              :limit="1"
+              :multiple="false"
+              :auto-upload="false"
+              :on-change="changeUpload"
+              :before-remove="beforeRemove"
+            >
+              <el-button type="primary">选择文件</el-button>
+              <template #tip>
+                <div class="el-upload__tip">仅允许上传一个文件</div>
+              </template>
+            </el-upload>
+          </div>
+          <div class="accomplish">
+            <el-button
+              type="primary"
+              :disabled="active == 1"
+              style="margin-top: 12px"
+              @click="next"
+              >完成导入</el-button
+            >
+          </div>
+        </div>
+      </el-dialog>
+    </div>
+    <div class="footer">
+      <el-table
+        :row-class-name="tableRowClassName"
+        :data="tableData.list"
+        style="width: 100%"
+        @selection-change="handleSelectionChange"
+      >
+        <el-table-column align="center" type="selection" width="80" />
+        <el-table-column align="center" prop="name" label="姓名" width="100" />
+        <el-table-column align="center" width="100" prop="sex" label="性别" />
+        <el-table-column align="center" prop="username" label="用户名(账号)" />
+        <el-table-column align="center" prop="adminType" label="类别" />
+        <el-table-column align="center" prop="phone" label="手机号码" />
+        <el-table-column align="center" width="300" prop="createTime" label="创建时间" />
+        <el-table-column align="center" label="操作">
+          <template #default="scope">
+            <el-button link type="primary" size="small" @click="handleUp(scope.row)"
+              >编辑</el-button
+            >
+            <el-button link type="primary" size="small" @click="handleDel(scope.row)"
+              >删除</el-button
+            >
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+    <el-pagination
+      v-model:page-size="pageSize"
+      v-model:current-page="currentPage"
+      layout="total, prev, pager, next, jumper"
+      :total="total"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onBeforeMount } from "vue";
+import { useRouter } from "vue-router";
+import { Select, Pointer, Upload } from "@element-plus/icons-vue";
+import { JSEncrypt } from "jsencrypt"; // 加密密码
+import { ElMessage, ElMessageBox } from "element-plus";
+import { dayjs } from "element-plus";
+import axios from "axios";
+import lodash from "lodash";
+import adminApi from "@/api/admin.js";
+const router = useRouter();
+
+// 搜索框数据
+const searchData = reactive({
+  input1: "",
+  input2: "",
+  input3: "",
+  input4: "",
+  input5: "",
+});
+// 表格数据
+const tableData = reactive({ list: [] });
+const dialongTitle = ref(""); // 弹窗标题
+const pageSize = ref(13);
+const currentPage = ref(1); // 当前页
+const total = ref(); // 当前总数
+const selectData = reactive({ list: [] }); // 多选框选择的数据
+const centerDialogVisible = ref(false); // 控制新增或修改弹窗开关
+
+const importByExcelDialog = ref(false); // 控制导入弹窗
+const active = ref(1); // 导入步骤计数
+const fileList = ref(); // 上传的文件
+
+// 表单数据
+const formSize = ref("default");
+const ruleFormRef = ref();
+
+const ruleForm = reactive({
+  name: "",
+  region: "",
+  date1: "",
+  date2: "",
+  phone: "",
+  resource: "",
+  id: "",
+});
+// 表单验证
+const rules = reactive({
+  region: [
+    {
+      required: true,
+      message: "请选择性别",
+      trigger: "blur",
+    },
+  ],
+  name: [
+    {
+      required: true,
+      message: "请输入姓名",
+      trigger: "blur",
+    },
+  ],
+  date1: [
+    {
+      required: true,
+      message: "请输入账号",
+      trigger: "blur",
+    },
+  ],
+  date2: [
+    {
+      required: true,
+      message: "请输入密码",
+      trigger: "blur",
+    },
+  ],
+  phone: [
+    {
+      required: true,
+      message: "请输入电话号码",
+      trigger: "blur",
+    },
+    {
+      validator: function (rule, value, callback) {
+        if (/^1[34578]\d{9}$/.test(value) == false) {
+          callback(new Error("请输入正确的手机号"));
+        } else {
+          callback();
+        }
+      },
+      trigger: "blur",
+    },
+  ],
+  resource: [
+    {
+      required: true,
+      message: "请选择类别",
+      trigger: "blur",
+    },
+  ],
+});
+// 路由
+const handleSelect = (key, keyPath) => {
+  router.push({
+    path: `/vidicons/${key}`,
+  });
+};
+// 获取管理员列表
+const getList = (curPage) => {
+  adminApi
+    .adminList({ pageSize: 13, curPage })
+    .then((res) => {
+      console.log(res);
+      total.value = res.data.totalCount;
+      res.data.list.forEach((item) => {
+        if (item.sex == 0) {
+          item.sex = "女";
+        } else if (item.sex == 1) {
+          item.sex = "男";
+        }
+        if (item.adminType == "1") {
+          item.adminType = "超级管理员";
+        } else if (item.adminType == "2") {
+          item.adminType = "管理员";
+        }
+      });
+
+      tableData.list = res.data.list;
+    })
+    .catch((err) => {
+      console.log(err);
+    });
+};
+
+// 搜索按钮
+const search = lodash.debounce(() => {
+  let startTime = "";
+  let endTime = "";
+  if (searchData.input5 == []) {
+    startTime = "";
+    endTime = "";
+  } else {
+    startTime = searchData.input5[0];
+    endTime = searchData.input5[1];
+  }
+
+  let data = {
+    pageSize: 13,
+    curPage: 1,
+    name: searchData.input1,
+    username: searchData.input2,
+    phone: searchData.input3,
+    adminType: searchData.input4,
+    startTime,
+    endTime,
+  };
+  adminApi
+    .adminList(data)
+    .then((res) => {
+      console.log(res);
+      total.value = res.data.totalCount;
+      res.data.list.forEach((item) => {
+        if (item.sex == 0) {
+          item.sex = "女";
+        } else {
+          item.sex = "男";
+        }
+        if (item.adminType == "1") {
+          item.adminType = "超级管理员";
+        } else {
+          item.adminType = "管理员";
+        }
+      });
+      ElMessage({
+        type: "success",
+        showClose: true,
+        message: "搜索成功",
+        center: true,
+      });
+      currentPage.value = 1;
+      tableData.list = res.data.list;
+    })
+    .catch((err) => {
+      console.log(err);
+      ElMessage({
+        type: "error",
+        showClose: true,
+        message: "搜索失败",
+        center: true,
+      });
+    });
+}, 300);
+// 重置搜索框
+const resetInput = lodash.debounce(() => {
+  searchData.input1 = "";
+  searchData.input2 = "";
+  searchData.input3 = "";
+  searchData.input4 = "";
+  searchData.input5 = "";
+  currentPage.value = 1;
+  getList(1);
+}, 300);
+// 多选框功能
+const handleSelectionChange = (val) => {
+  console.log(val);
+  selectData.list = val;
+};
+
+// 删除多选IP
+const allDel = () => {
+  console.log(selectData.list);
+  ElMessageBox.confirm("此操作将永久删除所选用户,是否继续?", "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(async () => {
+      let idIP = [];
+      selectData.list.forEach((item) => {
+        idIP.push(item.id);
+      });
+      console.log(idIP);
+      let res = await adminApi.adminDel(idIP);
+      if (res.success) {
+        if (
+          tableData.list.length - selectData.list.length == 0 &&
+          currentPage.value != 1
+        ) {
+          currentPage.value = currentPage.value - 1;
+        }
+        ElMessage({
+          type: "success",
+          showClose: true,
+          message: "删除成功",
+          center: true,
+        });
+        getList(currentPage.value);
+      } else {
+        ElMessage({
+          type: "warning",
+          showClose: true,
+          message: "删除失败",
+          center: true,
+        });
+      }
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "取消删除",
+      });
+    });
+};
+
+// 重置表单
+const resetForm = (formEl) => {
+  formEl.resetFields();
+  console.log("重置表单");
+  ruleForm.phone = "";
+  ruleForm.date1 = "";
+  ruleForm.resource = "";
+  // ruleForm.password=,
+  ruleForm.region = "";
+  ruleForm.name = "";
+  ruleForm.date2 = "";
+  // if (!formEl) return;
+
+  // formEl.resetFields();
+};
+
+//新增按钮
+const addlist = () => {
+  dialongTitle.value = "新增用户";
+  ruleForm.phone = "";
+  ruleForm.date1 = "";
+  ruleForm.resource = "";
+  // ruleForm.password=,
+  ruleForm.region = "";
+  ruleForm.name = "";
+  ruleForm.date2 = "";
+  centerDialogVisible.value = true;
+};
+//修改按钮
+const handleUp = (row) => {
+  console.log(row);
+  ruleForm.id = row.id;
+  ruleForm.phone = row.phone;
+  ruleForm.date1 = row.username;
+  ruleForm.name = row.name;
+  ruleForm.date2 = row.password;
+  ruleForm.region = row.sex;
+  ruleForm.resource = row.adminType;
+  dialongTitle.value = "修改用户";
+  centerDialogVisible.value = true;
+};
+
+// 关闭新增/修改页面
+const editClose = (done) => {
+  ruleFormRef.value.resetFields();
+  ruleForm.phone = "";
+  ruleForm.date1 = "";
+  ruleForm.resource = "";
+  // ruleForm.password=,
+  ruleForm.region = "";
+  ruleForm.name = "";
+  ruleForm.date2 = "";
+  centerDialogVisible.value = false;
+  done();
+};
+// 确定表单
+const submitForm = async (formEl) => {
+  if (!formEl) return;
+  await formEl.validate((valid, fields) => {
+    if (valid) {
+      console.log("submit!");
+      if (dialongTitle.value == "新增用户") {
+        let publicKey =
+          "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMOcPB06u5yKyQsPjfVWiWgbEIrd14kiXNNihciaVKb6HnkQvq7zpQuZ80WEX94spnUMI3iOAl/GmIvHrpGwcbB4hJbznm+PajiwnUSPuCCXA68YJF640cJKb/8KeM7WVz69OFkIEPHhVxOy4FFF5QWe/kt6zOZ19HmE+ak+5x/QIDAQAB";
+        let encryptor = new JSEncrypt(); // 新建JSEncrypt对象
+        encryptor.setPublicKey(publicKey); // 设置公钥
+        let rsaPassWord = encryptor.encrypt(ruleForm.date2); // 对密码进行加密
+        let data = {
+          phone: ruleForm.phone,
+          username: ruleForm.date1,
+          adminType: ruleForm.resource,
+          password: rsaPassWord,
+          sex: ruleForm.region,
+          name: ruleForm.name,
+          createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+        };
+        if (data.sex == "女") {
+          data.sex = 0;
+        } else if (data.sex == "男") {
+          data.sex = 1;
+        }
+        if (data.adminType == "超级管理员") {
+          data.adminType = 1;
+        } else if (data.adminType == "管理员") {
+          data.adminType = 2;
+        }
+        adminApi
+          .adminAdd(data)
+          .then((res) => {
+            console.log(tableData.list.length);
+            if (res.success) {
+              if (tableData.list.length == 13) {
+                currentPage.value = currentPage.value + 1;
+              }
+              ElMessage({
+                type: "success",
+                showClose: true,
+                message: res.message,
+                center: true,
+              });
+              getList(currentPage.value);
+              ruleFormRef.value.resetFields();
+              centerDialogVisible.value = false;
+            } else {
+              ElMessage({
+                type: "warning",
+                showClose: true,
+                message: res.message,
+                center: true,
+              });
+            }
+          })
+          .catch((err) => {
+            console.log(err);
+            ElMessage({
+              type: "error",
+              showClose: true,
+              message: "添加失败",
+              center: true,
+            });
+          });
+      } else {
+        // let publicKey =
+        //   "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDMOcPB06u5yKyQsPjfVWiWgbEIrd14kiXNNihciaVKb6HnkQvq7zpQuZ80WEX94spnUMI3iOAl/GmIvHrpGwcbB4hJbznm+PajiwnUSPuCCXA68YJF640cJKb/8KeM7WVz69OFkIEPHhVxOy4FFF5QWe/kt6zOZ19HmE+ak+5x/QIDAQAB";
+        // let encryptor = new JSEncrypt(); // 新建JSEncrypt对象
+        // encryptor.setPublicKey(publicKey); // 设置公钥
+        // let rsaPassWord = encryptor.encrypt(ruleForm.date2); // 对密码进行加密
+
+        let data = {
+          phone: ruleForm.phone,
+          username: ruleForm.date1,
+          adminType: ruleForm.resource,
+          sex: ruleForm.region,
+          name: ruleForm.name,
+          id: ruleForm.id,
+          createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+        };
+        if (data.sex == "女") {
+          data.sex = 0;
+        } else if (data.sex == "男") {
+          data.sex = 1;
+        }
+        if (data.adminType == "超级管理员") {
+          data.adminType = 1;
+        } else if (data.adminType == "管理员") {
+          data.adminType = 2;
+        }
+        adminApi
+          .adminUpdate(data)
+          .then((res) => {
+            console.log(res);
+            if (res.success) {
+              ElMessage({
+                type: "success",
+                showClose: true,
+                message: res.message,
+                center: true,
+              });
+              getList(currentPage.value);
+              ruleFormRef.value.resetFields();
+              centerDialogVisible.value = false;
+            } else {
+              ElMessage({
+                type: "warning",
+                showClose: true,
+                message: res.message,
+                center: true,
+              });
+            }
+          })
+          .catch((err) => {
+            console.log(err);
+            ElMessage({
+              type: "error",
+              showClose: true,
+              message: res.message,
+              center: true,
+            });
+          });
+      }
+    } else {
+      console.log("error submit!", fields);
+    }
+  });
+};
+// 删除按钮
+const handleDel = (row) => {
+  console.log("click");
+  ElMessageBox.confirm("此操作将永久删除所选中的IP,是否继续?", "提示", {
+    confirmButtonText: "确认",
+    cancelButtonText: "取消",
+    type: "warning",
+  })
+    .then(() => {
+      let idIP = [];
+      selectData.list.forEach((item) => {
+        idIP.push(item.id);
+      });
+      console.log(idIP);
+      adminApi
+        .adminDel([row.id])
+        .then((res) => {
+          console.log(tableData.list.length);
+          if (tableData.list.length == 1 && currentPage.value != 1) {
+            currentPage.value = currentPage.value - 1;
+          }
+          ElMessage({
+            type: "success",
+            showClose: true,
+            message: "删除成功",
+            center: true,
+          });
+          getList(currentPage.value);
+        })
+        .catch((err) => {
+          console.log(err);
+          ElMessage({
+            type: "warning",
+            showClose: true,
+            message: "删除失败",
+            center: true,
+          });
+        });
+    })
+    .catch(() => {
+      ElMessage({
+        type: "info",
+        message: "取消删除",
+      });
+    });
+};
+
+// 导出按钮
+const downLoad = lodash.debounce(() => {
+  let startTime = searchData.input5[0];
+  let endTime = searchData.input5[1];
+  let data = {
+    name: searchData.input1,
+    username: searchData.input2,
+    phone: searchData.input3,
+    adminType: searchData.input4,
+    startTime,
+    endTime,
+  };
+  adminApi
+    .download(data)
+    .then((res) => {
+      let name = "用户管理列表";
+      var content = res;
+      // var data = new Blob([content], { type: "application/octet-stream;charset=utf-8" });
+      var data = new Blob([content], { type: "application/vnd.ms-excel;charset=utf-8" });
+      var downloadUrl = window.URL.createObjectURL(data);
+      var anchor = document.createElement("a");
+      anchor.href = downloadUrl;
+      anchor.download = name + ".xlsx";
+      anchor.click();
+      window.URL.revokeObjectURL(data);
+      console.log(res);
+      ElMessage({
+        type: "success",
+        showClose: true,
+        message: "导出成功",
+        center: true,
+      });
+    })
+    .catch((err) => {
+      console.log(err);
+      ElMessage({
+        type: "error",
+        showClose: true,
+        message: "导出失败",
+        center: true,
+      });
+    });
+}, 300);
+// 导入按钮
+const importExcel = () => {
+  importByExcelDialog.value = true;
+};
+
+// 上传文件
+const changeUpload = (file, fileList) => {
+  // fileList.value = [];
+  if (file) {
+    fileList.value = file;
+  }
+  active.value = 2;
+};
+// 移出文件前的回调
+const beforeRemove = (file) => {
+  console.log(file);
+  active.value = 1;
+};
+
+// 步骤条功能
+const next = async () => {
+  console.log(fileList.value);
+  let formData = new FormData();
+  formData.append("file", fileList.value[0].raw);
+  let res = await axios({
+    method: "post",
+    url: "/video/transcoding/videoAdmin/importByExcel",
+    data: formData,
+    headers: {
+      admin_token: sessionStorage.getItem("admin_token"),
+    },
+  });
+  // console.log(res);
+  if (res.data.success) {
+    setTimeout(() => {
+      importByExcelDialog.value = false;
+      fileList.value = "";
+      active.value = 1;
+    }, 1000);
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: "导入成功",
+      center: true,
+    });
+    active.value = 3;
+    currentPage.value = 1;
+    getList(1);
+  } else {
+    ElMessage({
+      type: "warning",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+
+// 表格斑马纹颜色修改
+const tableRowClassName = ({ row, rowIndex }) => {
+  if (rowIndex % 2 === 0) {
+    return "even";
+  } else if (rowIndex % 2 !== 0) {
+    return "odd";
+  }
+  return "";
+};
+// 分页
+const handleCurrentChange = (value) => {
+  currentPage.value = value;
+  getList(value);
+};
+onBeforeMount(() => {
+  getList(1);
+});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 90%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+    border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 562px;
+    margin: 20px auto 30px;
+    .el-table--fit {
+      height: 100%;
+
+      :deep(.el-table__row) {
+        height: 40px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+      }
+      // 输出链接弹窗样式
+      :deep(.link .el-dialog__header) {
+        text-align: left;
+        font-size: 20px;
+        font-weight: 600;
+      }
+      :deep(.link .el-dialog__body) {
+        padding: 10px 20px;
+        height: 100px;
+        margin-bottom: 10px;
+      }
+      .link {
+        .linkcontent {
+          text-align: left;
+          height: 100px;
+          border: 1px solid #ccc;
+          p {
+            margin: 5px 10px;
+          }
+        }
+      }
+
+      :deep(.playVideo .el-dialog__header) {
+        text-align: left;
+        font-size: 20px;
+        font-weight: 600;
+        background-color: #ccc;
+        margin-right: 0;
+      }
+      :deep(.playVideo .el-dialog__body) {
+        height: 600px;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: #f5f8fc;
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+
+    :deep(.el-pager li) {
+      margin: 0 3px;
+    }
+    :deep(.btn-prev) {
+      margin-right: 3px;
+    }
+    :deep(.btn-next) {
+      margin-left: 3px;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 1629 - 0
src/views/vidicons/vidicon/index.vue


+ 302 - 0
src/views/waiting/waiting.vue

@@ -0,0 +1,302 @@
+<template>
+  <div class="content-box">
+    <div class="left">
+      <!-- <el-icon :size="23" class="camera"><VideoCameraFilled /></el-icon> -->
+      <span class="cameratxt">候补设置</span>
+    </div>
+    <div class="middle">
+      <div class="filter">
+        <div class="condition">
+          <span>候补截止时间&nbsp;</span>
+
+          <el-time-picker
+            v-model="valueTime"
+            placeholder="请选择时间"
+            :disabled="valueSwich2"
+            format="HH:mm"
+            value-format="HH:mm"
+            @change="timeUpdata"
+          />
+
+          <span>
+            &nbsp;
+            &nbsp;后作为通行条件,上车时提示已通行,其他时间段提示未预约</span
+          >
+          <el-switch
+            v-model="valueSwich"
+            class="ml-2"
+            style="
+              --el-switch-on-color: rgba(61, 81, 232, 1);
+              --el-switch-off-color: #ccc;
+              margin-left: 10px;
+            "
+            @change="first"
+          />
+        </div>
+        <div class="condition">
+          <span>当天最晚发车时间段&nbsp;</span>
+
+          <el-time-picker
+            v-model="valueTime2"
+            placeholder="请选择时间"
+            :disabled="valueSwich"
+            format="HH:mm"
+            value-format="HH:mm"
+            @change="timeUpdata"
+          />
+          <span>
+            &nbsp;
+            &nbsp;后作为通行条件,上车时提示已通行,其他时间段提示未预约</span
+          >
+          <el-switch
+            v-model="valueSwich2"
+            class="ml-2"
+            style="
+              --el-switch-on-color: rgba(61, 81, 232, 1);
+              --el-switch-off-color: #ccc;
+              margin-left: 10px;
+            "
+            @change="second"
+          />
+        </div>
+      </div>
+    </div>
+    <div class="footer"></div>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, nextTick, onBeforeMount, onUnmounted } from "vue";
+import { useRouter } from "vue-router";
+import { ElMessage, ElMessageBox } from "element-plus";
+import vidiconsApi from "@/api/vidicons.js";
+import { dayjs } from "element-plus";
+import lodash from "lodash";
+import axios from "axios";
+const router = useRouter();
+// 表格数据
+const valueTime = ref(); // 截止时间参数
+const valueTime2 = ref(); //最晚发车时间
+const valueSwich = ref(true); // 滑块开关按钮
+const valueSwich2 = ref(false); // 滑块开关按钮
+const id = ref();
+const state = ref(1); //启用
+const getlist = async (message) => {
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/cnqueryHb.action",
+    headers: {
+      token: sessionStorage.getItem("token"),
+    },
+  });
+  console.log(res);
+  if (res.data.code == 200) {
+    valueTime.value = res.data.data.start_time;
+    valueTime2.value = res.data.data.end_time;
+    id.value = res.data.data.id;
+    ElMessage({
+      type: "success",
+      showClose: true,
+      message: message || res.data.message,
+      center: true,
+    });
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+const first = (val) => {
+  if (val) {
+    valueSwich2.value = false;
+  } else {
+    valueSwich2.value = true;
+  }
+};
+const second = (val) => {
+  if (val) {
+    valueSwich.value = false;
+  } else {
+    valueSwich.value = true;
+  }
+};
+
+const timeUpdata = async (val) => {
+  console.log(val);
+  let data = {
+    state: state.value,
+    start_time: valueTime.value,
+    end_time: valueTime2.value,
+    id: id.value,
+  };
+  // let res = await admin.adminAdd(data);
+  let res = await axios({
+    method: "post",
+    url: "/api/carBook/cnupdate.action",
+    headers: {
+      "Content-Type": "application/json;charset=utf-8",
+      token: sessionStorage.getItem("token"),
+    },
+    data: data,
+  });
+  console.log(res, "修改候补");
+  if (res.data.code == 200) {
+    getlist(res.data.message);
+  } else {
+    ElMessage({
+      type: "error",
+      showClose: true,
+      message: res.data.message,
+      center: true,
+    });
+  }
+};
+
+onBeforeMount(() => {
+  getlist();
+});
+onUnmounted(() => {});
+</script>
+
+<style scoped lang="scss">
+.content-box {
+  width: 97.5%;
+  height: 89%;
+  margin: 20px auto;
+  background-color: #fff;
+  color: #fff;
+  display: flex;
+  flex-direction: column;
+
+  .left {
+    // width: calc(100wh - 40px);
+    display: flex;
+    align-items: center;
+    height: 60px;
+    margin: 0 30px;
+    border-bottom: 1px solid #ccc;
+    color: #000;
+    font-size: 18px;
+    font-weight: 600;
+    .camera {
+      margin-right: 15px;
+      color: #4392f7;
+    }
+  }
+  .middle {
+    width: 96%;
+    margin: 0 auto;
+
+    color: #000;
+    // border-bottom: 1px solid rgb(231, 231, 231);
+    .filter {
+      display: flex;
+      flex-wrap: wrap;
+      align-items: center;
+      margin: 10px 0 0 0;
+      .condition {
+        display: flex;
+        align-items: center;
+        margin: 10px 30px 10px 0;
+        span {
+          margin: 0 10px 0 0;
+        }
+      }
+    }
+    .gongneng {
+      margin: 10px 0;
+    }
+    :deep(.cont) {
+      width: 60%;
+      margin: 20px auto;
+    }
+    :deep(.download) {
+      display: flex;
+      align-items: center;
+      margin: 10px;
+    }
+    :deep(.download span) {
+      font-size: 16px;
+      margin-left: 20px;
+    }
+    :deep(.cont .el-button) {
+      margin-left: 60px;
+      margin-bottom: 30px;
+    }
+
+    :deep(.cont .accomplish) {
+      width: 100%;
+      display: flex;
+      justify-content: center;
+    }
+    :deep(.cont .accomplish .el-button) {
+      width: 50%;
+      margin: 0;
+    }
+  }
+  .footer {
+    width: 96%;
+    height: 550px;
+    margin: 10px auto 30px;
+    .el-table--fit {
+      height: 100%;
+      :deep(.el-table__header-wrapper) {
+        background-color: #000;
+      }
+      :deep(.el-table__row) {
+        height: 50px;
+      }
+      :deep(.el-table__row td) {
+        padding: 0;
+        border: 0;
+      }
+
+      .el-button--primary {
+        margin-left: 5px;
+      }
+      :deep(.el-table__body .even) {
+        background-color: #fff;
+      }
+      :deep(.el-table__body .odd) {
+        background-color: #f7fafe;
+      }
+      :deep(.look) {
+        padding: 5px 14px;
+        border-radius: 45px;
+        border: 0.74px solid rgba(30, 125, 251, 1);
+      }
+    }
+  }
+  .el-pagination {
+    // width: 1600px;
+    width: 96%;
+    margin: 0 auto 18px;
+    justify-content: flex-end;
+    :deep(.el-pagination__total) {
+      color: #000;
+    }
+    :deep(.el-pagination__goto) {
+      color: #000;
+    }
+    :deep(.el-pagination__classifier) {
+      color: #000;
+    }
+
+    :deep(.el-pager li) {
+      margin: 0 3px;
+    }
+    :deep(.btn-prev) {
+      margin-right: 3px;
+    }
+    :deep(.btn-next) {
+      margin-left: 3px;
+    }
+  }
+}
+.el-input {
+  width: 192px;
+}
+</style>

+ 22 - 0
text.json

@@ -0,0 +1,22 @@
+MD5( "${HA1}:${nonce}:${nc}:${cno
+nce}:${qop}:${HA2}" ) =
+6629fae49393a05397450978507c4
+ef1
+
+
+HA1 =
+MD5( "${username}:${realm}:${pas
+sword}" ) =
+939e7578ed9e3c518a452acee763
+bce9
+HA2 = MD5( "POST:${uri}" ) =
+39aff3a2bab6126f332b942af96d33
+66
+
+username="admin",realm="127.0.0.1host .com",nonce="dcd98b7102dd2f0e8b11d0f600bfh0c093"
+,uri="/dir/index,html",gop="auth",nc=00000001, cnonce="0a4f113b",response="f8877dald712423e963da0aed9fb7cf8",opague="5ccc069c403ebaf9f0171e9517f40e41"
+
+
+qop="auth",opaque="5ccc069c403ebaf9f0171e9517f40e41",realm="127.0.0.1@host.com",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"
+
+username="admin",realm="127.0.0.1@host.com",nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093",uri="/dir/index,html",qop="auth",nc=00000001,cnonce="0a4f113b",response="f8877dald712423e963da0aed9fb7cf8",opague="5ccc069c403ebaf9f0171e9517f40e41"

+ 81 - 0
vite.config.js

@@ -0,0 +1,81 @@
+import { defineConfig } from "vite";
+import vue from "@vitejs/plugin-vue";
+import path from "path";
+import { resolve, join } from 'path'
+// https://vitejs.dev/config/
+export default defineConfig({
+  plugins: [vue()],
+  resolve: {
+    alias: {
+      "@": path.resolve("./src"), // @代替src
+    },
+  },
+  css: {
+    preprocessorOptions: {
+      scss: {
+        additionalData: `@import "./src/style/mixin.scss";`, // 此处全局的scss文件
+      },
+    },
+  },
+  server: {
+    host: "0.0.0.0",
+    port: 8991,
+    // 是否开启 https
+    // https: false,
+    // 线上
+    proxy: {
+      "/api": {
+        target: "https://chtech.ncjti.edu.cn/carstop/carbook",
+        changeOrigin: true,
+        rewrite: (path) => path.replace(/^\/api/, ""),
+      },
+    },
+    // 线下
+    // proxy: {
+    //   "/video/transcoding": {
+    //     target: "http://192.168.161.34:8082", //实际请求地址
+    //     changeOrigin: true,
+    //     rewrite: (path) => path.replace(/^\/video\/transcoding/, ""),
+    //   },
+    // },
+  },
+  publicDir: "public",
+  base: "./",
+  // 打包配置
+  build: {
+    target: 'modules', //设置最终构建的浏览器兼容目标  //es2015(编译成es5) | modules
+    outDir: 'dist', // 构建得包名  默认:dist
+    assetsDir: 'static', // 静态资源得存放路径文件名  assets
+    // sourcemap: false, //构建后是否生成 source map 文件
+    // brotliSize: false, // 启用/禁用 brotli 压缩大小报告。 禁用该功能可能会提高大型项目的构建性能
+    chunkSizeWarningLimit: 1000, //chunk 大小警告的限制(以 kbs 为单位)默认:500
+    // cssTarget: 'chrome61' //防止 vite 将 rgba() 颜色转化为 #RGBA 十六进制符号的形式  (要兼容的场景是安卓微信中的 webview 时,它不支持 CSS 中的 #RGBA 十六进制颜色符号)
+    rollupOptions: {
+      input: {
+        index: resolve(__dirname, "index.html"),
+      },
+      output: {
+        manualChunks(id) {
+          if (id.includes("node_modules")) {
+            return id
+              .toString()
+              .split("node_modules/")[1]
+              .split("/")[0]
+              .toString();
+          }
+        },
+        chunkFileNames: 'static/js/[name]-[hash].js',
+        entryFileNames: "static/js/[name]-[hash].js",
+        assetFileNames: "static/[ext]/name-[hash].[ext]"
+      }
+    },
+    minify: 'terser', // 项目压缩 :boolean | 'terser' | 'esbuild'
+    terserOptions: {
+      // 生产环境移除console
+      compress: {
+        drop_console: true,
+        drop_debugger: true,
+      },
+    },
+  },
+});