Quellcode durchsuchen

Merge branch 'main' into hotfix/v1.0.55.2

lianbingjie vor 1 Jahr
Ursprung
Commit
b6e8dff3fb

+ 1 - 0
apps/bigmember_pc/package.json

@@ -16,6 +16,7 @@
     "@jianyu/easy-inject-qiankun": "^0.1.11",
     "@jianyu/icon": "^0.1.7",
     "@jianyu/reset.css": "~0.1.1",
+    "@jy/vue-anti": "workspace:^",
     "@jy/data-models": "workspace:^",
     "@jy/pc-ui": "workspace:^",
     "@jy/util": "workspace:^",

+ 7 - 1
apps/bigmember_pc/src/api/index.js

@@ -1,4 +1,10 @@
-import service from './interceptors-anti'
+import service from './service'
+
+// 必须先注册反爬弹窗,后注册解密方法。因为反爬弹窗会修改拦截器
+import './interceptors-anti'
+import './interceptors-decrypt'
+
+import './interceptors'
 import './interceptors-data-models'
 
 export default service

+ 4 - 81
apps/bigmember_pc/src/api/interceptors-anti.js

@@ -1,82 +1,5 @@
-import axios from './axios'
-import $ from 'jquery'
-import qs from 'qs'
+import service from './service'
+import { registerAntiInterceptors } from '@jy/vue-anti'
 
-// 此处添加全局请求拦截
-// $.ajaxPrefilter((options, originalOptions, jqXHR) => {
-//   console.log('options', options)
-//   console.log('originalOptions', originalOptions)
-//   console.log('jqXHR', jqXHR)
-// })
-// jquery请求
-const ajax = (config) =>
-  new Promise((resolve, reject) => {
-    // 获取url参数
-    const url = config.baseURL
-      ? config.baseURL + config.url
-      : import.meta.env.VITE_APP_BASE_API + config.url
-
-    const ajaxConf = {
-      url: url,
-      method: config.method || 'get'
-    }
-
-    // 获取data数据
-    const data = config.data
-    const params = config.params
-    const headers = config.headers
-
-    if (ajaxConf.method.toLowerCase() === 'post') {
-      if (data) {
-        if (typeof data === 'string') {
-          // formData
-          ajaxConf.data = qs.parse(data)
-        } else {
-          // json
-          ajaxConf.contentType = 'application/json;charset=UTF-8'
-          ajaxConf.data = JSON.stringify(data)
-        }
-      }
-    } else if (ajaxConf.method.toLowerCase() === 'get') {
-      if (params) {
-        ajaxConf.data = params
-      }
-    }
-    if (headers) {
-      ajaxConf.headers = headers
-    }
-
-    // 此处数据预处理
-    // 此处添加请求拦截(请求发送前处理参数)
-
-    $.ajax({
-      ...ajaxConf,
-      beforeSend: (xhr) => {
-        // 此处请求预处理
-        // 此处添加请求拦截(请求发送前处理参数)
-      },
-      success: (res) => {
-        // 此处添加响应拦截
-        if (
-          import.meta.env.NODE_ENV === 'production' &&
-          config &&
-          !config.noIntercept &&
-          !window.$noIntercept
-        ) {
-          const noPermissionText = ['未登录', '需要登录', '需要登录!']
-          const noPermission = noPermissionText.includes(res.error_msg)
-          if (noPermission) {
-            location.href = '/notin/page'
-            return
-          }
-        }
-        resolve(res)
-      },
-      error: (err) => {
-        reject(err)
-      }
-    })
-  })
-
-const useJQueryAjax = !!window.antiAdd
-export default useJQueryAjax ? ajax : axios
+registerAntiInterceptors(service)
+export default service

+ 1 - 1
apps/bigmember_pc/src/api/interceptors-data-models.js

@@ -1,4 +1,4 @@
-import service from './interceptors-anti'
+import service from './service'
 import { injectRequest } from '@jy/data-models'
 
 injectRequest(service)

+ 5 - 0
apps/bigmember_pc/src/api/interceptors-decrypt.js

@@ -0,0 +1,5 @@
+import service from './service'
+import { registerDecryptInterceptor } from '@jy/vue-anti'
+
+registerDecryptInterceptor(service)
+export default service

+ 1 - 6
apps/bigmember_pc/src/api/axios.js → apps/bigmember_pc/src/api/interceptors.js

@@ -1,9 +1,4 @@
-import axios from 'axios'
-
-console.log('[debug]当前环境:', import.meta.env)
-const service = axios.create({
-  baseURL: import.meta.env.VITE_APP_BASE_API
-})
+import service from './service'
 
 service.interceptors.request.use(
   (config) => {

+ 8 - 0
apps/bigmember_pc/src/api/service.js

@@ -0,0 +1,8 @@
+import axios from 'axios'
+
+console.log('[debug]当前环境:', import.meta.env)
+const service = axios.create({
+  baseURL: import.meta.env.VITE_APP_BASE_API
+})
+
+export default service

+ 100 - 0
apps/mobile/public/decrypt-js.html

@@ -0,0 +1,100 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title></title>
+</head>
+<body>
+    <script src='https://cdn-common.jianyu360.com/cdn/lib/jsencrypt/3.3.2/jsencrypt.min.js'></script>
+    <script>
+        const config = {
+            plainKey: '', // rsa解密后的key
+            privateKey: `-----BEGIN PRIVATE KEY-----
+                MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAOhM0pNOfGeiBr+t
+                nunphCHReY3RiS4Fuc2nD3cbjKNdLezeViGmsZwHsb2SVUb6rpPHyX0+3xjXYn//
+                n39/Q8uPjWRA332TtN8MDEkSR2HMbn8ufRRt2TnlfsFDFTgBywSP7cwd0CiEdvBX
+                5w8Jifc9VbedwbeplBWyDeLLqjRjAgMBAAECgYB4es+EAuLWxNwHMb8Hxkr3VzNZ
+                8GDbc7DIDmsg9TLdz4fwH+hAD7pyGDOBBJIh/AXrM2U3BhKjSaIWjLdmYtT/kzg8
+                BxQDr9YoO7u2jvTcEE+/6p2YugYX/ngpinawFJqyM+N7Or8yRABaw6Aq8VuKtv6p
+                980Y2BBVVYn+/KorYQJBAP+9lu8iolzKRzJrFt/rosdWkOpNg5ujcSCwbxhYnYC0
+                UY85sPLsMvnLgegkpO8jocSAt586BmcsA+Q9o97qVCkCQQDoiSVegtOvG3U0mNlN
+                rCVpPEL22s9Kkwps3ZCdTl3VtUtNiyfhE8rbw/qOGti3VxMCRhpKi9hTIgeq13UG
+                67WrAkEA/WQ1c5XGd9f4eU1AKffInmf4SB8rgn+L7I7EVMQgstB3a0kHOXqs+3IX
+                shL01PliJFhBF+QfSgSDipdEke9uGQJBAOcw46xxmhDw1bizdulYi+Fy/oj7xzi3
+                tJfEObGMZpLBKtsvzThkOz4APS3n1yuBMO8Dz8PqAeu1W7YpfLqiwv0CQF68N244
+                dFebDSoZLl1hbCExpbtC7SDBpYxlIVNVqwN7ymr+Z0rIcAMVv5Ldp/bJEWaXJs9C
+                0sPCBpjDnyK9Z04=
+                -----END PRIVATE KEY-----`
+        }
+    </script>
+
+    <script>
+        var decryptTools = {
+            // rsa解密
+            rsaDecrypt: function (cipherText, privateKey) {
+                // 解密
+                var decrypt = new JSEncrypt()//创建解密对象实例
+                decrypt.setPrivateKey(privateKey)//设置秘钥
+                var uncrypted = decrypt.decrypt(cipherText)//解密之前拿公钥加密的内容
+                return uncrypted
+            },
+            // AES解密
+            async AESDecrypt(content, base64Key) {
+                const key = new TextEncoder().encode(base64Key)
+                const encryptedBase64 = content;
+                const encryptedData = Uint8Array.from(window.atob(encryptedBase64), c => c.charCodeAt(0));
+
+                const decryptData = async () => {
+                    const iv = encryptedData.slice(0, 16);
+                    const ciphertext = encryptedData.slice(16);
+
+                    const aesKey = await crypto.subtle.importKey("raw", key, {name: "AES-CTR"}, false, ["encrypt", "decrypt"]);
+
+                    const decryptedData = await crypto.subtle.decrypt({name: "AES-CTR", counter: iv, length: 128}, aesKey, ciphertext);
+                    const decryptedText = new TextDecoder().decode(decryptedData);
+
+                    return {
+                        value: decryptedText
+                    }
+                };
+
+                const result = await decryptData()
+                return result
+            }
+        }
+
+        window.addEventListener('message', async (e) => {
+            if (e.data.type !== 'decrypt') {
+                return
+            }
+            if (window === e.source) {
+                return
+            }
+
+            var base64Key = e.data.base64Key
+            var cipherText = e.data.cipherText
+            // 1. 先解密base64Key
+            var plainKey = decryptTools.rsaDecrypt(base64Key, config.privateKey)
+            config.plainKey = plainKey
+            // 2. 再用key解密cipherText
+            var plainText = await decryptTools.AESDecrypt(cipherText, plainKey)
+
+            const result = {
+                ...e.data,
+                plainKey: plainKey,
+                plainText: plainText.value,
+            }
+            sendPostMessage(e, result)
+        })
+        function sendPostMessage(e, result) {
+            const win = e.source
+            var payload = {
+                ...result,
+                type: 'after-decrypt'
+            }
+            window.parent.postMessage(payload, payload.fromOrigin)
+        }
+    </script>
+</body>
+</html>

+ 2 - 0
apps/mobile/src/api/index.js

@@ -1,5 +1,7 @@
 import service from './service'
+// 必须先注册反爬弹窗,后注册解密方法。因为反爬弹窗会修改拦截器
 import './interceptors-anti'
+import './interceptors-decrypt'
 import './interceptors'
 import './interceptors-data-models'
 

+ 5 - 0
apps/mobile/src/api/interceptors-decrypt.js

@@ -0,0 +1,5 @@
+import service from './service'
+import { registerDecryptInterceptor } from '@jy/vue-anti'
+
+registerDecryptInterceptor(service)
+export default service

+ 17 - 3
packages/vue-anti/README.md

@@ -42,7 +42,9 @@ http://192.168.3.207:8080/EFE/vue-anti.git#v0.1.0
 
 
 
-#### 调用示例
+#### 调用<反爬弹窗>示例
+
+> !!!**<反爬弹窗>和<解密方法>同时使用的情况下。必须先注册反爬弹窗,后注册解密方法。因为反爬弹窗会修改拦截器**
 
 ```js
 // 库一共提供了以下内容
@@ -124,9 +126,9 @@ console.log(iReceivedMsg)
 
 > 该库还可以支持在vue组件内使用
 >
-> 在页面弹窗可以使用 VerifyPointsPopup 组件
+> 在页面弹窗可以使用 VerifyPointsPopup 组件(不推荐,由于弹窗样式由该库单独封装,动画以及各种api不够丰富,建议使用项目组件库弹窗嵌<VerifyPoints /> 使用。)
 >
-> 嵌入到页面信息流中可以使用 VerifyPoints 组件
+> 嵌入到页面信息流中可以使用 VerifyPoints 组件(**推荐**)
 
 ```vue
 <template>
@@ -150,3 +152,15 @@ export default {
 </script>
 ```
 
+#### 调用<解密方法>示例
+
+> !!!**<反爬弹窗>和<解密方法>同时使用的情况下。必须先注册反爬弹窗,后注册解密方法。因为反爬弹窗会操作拦截器**
+
+```js
+import service from './service'
+import { registerDecryptInterceptor } from '@jy/vue-anti'
+
+registerDecryptInterceptor(service)
+export default service
+```
+

+ 0 - 1
packages/vue-anti/package.json

@@ -13,7 +13,6 @@
   "dependencies": {
     "core-js": "^3.8.3",
     "qs": "^6.11.0",
-    "vant": "2.12.44",
     "vue": "^2.6.14"
   },
   "devDependencies": {

+ 63 - 0
packages/vue-anti/src/components/AntiPopup.vue

@@ -0,0 +1,63 @@
+<template>
+  <div class="j-anti-popup-container" v-if="value">
+    <div class="j-anti-popup-overlay" @touchmove.prevent :class="overlayClass"  :style="`z-index: ${zIndex};`"></div>
+    <div class="j-anti-popup j-anti-popup--center" :class="contentClass" :style="`z-index: ${zIndex + 1};`">
+      <slot name="default"></slot>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  name: 'AntiPopup',
+  props: {
+    value: {
+      value: Boolean,
+      default: false
+    },
+    overlayClass: {
+      type: String,
+      default: ''
+    },
+    contentClass: {
+      type: String,
+      default: ''
+    },
+    zIndex: {
+      type: Number,
+      default: 9999
+    }
+  },
+  model: {
+    prop: 'value',
+    event: 'input'
+  },
+  methods: {
+    syncInput(f = false) {
+      this.$emit('input', f)
+    }
+  }
+}
+</script>
+<style lang="scss" scoped>
+.j-anti-popup-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 9;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(0,0,0,.7);
+}
+.j-anti-popup {
+  position: fixed;
+  max-height: 100%;
+  transition: transform .3s;
+  -webkit-overflow-scrolling: touch;
+}
+.j-anti-popup--center {
+  top: 45%;
+  left: 50%;
+  transform: translate3d(-50%,-50%,0);
+}
+</style>
+  

+ 5 - 2
packages/vue-anti/src/components/VerifyPoints.vue

@@ -108,9 +108,10 @@ export default {
       const pointsPanelMain = this.$refs.pointsPanelMain
       const pointsPanelMainRect = pointsPanelMain.getBoundingClientRect()
       // 校正坐标(圆点的半径)圆的位置看起来正确
-      const correctNum = 9
+      // 使用相似计算,以图片宽度256px为例,其下坐标点的半径为9
+      const correctNum = parseInt(9 * (pointsPanelMainRect.width / 256))
 
-      // offsetX/offsetY圆点的位置坐标(需要偏移进行微调)
+      // offsetX/offsetY圆点的位置坐标(需要偏移进行微调) === pointDom.offsetX/offsetY
       // offset = 鼠标相对视口坐标 - pointsMain相对视口坐标 - 校正坐标
       const offsetX = e.clientX - pointsPanelMainRect.left - correctNum
       const offsetY = e.clientY - pointsPanelMainRect.top - correctNum
@@ -177,6 +178,8 @@ $black: #000;
 .verify-points {
   position: relative;
   width: 280Px;
+  transform-origin: center center;
+  transition: transform,width 0.2s ease;
   .verify-points-logo {
     width: 100%;
     height: 160Px;

+ 21 - 12
packages/vue-anti/src/components/VerifyPointsPopup.vue

@@ -1,29 +1,27 @@
 <template>
-  <van-popup
+  <AntiPopup
     :value="value"
     @input="onInput"
-    get-container="body"
+    class="use-anti-popup-container"
     overlay-class="verify-points-overlay"
-    :close-on-click-overlay="false"
-    close-on-popstate
-    transition-appear
-    class="verify-points-popup">
+    content-class="verify-points-popup"
+    transition-appear>
     <VerifyPoints
       ref="verifyPoints"
       @refresh="onRefresh"
       @confirm="onConfirm"
       :imgBase64="imgBase64"
       :textVerify="textVerify" />
-  </van-popup>
+  </AntiPopup>
 </template>
 <script>
 import VerifyPoints from './VerifyPoints'
-import { Popup } from 'vant'
+import AntiPopup from './AntiPopup'
 
 export default {
   name: 'VerifyPointsPopup',
   components: {
-    [Popup.name]: Popup,
+    AntiPopup,
     VerifyPoints
   },
   props: {
@@ -67,8 +65,19 @@ export default {
 }
 </script>
 <style lang="scss" scoped>
-.verify-points-popup {
-  top: 45%;
-  background-color: transparent;
+// 卡片适配不同屏幕的网页
+// 屏幕大于 1024px 或小于 1440px 时应用该样式
+::v-deep {
+  @media screen and (min-width: 1024px) and (max-width: 1440px) {
+    .verify-points {
+      width: 320Px;
+    }
+  }
+  // 屏幕大于 1440px
+  @media screen and (min-width: 1440px) {
+    .verify-points {
+      width: 340Px;
+    }
+  }
 }
 </style>

+ 185 - 0
packages/vue-anti/src/decrypt/decrypt-iframe.js

@@ -0,0 +1,185 @@
+class DecryptIframe {
+  constructor() {
+    this.createIframe()
+  }
+
+  iframeOrigin = 'https://www.jianyu360.cn'
+  // iframeOrigin = 'http://localhost'
+  iframePath = '/jy_mobile/decrypt-js.html'
+  iframeEl = null // iframe的dom引用
+  iframeName = '' // iframe的name
+  iframeReady = false // 子页面是否初始化完成
+
+  taskList = [] // 任务队列,当ready=false时候,操作postMessage的回调放在列表中等待调用
+
+  receiveMap = {}
+
+  // doDecrypt解密函数promise回调缓存
+  _cacheMessagePromiseMap = {
+    // key: {
+    //   resolve,
+    //   reject
+    // }
+  }
+  _cacheWaitingReadyPromise = {
+    // resolve,
+    // reject
+  }
+
+  get iframeUrl() {
+    return this.iframePath
+  }
+  get iframeUrlBackup() {
+    return `${this.iframeOrigin}${this.iframePath}`
+  }
+
+  createIframe() {
+    const iframe = document.createElement('iframe')
+    const name = `jianyu_${Date.now()}`
+    iframe.name = name
+    iframe.src = this.iframeUrl
+    iframe.style.display = 'none'
+    this.iframeName = name
+    this.iframeEl = iframe
+    document.body.append(iframe)
+
+    this.initEvents()
+  }
+
+  getRandomString(len = 8) {
+    let randomString = ''
+    if (len) {
+      /** 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1 **/
+      const $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'
+      const maxPos = $chars.length
+      for (let i = 0; i < len; i++) {
+        randomString += $chars.charAt(Math.floor(Math.random() * maxPos))
+      }
+    } else {
+      // Math.random()  生成随机数字, eg: 0.123456
+      // .toString(36)  转化成36进制 : "0.4fzyo82mvyr"
+      // .substring(2)  去掉前面两位 : "yo82mvyr"
+      // .slice(-8)  截取最后八位 : "yo82mvyr"
+      randomString = Math.random().toString(36).substring(2)
+    }
+    return randomString
+  }
+
+  createPostMessageKey() {
+    return `${this.getRandomString()}_${this.iframeName}`
+  }
+
+  async doDecrypt(base64Key, cipherText) {
+    if (!this.iframeReady) {
+      await this.waitIframeReady()
+    }
+    return new Promise((resolve, reject) => {
+      const id = this.createPostMessageKey()
+      this.sendMessage({
+        id,
+        base64Key,
+        cipherText
+      })
+      this._cacheMessagePromiseMap[id] = {
+        resolve,
+        reject
+      }
+    })
+  }
+
+  async waitIframeReady() {
+    return new Promise((resolve, reject) => {
+      if (this.iframeReady) {
+        resolve()
+      } else {
+        this._cacheWaitingReadyPromise = {
+          resolve,
+          reject
+        }
+      }
+    })
+  }
+
+  initEvents() {
+    this.initIframeEvents()
+    this.initMessageEvents()
+  }
+
+  initIframeEvents() {
+    // iframe相关事件
+    const iframe = this.iframeEl
+    iframe.addEventListener('load', () => {
+      // iframe 中的 JavaScript 已经加载完成
+      this.iframeReady = true
+      if (this._cacheWaitingReadyPromise.resolve) {
+        this._cacheWaitingReadyPromise.resolve()
+      }
+      console.log('iframe JavaScript loaded!');
+    })
+    iframe.addEventListener('error', (e) => {
+      console.log(e)
+      if (iframe.src.includes(this.iframeUrl)) {
+        iframe.src = this.iframeUrlBackup
+      } else {
+        throw new Error('解密iframe加载失败')
+      }
+    })
+  }
+
+  onMessage(e) {
+    const id = e.data.id
+    if (e.data.type !== 'after-decrypt') {
+      if (this._cacheMessagePromiseMap[id]) {
+        this._cacheMessagePromiseMap[id].reject()
+      }
+      return
+    }
+    if (window === e.source) {
+      if (this._cacheMessagePromiseMap[id]) {
+        this._cacheMessagePromiseMap[id].reject()
+      }
+      return
+    }
+    const result = e.data
+    if (this._cacheMessagePromiseMap[id]) {
+      this._cacheMessagePromiseMap[id].resolve(result)
+    }
+  }
+
+  initMessageEvents() {
+    window.removeEventListener('message', this.onMessage.bind(this))
+    // 接收解密后的结果
+    window.addEventListener('message', this.onMessage.bind(this))
+  }
+
+  sendMessage(payload = {}) {
+    const p = {
+      ...payload,
+      fromOrigin: location.origin,
+      type: 'decrypt'
+    }
+    const win = window.frames[this.iframeName]
+    const targetOrigin = win.origin
+    win.postMessage(p, targetOrigin)
+  }
+}
+
+function SingleWrapper(cons) {
+  // 排除非函数与箭头函数
+  if (!(cons instanceof Function) || !cons.prototype) {
+    throw new Error('不是合法的构造函数')
+  }
+  var instance
+  return function () {
+    if (!instance) {
+      instance = new cons()
+    }
+    return instance
+  }
+}
+
+const SingleDecryptIframe = SingleWrapper(DecryptIframe)
+
+export function createDecryptIframe () {
+  return new SingleDecryptIframe()
+}

+ 7 - 0
packages/vue-anti/src/decrypt/index.js

@@ -0,0 +1,7 @@
+import { createDecryptIframe } from './decrypt-iframe'
+import registerDecryptInterceptor from './register-decrypt-interceptors'
+
+export {
+  createDecryptIframe,
+  registerDecryptInterceptor
+}

+ 39 - 0
packages/vue-anti/src/decrypt/register-decrypt-interceptors.js

@@ -0,0 +1,39 @@
+import { createDecryptIframe } from './decrypt-iframe'
+
+let cacheIframe = null
+export default function registerDecryptInterceptor (service) {
+  const iframe = createDecryptIframe()
+  cacheIframe = iframe
+  window.cacheIframe = cacheIframe
+
+  // 注册响应拦截器
+  service.interceptors.response.use(async (response) => {
+    const res = response.data
+    const headers = response.headers
+
+    // 如果请求不是200,有可能是403(被拉黑了),就直接跳过本次拦截
+    if (response.status !== 200) {
+      return response
+    }
+
+    // 需要验证
+    const needDecrypt1 = res.antiEncrypt && res.data
+    const needDecrypt2 = headers.antiEncrypt && res.data
+    if (needDecrypt1 || needDecrypt2) {
+      // 等待解密iframe加载完成
+      const base64Key = res.secretKey
+      const cipherText = res.data
+      const result = await iframe.doDecrypt(base64Key, cipherText)
+      if (result && result.plainText) {
+        const p = JSON.parse(result.plainText)
+        console.log('解密后的内容: -------->', p)
+        response.data = p
+      }
+      return response
+    } else {
+      return response
+    }
+  }, error => {
+    return error
+  })
+}

+ 3 - 1
packages/vue-anti/src/index.js

@@ -2,11 +2,13 @@ import VerifyPointsComponent from './components/VerifyPoints.vue'
 import VerifyPointsPopup from './components/VerifyPointsPopup.vue'
 import VerifyPointsClass from './utils/verify-popup'
 import registerAntiInterceptors from './utils/register-anti-interceptors'
+import { registerDecryptInterceptor } from './decrypt'
 
 export {
   VerifyPointsComponent,
   VerifyPointsPopup,
   VerifyPointsClass,
-  registerAntiInterceptors
+  registerAntiInterceptors,
+  registerDecryptInterceptor
 }
 

+ 10 - 23
pnpm-lock.yaml

@@ -72,6 +72,9 @@ importers:
       '@jy/util':
         specifier: workspace:^
         version: link:../../packages/util
+      '@jy/vue-anti':
+        specifier: workspace:^
+        version: link:../../packages/vue-anti
       '@sentry/vue':
         specifier: ^7.64.0
         version: 7.64.0(vue@2.7.16)
@@ -614,9 +617,6 @@ importers:
       qs:
         specifier: ^6.11.0
         version: 6.11.2
-      vant:
-        specifier: 2.12.44
-        version: 2.12.44(vue@2.7.14)
       vue:
         specifier: ^2.6.14
         version: 2.7.14
@@ -6326,7 +6326,7 @@ packages:
     dependencies:
       '@babel/core': 7.22.9
       '@vue/babel-preset-app': 5.0.8(@babel/core@7.22.9)(core-js@3.31.1)(vue@2.7.14)
-      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(sass-loader@12.6.0)(vue-template-compiler@2.7.14)(vue@2.7.14)
+      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.14)
       '@vue/cli-shared-utils': 5.0.8
       babel-loader: 8.3.0(@babel/core@7.22.9)(webpack@5.88.2)
       thread-loader: 3.0.4(webpack@5.88.2)
@@ -6371,7 +6371,7 @@ packages:
       '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0
       eslint: '>=7.5.0'
     dependencies:
-      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.16)
+      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.14)
       '@vue/cli-shared-utils': 5.0.8
       eslint: 7.32.0
       eslint-webpack-plugin: 3.2.0(eslint@7.32.0)(webpack@5.88.2)
@@ -6391,7 +6391,7 @@ packages:
     peerDependencies:
       '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0
     dependencies:
-      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.16)
+      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.14)
       '@vue/cli-shared-utils': 5.0.8
     transitivePeerDependencies:
       - encoding
@@ -6402,7 +6402,7 @@ packages:
     peerDependencies:
       '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0
     dependencies:
-      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.16)
+      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.14)
       '@vue/cli-shared-utils': 5.0.8
     transitivePeerDependencies:
       - encoding
@@ -6413,7 +6413,7 @@ packages:
     peerDependencies:
       '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0
     dependencies:
-      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.16)
+      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.14)
     dev: true
 
   /@vue/cli-plugin-vuex@5.0.8(@vue/cli-service@5.0.1):
@@ -6421,7 +6421,7 @@ packages:
     peerDependencies:
       '@vue/cli-service': ^3.0.0 || ^4.0.0 || ^5.0.0-0
     dependencies:
-      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.16)
+      '@vue/cli-service': 5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.14)
     dev: true
 
   /@vue/cli-service@5.0.1(@babel/core@7.22.9)(lodash@4.17.21)(sass-loader@12.0.0)(vue-template-compiler@2.6.14)(vue@2.7.14):
@@ -15835,19 +15835,6 @@ packages:
       spdx-expression-parse: 3.0.1
     dev: true
 
-  /vant@2.12.44(vue@2.7.14):
-    resolution: {integrity: sha512-0ZRq0v+26Ny6Ezt3M1GEN5KLEh6vVp6rBmQENi6jd0HejgpwfCRUWTFCH9sQqB3LhfxJ7ywHmrLAn5oj1eHozA==}
-    peerDependencies:
-      vue: '>= 2.6.0'
-    dependencies:
-      '@babel/runtime': 7.22.6
-      '@vant/icons': 1.8.0
-      '@vant/popperjs': 1.3.0
-      '@vue/babel-helper-vue-jsx-merge-props': 1.4.0
-      vue: 2.7.14
-      vue-lazyload: 1.2.3
-    dev: false
-
   /vant@2.12.44(vue@2.7.16):
     resolution: {integrity: sha512-0ZRq0v+26Ny6Ezt3M1GEN5KLEh6vVp6rBmQENi6jd0HejgpwfCRUWTFCH9sQqB3LhfxJ7ywHmrLAn5oj1eHozA==}
     peerDependencies:
@@ -15962,7 +15949,7 @@ packages:
       '@types/eslint': 8.44.1
       eslint: 8.37.0
       rollup: 2.79.1
-      vite: 4.3.9(less@4.1.3)(sass@1.63.2)(terser@5.19.2)
+      vite: 4.3.9(sass@1.71.1)(terser@5.19.2)
     dev: true
 
   /vite-plugin-externals@0.6.2(vite@4.3.9):