MokiBox 1 jaar geleden
commit
fcf0a7d114
13 gewijzigde bestanden met toevoegingen van 1875 en 0 verwijderingen
  1. 24 0
      .gitignore
  2. 7 0
      README.md
  3. 13 0
      index.html
  4. 1133 0
      package-lock.json
  5. 27 0
      package.json
  6. 0 0
      public/icon.svg
  7. 12 0
      src/App.vue
  8. 253 0
      src/components/Index.vue
  9. 304 0
      src/components/Login.vue
  10. 18 0
      src/main.js
  11. 50 0
      src/route/index.js
  12. 16 0
      src/style.css
  13. 18 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?

+ 7 - 0
README.md

@@ -0,0 +1,7 @@
+# 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).

+ 13 - 0
index.html

@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8" />
+    <link rel="icon" type="image/svg+xml" href="/icon.svg" />
+    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+    <title>FQ监控平台</title>
+  </head>
+  <body>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
+  </body>
+</html>

+ 1133 - 0
package-lock.json

@@ -0,0 +1,1133 @@
+{
+  "name": "fq",
+  "version": "0.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "fq",
+      "version": "0.0.0",
+      "dependencies": {
+        "@element-plus/icons-vue": "^2.1.0",
+        "axios": "^1.3.4",
+        "clipboard": "^2.0.11",
+        "element-plus": "^2.3.4",
+        "js-md5": "^0.7.3",
+        "vue": "^3.2.47",
+        "vue-axios": "^3.5.2",
+        "vue-router": "^4.1.6",
+        "vue-verify-code": "^1.0.5"
+      },
+      "devDependencies": {
+        "@types/js-md5": "^0.7.0",
+        "@vitejs/plugin-vue": "^4.1.0",
+        "vite": "^4.2.0"
+      }
+    },
+    "node_modules/@babel/parser": {
+      "version": "7.21.3",
+      "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.3.tgz",
+      "integrity": "sha512-lobG0d7aOfQRXh8AyklEAgZGvA4FShxo6xQbUrrT/cNBPUdIDojlokwJsQyCC/eKia7ifqM0yP+2DRZ4WKw2RQ==",
+      "bin": {
+        "parser": "bin/babel-parser.js"
+      },
+      "engines": {
+        "node": ">=6.0.0"
+      }
+    },
+    "node_modules/@ctrl/tinycolor": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.0.tgz",
+      "integrity": "sha512-/Z3l6pXthq0JvMYdUFyX9j0MaCltlIn6mfh9jLyQwg5aPKxkyNa0PTHtU1AlFXLNk55ZuAeJRcpvq+tmLfKmaQ==",
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@element-plus/icons-vue": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.1.0.tgz",
+      "integrity": "sha512-PSBn3elNoanENc1vnCfh+3WA9fimRC7n+fWkf3rE5jvv+aBohNHABC/KAR5KWPecxWxDTVT1ERpRbOMRcOV/vA==",
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/@esbuild/android-arm": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.13.tgz",
+      "integrity": "sha512-5tZZ/hLIfBmt7E8JsE5KbsknoAFmoElkg+A/gjyPtmSQvJjPf+9GsSJihid8VMa08lrsYyaEXOT9RLh3xXQONw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-arm64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.13.tgz",
+      "integrity": "sha512-F5DgvJMV2ZEpLNpPCO7FEk1wy8O5tg6cikWSB6uvvncsgE1xgbPlm+Boio/4820C2/mj713X83X1h01v0qoeHg==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/android-x64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.13.tgz",
+      "integrity": "sha512-5m1UUslzpfVrumG3m3Zv2x9VNAcvMOQWJy009y6jt10tcHpzIq2/b0I0k4fz0QYqGSNS1GteRIhVPN4H7OyCXg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "android"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-arm64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.13.tgz",
+      "integrity": "sha512-TXbXp/05r7heRsG8yWwbHw9diay+wXIyRNcIHFoNARRIGahYbTW/qwJzE37zkfxLIUPHgR/SyLTUlnTICg14ag==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/darwin-x64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.13.tgz",
+      "integrity": "sha512-Ku9Db2sblCxFvQdEO7X9nBaLR/S81uch81e2Q2+Os5z1NcnsFjuqhIYH0Gm6KNNpIKaEbC7gCLbiIPbLLMX4Pg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-arm64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.13.tgz",
+      "integrity": "sha512-t1T5/nIf2j+FdSf1Fa3dcU0cXycr0nK4xJe52qjWa+1I249mM5NBY1ODjiabZxZ0x3CG05y4fd9bxfDLy9kQtA==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/freebsd-x64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.13.tgz",
+      "integrity": "sha512-/zbkgEO4gY2qGZr9UNAGI38w/FwUY4bx4EC88k9VeiCKNr3ukNgwH/oIgB5Z9/OqpkNLlcS4w9e2d/MIiy5fbw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "freebsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.13.tgz",
+      "integrity": "sha512-RrhjzrCF6aCDH248nUAQoldnRmN7nHMxv85GOj5AH+qkxxYvcig7fnUmgANngntRu4btXhN9WKHMgQ5seERDMw==",
+      "cpu": [
+        "arm"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-arm64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.13.tgz",
+      "integrity": "sha512-siu3QZrQ7eGrSttvFaRKyjT7kNRbUuHEKzCCyqRh19MbpGokGY13jbIsBEjx6JmH3T50hds325oweS9Ey2ihAQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ia32": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.13.tgz",
+      "integrity": "sha512-ADHA1PqP5gIegehVP0RvxMmNPxpLgetI8QCwYOjUheGXKIKWSdUN8ZS3rusQv3NGZmFCpYdMZzFoI0QtzzGAdw==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-loong64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.13.tgz",
+      "integrity": "sha512-n1JQPxETmR0brkpWlJHeohReEPLH+m00bnJdNnFyHN3zLBt1QypevuZSmnmFWsC+7r7HTwWILj3lBDjtPH3ydg==",
+      "cpu": [
+        "loong64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-mips64el": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.13.tgz",
+      "integrity": "sha512-d0pnD/j5KKQ43xtSIvOD+wNIy6D/Vh9GbXVRa3u4zCyiJMYWjxkPkbBzlEgNjdDmUM+5gBFen9k7B8Xscy+Myg==",
+      "cpu": [
+        "mips64el"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-ppc64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.13.tgz",
+      "integrity": "sha512-C9sMpa/VcGLjVtsT01sXtzZNS7bAZ+icUclkKkiUwBQ9hzT+J+/Xpj+EykI5hB3KgtxQVo4XUahanFoZNxbQ1g==",
+      "cpu": [
+        "ppc64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-riscv64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.13.tgz",
+      "integrity": "sha512-jYkc5EpNpvjccAHNYekiAtklusVGWftR0VVLtng7dJzDyy+5adAsf1fOG3LllP0WALxS55/w6boLE/728J/bXw==",
+      "cpu": [
+        "riscv64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-s390x": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.13.tgz",
+      "integrity": "sha512-4jAJI5O6E/hATL4lsrG2A+noDjZ377KlATVFKwV3SWaNHj+OvoXe/T84ScQIXEtPI7ndJyLkMYruXj8RR5Ilyw==",
+      "cpu": [
+        "s390x"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/linux-x64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.13.tgz",
+      "integrity": "sha512-eFLQhJq98qijGRcv9je/9M4Mz1suZ+pOtj62ArsLd0gubNGhhQDz6T30X2X3f1KZ8lkKkr+zN5vtZzx1GAMoFw==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "linux"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/netbsd-x64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.13.tgz",
+      "integrity": "sha512-F8PXDeT+3eQpPjf4bmNJapPLu0SKKlWRGPQvBQqVS+YDGoMKnyyYp2UENLFMV8zT7kS39zKxZRZvUL3fMz/7Ww==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "netbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/openbsd-x64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.13.tgz",
+      "integrity": "sha512-9jWfzbFCnIZdHjNs+00KQHArUbp7kjQDNmiuqkwGOQFs67m4/dKNupBv2DP5hTqVlQY4tW4RG3qpb6Y3zOHJeA==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "openbsd"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/sunos-x64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.13.tgz",
+      "integrity": "sha512-ALbOMlTIBkAVi6KqYjONa7u2oH95RN7OpetFqMtjufFLBiSaayRuwUzhs2yuR9CfGT4qi0jv6HQDav+EG314TQ==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "sunos"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-arm64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.13.tgz",
+      "integrity": "sha512-FJBLYL4PkrZGeuHzEqme+0DjNetxkJ+XbB+Aoeow7aQ53JCwsA0/mo8sS5aPkDHgCnMkN4A5GLoFTlDj3BKDrQ==",
+      "cpu": [
+        "arm64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-ia32": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.13.tgz",
+      "integrity": "sha512-Qrvst9RkLz4qgi3hqswNliYuKW92/HGJnd7xLWkGaGPa8S4qsONf81FW0ebDc5iUHb0I7QJwQATutvghTabnFA==",
+      "cpu": [
+        "ia32"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@esbuild/win32-x64": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.13.tgz",
+      "integrity": "sha512-pZ/NIgz861XaUPlIkPFjP55nJ4PJa0o/CD4zgeRb1Q9FVE+8GvdB6ifJcK05jRhny5hKExhnRFIdgHmmCYH8vg==",
+      "cpu": [
+        "x64"
+      ],
+      "dev": true,
+      "optional": true,
+      "os": [
+        "win32"
+      ],
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@floating-ui/core": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.2.6.tgz",
+      "integrity": "sha512-EvYTiXet5XqweYGClEmpu3BoxmsQ4hkj3QaYA6qEnigCWffTP3vNRwBReTdrwDwo7OoJ3wM8Uoe9Uk4n+d4hfg=="
+    },
+    "node_modules/@floating-ui/dom": {
+      "version": "1.2.7",
+      "resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.2.7.tgz",
+      "integrity": "sha512-DyqylONj1ZaBnzj+uBnVfzdjjCkFCL2aA9ESHLyUOGSqb03RpbLMImP1ekIQXYs4KLk9jAjJfZAU8hXfWSahEg==",
+      "dependencies": {
+        "@floating-ui/core": "^1.2.6"
+      }
+    },
+    "node_modules/@popperjs/core": {
+      "name": "@sxzz/popperjs-es",
+      "version": "2.11.7",
+      "resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz",
+      "integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ=="
+    },
+    "node_modules/@types/js-md5": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmmirror.com/@types/js-md5/-/js-md5-0.7.0.tgz",
+      "integrity": "sha512-4mN02EhCni6MlvCUl9bpcbMfu7R3G+ac+J37b7gKCu3tWhZc5/Ya41T+2QNR2aFt6vnMe+uTa5rFQ+nT2kl6cA==",
+      "dev": true
+    },
+    "node_modules/@types/lodash": {
+      "version": "4.14.194",
+      "resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.14.194.tgz",
+      "integrity": "sha512-r22s9tAS7imvBt2lyHC9B8AGwWnXaYb1tY09oyLkXDs4vArpYJzw09nj8MLx5VfciBPGIb+ZwG0ssYnEPJxn/g=="
+    },
+    "node_modules/@types/lodash-es": {
+      "version": "4.17.7",
+      "resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.7.tgz",
+      "integrity": "sha512-z0ptr6UI10VlU6l5MYhGwS4mC8DZyYer2mCoyysZtSF7p26zOX8UpbrV0YpNYLGS8K4PUFIyEr62IMFFjveSiQ==",
+      "dependencies": {
+        "@types/lodash": "*"
+      }
+    },
+    "node_modules/@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=="
+    },
+    "node_modules/@vitejs/plugin-vue": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-4.1.0.tgz",
+      "integrity": "sha512-++9JOAFdcXI3lyer9UKUV4rfoQ3T1RN8yDqoCLar86s0xQct5yblxAE+yWgRnU5/0FOlVCpTZpYSBV/bGWrSrQ==",
+      "dev": true,
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "vite": "^4.0.0",
+        "vue": "^3.2.25"
+      }
+    },
+    "node_modules/@vue/compiler-core": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.2.47.tgz",
+      "integrity": "sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==",
+      "dependencies": {
+        "@babel/parser": "^7.16.4",
+        "@vue/shared": "3.2.47",
+        "estree-walker": "^2.0.2",
+        "source-map": "^0.6.1"
+      }
+    },
+    "node_modules/@vue/compiler-dom": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.2.47.tgz",
+      "integrity": "sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==",
+      "dependencies": {
+        "@vue/compiler-core": "3.2.47",
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "node_modules/@vue/compiler-sfc": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.2.47.tgz",
+      "integrity": "sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==",
+      "dependencies": {
+        "@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"
+      }
+    },
+    "node_modules/@vue/compiler-ssr": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.2.47.tgz",
+      "integrity": "sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==",
+      "dependencies": {
+        "@vue/compiler-dom": "3.2.47",
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "node_modules/@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=="
+    },
+    "node_modules/@vue/reactivity": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.2.47.tgz",
+      "integrity": "sha512-7khqQ/75oyyg+N/e+iwV6lpy1f5wq759NdlS1fpAhFXa8VeAIKGgk2E/C4VF59lx5b+Ezs5fpp/5WsRYXQiKxQ==",
+      "dependencies": {
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "node_modules/@vue/reactivity-transform": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/reactivity-transform/-/reactivity-transform-3.2.47.tgz",
+      "integrity": "sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==",
+      "dependencies": {
+        "@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"
+      }
+    },
+    "node_modules/@vue/runtime-core": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.2.47.tgz",
+      "integrity": "sha512-RZxbLQIRB/K0ev0K9FXhNbBzT32H9iRtYbaXb0ZIz2usLms/D55dJR2t6cIEUn6vyhS3ALNvNthI+Q95C+NOpA==",
+      "dependencies": {
+        "@vue/reactivity": "3.2.47",
+        "@vue/shared": "3.2.47"
+      }
+    },
+    "node_modules/@vue/runtime-dom": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.2.47.tgz",
+      "integrity": "sha512-ArXrFTjS6TsDei4qwNvgrdmHtD930KgSKGhS5M+j8QxXrDJYLqYw4RRcDy1bz1m1wMmb6j+zGLifdVHtkXA7gA==",
+      "dependencies": {
+        "@vue/runtime-core": "3.2.47",
+        "@vue/shared": "3.2.47",
+        "csstype": "^2.6.8"
+      }
+    },
+    "node_modules/@vue/server-renderer": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.2.47.tgz",
+      "integrity": "sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==",
+      "dependencies": {
+        "@vue/compiler-ssr": "3.2.47",
+        "@vue/shared": "3.2.47"
+      },
+      "peerDependencies": {
+        "vue": "3.2.47"
+      }
+    },
+    "node_modules/@vue/shared": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.2.47.tgz",
+      "integrity": "sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ=="
+    },
+    "node_modules/@vueuse/core": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz",
+      "integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==",
+      "dependencies": {
+        "@types/web-bluetooth": "^0.0.16",
+        "@vueuse/metadata": "9.13.0",
+        "@vueuse/shared": "9.13.0",
+        "vue-demi": "*"
+      }
+    },
+    "node_modules/@vueuse/core/node_modules/vue-demi": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.0.tgz",
+      "integrity": "sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@vueuse/metadata": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz",
+      "integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ=="
+    },
+    "node_modules/@vueuse/shared": {
+      "version": "9.13.0",
+      "resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz",
+      "integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==",
+      "dependencies": {
+        "vue-demi": "*"
+      }
+    },
+    "node_modules/@vueuse/shared/node_modules/vue-demi": {
+      "version": "0.14.0",
+      "resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.0.tgz",
+      "integrity": "sha512-gt58r2ogsNQeVoQ3EhoUAvUsH9xviydl0dWJj7dabBC/2L4uBId7ujtCwDRD0JhkGsV1i0CtfLAeyYKBht9oWg==",
+      "hasInstallScript": true,
+      "bin": {
+        "vue-demi-fix": "bin/vue-demi-fix.js",
+        "vue-demi-switch": "bin/vue-demi-switch.js"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "peerDependencies": {
+        "@vue/composition-api": "^1.0.0-rc.1",
+        "vue": "^3.0.0-0 || ^2.6.0"
+      },
+      "peerDependenciesMeta": {
+        "@vue/composition-api": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/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=="
+    },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
+    },
+    "node_modules/axios": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/axios/-/axios-1.3.4.tgz",
+      "integrity": "sha512-toYm+Bsyl6VC5wSkfkbbNB6ROv7KY93PEBBL6xyDczaIHasAiv4wPqQ/c4RjoQzipxRD2W5g21cOqQulZ7rHwQ==",
+      "dependencies": {
+        "follow-redirects": "^1.15.0",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
+      }
+    },
+    "node_modules/clipboard": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+      "dependencies": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
+    "node_modules/csstype": {
+      "version": "2.6.21",
+      "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz",
+      "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w=="
+    },
+    "node_modules/dayjs": {
+      "version": "1.11.7",
+      "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.7.tgz",
+      "integrity": "sha512-+Yw9U6YO5TQohxLcIkrXBeY73WP3ejHWVvx8XCk3gxvQDCTEmS48ZrSZCKciI7Bhl/uCMyxYtE9UqRILmFphkQ=="
+    },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
+    "node_modules/delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw=="
+    },
+    "node_modules/element-plus": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.3.4.tgz",
+      "integrity": "sha512-SQr0J9z7N4z48WYk/l9NE2tizl8Q7j2OhqlpTc42k4pGncry3+rVX6dsmcsglFynn6vt3NzYxWJqmLFyDKQq+g==",
+      "dependencies": {
+        "@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"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/esbuild": {
+      "version": "0.17.13",
+      "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.13.tgz",
+      "integrity": "sha512-4ixMwdErBcQHgTBeoxnowENCPKWFAGxgTyKHMK8gqn9sZaC7ZNWFKtim16g2rzQ2b/FYyy3lIUUJboFtjolhqg==",
+      "dev": true,
+      "hasInstallScript": true,
+      "bin": {
+        "esbuild": "bin/esbuild"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "optionalDependencies": {
+        "@esbuild/android-arm": "0.17.13",
+        "@esbuild/android-arm64": "0.17.13",
+        "@esbuild/android-x64": "0.17.13",
+        "@esbuild/darwin-arm64": "0.17.13",
+        "@esbuild/darwin-x64": "0.17.13",
+        "@esbuild/freebsd-arm64": "0.17.13",
+        "@esbuild/freebsd-x64": "0.17.13",
+        "@esbuild/linux-arm": "0.17.13",
+        "@esbuild/linux-arm64": "0.17.13",
+        "@esbuild/linux-ia32": "0.17.13",
+        "@esbuild/linux-loong64": "0.17.13",
+        "@esbuild/linux-mips64el": "0.17.13",
+        "@esbuild/linux-ppc64": "0.17.13",
+        "@esbuild/linux-riscv64": "0.17.13",
+        "@esbuild/linux-s390x": "0.17.13",
+        "@esbuild/linux-x64": "0.17.13",
+        "@esbuild/netbsd-x64": "0.17.13",
+        "@esbuild/openbsd-x64": "0.17.13",
+        "@esbuild/sunos-x64": "0.17.13",
+        "@esbuild/win32-arm64": "0.17.13",
+        "@esbuild/win32-ia32": "0.17.13",
+        "@esbuild/win32-x64": "0.17.13"
+      }
+    },
+    "node_modules/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=="
+    },
+    "node_modules/estree-walker": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+      "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
+    },
+    "node_modules/follow-redirects": {
+      "version": "1.15.2",
+      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz",
+      "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==",
+      "funding": [
+        {
+          "type": "individual",
+          "url": "https://github.com/sponsors/RubenVerborgh"
+        }
+      ],
+      "engines": {
+        "node": ">=4.0"
+      },
+      "peerDependenciesMeta": {
+        "debug": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/form-data": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
+      "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
+    "node_modules/fsevents": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
+      "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
+      "dev": true,
+      "hasInstallScript": true,
+      "optional": true,
+      "os": [
+        "darwin"
+      ],
+      "engines": {
+        "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+      }
+    },
+    "node_modules/function-bind": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
+      "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
+      "dev": true
+    },
+    "node_modules/good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+      "dependencies": {
+        "delegate": "^3.1.2"
+      }
+    },
+    "node_modules/has": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
+      "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
+      "dev": true,
+      "dependencies": {
+        "function-bind": "^1.1.1"
+      },
+      "engines": {
+        "node": ">= 0.4.0"
+      }
+    },
+    "node_modules/is-core-module": {
+      "version": "2.11.0",
+      "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz",
+      "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==",
+      "dev": true,
+      "dependencies": {
+        "has": "^1.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/js-md5": {
+      "version": "0.7.3",
+      "resolved": "https://registry.npmmirror.com/js-md5/-/js-md5-0.7.3.tgz",
+      "integrity": "sha512-ZC41vPSTLKGwIRjqDh8DfXoCrdQIyBgspJVPXHBGu4nZlAEvG3nf+jO9avM9RmLiGakg7vz974ms99nEV0tmTQ=="
+    },
+    "node_modules/lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+    },
+    "node_modules/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=="
+    },
+    "node_modules/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==",
+      "peerDependencies": {
+        "@types/lodash-es": "*",
+        "lodash": "*",
+        "lodash-es": "*"
+      }
+    },
+    "node_modules/magic-string": {
+      "version": "0.25.9",
+      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
+      "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
+      "dependencies": {
+        "sourcemap-codec": "^1.4.8"
+      }
+    },
+    "node_modules/memoize-one": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
+      "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
+    },
+    "node_modules/mime-db": {
+      "version": "1.52.0",
+      "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+      "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/mime-types": {
+      "version": "2.1.35",
+      "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+      "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+      "dependencies": {
+        "mime-db": "1.52.0"
+      },
+      "engines": {
+        "node": ">= 0.6"
+      }
+    },
+    "node_modules/nanoid": {
+      "version": "3.3.4",
+      "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.4.tgz",
+      "integrity": "sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw==",
+      "bin": {
+        "nanoid": "bin/nanoid.cjs"
+      },
+      "engines": {
+        "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+      }
+    },
+    "node_modules/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=="
+    },
+    "node_modules/path-parse": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+      "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+      "dev": true
+    },
+    "node_modules/picocolors": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
+      "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ=="
+    },
+    "node_modules/postcss": {
+      "version": "8.4.21",
+      "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz",
+      "integrity": "sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg==",
+      "funding": [
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/postcss/"
+        },
+        {
+          "type": "tidelift",
+          "url": "https://tidelift.com/funding/github/npm/postcss"
+        }
+      ],
+      "dependencies": {
+        "nanoid": "^3.3.4",
+        "picocolors": "^1.0.0",
+        "source-map-js": "^1.0.2"
+      },
+      "engines": {
+        "node": "^10 || ^12 || >=14"
+      }
+    },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
+    },
+    "node_modules/resolve": {
+      "version": "1.22.1",
+      "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz",
+      "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==",
+      "dev": true,
+      "dependencies": {
+        "is-core-module": "^2.9.0",
+        "path-parse": "^1.0.7",
+        "supports-preserve-symlinks-flag": "^1.0.0"
+      },
+      "bin": {
+        "resolve": "bin/resolve"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/rollup": {
+      "version": "3.20.2",
+      "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.20.2.tgz",
+      "integrity": "sha512-3zwkBQl7Ai7MFYQE0y1MeQ15+9jsi7XxfrqwTb/9EK8D9C9+//EBR4M+CuA1KODRaNbFez/lWxA5vhEGZp4MUg==",
+      "dev": true,
+      "bin": {
+        "rollup": "dist/bin/rollup"
+      },
+      "engines": {
+        "node": ">=14.18.0",
+        "npm": ">=8.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      }
+    },
+    "node_modules/select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA=="
+    },
+    "node_modules/source-map": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+      "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/source-map-js": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz",
+      "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/sourcemap-codec": {
+      "version": "1.4.8",
+      "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
+      "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
+      "deprecated": "Please use @jridgewell/sourcemap-codec instead"
+    },
+    "node_modules/supports-preserve-symlinks-flag": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+      "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+      "dev": true,
+      "engines": {
+        "node": ">= 0.4"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/ljharb"
+      }
+    },
+    "node_modules/tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
+    },
+    "node_modules/vite": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/vite/-/vite-4.2.1.tgz",
+      "integrity": "sha512-7MKhqdy0ISo4wnvwtqZkjke6XN4taqQ2TBaTccLIpOKv7Vp2h4Y+NpmWCnGDeSvvn45KxvWgGyb0MkHvY1vgbg==",
+      "dev": true,
+      "dependencies": {
+        "esbuild": "^0.17.5",
+        "postcss": "^8.4.21",
+        "resolve": "^1.22.1",
+        "rollup": "^3.18.0"
+      },
+      "bin": {
+        "vite": "bin/vite.js"
+      },
+      "engines": {
+        "node": "^14.18.0 || >=16.0.0"
+      },
+      "optionalDependencies": {
+        "fsevents": "~2.3.2"
+      },
+      "peerDependencies": {
+        "@types/node": ">= 14",
+        "less": "*",
+        "sass": "*",
+        "stylus": "*",
+        "sugarss": "*",
+        "terser": "^5.4.0"
+      },
+      "peerDependenciesMeta": {
+        "@types/node": {
+          "optional": true
+        },
+        "less": {
+          "optional": true
+        },
+        "sass": {
+          "optional": true
+        },
+        "stylus": {
+          "optional": true
+        },
+        "sugarss": {
+          "optional": true
+        },
+        "terser": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/vue": {
+      "version": "3.2.47",
+      "resolved": "https://registry.npmjs.org/vue/-/vue-3.2.47.tgz",
+      "integrity": "sha512-60188y/9Dc9WVrAZeUVSDxRQOZ+z+y5nO2ts9jWXSTkMvayiWxCWOWtBQoYjLeccfXkiiPZWAHcV+WTPhkqJHQ==",
+      "dependencies": {
+        "@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"
+      }
+    },
+    "node_modules/vue-axios": {
+      "version": "3.5.2",
+      "resolved": "https://registry.npmjs.org/vue-axios/-/vue-axios-3.5.2.tgz",
+      "integrity": "sha512-GP+dct7UlAWkl1qoP3ppw0z6jcSua5/IrMpjB5O8bh089iIiJ+hdxPYH2NPEpajlYgkW5EVMP95ttXWdas1O0g==",
+      "peerDependencies": {
+        "axios": "*",
+        "vue": "^3.0.0 || ^2.0.0"
+      }
+    },
+    "node_modules/vue-router": {
+      "version": "4.1.6",
+      "resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.1.6.tgz",
+      "integrity": "sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==",
+      "dependencies": {
+        "@vue/devtools-api": "^6.4.5"
+      },
+      "peerDependencies": {
+        "vue": "^3.2.0"
+      }
+    },
+    "node_modules/vue-verify-code": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/vue-verify-code/-/vue-verify-code-1.0.5.tgz",
+      "integrity": "sha512-C19MxAPor6vGn9jAaq7XOUmzI97HjKsmGpFS7fGeyuv06jR4nuDw2m1c0VRvnel9XPC/3hhc8RBGDhJgUfZPTg=="
+    }
+  }
+}

+ 27 - 0
package.json

@@ -0,0 +1,27 @@
+{
+  "name": "fq",
+  "private": true,
+  "version": "0.0.0",
+  "type": "module",
+  "scripts": {
+    "dev": "vite",
+    "build": "vite build",
+    "preview": "vite preview"
+  },
+  "dependencies": {
+    "@element-plus/icons-vue": "^2.1.0",
+    "axios": "^1.3.4",
+    "clipboard": "^2.0.11",
+    "element-plus": "^2.3.4",
+    "js-md5": "^0.7.3",
+    "vue": "^3.2.47",
+    "vue-axios": "^3.5.2",
+    "vue-router": "^4.1.6",
+    "vue-verify-code": "^1.0.5"
+  },
+  "devDependencies": {
+    "@types/js-md5": "^0.7.0",
+    "@vitejs/plugin-vue": "^4.1.0",
+    "vite": "^4.2.0"
+  }
+}

File diff suppressed because it is too large
+ 0 - 0
public/icon.svg


+ 12 - 0
src/App.vue

@@ -0,0 +1,12 @@
+<script setup>
+import Index from './components/Index.vue'
+import Login from "./components/Login.vue";
+</script>
+
+<template>
+    <RouterView></RouterView>
+</template>
+
+
+<style scoped>
+</style>

+ 253 - 0
src/components/Index.vue

@@ -0,0 +1,253 @@
+<template>
+    <div class="box-content" v-loading="loading">
+        <el-card class="box-card">
+            <template #header>
+                <div class="card-header">
+                    <el-icon>
+                        <Opportunity/>
+                    </el-icon>
+                    <span style="display: flex;align-items: center;margin-left: 10px;">套餐详情</span>
+                </div>
+            </template>
+            <div style="width: 100%;border: none;text-align: left;">
+                <span style="display: block;margin: 5px 10px 5px 10px;">订阅到期时间:{{ dqsj }}</span>
+                <el-progress
+                        :text-inside="false"
+                        :show-text="false"
+                        :stroke-width="10"
+                        :percentage=pre
+                        status="success"
+                        style="margin: 5px 10px 5px 10px"
+                />
+                <span style="display: block;margin: 5px 10px 5px 10px;font-size: 12px">剩余 {{ syll }} / 总计 {{
+                    zjll
+                    }}</span>
+            </div>
+        </el-card>
+
+        <el-card class="box-card">
+            <template #header>
+                <div class="card-header">
+                    <el-icon>
+                        <Flag/>
+                    </el-icon>
+                    <span style="display: flex;align-items: center;margin-left: 10px;">在线设备</span>
+                </div>
+            </template>
+            <div style="width: 100%;border: none;text-align: left;">
+                <span style="display: block;margin: 5px 10px 5px 10px;">在线设备/设备限制数:<span v-text="zxsb"></span></span>
+                <span style="display: block;margin: 5px 10px 5px 10px;font-size: 12px">上次使用:{{ sysj }}</span>
+            </div>
+        </el-card>
+
+        <el-card class="box-card">
+            <template #header>
+                <div class="card-header">
+                    <el-icon>
+                        <Briefcase/>
+                    </el-icon>
+                    <span style="display: flex;align-items: center;margin-left: 10px;">软件下载</span>
+                </div>
+            </template>
+            <div style="width: 100%;border: none;text-align: left;display: flex;justify-content: space-evenly;">
+                <el-button type="primary" tag="a" target="_blank" rel="noopener noreferrer"
+                           style="text-decoration: none" :href="downloadUrlA">安卓端
+                </el-button>
+                <el-button type="success" tag="a" target="_blank" rel="noopener noreferrer"
+                           style="text-decoration: none" :href="downloadUrlW">电脑端
+                </el-button>
+            </div>
+        </el-card>
+
+        <el-card class="box-card">
+            <template #header>
+                <div class="card-header">
+                    <el-icon>
+                        <GoodsFilled/>
+                    </el-icon>
+                    <span style="display: flex;align-items: center;margin-left: 10px;">订阅链接</span>
+                </div>
+            </template>
+            <div style="width: 100%;border: none;display: flex;justify-content: space-evenly;">
+                <el-button type="primary" @click="dialogVisible = true;change('V2ray')">V2ray</el-button>
+                <el-button type="success" @click="dialogVisible = true;change('SSR')">SSR</el-button>
+                <el-button type="info" @click="dialogVisible = true; change('Clash')">Clash</el-button>
+            </div>
+        </el-card>
+
+        <el-dialog
+                v-model="dialogVisible"
+                title="订阅地址"
+                width="340px"
+                class="dia"
+        >
+    <span class="spA"><el-icon size="16" style="vertical-align: middle"><Lightning/></el-icon>
+      {{ tempName }}默认订阅地址:
+    </span>
+            <el-input v-model="input" :value=tempA>
+                <template #append>
+                    <el-icon @click="copy(tempA)">
+                        <DocumentCopy/>
+                    </el-icon>
+                </template>
+            </el-input>
+            <span :class=spb><el-icon size="16" style="vertical-align: middle"><Lightning/></el-icon>
+      {{ tempName }}备用订阅地址:
+    </span>
+            <el-input :class=cla v-model="input" :value=tempB>
+                <template #append>
+                    <el-icon @click="copy(tempB)">
+                        <DocumentCopy/>
+                    </el-icon>
+                </template>
+            </el-input>
+            <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="dialogVisible = false">关闭</el-button>
+      </span>
+            </template>
+        </el-dialog>
+    </div>
+</template>
+
+<script setup>
+import {onBeforeMount, onMounted, ref} from 'vue'
+import {ElMessage} from 'element-plus'
+import axios from "axios";
+import {Briefcase, DocumentCopy, Flag, GoodsFilled, Opportunity} from "@element-plus/icons-vue";
+import {useRouter} from "vue-router";
+
+const loading = ref(true);
+
+onBeforeMount(async () => {
+    await axios.get("/api/getData").then(
+        (rep) => {
+            if (rep.data.code !== 1) {
+                ElMessage.error(rep.data.message);
+                useRouter().push({path: "/login"})
+                return;
+            }
+            zxsb.value = rep.data.data.zxsb;
+            sysj.value = rep.data.data.sysj;
+            syll.value = rep.data.data.syll;
+            dqsj.value = rep.data.data.dqsj;
+            v2rayurl.value = rep.data.data.v2rayurl;
+            v2rayBakurl.value = rep.data.data.v2rayBakurl;
+            SSRurl.value = rep.data.data.SSRurl;
+            SSRBakurl.value = rep.data.data.SSRBakurl;
+            Clash.value = rep.data.data.Clash;
+            downloadUrlA.value = rep.data.data.AD;
+            downloadUrlW.value = rep.data.data.WD;
+            let preTmp = rep.data.data.syll;
+            const index = preTmp.indexOf("G");
+            if (index === -1) {
+                pre.value = 100;
+            } else {
+                pre.value = (Number(preTmp.substring(0, index)) / 1024).toFixed(2) * 100
+            }
+            loading.value = false;
+
+        }).catch(() => {
+            ElMessage.error("未知错误!");
+            loading.value = false;
+        }
+    )
+})
+
+const dialogVisible = ref(false)
+
+
+const input = ref('')
+
+let tempA = "";
+let tempB = "";
+let tempName = ""
+let cla = "";
+let spb = "spB"
+const zxsb = ref("");
+const sysj = ref("");
+const syll = ref("");
+const dqsj = ref("");
+const zjll = "1049 GB";
+const pre = ref(0);
+const v2rayurl = ref("");
+const v2rayBakurl = ref("");
+const SSRurl = ref("");
+const SSRBakurl = ref("");
+const Clash = ref("");
+const downloadUrlA = ref("");
+const downloadUrlW = ref("");
+const copy = (e) => {
+    navigator.clipboard.writeText(e);
+    ElMessage('链接接已复制到剪贴板!')
+
+}
+
+const change = (e) => {
+    switch (e) {
+        case 'V2ray':
+            tempName = e;
+            tempA = v2rayurl;
+            tempB = v2rayBakurl;
+            cla = "";
+            spb = "spB"
+            break;
+        case 'SSR':
+            tempName = e;
+            tempA = SSRurl;
+            tempB = SSRBakurl;
+            cla = "";
+            spb = "spB"
+            break;
+        case 'Clash':
+            tempName = e;
+            tempA = Clash;
+            cla = "none";
+            spb = "none"
+            break;
+    }
+}
+
+
+</script>
+
+<style scoped>
+.box-content {
+    margin: 0 auto;
+}
+
+.dialog-footer button:first-child {
+    margin-right: 10px;
+}
+
+.card-header {
+    display: flex;
+    align-items: center;
+}
+
+@media screen and (max-width: 480px) {
+    .box-card {
+        width: 330px !important;
+        font-size: 16px;
+    }
+}
+
+.box-card {
+    width: 480px;
+    margin: 20px;
+}
+
+.spA, .spB {
+    display: block;
+    margin: 10px;
+}
+
+.none {
+    display: none;
+}
+
+/*.dia {*/
+/*  text-align: left;*/
+/*}*/
+
+</style>

+ 304 - 0
src/components/Login.vue

@@ -0,0 +1,304 @@
+<script lang="ts" setup>
+
+import {reactive, ref} from 'vue'
+import type {FormInstance, FormRules} from 'element-plus'
+import {ChatSquare, Unlock, User} from "@element-plus/icons-vue";
+import VerifyCode from 'vue-verify-code'
+import {ElMessage} from "element-plus";
+import md5 from 'js-md5'
+import axios, {toFormData} from "axios";
+import {useRouter} from "vue-router";
+
+const router = useRouter();
+
+const loginForm = ref<FormInstance>()
+
+const registerFrom = ref<FormInstance>()
+
+const code = ref('');
+
+const verCode = ref(null);
+
+const tabTag = ref(1)
+
+const fromData = reactive({
+  username: '',
+  password: '',
+  code: ''
+})
+
+const buttonText = ref("点击获取验证码")
+const btnTag = ref(false)
+
+const btnDisable = ref(false)
+
+let interval = setInterval(() => {})
+
+const validateUsername = (rule: any, value: any, callback: any) => {
+  if (value === '') {
+    callback(new Error())
+    ElMessage.error('用户名为必填项!')
+    return;
+  }
+  if (value.indexOf('@') === -1 || value.indexOf('@') === 0 || value.indexOf('@') === value.length - 1) {
+    ElMessage.error("邮箱格式错误!")
+    return;
+  }
+  callback();
+}
+const validatePassword = (rule: any, value: any, callback: any) => {
+  if (value === '') {
+    callback(new Error())
+    ElMessage.error('密码为必填项!')
+    return;
+  }
+  callback();
+}
+
+const checkCode = (rule: any, value: any, callback: any) => {
+  if (value === '') {
+    callback(new Error())
+    ElMessage.error('验证码为必填项!')
+  } else if (verCode.value.result.toLowerCase() == value.toLowerCase()) {
+    callback()
+  } else {
+    callback(new Error())
+    ElMessage.error('验证码错误!')
+  }
+}
+
+const rules = reactive<FormRules>({
+  username: [{validator: validateUsername, trigger: 'blur'}],
+  password: [{validator: validatePassword, trigger: 'blur'}],
+  code: [{validator: checkCode, trigger: 'blur'}]
+})
+
+const submitForm = (formEl: FormInstance | undefined) => {
+  if (!formEl) return
+  formEl.validate((valid) => {
+    if (valid) {
+      login();
+    }
+  })
+}
+
+const handleChange = (val) => {
+  code.value = val;
+}
+
+const switchTag = value => {
+  fromData.username = ''
+  fromData.password = ''
+  fromData.code = ''
+  buttonText.value = '点击获取验证码'
+  btnTag.value = false
+  btnDisable.value = false
+  clearInterval(interval)
+  tabTag.value = value
+}
+
+const login = () => {
+  const data = {
+    username: md5(fromData.username),
+    password: md5(fromData.password)
+  }
+  axios.post('/api/login', data).then((rep) => {
+        if (rep.data.code == 1) {
+          ElMessage.success(rep.data.msg);
+          router.push({
+            path: '/'
+          })
+        } else {
+          ElMessage.error(rep.data.msg);
+        }
+      }
+  )
+}
+
+const getVerCode = () => {
+  if (fromData.username == "") {
+    ElMessage.error("邮箱为空, 请输入邮箱!")
+    return
+  }
+  const data = {
+    username: fromData.username
+  }
+  axios.post('/api/getVerCode', data).then((rep) => {
+        if (rep.data.code == 1) {
+          btnDisable.value = true
+          let i = 30;
+          interval = setInterval(() => {
+            buttonText.value = i-- + "秒后重新获取"
+          }, 1000)
+          btnTag.value = true
+          setTimeout(() => {
+            clearInterval(interval)
+            buttonText.value = "点击获取验证码"
+            btnDisable.value = false
+          }, 30000)
+        } else {
+          ElMessage.error("验证码获取失败,请重新获取!")
+        }
+
+      }
+  )
+}
+
+</script>
+
+<template>
+  <div class="content">
+    <el-card class="box-card">
+      <el-form
+          v-if="tabTag===1"
+          ref="loginForm"
+          :model="fromData"
+          status-icon
+          :rules="rules"
+          label-width="120px"
+          class="demo-fromData"
+      >
+        <h1>登&nbsp;&nbsp;&nbsp;&nbsp;录</h1>
+        <el-form-item label-width="0" prop="username" class="in">
+          <el-input class="h-40" v-model="fromData.username" type="text" autocomplete="off"
+                    placeholder="请输入邮箱" :prefix-icon="User"/>
+        </el-form-item>
+        <el-form-item label-width="0" prop="password" class="in">
+          <el-input class="h-40" v-model="fromData.password" type="password" autocomplete="off"
+                    placeholder="请输入密码" :prefix-icon="Unlock"/>
+        </el-form-item>
+        <el-form-item label-width="0" prop="code" class="in">
+          <el-input class="h-40" style="width: 50%" v-model="fromData.code" type="text" autocomplete="off"
+                    placeholder="请输入验证码" :prefix-icon="ChatSquare"/>
+          <el-button class="h-40" type="primary" style="width: 49%;margin-left: 1%" :disabled="btnDisable"
+                     @click="getVerCode">
+            {{ buttonText }}
+          </el-button>
+        </el-form-item>
+      </el-form>
+
+      <el-form
+          v-if="tabTag===2"
+          ref="registerFrom"
+          :model="fromData"
+          status-icon
+          :rules="rules"
+          label-width="120px"
+          class="demo-fromData"
+      >
+        <h1>注&nbsp;&nbsp;&nbsp;&nbsp;册</h1>
+        <el-form-item label-width="0" prop="username" class="in">
+          <el-input class="h-40" v-model="fromData.username" type="text" autocomplete="off"
+                    placeholder="请输入邮箱" :prefix-icon="User"/>
+        </el-form-item>
+        <el-form-item label-width="0" prop="password" class="in">
+          <el-input class="h-40" v-model="fromData.password" type="password" autocomplete="off"
+                    placeholder="请输入密码" :prefix-icon="Unlock"/>
+        </el-form-item>
+        <el-form-item label-width="0" prop="password" class="in">
+          <el-input class="h-40" v-model="fromData.password" type="password" autocomplete="off"
+                    placeholder="请再次输入密码" :prefix-icon="Unlock"/>
+        </el-form-item>
+        <el-form-item label-width="0" prop="code" class="in">
+          <el-input class="h-40" style="width: 50%" v-model="fromData.code" type="text" autocomplete="off"
+                    placeholder="请输入验证码" :prefix-icon="ChatSquare"/>
+          <el-button class="h-40" type="primary" style="width: 49%;margin-left: 1%" @click="getVerCode">{{ buttonText }}
+          </el-button>
+        </el-form-item>
+      </el-form>
+
+      <el-form
+          v-if="tabTag===3"
+          ref="resetFrom"
+          :model="fromData"
+          status-icon
+          :rules="rules"
+          label-width="120px"
+          class="demo-fromData"
+      >
+        <h1>重置密码</h1>
+        <el-form-item label-width="0" prop="username" class="in">
+          <el-input class="h-40" v-model="fromData.username" type="text" autocomplete="off"
+                    placeholder="请输入邮箱" :prefix-icon="User"/>
+        </el-form-item>
+        <el-form-item label-width="0" prop="code" class="in">
+          <el-input class="h-40" style="width: 50%" v-model="fromData.code" type="text" autocomplete="off"
+                    placeholder="请输入验证码" :prefix-icon="ChatSquare"/>
+          <el-button class="h-40" type="primary" style="width: 49%;margin-left: 1%" @click="getVerCode">{{ buttonText }}
+          </el-button>
+        </el-form-item>
+        <el-form-item v-if="btnTag" label-width="0" prop="password" class="in">
+          <el-input class="h-40" v-model="fromData.password" type="password" autocomplete="off"
+                    placeholder="请输入密码" :prefix-icon="Unlock"/>
+        </el-form-item>
+        <el-form-item v-if="btnTag" label-width="0" prop="password" class="in">
+          <el-input class="h-40" v-model="fromData.password" type="password" autocomplete="off"
+                    placeholder="请再次输入密码" :prefix-icon="Unlock"/>
+        </el-form-item>
+      </el-form>
+
+      <div style="padding: 0 8% 0 8%;margin-bottom: 10%;">
+        <span style="font-size: 14px;display: flex;align-items: center;justify-content: space-between;">
+          <span v-if="tabTag===1" style="display: flex;">&nbsp;没有账号?去
+            <el-link @click="switchTag(2)">注册</el-link>
+          </span>
+          <span v-if="tabTag===2" style="display: flex;">已有账号?去
+            <el-link @click="switchTag(1)">登录</el-link>
+          </span>
+          <el-link v-if="tabTag===1" @click="switchTag(3)">忘记密码&nbsp;</el-link>
+        </span>
+        <el-button class="btn in h-40" type="primary" @click="submitForm(resetFrom)">
+          {{ tabTag === 1 ? "登录" : tabTag === 2 ? "注册" : "提交" }}
+        </el-button>
+        <el-button v-if="tabTag===3" class="btn in h-40" type="primary" @click="switchTag(1)">
+          返回登录
+        </el-button>
+      </div>
+    </el-card>
+
+  </div>
+
+</template>
+
+<style scoped>
+.h-40 {
+  height: 40px;
+}
+
+.in {
+  padding: 0 8% 0 8%;
+}
+
+.btn {
+  width: 100%;
+  margin: 10px 0;
+}
+
+.box-card {
+  width: 30vw;
+}
+
+.content {
+  height: 100%;
+  width: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.demo-ruleForm {
+  border-radius: 8px;
+  margin: 0 auto;
+}
+
+.demo-ruleForm, .demo-fromData h1 {
+  text-align: center;
+  margin-top: 30px;
+}
+
+@media screen and (max-width: 480px) {
+  .box-card {
+    width: 80vw;
+  }
+}
+</style>

+ 18 - 0
src/main.js

@@ -0,0 +1,18 @@
+import {createApp} from 'vue'
+import ElementPlus from 'element-plus'
+import 'element-plus/dist/index.css'
+import './style.css'
+import * as ElementPlusIconsVue from '@element-plus/icons-vue'
+import axios from "axios";
+import vueAxios from 'vue-axios';
+import App from './App.vue'
+import router from "./route/index.js";
+
+const app = createApp(App);
+app.use(vueAxios, axios)
+for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
+    app.component(key, component)
+}
+app.use(ElementPlus);
+app.use(router);
+app.mount('#app')

+ 50 - 0
src/route/index.js

@@ -0,0 +1,50 @@
+import {createRouter, createWebHistory,} from "vue-router";
+import Index from "../components/Index.vue";
+import Login from "../components/Login.vue";
+
+const routes = [
+    {path: '/', component: Index},
+    {path: '/login', component: Login},
+]
+
+const router = createRouter({
+    // 4. 内部提供了 history 模式的实现。为了简单起见,我们在这里使用 hash 模式。
+    history: createWebHistory(),
+    routes, // `routes: routes` 的缩写
+})
+
+router.beforeEach((to, from, next) => {
+    const cookies = document.cookie.split(';');
+    let token, exp_time;
+    for (let i = 0; i < cookies.length; i++) {
+        const cookie = cookies[i].trim();
+        if (cookie.startsWith('token=')) {
+            token = cookie.substring('token='.length);
+        }
+        if (cookie.startsWith('time=')) {
+            exp_time = cookie.substring('time='.length).replaceAll('\"', '');
+        }
+    }
+    const now_time = new Date().toLocaleString('zh-CN', {
+        year: 'numeric',
+        month: '2-digit',
+        day: '2-digit',
+        hour: '2-digit',
+        minute: '2-digit',
+        second: '2-digit'
+    }).replaceAll('\/', '-');
+
+
+    // 如果token不存在或者超时  就需要重新登陆
+    if (to.path === '/login') {
+        next();
+    }
+    // } else if (!token || now_time >= exp_time) {
+    //     return next('/login')
+    // } else {
+    //     next();
+    // }
+
+})
+
+export default router

+ 16 - 0
src/style.css

@@ -0,0 +1,16 @@
+html {
+  height: 100%;
+}
+body {
+  margin: 0;
+  display: flex;
+  height: 100%;
+  align-items: center;
+}
+
+#app {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+}

+ 18 - 0
vite.config.js

@@ -0,0 +1,18 @@
+import {defineConfig} from 'vite'
+import vue from '@vitejs/plugin-vue'
+
+// https://vitejs.dev/config/
+export default defineConfig({
+    plugins: [vue()],
+    server: {
+        host: '0.0.0.0',
+        port: 8010,
+        proxy: {
+            "/api": {
+                target: "http://127.0.0.1:8100",
+                changeOrigin: true,
+                rewrite: (path) => path.replace(/^\/api/, ""),
+            }
+        }
+    }
+})

Some files were not shown because too many files changed in this diff