Procházet zdrojové kódy

实现验证码模块
初始化登陆注册页面

枫叶秋林 před 2 roky
rodič
revize
f4a75ec674

+ 12 - 0
components.d.ts

@@ -7,10 +7,20 @@ export {}
 
 declare module '@vue/runtime-core' {
   export interface GlobalComponents {
+    Cod: typeof import('./src/components/cod/index.vue')['default']
+    ElAvatar: typeof import('element-plus/es')['ElAvatar']
+    ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
+    ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
     ElButton: typeof import('element-plus/es')['ElButton']
+    ElButtonGroup: typeof import('element-plus/es')['ElButtonGroup']
+    ElCard: typeof import('element-plus/es')['ElCard']
     ElCol: typeof import('element-plus/es')['ElCol']
     ElContainer: typeof import('element-plus/es')['ElContainer']
+    ElDescriptions: typeof import('element-plus/es')['ElDescriptions']
+    ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem']
+    ElDivider: typeof import('element-plus/es')['ElDivider']
     ElDrawer: typeof import('element-plus/es')['ElDrawer']
+    ElEmpty: typeof import('element-plus/es')['ElEmpty']
     ElFooter: typeof import('element-plus/es')['ElFooter']
     ElForm: typeof import('element-plus/es')['ElForm']
     ElFormItem: typeof import('element-plus/es')['ElFormItem']
@@ -22,10 +32,12 @@ declare module '@vue/runtime-core' {
     ElMenu: typeof import('element-plus/es')['ElMenu']
     ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
     ElOption: typeof import('element-plus/es')['ElOption']
+    ElPageHeader: typeof import('element-plus/es')['ElPageHeader']
     ElRow: typeof import('element-plus/es')['ElRow']
     ElSelect: typeof import('element-plus/es')['ElSelect']
     ElSpace: typeof import('element-plus/es')['ElSpace']
     ElSwitch: typeof import('element-plus/es')['ElSwitch']
+    ElTag: typeof import('element-plus/es')['ElTag']
     Hleft: typeof import('./src/components/header/hleft.vue')['default']
     Hright: typeof import('./src/components/header/hright.vue')['default']
     Hsearch: typeof import('./src/components/header/hsearch.vue')['default']

binární
dump.rdb


+ 71 - 1
package-lock.json

@@ -10,11 +10,15 @@
       "dependencies": {
         "@element-plus/icons-vue": "^2.0.10",
         "@vueuse/core": "^9.5.0",
+        "crypto-js": "^4.1.1",
+        "cryptojs": "^2.5.3",
         "element-plus": "^2.2.21",
         "unplugin-icons": "^0.14.13",
-        "vue": "^3.2.41"
+        "vue": "^3.2.41",
+        "vue-router": "4"
       },
       "devDependencies": {
+        "@types/crypto-js": "^4.1.1",
         "@types/node": "^18.11.9",
         "@vitejs/plugin-vue": "^3.2.0",
         "typescript": "^4.6.4",
@@ -192,6 +196,13 @@
         }
       }
     },
+    "node_modules/@types/crypto-js": {
+      "version": "4.1.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/@types%2fcrypto-js/-/crypto-js-4.1.1.tgz",
+      "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==",
+      "dev": true,
+      "license": "MIT"
+    },
     "node_modules/@types/estree": {
       "version": "1.0.0",
       "resolved": "https://mirrors.cloud.tencent.com/npm/@types%2festree/-/estree-1.0.0.tgz",
@@ -351,6 +362,11 @@
         "@vue/shared": "3.2.45"
       }
     },
+    "node_modules/@vue/devtools-api": {
+      "version": "6.4.5",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz",
+      "integrity": "sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ=="
+    },
     "node_modules/@vue/reactivity": {
       "version": "3.2.45",
       "resolved": "https://mirrors.cloud.tencent.com/npm/@vue%2freactivity/-/reactivity-3.2.45.tgz",
@@ -611,6 +627,20 @@
         "node": ">= 8"
       }
     },
+    "node_modules/crypto-js": {
+      "version": "4.1.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/crypto-js/-/crypto-js-4.1.1.tgz",
+      "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==",
+      "license": "MIT"
+    },
+    "node_modules/cryptojs": {
+      "version": "2.5.3",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/cryptojs/-/cryptojs-2.5.3.tgz",
+      "integrity": "sha1-kJVH7PFbrEVuJ1RZs58u9F1/y7k=",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/csstype": {
       "version": "2.6.21",
       "resolved": "https://mirrors.cloud.tencent.com/npm/csstype/-/csstype-2.6.21.tgz",
@@ -1769,6 +1799,17 @@
         "@vue/shared": "3.2.45"
       }
     },
+    "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-template-compiler": {
       "version": "2.7.14",
       "resolved": "https://mirrors.cloud.tencent.com/npm/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
@@ -1951,6 +1992,12 @@
         "picomatch": "^2.3.1"
       }
     },
+    "@types/crypto-js": {
+      "version": "4.1.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/@types%2fcrypto-js/-/crypto-js-4.1.1.tgz",
+      "integrity": "sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==",
+      "dev": true
+    },
     "@types/estree": {
       "version": "1.0.0",
       "resolved": "https://mirrors.cloud.tencent.com/npm/@types%2festree/-/estree-1.0.0.tgz",
@@ -2089,6 +2136,11 @@
         "@vue/shared": "3.2.45"
       }
     },
+    "@vue/devtools-api": {
+      "version": "6.4.5",
+      "resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz",
+      "integrity": "sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ=="
+    },
     "@vue/reactivity": {
       "version": "3.2.45",
       "resolved": "https://mirrors.cloud.tencent.com/npm/@vue%2freactivity/-/reactivity-3.2.45.tgz",
@@ -2254,6 +2306,16 @@
         "which": "^2.0.1"
       }
     },
+    "crypto-js": {
+      "version": "4.1.1",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/crypto-js/-/crypto-js-4.1.1.tgz",
+      "integrity": "sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw=="
+    },
+    "cryptojs": {
+      "version": "2.5.3",
+      "resolved": "https://mirrors.cloud.tencent.com/npm/cryptojs/-/cryptojs-2.5.3.tgz",
+      "integrity": "sha1-kJVH7PFbrEVuJ1RZs58u9F1/y7k="
+    },
     "csstype": {
       "version": "2.6.21",
       "resolved": "https://mirrors.cloud.tencent.com/npm/csstype/-/csstype-2.6.21.tgz",
@@ -2971,6 +3033,14 @@
         "@vue/shared": "3.2.45"
       }
     },
+    "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==",
+      "requires": {
+        "@vue/devtools-api": "^6.4.5"
+      }
+    },
     "vue-template-compiler": {
       "version": "2.7.14",
       "resolved": "https://mirrors.cloud.tencent.com/npm/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",

+ 4 - 0
package.json

@@ -11,12 +11,16 @@
   "dependencies": {
     "@element-plus/icons-vue": "^2.0.10",
     "@vueuse/core": "^9.5.0",
+    "axios": "^1.2.0",
+    "crypto-js": "^4.1.1",
+    "cryptojs": "^2.5.3",
     "element-plus": "^2.2.21",
     "unplugin-icons": "^0.14.13",
     "vue": "^3.2.41",
     "vue-router": "4"
   },
   "devDependencies": {
+    "@types/crypto-js": "^4.1.1",
     "@types/node": "^18.11.9",
     "@vitejs/plugin-vue": "^3.2.0",
     "typescript": "^4.6.4",

+ 1 - 0
src/App.vue

@@ -1,4 +1,5 @@
 <script setup lang="ts">
+console.log(import.meta.env)
 </script>
 
 <template>

+ 198 - 0
src/components/cod/index.vue

@@ -0,0 +1,198 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue'
+import service from '../../plugins/axios'
+import { encrypt, decrypt } from '../../plugins/crypto'
+import { ElMessage } from 'element-plus'
+const loging = ref(false)
+type data = {
+  world: string
+  x: number
+  y: number
+}
+type res = {
+  status: boolean
+  msg: string
+  cod: string
+}
+//emits
+const codsuccess = defineEmits<{
+  (e: 'success', rescod: string): void
+}>()
+
+async function getdata() {
+  const data = (await (await service.get('cod/getcod')).data) as res
+  if (data.status) {
+    const cod = data.cod
+    let res = decrypt(cod) as string
+    if (res.match(/\[(.*?)\]/)?.[1]) {
+      res = `[${res.match(/\[(.*?)\]/)?.[1]}]` as string
+      try {
+        const json = JSON.parse(res) as data
+        return json
+      } catch (error) {
+        console.log(error)
+      }
+    }
+
+    return []
+  }
+}
+let cod = (await getdata()) as data[]
+let seed = [] as any
+onMounted(async () => {
+  const canvas = document?.getElementById('canvas') as HTMLCanvasElement
+  const ctx = canvas?.getContext('2d') as CanvasRenderingContext2D
+  const width = 300
+  const height = 300
+  function canvasCod() {
+    canvas.width = width
+    canvas.height = height
+    ctx.fillStyle = `rgb(${Math.random() * 255},${Math.random() * 255},${
+      Math.random() * 255
+    })`
+    ctx.fillRect(0, 0, width, height)
+    //干扰线
+    for (let i = 0; i < 10; i++) {
+      ctx.beginPath()
+      ctx.moveTo(Math.random() * width, Math.random() * height)
+      ctx.lineTo(Math.random() * width, Math.random() * height)
+      ctx.strokeStyle = `rgb(${Math.random() * 255},${Math.random() * 255},${
+        Math.random() * 255
+      })`
+      ctx.stroke()
+    }
+  }
+  //绘制文字
+  function drawText(
+    text: string,
+    x: number,
+    y: number,
+    clor = true,
+    pen = ctx,
+    size = 20
+  ) {
+    if (clor) {
+      pen.fillStyle = `rgb(${Math.random() * 255},${Math.random() * 255},${
+        Math.random() * 255
+      })`
+    } else {
+      pen.fillStyle = `rgb(0,0,0)`
+    }
+    pen.font = `bold ${size}px Arial`
+    pen.textAlign = 'center'
+    pen.textBaseline = 'middle'
+    pen.fillText(text, x, y)
+  }
+  let si = 0
+  canvas.onmousedown = function (e) {
+    const x = e.offsetX
+    const y = e.offsetY
+    const s = ['①', '②', '③', '④']
+    //依次绘画s中的每个字符
+    if (si < s.length) {
+      drawText(s[si], x, y, false)
+      seed.push({ x, y })
+      si++
+    }
+    //
+    let success = 0
+    for (let index = 0; index < cod.length; index++) {
+      const element = cod[index]
+      if (
+        Math.abs(seed[index]?.x - element.x) < 30 &&
+        Math.abs(seed[index]?.y - element.y) < 30
+      ) {
+        success++
+        continue
+      }
+    }
+    if (success === 4) {
+      console.log('通过')
+      codsuccess('success', encrypt(JSON.stringify(seed)))
+      init()
+      return
+    }
+    if (seed.length === 4) {
+      ElMessage({
+        message: '验证失败',
+        type: 'error',
+      })
+      console.log('失败')
+      init()
+    }
+  }
+  async function init() {
+    cod = (await getdata()) as data[]
+    canvasCod()
+    rendcod()
+    si = 0
+    seed = []
+    for (let i = 0; i < cod.length; i++) {
+      drawText(cod[i].world, cod[i].x, cod[i].y, true, ctx, 30)
+    }
+  }
+  await init()
+  //刷新验证码
+  const refresh_bt = document?.getElementById('bt_refresh')
+  //验证图
+  refresh_bt?.addEventListener('click', async () => {
+    loging.value = true
+    await init()
+    loging.value = false
+  })
+  function rendcod() {
+    const cods = document?.getElementsByClassName(
+      'cod'
+    ) as HTMLCollectionOf<HTMLCanvasElement>
+    for (let index = 0; index < cods.length; index++) {
+      const element = cods[index]
+      const ctx = element?.getContext('2d') as CanvasRenderingContext2D
+      element.width = 24
+      element.height = 24
+      ctx.fillStyle = `rgb(${Math.random() * 255},${Math.random() * 255},${
+        Math.random() * 255
+      })`
+      ctx.fillRect(0, 0, width, height)
+      drawText(cod[index].world, 12, 12, true, ctx, 12)
+    }
+  }
+})
+</script>
+
+<template>
+  <div>
+    <el-space direction="vertical">
+      <div>
+        <el-space>
+          <span>请点击下图所示字符</span>
+          <Canvas class="cod"
+                  style="width:24px;height: 24px;" />
+          <Canvas class="cod"
+                  style="width:24px;height: 24px;" />
+          <Canvas class="cod"
+                  style="width:24px;height: 24px;" />
+          <Canvas class="cod"
+                  style="width:24px;height: 24px;" />
+        </el-space>
+      </div>
+      <div>
+        <Canvas id="canvas"
+                style="width:300px;height: 300px;" />
+      </div>
+      <div id="refresh">
+        看不清,点击下刷新<el-button id="bt_refresh"
+                   :loading="loging">刷新验证码</el-button>
+      </div>
+    </el-space>
+  </div>
+
+</template>
+
+<style scoped>
+#refresh {
+  width: 300px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+</style>

+ 1 - 1
src/components/header/hsearch.vue

@@ -3,7 +3,7 @@
 <script setup lang="ts">
 import { ref } from 'vue'
 import { Search } from '@element-plus/icons-vue'
-const select = ref('')
+const select = ref('帖子')
 const input3 = ref('')
 </script>
 

+ 8 - 0
src/layouts/auth.vue

@@ -0,0 +1,8 @@
+<script setup lang="ts"></script>
+
+<template>
+
+</template>
+
+<style scoped lang="scss">
+</style>

+ 8 - 1
src/layouts/home.vue

@@ -1,5 +1,6 @@
 <script setup lang="ts">
 import homeHeader from '../views/homeHeader/index.vue'
+import homefooter from '../views/homefooter/index.vue'
 </script>
 
 <template>
@@ -11,10 +12,16 @@ import homeHeader from '../views/homeHeader/index.vue'
       <router-view></router-view>
     </el-main>
     <el-footer>
-      BY:枫叶秋林
+      <div class="center">
+        <homefooter />
+      </div>
+
     </el-footer>
   </el-container>
 </template>
 
 <style scoped>
+.center {
+  text-align: center;
+}
 </style>

+ 1 - 0
src/main.ts

@@ -1,5 +1,6 @@
 import { createApp } from "vue";
 import App from "./App.vue";
+import "element-plus/es/components/message/style/css";
 import "element-plus/theme-chalk/dark/css-vars.css";
 import "element-plus/theme-chalk/display.css";
 import router, { setupRouter } from "./plugins/router";

+ 17 - 0
src/plugins/axios.ts

@@ -0,0 +1,17 @@
+import axios from "axios";
+//axios封装
+const service = axios.create({
+  baseURL: "http://localhost:3000",
+  timeout: 5000,
+});
+//请求拦截器
+service.interceptors.request.use((config) => {
+  //在发送请求之前做某事
+  return config;
+});
+//响应拦截器
+service.interceptors.response.use((response) => {
+  //对响应数据做些事
+  return response;
+});
+export default service;

+ 29 - 0
src/plugins/crypto.ts

@@ -0,0 +1,29 @@
+import { AES, enc, mode, pad } from "crypto-js";
+export function encrypt(
+  word: string | Object,
+  keyStr: string = import.meta.env.VITE_CODE_KEY
+) {
+  if (word instanceof Object) {
+    word = JSON.stringify(word);
+  }
+  var key = enc.Utf8.parse(keyStr);
+  var encryptedObj = AES.encrypt(enc.Utf8.parse(word as string), key, {
+    mode: mode.ECB,
+    padding: pad.Pkcs7,
+  });
+  return encryptedObj.toString();
+}
+// 解密函數
+export function decrypt(
+  word: string,
+  keyStr: string = import.meta.env.VITE_CODE_KEY
+) {
+  let key = enc.Utf8.parse(keyStr);
+  let decrypt = AES.decrypt(word, key, {
+    mode: mode.ECB,
+    padding: pad.ZeroPadding,
+  });
+  let decString = enc.Utf8.stringify(decrypt).toString();
+  console.log("decString", decString);
+  return decString;
+}

+ 20 - 0
src/plugins/router.ts

@@ -7,6 +7,26 @@ const router = createRouter({
     {
       path: "/",
       component: () => import("../layouts/home.vue"),
+      children: [
+        {
+          path: "/home",
+          component: () => import("../views/home/index.vue"),
+        },
+        {
+          path: "/auth",
+          component: () => import("../layouts/auth.vue"),
+          children: [
+            {
+              path: "/auth/login",
+              component: () => import("../views/auth/login.vue"),
+            },
+            {
+              path: "/auth/register",
+              component: () => import("../views/auth/register.vue"),
+            },
+          ],
+        },
+      ],
     },
   ],
 });

+ 8 - 0
src/views/auth/login.vue

@@ -0,0 +1,8 @@
+<script setup lang="ts"></script>
+
+<template>
+
+</template>
+
+<style scoped lang="scss">
+</style>

+ 8 - 0
src/views/auth/register.vue

@@ -0,0 +1,8 @@
+<script setup lang="ts"></script>
+
+<template>
+
+</template>
+
+<style scoped lang="scss">
+</style>

+ 34 - 0
src/views/home/index.vue

@@ -0,0 +1,34 @@
+<script setup lang="ts">
+import { onMounted, ref, VNodeRef } from 'vue'
+import { encrypt, decrypt } from '../../plugins/crypto'
+import { ElMessage } from 'element-plus'
+import cod from '../../components/cod/index.vue'
+const successcod = (success: string) => {
+  ElMessage({
+    message: `验证通过,加密验证为${success}`,
+    type: 'success',
+  })
+  console.log(success)
+}
+</script>
+
+<template>
+  <Suspense>
+    <template #default>
+      <el-space>
+        <el-card class="box-card">
+          <cod @success="successcod" />
+        </el-card>
+      </el-space>
+    </template>
+    <template #fallback>
+      验证码获取中
+    </template>
+  </Suspense>
+  <el-empty description="没有内容哦" />
+  <button @click="ElMessage('text')">tx</button>
+</template>
+
+<style scoped>
+</style>
+

+ 13 - 0
src/views/homefooter/index.vue

@@ -0,0 +1,13 @@
+<script setup lang="ts">
+</script>
+
+<template>
+  <span>by 枫叶秋林</span>
+  <el-divider direction="vertical" />
+  <span>备案号:XXXX</span>
+  <el-divider direction="vertical" />
+  <span>Grass</span>
+</template>
+
+<style scoped>
+</style>

+ 74 - 2
yarn.lock

@@ -109,6 +109,11 @@
     estree-walker "^2.0.2"
     picomatch "^2.3.1"
 
+"@types/crypto-js@^4.1.1":
+  version "4.1.1"
+  resolved "https://mirrors.cloud.tencent.com/npm/@types%2fcrypto-js/-/crypto-js-4.1.1.tgz"
+  integrity sha512-BG7fQKZ689HIoc5h+6D2Dgq1fABRa0RbBWKBd9SP/MVRVXROflpm5fhwyATX5duFmbStzyzyycPB8qUYKDH3NA==
+
 "@types/estree@^1.0.0":
   version "1.0.0"
   resolved "https://mirrors.cloud.tencent.com/npm/@types%2festree/-/estree-1.0.0.tgz"
@@ -230,7 +235,7 @@
 
 "@vue/devtools-api@^6.4.5":
   version "6.4.5"
-  resolved "https://mirrors.cloud.tencent.com/npm/@vue%2fdevtools-api/-/devtools-api-6.4.5.tgz#d54e844c1adbb1e677c81c665ecef1a2b4bb8380"
+  resolved "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.4.5.tgz"
   integrity sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==
 
 "@vue/reactivity-transform@3.2.45":
@@ -321,6 +326,20 @@ async-validator@^4.2.5:
   resolved "https://mirrors.cloud.tencent.com/npm/async-validator/-/async-validator-4.2.5.tgz"
   integrity sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==
 
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://mirrors.cloud.tencent.com/npm/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha1-x57Zf380y48robyXkLzDZkdLS3k=
+
+axios@^1.2.0:
+  version "1.2.0"
+  resolved "https://mirrors.cloud.tencent.com/npm/axios/-/axios-1.2.0.tgz#1cb65bd75162c70e9f8d118a905126c4a201d383"
+  integrity sha512-zT7wZyNYu3N5Bu0wuZ6QccIf93Qk1eV8LOewxgjOZFd2DenOs98cJ7+Y6703d0wkaXGY6/nZd4EweJaHz9uzQw==
+  dependencies:
+    follow-redirects "^1.15.0"
+    form-data "^4.0.0"
+    proxy-from-env "^1.1.0"
+
 balanced-match@^1.0.0:
   version "1.0.2"
   resolved "https://mirrors.cloud.tencent.com/npm/balanced-match/-/balanced-match-1.0.2.tgz"
@@ -360,6 +379,13 @@ chokidar@^3.5.3:
   optionalDependencies:
     fsevents "~2.3.2"
 
+combined-stream@^1.0.8:
+  version "1.0.8"
+  resolved "https://mirrors.cloud.tencent.com/npm/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
 cross-spawn@^7.0.3:
   version "7.0.3"
   resolved "https://mirrors.cloud.tencent.com/npm/cross-spawn/-/cross-spawn-7.0.3.tgz"
@@ -369,6 +395,16 @@ cross-spawn@^7.0.3:
     shebang-command "^2.0.0"
     which "^2.0.1"
 
+crypto-js@^4.1.1:
+  version "4.1.1"
+  resolved "https://mirrors.cloud.tencent.com/npm/crypto-js/-/crypto-js-4.1.1.tgz"
+  integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw==
+
+cryptojs@^2.5.3:
+  version "2.5.3"
+  resolved "https://mirrors.cloud.tencent.com/npm/cryptojs/-/cryptojs-2.5.3.tgz"
+  integrity sha1-kJVH7PFbrEVuJ1RZs58u9F1/y7k=
+
 csstype@^2.6.8:
   version "2.6.21"
   resolved "https://mirrors.cloud.tencent.com/npm/csstype/-/csstype-2.6.21.tgz"
@@ -391,6 +427,11 @@ debug@^4.3.4:
   dependencies:
     ms "2.1.2"
 
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://mirrors.cloud.tencent.com/npm/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha1-3zrhmayt+31ECqrgsp4icrJOxhk=
+
 element-plus@^2.2.21:
   version "2.2.21"
   resolved "https://mirrors.cloud.tencent.com/npm/element-plus/-/element-plus-2.2.21.tgz"
@@ -603,6 +644,20 @@ find-up@^5.0.0:
     locate-path "^6.0.0"
     path-exists "^4.0.0"
 
+follow-redirects@^1.15.0:
+  version "1.15.2"
+  resolved "https://mirrors.cloud.tencent.com/npm/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
+  integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
+
+form-data@^4.0.0:
+  version "4.0.0"
+  resolved "https://mirrors.cloud.tencent.com/npm/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+  integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    mime-types "^2.1.12"
+
 fsevents@~2.3.2:
   version "2.3.2"
   resolved "https://mirrors.cloud.tencent.com/npm/fsevents/-/fsevents-2.3.2.tgz"
@@ -757,6 +812,18 @@ micromatch@^4.0.4:
     braces "^3.0.2"
     picomatch "^2.3.1"
 
+mime-db@1.52.0:
+  version "1.52.0"
+  resolved "https://mirrors.cloud.tencent.com/npm/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@^2.1.12:
+  version "2.1.35"
+  resolved "https://mirrors.cloud.tencent.com/npm/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+  dependencies:
+    mime-db "1.52.0"
+
 mimic-fn@^2.1.0:
   version "2.1.0"
   resolved "https://mirrors.cloud.tencent.com/npm/mimic-fn/-/mimic-fn-2.1.0.tgz"
@@ -880,6 +947,11 @@ postcss@^8.1.10, postcss@^8.4.18:
     picocolors "^1.0.0"
     source-map-js "^1.0.2"
 
+proxy-from-env@^1.1.0:
+  version "1.1.0"
+  resolved "https://mirrors.cloud.tencent.com/npm/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+  integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
 queue-microtask@^1.2.2:
   version "1.2.3"
   resolved "https://mirrors.cloud.tencent.com/npm/queue-microtask/-/queue-microtask-1.2.3.tgz"
@@ -1078,7 +1150,7 @@ vue-demi@*:
 
 vue-router@4:
   version "4.1.6"
-  resolved "https://mirrors.cloud.tencent.com/npm/vue-router/-/vue-router-4.1.6.tgz#b70303737e12b4814578d21d68d21618469375a1"
+  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"