Hello小健 1 tydzień temu
commit
b8c74feffb
100 zmienionych plików z 18310 dodań i 0 usunięć
  1. 13 0
      .editorconfig
  2. 42 0
      .eslintrc.cjs
  3. 13 0
      .gitignore
  4. 23 0
      .hbuilderx/launch.json
  5. 4 0
      .husky/pre-commit
  6. 7 0
      .prettierrc.json
  7. 13 0
      .vscode/extensions.json
  8. 13 0
      .vscode/settings.json
  9. 201 0
      LICENSE
  10. 184 0
      README.md
  11. BIN
      README/images/code-android.png
  12. BIN
      README/images/code-h5.png
  13. BIN
      README/images/code-mp-weixin.png
  14. BIN
      README/images/project_structure.png
  15. BIN
      README/images/screenshot_1.jpg
  16. BIN
      README/images/screenshot_10.jpg
  17. BIN
      README/images/screenshot_11.jpg
  18. BIN
      README/images/screenshot_12.jpg
  19. BIN
      README/images/screenshot_2.jpg
  20. BIN
      README/images/screenshot_3.jpg
  21. BIN
      README/images/screenshot_4.jpg
  22. BIN
      README/images/screenshot_5.jpg
  23. BIN
      README/images/screenshot_6.jpg
  24. BIN
      README/images/screenshot_7.jpg
  25. BIN
      README/images/screenshot_8.jpg
  26. BIN
      README/images/screenshot_9.jpg
  27. 36 0
      index.html
  28. 94 0
      package.json
  29. 7916 0
      pnpm-lock.yaml
  30. 51 0
      src/App.vue
  31. 152 0
      src/components/XtxGuess.vue
  32. 40 0
      src/components/XtxSwiper.vue
  33. 31 0
      src/components/styles/XtxSwiper.scss
  34. 145 0
      src/components/vk-data-goods-sku-popup/vk-data-goods-sku-popup.d.ts
  35. 1426 0
      src/components/vk-data-goods-sku-popup/vk-data-goods-sku-popup.vue
  36. 52 0
      src/components/vk-data-input-number-box/vk-data-input-number-box.d.ts
  37. 458 0
      src/components/vk-data-input-number-box/vk-data-input-number-box.vue
  38. 21 0
      src/composables/index.ts
  39. 8 0
      src/env.d.ts
  40. 17 0
      src/main.ts
  41. 119 0
      src/manifest.json
  42. 183 0
      src/pages.json
  43. 13 0
      src/pages/cart/cart.vue
  44. 14 0
      src/pages/cart/cart2.vue
  45. 509 0
      src/pages/cart/components/CartMain.vue
  46. 98 0
      src/pages/category/category.vue
  47. 510 0
      src/pages/category/components/PageSkeleton.vue
  48. 138 0
      src/pages/category/styles/category.scss
  49. 124 0
      src/pages/goods/components/AddressPanel.vue
  50. 96 0
      src/pages/goods/components/ServicePanel.vue
  51. 533 0
      src/pages/goods/goods.vue
  52. 221 0
      src/pages/hot/hot.vue
  53. 27 0
      src/pages/index/components/CategoryPanel.vue
  54. 71 0
      src/pages/index/components/CustomNavbar.vue
  55. 33 0
      src/pages/index/components/HotPanel.vue
  56. 394 0
      src/pages/index/components/PageSkeleton.vue
  57. 114 0
      src/pages/index/index.vue
  58. 26 0
      src/pages/index/styles/category.scss
  59. 52 0
      src/pages/index/styles/hot.scss
  60. 225 0
      src/pages/login/login.vue
  61. 232 0
      src/pages/my/my.vue
  62. 263 0
      src/pagesMember/address-form/address-form.vue
  63. 194 0
      src/pagesMember/address/address.vue
  64. 330 0
      src/pagesMember/profile/profile.vue
  65. 112 0
      src/pagesMember/settings/settings.vue
  66. 392 0
      src/pagesOrder/create/create.vue
  67. 430 0
      src/pagesOrder/detail/components/PageSkeleton.vue
  68. 839 0
      src/pagesOrder/detail/detail.vue
  69. 380 0
      src/pagesOrder/list/components/OrderList.vue
  70. 105 0
      src/pagesOrder/list/list.vue
  71. 95 0
      src/pagesOrder/payment/payment.vue
  72. 59 0
      src/services/address.ts
  73. 63 0
      src/services/cart.ts
  74. 12 0
      src/services/category.ts
  75. 25 0
      src/services/constants.ts
  76. 14 0
      src/services/goods.ts
  77. 48 0
      src/services/home.ts
  78. 17 0
      src/services/hot.ts
  79. 49 0
      src/services/login.ts
  80. 142 0
      src/services/order.ts
  81. 25 0
      src/services/pay.ts
  82. 24 0
      src/services/profile.ts
  83. BIN
      src/static/images/blank.png
  84. BIN
      src/static/images/blank_cart.png
  85. BIN
      src/static/images/bubble.png
  86. BIN
      src/static/images/car.png
  87. BIN
      src/static/images/center_bg.png
  88. BIN
      src/static/images/locate.png
  89. BIN
      src/static/images/logo.png
  90. BIN
      src/static/images/logo_icon.png
  91. BIN
      src/static/images/navigator_bg.png
  92. BIN
      src/static/images/order_bg.png
  93. BIN
      src/static/images/rating_off.png
  94. BIN
      src/static/images/rating_on.png
  95. BIN
      src/static/images/remove.png
  96. BIN
      src/static/images/stars.png
  97. BIN
      src/static/tabs/cart_default.png
  98. BIN
      src/static/tabs/cart_selected.png
  99. BIN
      src/static/tabs/category_default.png
  100. BIN
      src/static/tabs/category_selected.png

+ 13 - 0
.editorconfig

@@ -0,0 +1,13 @@
+# editorconfig.org
+root = true
+
+[*]
+indent_style = space
+indent_size = 2
+end_of_line = lf
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+
+[*.md]
+trim_trailing_whitespace = false

+ 42 - 0
.eslintrc.cjs

@@ -0,0 +1,42 @@
+/* eslint-env node */
+require('@rushstack/eslint-patch/modern-module-resolution')
+
+module.exports = {
+  root: true,
+  extends: [
+    'plugin:vue/vue3-essential',
+    'eslint:recommended',
+    '@vue/eslint-config-typescript',
+    '@vue/eslint-config-prettier',
+  ],
+  // 小程序全局变量
+  globals: {
+    uni: true,
+    wx: true,
+    WechatMiniprogram: true,
+    getCurrentPages: true,
+    UniApp: true,
+    UniHelper: true,
+    Page: true,
+    AnyObject: true,
+  },
+  parserOptions: {
+    ecmaVersion: 'latest',
+  },
+  rules: {
+    'prettier/prettier': [
+      'warn',
+      {
+        singleQuote: true,
+        semi: false,
+        printWidth: 100,
+        trailingComma: 'all',
+        endOfLine: 'auto',
+      },
+    ],
+    'vue/multi-word-component-names': ['off'],
+    'vue/no-setup-props-destructure': ['off'],
+    'vue/no-deprecated-html-element-is': ['off'],
+    '@typescript-eslint/no-unused-vars': ['off'],
+  },
+}

+ 13 - 0
.gitignore

@@ -0,0 +1,13 @@
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+lerna-debug.log*
+
+node_modules
+dist
+.DS_Store
+*.local

+ 23 - 0
.hbuilderx/launch.json

@@ -0,0 +1,23 @@
+{
+  // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
+  // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
+  "version": "0.0",
+  "configurations": [
+    {
+      "app-plus": {
+        "launchtype": "local"
+      },
+      "default": {
+        "launchtype": "local"
+      },
+      "h5": {
+        "launchtype": "local"
+      },
+      "type": "uniCloud"
+    },
+    {
+      "playground": "standard",
+      "type": "uni-app:app-android"
+    }
+  ]
+}

+ 4 - 0
.husky/pre-commit

@@ -0,0 +1,4 @@
+#!/usr/bin/env sh
+. "$(dirname -- "$0")/_/husky.sh"
+
+npm run lint-staged

+ 7 - 0
.prettierrc.json

@@ -0,0 +1,7 @@
+{
+  "singleQuote": true,
+  "semi": false,
+  "printWidth": 100,
+  "trailingComma": "all",
+  "endOfLine": "auto"
+}

+ 13 - 0
.vscode/extensions.json

@@ -0,0 +1,13 @@
+{
+  // 推荐的扩展插件
+  "recommendations": [
+    "mrmaoddxxaa.create-uniapp-view", // 创建 uni-app 页面
+    "uni-helper.uni-helper-vscode", // uni-app 代码提示
+    "evils.uniapp-vscode", // uni-app 文档
+    "vue.volar", // vue3 语法支持
+    "vue.vscode-typescript-vue-plugin", // vue3 ts 插件
+    "editorconfig.editorconfig", // editorconfig
+    "dbaeumer.vscode-eslint", // eslint
+    "esbenp.prettier-vscode" // prettier
+  ]
+}

+ 13 - 0
.vscode/settings.json

@@ -0,0 +1,13 @@
+{
+  // 在保存时格式化文件
+  "editor.formatOnSave": true,
+  // 文件格式化配置
+  "[json]": {
+    "editor.defaultFormatter": "esbenp.prettier-vscode"
+  },
+  // 配置语言的文件关联
+  "files.associations": {
+    "pages.json": "jsonc", // pages.json 可以写注释
+    "manifest.json": "jsonc" // manifest.json 可以写注释
+  }
+}

+ 201 - 0
LICENSE

@@ -0,0 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 184 - 0
README.md

@@ -0,0 +1,184 @@
+## 项目简介
+
+### 项目说明
+
+小兔鲜儿体系包含五个端:微信小程序端,H5 端,App 端,PC 端,管理后台。
+
+当前仓库是 **uni-app** 开发的**微信小程序端**,通过**条件编译**能兼容 **H5 端** 和 **App 端**。
+
+### 在线体验
+
+<table>
+  <tr>
+    <td>体验小程序端</td>
+    <td>体验 H5 端</td>
+    <td>体验 App 端(安卓)</td>
+  </tr>
+  <tr>
+    <td><img width="300" src="./README/images/code-mp-weixin.png" alt=""></td>
+    <td><img width="300" src="./README/images/code-h5.png" alt=""></td>
+    <td><img width="300" src="./README/images/code-android.png" alt=""></td>
+  </tr>
+</table>
+
+## 资料说明
+
+### 📀 视频学习
+
+[https://www.bilibili.com/video/BV1Bp4y1379L/](https://www.bilibili.com/video/BV1Bp4y1379L/?share_source=copy_web&vd_source=2ac50d29193927b3c8597537dc4bc81d)
+
+### 📗 接口文档
+
+[https://www.apifox.cn/apidoc/shared-0e6ee326-d646-41bd-9214-29dbf47648fa/](https://www.apifox.cn/apidoc/shared-0e6ee326-d646-41bd-9214-29dbf47648fa/)
+
+### ✏️ 在线笔记
+
+[https://megasu.gitee.io/uni-app-shop-note/](https://megasu.gitee.io/uni-app-shop-note/)
+
+### 📦 项目源码
+
+[https://gitee.com/Megasu/uniapp-shop-vue3-ts/](https://gitee.com/Megasu/uniapp-shop-vue3-ts/)
+
+### 项目架构
+
+![项目架构图](./README/images/project_structure.png)
+
+## 项目演示
+
+### 在线演示
+
+项目已上线,微信搜索小程序 **小兔鲜儿** 即可体验。
+
+### 项目截图
+
+<table>
+  <tr>
+    <td><img width="100" src="./README/images/screenshot_1.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_2.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_3.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_4.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_5.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_6.jpg" alt=""></td>
+  </tr>
+  <tr>
+    <td><img width="100" src="./README/images/screenshot_7.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_8.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_9.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_10.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_11.jpg" alt=""></td>
+    <td><img width="100" src="./README/images/screenshot_12.jpg" alt=""></td>
+  </tr>
+</table>
+
+## 小兔鲜儿-微信小程序
+
+### 项目简介
+
+微信小程序端:该项目包含了从首页浏览商品,到商品详情,微信登录,加入购物车,提交订单,微信支付,订单管理等功能。
+
+### 技术栈
+
+- 前端框架:[uni-app](https://uniapp.dcloud.net.cn/) (Vue3 + TS)
+- 状态管理:[pinia](https://pinia.vuejs.org/zh/)
+- 组件库:[uni-ui](https://uniapp.dcloud.net.cn/component/uniui/uni-ui.html)
+
+### 项目模块
+
+- 项目起步
+- 首页模块
+- 推荐模块
+- 分类模块
+- 详情模块
+- 登录模块
+- 用户模块
+- 地址模块
+- SKU 模块
+- 购物车模块
+- 订单模块
+- 项目打包
+
+### 开发环境
+
+- Windows 版本: Windows 11 家庭中文版
+- 开发工具: VS Code 、 HbuilderX 、 微信开发者工具
+- Node 版本: v16.15.0
+- pnpm 版本:v8.6.10
+
+### 运行程序
+
+1. 安装依赖
+
+```shell
+# npm
+npm i --registry=https://registry.npmmirror.com
+
+# pnpm
+pnpm i --registry=https://registry.npmmirror.com
+```
+
+2. 运行程序
+
+```shell
+# 微信小程序端
+npm run dev:mp-weixin
+
+# H5端
+npm run dev:h5
+
+# App端
+需 HbuilderX 工具,运行 - 运行到手机或模拟器
+```
+
+3. 微信开发者工具导入 `/dist/dev/mp-weixin` 目录
+
+### 工程结构解析
+
+```
+├── .husky                     # Git Hooks
+├── .vscode                    # VS Code 插件 + 设置
+├── dist                       # 打包文件夹(可删除重新打包)
+├── src                        # 源代码
+│   ├── components             # 全局组件
+│   ├── composables            # 组合式函数
+│   ├── pages                  # 主包页面
+│       ├── index               # 首页
+│       ├── category            # 分类页
+│       ├── cart                # 购物车
+│       ├── my                  # 我的
+│       ├── goods               # 商品详情
+│       └── hot                 # 热门推荐
+│       └── login               # 登录页
+│   ├── pagesMember            # 分包页面(用户模块)
+│       ├── address             # 地址管理
+│       ├── address-form        # 地址表单
+│       ├── profile             # 用户信息
+│       └── settings            # 用户设置
+│   ├── pagesOrder             # 分包页面(订单模块)
+│       ├── create              # 创建订单
+│       ├── detail              # 订单详情
+│       ├── list                # 订单列表
+│       └── payment             # 支付结果
+│   ├── services               # 所有请求
+│   ├── static                 # 存放应用引用的本地静态资源的目录
+│       ├── images              # 普通图片
+│       └── tabs                # tabBar 图片
+│   ├── stores                 # 全局 pinia store
+│       ├── modules             # 模块
+│       └── index.ts            # store 入口
+│   ├── styles                 # 全局样式
+│       └── fonts.scss          # 字体图标
+│   ├── types                  # 类型声明文件
+│   ├── utils                  # 全局方法
+│   ├── App.vue                # 入口页面
+│   ├── main.ts                # Vue初始化入口文件
+│   ├── pages.json             # 配置页面路由等页面类信息
+│   ├── manifest.json          # 配置appid等打包信息
+│   └── uni.scss               # uni-app 内置的常用样式变量
+├── .eslintrc.cjs              # eslint 配置
+├── .prettierrc.json           # prettier 配置
+├── .gitignore                 # git 忽略文件
+├── index.html                 # H5 端首页
+├── package.json               # package.json 依赖
+├── tsconfig.json              # typescript 配置
+└── vite.config.ts             # vue-cli 配置
+```

BIN
README/images/code-android.png


BIN
README/images/code-h5.png


BIN
README/images/code-mp-weixin.png


BIN
README/images/project_structure.png


BIN
README/images/screenshot_1.jpg


BIN
README/images/screenshot_10.jpg


BIN
README/images/screenshot_11.jpg


BIN
README/images/screenshot_12.jpg


BIN
README/images/screenshot_2.jpg


BIN
README/images/screenshot_3.jpg


BIN
README/images/screenshot_4.jpg


BIN
README/images/screenshot_5.jpg


BIN
README/images/screenshot_6.jpg


BIN
README/images/screenshot_7.jpg


BIN
README/images/screenshot_8.jpg


BIN
README/images/screenshot_9.jpg


+ 36 - 0
index.html

@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta charset="UTF-8" />
+    <script>
+      var coverSupport =
+        'CSS' in window &&
+        typeof CSS.supports === 'function' &&
+        (CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
+      document.write(
+        '<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
+          (coverSupport ? ', viewport-fit=cover' : '') +
+          '" />',
+      )
+    </script>
+    <title></title>
+    <!--preload-links-->
+    <!--app-context-->
+  </head>
+
+  <body>
+    <div id="app">
+      <!--app-html-->
+    </div>
+    <script type="module" src="/src/main.ts"></script>
+    <script>
+      var _hmt = _hmt || []
+      ;(function () {
+        var hm = document.createElement('script')
+        hm.src = 'https://hm.baidu.com/hm.js?47837f06b6f8cf08ba80165ca9a80711'
+        var s = document.getElementsByTagName('script')[0]
+        s.parentNode.insertBefore(hm, s)
+      })()
+    </script>
+  </body>
+</html>

+ 94 - 0
package.json

@@ -0,0 +1,94 @@
+{
+  "name": "uni-app-xiaotuxian",
+  "version": "0.0.0",
+  "scripts": {
+    "dev:app": "uni -p app",
+    "dev:app-android": "uni -p app-android",
+    "dev:app-ios": "uni -p app-ios",
+    "dev:custom": "uni -p",
+    "dev:h5": "uni",
+    "dev:h5:ssr": "uni --ssr",
+    "dev:mp-alipay": "uni -p mp-alipay",
+    "dev:mp-baidu": "uni -p mp-baidu",
+    "dev:mp-kuaishou": "uni -p mp-kuaishou",
+    "dev:mp-lark": "uni -p mp-lark",
+    "dev:mp-qq": "uni -p mp-qq",
+    "dev:mp-toutiao": "uni -p mp-toutiao",
+    "dev:mp-weixin": "uni -p mp-weixin",
+    "dev:quickapp-webview": "uni -p quickapp-webview",
+    "dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
+    "dev:quickapp-webview-union": "uni -p quickapp-webview-union",
+    "build:app": "uni build -p app",
+    "build:app-android": "uni build -p app-android",
+    "build:app-ios": "uni build -p app-ios",
+    "build:custom": "uni build -p",
+    "build:h5": "uni build",
+    "build:h5:ssr": "uni build --ssr",
+    "build:mp-alipay": "uni build -p mp-alipay",
+    "build:mp-baidu": "uni build -p mp-baidu",
+    "build:mp-kuaishou": "uni build -p mp-kuaishou",
+    "build:mp-lark": "uni build -p mp-lark",
+    "build:mp-qq": "uni build -p mp-qq",
+    "build:mp-toutiao": "uni build -p mp-toutiao",
+    "build:mp-weixin": "uni build -p mp-weixin",
+    "build:quickapp-webview": "uni build -p quickapp-webview",
+    "build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
+    "build:quickapp-webview-union": "uni build -p quickapp-webview-union",
+    "tsc": "vue-tsc --noEmit --skipLibCheck",
+    "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
+    "prepare": "husky install",
+    "lint-staged": "lint-staged"
+  },
+  "lint-staged": {
+    "*.{js,ts,vue}": [
+      "eslint --fix"
+    ]
+  },
+  "dependencies": {
+    "@dcloudio/uni-app": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-app-plus": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-components": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-h5": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-alipay": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-baidu": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-jd": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-kuaishou": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-lark": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-qq": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-toutiao": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-weixin": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-mp-xhs": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-quickapp-webview": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-ui": "^1.4.23",
+    "pinia": "2.0.27",
+    "pinia-plugin-persistedstate": "^3.2.0",
+    "vue": "^3.2.47",
+    "vue-i18n": "^9.2.2"
+  },
+  "devDependencies": {
+    "@dcloudio/types": "^3.3.3",
+    "@dcloudio/uni-automator": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-cli-shared": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-stacktracey": "3.0.0-alpha-3081220230802001",
+    "@dcloudio/uni-vue-devtools": "3.0.0-alpha-3080220230511001",
+    "@dcloudio/vite-plugin-uni": "3.0.0-alpha-3081220230802001",
+    "@rushstack/eslint-patch": "^1.1.4",
+    "@types/node": "^18.11.9",
+    "@uni-helper/uni-app-types": "^0.5.8",
+    "@uni-helper/uni-ui-types": "^0.5.11",
+    "@vue/eslint-config-prettier": "^7.0.0",
+    "@vue/eslint-config-typescript": "^11.0.0",
+    "@vue/runtime-core": "^3.2.45",
+    "@vue/tsconfig": "^0.4.0",
+    "eslint": "^8.22.0",
+    "eslint-plugin-vue": "^9.3.0",
+    "husky": "^8.0.0",
+    "lint-staged": "^13.0.3",
+    "miniprogram-api-typings": "^3.12.0",
+    "prettier": "^2.7.1",
+    "sass": "^1.56.1",
+    "typescript": "^5.1.6",
+    "vite": "^4.4.9",
+    "vue-tsc": "^1.8.8"
+  }
+}

+ 7916 - 0
pnpm-lock.yaml

@@ -0,0 +1,7916 @@
+lockfileVersion: '6.0'
+
+settings:
+  autoInstallPeers: true
+  excludeLinksFromLockfile: false
+
+dependencies:
+  '@dcloudio/uni-app':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(@dcloudio/types@3.3.3)(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-app-plus':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vite@4.4.9)(vue@3.3.4)
+  '@dcloudio/uni-components':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-h5':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-alipay':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-baidu':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-jd':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-kuaishou':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-lark':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-qq':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-toutiao':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-weixin':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-mp-xhs':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-quickapp-webview':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-ui':
+    specifier: ^1.4.23
+    version: 1.4.23
+  pinia:
+    specifier: 2.0.27
+    version: 2.0.27(typescript@5.1.6)(vue@3.3.4)
+  pinia-plugin-persistedstate:
+    specifier: ^3.2.0
+    version: 3.2.0(pinia@2.0.27)
+  vue:
+    specifier: ^3.2.47
+    version: 3.3.4
+  vue-i18n:
+    specifier: ^9.2.2
+    version: 9.2.2(vue@3.3.4)
+
+devDependencies:
+  '@dcloudio/types':
+    specifier: ^3.3.3
+    version: 3.3.3
+  '@dcloudio/uni-automator':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(jest-environment-node@27.5.1)(jest@27.0.4)(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-cli-shared':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/uni-stacktracey':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001
+  '@dcloudio/uni-vue-devtools':
+    specifier: 3.0.0-alpha-3080220230511001
+    version: 3.0.0-alpha-3080220230511001(postcss@8.4.27)(vue@3.3.4)
+  '@dcloudio/vite-plugin-uni':
+    specifier: 3.0.0-alpha-3081220230802001
+    version: 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vite@4.4.9)(vue@3.3.4)
+  '@rushstack/eslint-patch':
+    specifier: ^1.1.4
+    version: 1.2.0
+  '@types/node':
+    specifier: ^18.11.9
+    version: 18.11.10
+  '@uni-helper/uni-app-types':
+    specifier: ^0.5.8
+    version: 0.5.8(typescript@5.1.6)
+  '@uni-helper/uni-ui-types':
+    specifier: ^0.5.11
+    version: 0.5.11(@uni-helper/uni-app-types@0.5.8)
+  '@vue/eslint-config-prettier':
+    specifier: ^7.0.0
+    version: 7.0.0(eslint@8.28.0)(prettier@2.8.0)
+  '@vue/eslint-config-typescript':
+    specifier: ^11.0.0
+    version: 11.0.2(eslint-plugin-vue@9.8.0)(eslint@8.28.0)(typescript@5.1.6)
+  '@vue/runtime-core':
+    specifier: ^3.2.45
+    version: 3.2.45
+  '@vue/tsconfig':
+    specifier: ^0.4.0
+    version: 0.4.0
+  eslint:
+    specifier: ^8.22.0
+    version: 8.28.0
+  eslint-plugin-vue:
+    specifier: ^9.3.0
+    version: 9.8.0(eslint@8.28.0)
+  husky:
+    specifier: ^8.0.0
+    version: 8.0.2
+  lint-staged:
+    specifier: ^13.0.3
+    version: 13.0.4
+  miniprogram-api-typings:
+    specifier: ^3.12.0
+    version: 3.12.0
+  prettier:
+    specifier: ^2.7.1
+    version: 2.8.0
+  sass:
+    specifier: ^1.56.1
+    version: 1.56.1
+  typescript:
+    specifier: ^5.1.6
+    version: 5.1.6
+  vite:
+    specifier: ^4.4.9
+    version: 4.4.9(@types/node@18.11.10)(sass@1.56.1)(terser@5.16.0)
+  vue-tsc:
+    specifier: ^1.8.8
+    version: 1.8.8(typescript@5.1.6)
+
+packages:
+
+  /@ampproject/remapping@2.2.0:
+    resolution: {integrity: sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/gen-mapping': 0.1.1
+      '@jridgewell/trace-mapping': 0.3.17
+
+  /@babel/code-frame@7.21.4:
+    resolution: {integrity: sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/highlight': 7.18.6
+
+  /@babel/compat-data@7.21.7:
+    resolution: {integrity: sha512-KYMqFYTaenzMK4yUtf4EW9wc4N9ef80FsbMtkwool5zpwl4YrT1SdWYSTRcT94KO4hannogdS+LxY7L+arP3gA==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/core@7.21.8:
+    resolution: {integrity: sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@ampproject/remapping': 2.2.0
+      '@babel/code-frame': 7.21.4
+      '@babel/generator': 7.21.5
+      '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8)
+      '@babel/helper-module-transforms': 7.21.5
+      '@babel/helpers': 7.21.5
+      '@babel/parser': 7.21.8
+      '@babel/template': 7.20.7
+      '@babel/traverse': 7.21.5
+      '@babel/types': 7.21.5
+      convert-source-map: 1.9.0
+      debug: 4.3.4
+      gensync: 1.0.0-beta.2
+      json5: 2.2.3
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+
+  /@babel/generator@7.21.5:
+    resolution: {integrity: sha512-SrKK/sRv8GesIW1bDagf9cCG38IOMYZusoe1dfg0D8aiUe3Amvoj1QtjTPAWcfrZFvIwlleLb0gxzQidL9w14w==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+      '@jridgewell/gen-mapping': 0.3.2
+      '@jridgewell/trace-mapping': 0.3.17
+      jsesc: 2.5.2
+
+  /@babel/helper-annotate-as-pure@7.18.6:
+    resolution: {integrity: sha512-duORpUiYrEpzKIop6iNbjnwKLAKnJ47csTyRACyEmWj0QdUrm5aqNJGHSSEQSUAvNW0ojX0dOmK9dZduvkfeXA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@babel/helper-builder-binary-assignment-operator-visitor@7.18.9:
+    resolution: {integrity: sha512-yFQ0YCHoIqarl8BCRwBL8ulYUaZpz3bNsA7oFepAzee+8/+ImtADXNOmO5vJvsPff3qi+hvpkY/NYBTrBQgdNw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-explode-assignable-expression': 7.18.6
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@babel/helper-compilation-targets@7.21.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/compat-data': 7.21.7
+      '@babel/core': 7.21.8
+      '@babel/helper-validator-option': 7.21.0
+      browserslist: 4.21.5
+      lru-cache: 5.1.1
+      semver: 6.3.0
+
+  /@babel/helper-create-class-features-plugin@7.21.8(@babel/core@7.21.8):
+    resolution: {integrity: sha512-+THiN8MqiH2AczyuZrnrKL6cAxFRRQDKW9h1YkBvbgKmAm6mwiacig1qT73DHIWMGo40GRnsEfN3LA+E6NtmSw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-annotate-as-pure': 7.18.6
+      '@babel/helper-environment-visitor': 7.21.5
+      '@babel/helper-function-name': 7.21.0
+      '@babel/helper-member-expression-to-functions': 7.21.5
+      '@babel/helper-optimise-call-expression': 7.18.6
+      '@babel/helper-replace-supers': 7.21.5
+      '@babel/helper-skip-transparent-expression-wrappers': 7.20.0
+      '@babel/helper-split-export-declaration': 7.18.6
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helper-create-regexp-features-plugin@7.20.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-m68B1lkg3XDGX5yCvGO0kPx3v9WIYLnzjKfPcQiwntEQa5ZeRkPmo2X/ISJc8qxWGfwUr+kvZAeEzAwLec2r2w==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-annotate-as-pure': 7.18.6
+      regexpu-core: 5.3.0
+    dev: true
+
+  /@babel/helper-define-polyfill-provider@0.3.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-z5aQKU4IzbqCC1XH0nAqfsFLMVSo22SBKUc0BxGrLkolTdPTructy0ToNnlO2zA4j9Q/7pjMZf0DSY+DSTYzww==}
+    peerDependencies:
+      '@babel/core': ^7.4.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+      debug: 4.3.4
+      lodash.debounce: 4.0.8
+      resolve: 1.22.1
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helper-environment-visitor@7.21.5:
+    resolution: {integrity: sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/helper-explode-assignable-expression@7.18.6:
+    resolution: {integrity: sha512-eyAYAsQmB80jNfg4baAtLeWAQHfHFiR483rzFK+BhETlGZaQC9bsfrugfXDCbRHLQbIA7U5NxhhOxN7p/dWIcg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@babel/helper-function-name@7.21.0:
+    resolution: {integrity: sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.20.7
+      '@babel/types': 7.21.5
+
+  /@babel/helper-hoist-variables@7.18.6:
+    resolution: {integrity: sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+
+  /@babel/helper-member-expression-to-functions@7.21.5:
+    resolution: {integrity: sha512-nIcGfgwpH2u4n9GG1HpStW5Ogx7x7ekiFHbjjFRKXbn5zUvqO9ZgotCO4x1aNbKn/x/xOUaXEhyNHCwtFCpxWg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@babel/helper-module-imports@7.21.4:
+    resolution: {integrity: sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+
+  /@babel/helper-module-transforms@7.21.5:
+    resolution: {integrity: sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-environment-visitor': 7.21.5
+      '@babel/helper-module-imports': 7.21.4
+      '@babel/helper-simple-access': 7.21.5
+      '@babel/helper-split-export-declaration': 7.18.6
+      '@babel/helper-validator-identifier': 7.19.1
+      '@babel/template': 7.20.7
+      '@babel/traverse': 7.21.5
+      '@babel/types': 7.21.5
+    transitivePeerDependencies:
+      - supports-color
+
+  /@babel/helper-optimise-call-expression@7.18.6:
+    resolution: {integrity: sha512-HP59oD9/fEHQkdcbgFCnbmgH5vIQTJbxh2yf+CdM89/glUNnuzr87Q8GIjGEnOktTROemO0Pe0iPAYbqZuOUiA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@babel/helper-plugin-utils@7.20.2:
+    resolution: {integrity: sha512-8RvlJG2mj4huQ4pZ+rU9lqKi9ZKiRmuvGuM2HlWmkmgOhbs6zEAw6IEiJ5cQqGbDzGZOhwuOQNtZMi/ENLjZoQ==}
+    engines: {node: '>=6.9.0'}
+    dev: true
+
+  /@babel/helper-remap-async-to-generator@7.18.9(@babel/core@7.21.8):
+    resolution: {integrity: sha512-dI7q50YKd8BAv3VEfgg7PS7yD3Rtbi2J1XMXaalXO0W0164hYLnh8zpjRS0mte9MfVp/tltvr/cfdXPvJr1opA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-annotate-as-pure': 7.18.6
+      '@babel/helper-environment-visitor': 7.21.5
+      '@babel/helper-wrap-function': 7.20.5
+      '@babel/types': 7.21.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helper-replace-supers@7.21.5:
+    resolution: {integrity: sha512-/y7vBgsr9Idu4M6MprbOVUfH3vs7tsIfnVWv/Ml2xgwvyH6LTngdfbf5AdsKwkJy4zgy1X/kuNrEKvhhK28Yrg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-environment-visitor': 7.21.5
+      '@babel/helper-member-expression-to-functions': 7.21.5
+      '@babel/helper-optimise-call-expression': 7.18.6
+      '@babel/template': 7.20.7
+      '@babel/traverse': 7.21.5
+      '@babel/types': 7.21.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helper-simple-access@7.21.5:
+    resolution: {integrity: sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+
+  /@babel/helper-skip-transparent-expression-wrappers@7.20.0:
+    resolution: {integrity: sha512-5y1JYeNKfvnT8sZcK9DVRtpTbGiomYIHviSP3OQWmDPU3DeH4a1ZlT/N2lyQ5P8egjcRaT/Y9aNqUxK0WsnIIg==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@babel/helper-split-export-declaration@7.18.6:
+    resolution: {integrity: sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/types': 7.21.5
+
+  /@babel/helper-string-parser@7.21.5:
+    resolution: {integrity: sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/helper-validator-identifier@7.19.1:
+    resolution: {integrity: sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/helper-validator-option@7.21.0:
+    resolution: {integrity: sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==}
+    engines: {node: '>=6.9.0'}
+
+  /@babel/helper-wrap-function@7.20.5:
+    resolution: {integrity: sha512-bYMxIWK5mh+TgXGVqAtnu5Yn1un+v8DDZtqyzKRLUzrh70Eal2O3aZ7aPYiMADO4uKlkzOiRiZ6GX5q3qxvW9Q==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-function-name': 7.21.0
+      '@babel/template': 7.20.7
+      '@babel/traverse': 7.21.5
+      '@babel/types': 7.21.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/helpers@7.21.5:
+    resolution: {integrity: sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/template': 7.20.7
+      '@babel/traverse': 7.21.5
+      '@babel/types': 7.21.5
+    transitivePeerDependencies:
+      - supports-color
+
+  /@babel/highlight@7.18.6:
+    resolution: {integrity: sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-validator-identifier': 7.19.1
+      chalk: 2.4.2
+      js-tokens: 4.0.0
+
+  /@babel/parser@7.21.8:
+    resolution: {integrity: sha512-6zavDGdzG3gUqAdWvlLFfk+36RilI+Pwyuuh7HItyeScCWP3k6i8vKclAQ0bM/0y/Kz/xiwvxhMv9MgTJP5gmA==}
+    engines: {node: '>=6.0.0'}
+    hasBin: true
+    dependencies:
+      '@babel/types': 7.21.5
+
+  /@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Dgxsyg54Fx1d4Nge8UnvTrED63vrwOdPmyvPzlNN/boaliRP54pm3pGzZD1SJUwrBA+Cs/xdG8kXX6Mn/RfISQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-sbr9+wNE5aXMBBFBICk01tt7sBf2Oc9ikRFEcem/ZORup9IMUdNhW7/wVLEbbtlWOsEubJet46mHAL2C8+2jKQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.13.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-skip-transparent-expression-wrappers': 7.20.0
+      '@babel/plugin-proposal-optional-chaining': 7.20.7(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-async-generator-functions@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-xMbiLsn/8RK7Wq7VeVytytS2L6qE69bXPB10YCmMdDZbKF4okCqY74pI/jJQ/8U0b/F6NrT2+14b8/P9/3AMGA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-environment-visitor': 7.21.5
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.8)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.8)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-proposal-class-properties@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-proposal-class-static-block@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-AveGOoi9DAjUYYuUAG//Ig69GlazLnoyzMw68VCDux+c1tsnnH/OkYcpz/5xzMkEFC6UxjR5Gw1c+iY2wOGVeQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.12.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.8)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-proposal-dynamic-import@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-1auuwmK+Rz13SJj36R+jqFPMJWyKEDd7lLSdOj4oJK0UTgGueSAtkrCvz9ewmgyU/P941Rv2fQwZJN8s6QruXw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-export-namespace-from@7.18.9(@babel/core@7.21.8):
+    resolution: {integrity: sha512-k1NtHyOMvlDDFeb9G5PhUXuGj8m/wiwojgQVEhJ/fsVsMCpLyOP4h0uGEjYJKrRI+EVPlb5Jk+Gt9P97lOGwtA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-json-strings@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-lr1peyn9kOdbYc0xr0OdHTZ5FMqS6Di+H0Fz2I/JwMzGmzJETNeOFq2pBySw6X/KFL5EWDjlJuMsUGRFb8fQgQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-logical-assignment-operators@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-y7C7cZgpMIjWlKE5T7eJwp+tnRYM89HmRvWM5EQuB5BoHEONjmQ8lSNmBUwOyy/GFRsohJED51YBF79hE1djug==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-nullish-coalescing-operator@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-wQxQzxYeJqHcfppzBDnm1yAY0jSRkUXR2z8RePZYrKwMKgMlE8+Z6LUno+bd6LvbGh8Gltvy74+9pIYkr+XkKA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-numeric-separator@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-ozlZFogPqoLm8WBr5Z8UckIoE4YQ5KESVcNudyXOR8uqIkliTEgJ3RoketfG6pmzLdeZF0H/wjE9/cCEitBl7Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-object-rest-spread@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-d2S98yCiLxDVmBmE8UjGcfPvNEUbA1U5q5WxaWFUGRzJSVAZqm5W6MbPct0jxnegUZ0niLeNX+IOzEs7wYg9Dg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/compat-data': 7.21.7
+      '@babel/core': 7.21.8
+      '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-transform-parameters': 7.20.7(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-optional-catch-binding@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Q40HEhs9DJQyaZfUjjn6vE8Cv4GmMHCYuMGIWUnlxH6400VGxOuwWsPt4FxXxJkC/5eOzgn0z21M9gMT4MOhbw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-optional-chaining@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-T+A7b1kfjtRM51ssoOfS1+wbyCVqorfyZhT99TvxxLMirPShD8CzKMRepMlCBGM5RpHMbn8s+5MMHnPstJH6mQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-skip-transparent-expression-wrappers': 7.20.0
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.8)
+    dev: true
+
+  /@babel/plugin-proposal-private-methods@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-nutsvktDItsNn4rpGItSNV2sz1XwS+nfU0Rg8aCx3W3NOKVzdMjJRu0O5OkgDp3ZGICSTbgRpxZoWsxoKRvbeA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-proposal-private-property-in-object@7.20.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Vq7b9dUA12ByzB4EjQTPo25sFhY+08pQDBSZRtUAkj7lb7jahaHR5igera16QZ+3my1nYR4dKsNdYj5IjPHilQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-annotate-as-pure': 7.18.6
+      '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.8)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-proposal-unicode-property-regex@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-2BShG/d5yoZyXZfVePH91urL5wTG6ASZU9M4o03lKK8u8UW1y08OMttBSOADTcJrnPMpvDXRG3G8fyLh4ovs8w==}
+    engines: {node: '>=4'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.21.8):
+    resolution: {integrity: sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.21.8):
+    resolution: {integrity: sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-5gdGbFon+PszYzqs83S3E5mpi7/y/8M9eC90MRTZfduQOYW76ig6SOSPNe41IG5LoP3FGBn2N0RjVDSQiS94kQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-export-namespace-from@7.8.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-MXf5laXo6c1IbEbegDmzGPwGNTsHZmEy6QGznu5Sh2UCWvueywb2ee+CCE4zQiZstxU9BMoQO9i6zUFSY0Kj0Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-import-assertions@7.20.0(@babel/core@7.21.8):
+    resolution: {integrity: sha512-IUh1vakzNoWalR8ch/areW7qFopR2AEw03JlG7BbrDqmQ4X3q9uuipQwSGrUn7oGiemKjtSLDhNtQHzMHr1JdQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-jsx@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-6mmljtAedFGTWu2p/8WIORGwy+61PLgOMPOdazc7YoJ9ZCWUyFy3A6CpPkRKLKD1ToAesxX8KGEViAiLo9N+7Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.21.8):
+    resolution: {integrity: sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.21.8):
+    resolution: {integrity: sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-syntax-typescript@7.20.0(@babel/core@7.21.8):
+    resolution: {integrity: sha512-rd9TkG+u1CExzS4SM1BlMEhMXwFLKVjOAFFCDx9PbX5ycJWDoWMcwdJH9RhkPu1dOgn5TrxLot/Gx6lWFuAUNQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-arrow-functions@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-3poA5E7dzDomxj9WXWwuD6A5F3kc7VXwIJO+E+J8qtDtS+pXPAhrgEyh+9GBwBgPq1Z+bB+/JD60lp5jsN7JPQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-async-to-generator@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Uo5gwHPT9vgnSXQxqGtpdufUiWp96gk7yiP4Mp5bm1QMkEmLXBO7PAGYbKoJ6DhAwiNkcHFBol/x5zZZkL/t0Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-module-imports': 7.21.4
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-remap-async-to-generator': 7.18.9(@babel/core@7.21.8)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-transform-block-scoped-functions@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-ExUcOqpPWnliRcPqves5HJcJOvHvIIWfuS4sroBUenPuMdmW+SMHDakmtS7qOo13sVppmUijqeTv7qqGsvURpQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-block-scoping@7.20.15(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Vv4DMZ6MiNOhu/LdaZsT/bsLRxgL94d269Mv4R/9sp6+Mp++X/JqypZYypJXLlM4mlL352/Egzbzr98iABH1CA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-classes@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-LWYbsiXTPKl+oBlXUGlwNlJZetXD5Am+CyBdqhPsDVjM9Jc8jwBJFrKhHf900Kfk2eZG1y9MAG3UNajol7A4VQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-annotate-as-pure': 7.18.6
+      '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8)
+      '@babel/helper-environment-visitor': 7.21.5
+      '@babel/helper-function-name': 7.21.0
+      '@babel/helper-optimise-call-expression': 7.18.6
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-replace-supers': 7.21.5
+      '@babel/helper-split-export-declaration': 7.18.6
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-transform-computed-properties@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Lz7MvBK6DTjElHAmfu6bfANzKcxpyNPeYBGEafyA6E5HtRpjpZwU+u7Qrgz/2OR0z+5TvKYbPdphfSaAcZBrYQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/template': 7.20.7
+    dev: true
+
+  /@babel/plugin-transform-destructuring@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Xwg403sRrZb81IVB79ZPqNQME23yhugYVqgTxAhT99h485F4f+GMELFhhOsscDUB7HCswepKeCKLn/GZvUKoBA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-dotall-regex@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-6S3jpun1eEbAxq7TdjLotAsl4WpQI9DxfkycRcKrjhQYzU87qpXdknpBg/e+TdcMehqGnLFi7tnFUBR02Vq6wg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-duplicate-keys@7.18.9(@babel/core@7.21.8):
+    resolution: {integrity: sha512-d2bmXCtZXYc59/0SanQKbiWINadaJXqtvIQIzd4+hNwkWBgyCd5F/2t1kXoUdvPMrxzPvhK6EMQRROxsue+mfw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-exponentiation-operator@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-wzEtc0+2c88FVR34aQmiz56dxEkxr2g8DQb/KfaFa1JYXOFVsbhvAonFN6PwVWj++fKmku8NP80plJ5Et4wqHw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-builder-binary-assignment-operator-visitor': 7.18.9
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-for-of@7.18.8(@babel/core@7.21.8):
+    resolution: {integrity: sha512-yEfTRnjuskWYo0k1mHUqrVWaZwrdq8AYbfrpqULOJOaucGSp4mNMVps+YtA8byoevxS/urwU75vyhQIxcCgiBQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-function-name@7.18.9(@babel/core@7.21.8):
+    resolution: {integrity: sha512-WvIBoRPaJQ5yVHzcnJFor7oS5Ls0PYixlTYE63lCj2RtdQEl15M68FXQlxnG6wdraJIXRdR7KI+hQ7q/9QjrCQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8)
+      '@babel/helper-function-name': 7.21.0
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-literals@7.18.9(@babel/core@7.21.8):
+    resolution: {integrity: sha512-IFQDSRoTPnrAIrI5zoZv73IFeZu2dhu6irxQjY9rNjTT53VmKg9fenjvoiOWOkJ6mm4jKVPtdMzBY98Fp4Z4cg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-member-expression-literals@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-qSF1ihLGO3q+/g48k85tUjD033C29TNTVB2paCwZPVmOsjn9pClvYYrM2VeJpBY2bcNkuny0YUyTNRyRxJ54KA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-modules-amd@7.20.11(@babel/core@7.21.8):
+    resolution: {integrity: sha512-NuzCt5IIYOW0O30UvqktzHYR2ud5bOWbY0yaxWZ6G+aFzOMJvrs5YHNikrbdaT15+KNO31nPOy5Fim3ku6Zb5g==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-module-transforms': 7.21.5
+      '@babel/helper-plugin-utils': 7.20.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-transform-modules-commonjs@7.20.11(@babel/core@7.21.8):
+    resolution: {integrity: sha512-S8e1f7WQ7cimJQ51JkAaDrEtohVEitXjgCGAS2N8S31Y42E+kWwfSz83LYz57QdBm7q9diARVqanIaH2oVgQnw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-module-transforms': 7.21.5
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-simple-access': 7.21.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-transform-modules-systemjs@7.20.11(@babel/core@7.21.8):
+    resolution: {integrity: sha512-vVu5g9BPQKSFEmvt2TA4Da5N+QVS66EX21d8uoOihC+OCpUoGvzVsXeqFdtAEfVa5BILAeFt+U7yVmLbQnAJmw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-hoist-variables': 7.18.6
+      '@babel/helper-module-transforms': 7.21.5
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-validator-identifier': 7.19.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-transform-modules-umd@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-dcegErExVeXcRqNtkRU/z8WlBLnvD4MRnHgNs3MytRO1Mn1sHRyhbcpYbVMGclAqOjdW+9cfkdZno9dFdfKLfQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-module-transforms': 7.21.5
+      '@babel/helper-plugin-utils': 7.20.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-transform-named-capturing-groups-regex@7.20.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-mOW4tTzi5iTLnw+78iEq3gr8Aoq4WNRGpmSlrogqaiCBoR1HFhpU4JkpQFOHfeYx3ReVIFWOQJS4aZBRvuZ6mA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-new-target@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-DjwFA/9Iu3Z+vrAn+8pBUGcjhxKguSMlsFqeCKbhb9BAV756v0krzVK04CRDi/4aqmk8BsHb4a/gFcaA5joXRw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-object-super@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-uvGz6zk+pZoS1aTZrOvrbj6Pp/kK2mp45t2B+bTDre2UgsZZ8EZLSJtUg7m/no0zOJUWgFONpB7Zv9W2tSaFlA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-replace-supers': 7.21.5
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-transform-parameters@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-WiWBIkeHKVOSYPO0pWkxGPfKeWrCJyD3NJ53+Lrp/QMSZbsVPovrVl2aWZ19D/LTVnaDv5Ap7GJ/B2CTOZdrfA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-property-literals@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-cYcs6qlgafTud3PAzrrRNbQtfpQ8+y/+M5tKmksS9+M1ckbH6kzY8MrexEM9mcA6JDsukE19iIRvAyYl463sMg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-regenerator@7.20.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-kW/oO7HPBtntbsahzQ0qSE3tFvkFwnbozz3NWFhLGqH75vLEg+sCGngLlhVkePlCs3Jv0dBBHDzCHxNiFAQKCQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      regenerator-transform: 0.15.1
+    dev: true
+
+  /@babel/plugin-transform-reserved-words@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-oX/4MyMoypzHjFrT1CdivfKZ+XvIPMFXwwxHp/r0Ddy2Vuomt4HDFGmft1TAY2yiTKiNSsh3kjBAzcM8kSdsjA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-shorthand-properties@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-eCLXXJqv8okzg86ywZJbRn19YJHU4XUa55oz2wbHhaQVn/MM+XhukiT7SYqp/7o00dg52Rj51Ny+Ecw4oyoygw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-spread@7.20.7(@babel/core@7.21.8):
+    resolution: {integrity: sha512-ewBbHQ+1U/VnH1fxltbJqDeWBU1oNLG8Dj11uIv3xVf7nrQu0bPGe5Rf716r7K5Qz+SqtAOVswoVunoiBtGhxw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-skip-transparent-expression-wrappers': 7.20.0
+    dev: true
+
+  /@babel/plugin-transform-sticky-regex@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-kfiDrDQ+PBsQDO85yj1icueWMfGfJFKN1KCkndygtu/C9+XUfydLC8Iv5UYJqRwy4zk8EcplRxEOeLyjq1gm6Q==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-template-literals@7.18.9(@babel/core@7.21.8):
+    resolution: {integrity: sha512-S8cOWfT82gTezpYOiVaGHrCbhlHgKhQt8XH5ES46P2XWmX92yisoZywf5km75wv5sYcXDUCLMmMxOLCtthDgMA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-typeof-symbol@7.18.9(@babel/core@7.21.8):
+    resolution: {integrity: sha512-SRfwTtF11G2aemAZWivL7PD+C9z52v9EvMqH9BuYbabyPuKUvSWks3oCg6041pT925L4zVFqaVBeECwsmlguEw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-typescript@7.21.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-RQxPz6Iqt8T0uw/WsJNReuBpWpBqs/n7mNo18sKLoTbMp+UrEekhH+pKSVC7gWz+DNjo9gryfV8YzCiT45RgMw==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-annotate-as-pure': 7.18.6
+      '@babel/helper-create-class-features-plugin': 7.21.8(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.21.8)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/plugin-transform-unicode-escapes@7.18.10(@babel/core@7.21.8):
+    resolution: {integrity: sha512-kKAdAI+YzPgGY/ftStBFXTI1LZFju38rYThnfMykS+IXy8BVx+res7s2fxf1l8I35DV2T97ezo6+SGrXz6B3iQ==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/plugin-transform-unicode-regex@7.18.6(@babel/core@7.21.8):
+    resolution: {integrity: sha512-gE7A6Lt7YLnNOL3Pb9BNeZvi+d8l7tcRrG4+pwJjK9hD2xX4mEvjlQW60G9EEmfXVYRPv9VRQcyegIVHCql/AA==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-create-regexp-features-plugin': 7.20.5(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+    dev: true
+
+  /@babel/preset-env@7.20.2(@babel/core@7.21.8):
+    resolution: {integrity: sha512-1G0efQEWR1EHkKvKHqbG+IN/QdgwfByUpM5V5QroDzGV2t3S/WXNQd693cHiHTlCFMpr9B6FkPFXDA2lQcKoDg==}
+    engines: {node: '>=6.9.0'}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/compat-data': 7.21.7
+      '@babel/core': 7.21.8
+      '@babel/helper-compilation-targets': 7.21.5(@babel/core@7.21.8)
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/helper-validator-option': 7.21.0
+      '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-proposal-async-generator-functions': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-proposal-class-properties': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-proposal-class-static-block': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-proposal-dynamic-import': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-proposal-export-namespace-from': 7.18.9(@babel/core@7.21.8)
+      '@babel/plugin-proposal-json-strings': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-proposal-logical-assignment-operators': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-proposal-nullish-coalescing-operator': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-proposal-numeric-separator': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-proposal-object-rest-spread': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-proposal-optional-catch-binding': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-proposal-optional-chaining': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-proposal-private-methods': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-proposal-private-property-in-object': 7.20.5(@babel/core@7.21.8)
+      '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.8)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.8)
+      '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.21.8)
+      '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-export-namespace-from': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-import-assertions': 7.20.0(@babel/core@7.21.8)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.8)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.8)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.21.8)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.8)
+      '@babel/plugin-transform-arrow-functions': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-transform-async-to-generator': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-transform-block-scoped-functions': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-block-scoping': 7.20.15(@babel/core@7.21.8)
+      '@babel/plugin-transform-classes': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-transform-computed-properties': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-transform-destructuring': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-duplicate-keys': 7.18.9(@babel/core@7.21.8)
+      '@babel/plugin-transform-exponentiation-operator': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-for-of': 7.18.8(@babel/core@7.21.8)
+      '@babel/plugin-transform-function-name': 7.18.9(@babel/core@7.21.8)
+      '@babel/plugin-transform-literals': 7.18.9(@babel/core@7.21.8)
+      '@babel/plugin-transform-member-expression-literals': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-modules-amd': 7.20.11(@babel/core@7.21.8)
+      '@babel/plugin-transform-modules-commonjs': 7.20.11(@babel/core@7.21.8)
+      '@babel/plugin-transform-modules-systemjs': 7.20.11(@babel/core@7.21.8)
+      '@babel/plugin-transform-modules-umd': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-named-capturing-groups-regex': 7.20.5(@babel/core@7.21.8)
+      '@babel/plugin-transform-new-target': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-object-super': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-parameters': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-transform-property-literals': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-regenerator': 7.20.5(@babel/core@7.21.8)
+      '@babel/plugin-transform-reserved-words': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-shorthand-properties': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-spread': 7.20.7(@babel/core@7.21.8)
+      '@babel/plugin-transform-sticky-regex': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-template-literals': 7.18.9(@babel/core@7.21.8)
+      '@babel/plugin-transform-typeof-symbol': 7.18.9(@babel/core@7.21.8)
+      '@babel/plugin-transform-unicode-escapes': 7.18.10(@babel/core@7.21.8)
+      '@babel/plugin-transform-unicode-regex': 7.18.6(@babel/core@7.21.8)
+      '@babel/preset-modules': 0.1.5(@babel/core@7.21.8)
+      '@babel/types': 7.21.5
+      babel-plugin-polyfill-corejs2: 0.3.3(@babel/core@7.21.8)
+      babel-plugin-polyfill-corejs3: 0.6.0(@babel/core@7.21.8)
+      babel-plugin-polyfill-regenerator: 0.4.1(@babel/core@7.21.8)
+      core-js-compat: 3.28.0
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@babel/preset-modules@0.1.5(@babel/core@7.21.8):
+    resolution: {integrity: sha512-A57th6YRG7oR3cq/yt/Y84MvGgE0eJG2F1JLhKuyG+jFxEgrd/HAMJatiFtmOiZurz+0DkrvbheCLaV5f2JfjA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-plugin-utils': 7.20.2
+      '@babel/plugin-proposal-unicode-property-regex': 7.18.6(@babel/core@7.21.8)
+      '@babel/plugin-transform-dotall-regex': 7.18.6(@babel/core@7.21.8)
+      '@babel/types': 7.21.5
+      esutils: 2.0.3
+    dev: true
+
+  /@babel/regjsgen@0.8.0:
+    resolution: {integrity: sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==}
+    dev: true
+
+  /@babel/runtime@7.20.13:
+    resolution: {integrity: sha512-gt3PKXs0DBoL9xCvOIIZ2NEqAGZqHjAnmVbfQtB620V0uReIQutpel14KcneZuer7UioY8ALKZ7iocavvzTNFA==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      regenerator-runtime: 0.13.11
+
+  /@babel/template@7.20.7:
+    resolution: {integrity: sha512-8SegXApWe6VoNw0r9JHpSteLKTpTiLZ4rMlGIm9JQ18KiCtyQiAMEazujAHrUS5flrcqYZa75ukev3P6QmUwUw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.21.4
+      '@babel/parser': 7.21.8
+      '@babel/types': 7.21.5
+
+  /@babel/traverse@7.21.5:
+    resolution: {integrity: sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/code-frame': 7.21.4
+      '@babel/generator': 7.21.5
+      '@babel/helper-environment-visitor': 7.21.5
+      '@babel/helper-function-name': 7.21.0
+      '@babel/helper-hoist-variables': 7.18.6
+      '@babel/helper-split-export-declaration': 7.18.6
+      '@babel/parser': 7.21.8
+      '@babel/types': 7.21.5
+      debug: 4.3.4
+      globals: 11.12.0
+    transitivePeerDependencies:
+      - supports-color
+
+  /@babel/types@7.21.5:
+    resolution: {integrity: sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==}
+    engines: {node: '>=6.9.0'}
+    dependencies:
+      '@babel/helper-string-parser': 7.21.5
+      '@babel/helper-validator-identifier': 7.19.1
+      to-fast-properties: 2.0.0
+
+  /@bcoe/v8-coverage@0.2.3:
+    resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==}
+    dev: true
+
+  /@dcloudio/types@3.3.3:
+    resolution: {integrity: sha512-xfp88QOJ2fgCzv49HhEGrX0L+3xDsCyyvcoApL7z0J1Lr7tqPUkxqAVBe9zBlKsDX/mO9mNj7NzKIisHfp+fNQ==}
+
+  /@dcloudio/uni-app-plus@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vite@4.4.9)(vue@3.3.4):
+    resolution: {integrity: sha512-WQ8P60e9ucUpT4iCG51qqWmbvsEvpTNljLyDO71Ese85QFR814M5flnMLVeD3ILfV9mPaeIaBx+hixKBE6KrLQ==}
+    dependencies:
+      '@dcloudio/uni-app-uts': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-app-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vite@4.4.9)(vue@3.3.4)
+      '@dcloudio/uni-app-vue': 3.0.0-alpha-3081220230802001
+      debug: 4.3.4
+      fs-extra: 10.1.0
+      licia: 1.37.0
+      postcss-selector-parser: 6.0.11
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vite
+      - vue
+    dev: false
+
+  /@dcloudio/uni-app-uts@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-HOF0ccWHTQ4O03LXLz0Yrxv0TDXwc32VbwrKwLgbjfjvdfUZDZlJyjX4VShmq9cEuudU7PuN2e1SUzV34lQvpA==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@babel/types': 7.21.5
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-nvue-styler': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-core': 3.2.47
+      '@vue/compiler-sfc': 3.2.47
+      '@vue/shared': 3.2.47
+      debug: 4.3.4
+      es-module-lexer: 1.3.0
+      fs-extra: 10.1.0
+      picocolors: 1.0.0
+      source-map: 0.6.1
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-app-vite@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vite@4.4.9)(vue@3.3.4):
+    resolution: {integrity: sha512-Ia646k2G/t+o9wi0pnJQ1x1YWz+/lB9M0OevFEmKDzSXIqjVnTvIcWGYSl0+R43sfcxDvdmacT0XAK5+IJ2+gw==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-nvue-styler': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@rollup/pluginutils': 4.2.1
+      '@vitejs/plugin-vue': 4.2.1(vite@4.4.9)(vue@3.3.4)
+      '@vue/compiler-dom': 3.2.47
+      '@vue/compiler-sfc': 3.2.47
+      debug: 4.3.4
+      fs-extra: 10.1.0
+      picocolors: 1.0.0
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vite
+      - vue
+    dev: false
+
+  /@dcloudio/uni-app-vue@3.0.0-alpha-3081220230802001:
+    resolution: {integrity: sha512-HK6nvrGyX5kNEMTm9EXVSGbB6RlMHP0vTTsZl8VoQC6xVWPfyCA1uZApJNqIcA+gSvFVL2NK4x3gYfIhCU+SHg==}
+    dev: false
+
+  /@dcloudio/uni-app@3.0.0-alpha-3081220230802001(@dcloudio/types@3.3.3)(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-C00W+cEAvckJEnnyiJWG5gcYSsyGmli5yWwLB8ne/9xaf0wRoBzoSG6QKv2UwTY33C/RcW34VnF4iMKHLUUX/Q==}
+    peerDependencies:
+      '@dcloudio/types': ^3.3.2
+    dependencies:
+      '@dcloudio/types': 3.3.3
+      '@dcloudio/uni-cloud': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-components': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-push': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-stat': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-automator@3.0.0-alpha-3081220230802001(jest-environment-node@27.5.1)(jest@27.0.4)(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-I4my4eu0i2+3/s4l5MCRzsSaMax+xaSKjlUfbYnhB++TsRXOb0/YwvjUNGOiZCdNjWFBTw17h700Tr2D5/FleA==}
+    peerDependencies:
+      jest: 27.0.4
+      jest-environment-node: 27.5.1
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      address: 1.2.1
+      cross-env: 7.0.3
+      debug: 4.3.4
+      default-gateway: 6.0.3
+      fs-extra: 10.1.0
+      jest: 27.0.4
+      jest-environment-node: 27.5.1
+      jsonc-parser: 3.2.0
+      licia: 1.37.0
+      qrcode-reader: 1.0.4
+      qrcode-terminal: 0.12.0
+      ws: 8.11.0
+    transitivePeerDependencies:
+      - bufferutil
+      - postcss
+      - supports-color
+      - ts-node
+      - utf-8-validate
+      - vue
+    dev: true
+
+  /@dcloudio/uni-cli-shared@3.0.0-alpha-3080220230511001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-sPZqhSogx8tVCHUocfQpsdnxGJbXiH2KjKh4M+4ZMdmtv5cMfktwbahCciXMvMCsATBArGar1toOdjux/QLDpQ==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    dependencies:
+      '@ampproject/remapping': 2.2.0
+      '@babel/core': 7.21.8
+      '@babel/parser': 7.21.8
+      '@babel/types': 7.21.5
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3080220230511001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3080220230511001
+      '@intlify/core-base': 9.1.9
+      '@intlify/shared': 9.1.9
+      '@intlify/vue-devtools': 9.1.9
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-core': 3.2.47
+      '@vue/compiler-dom': 3.2.47
+      '@vue/compiler-sfc': 3.2.47
+      '@vue/server-renderer': 3.2.47(vue@3.3.4)
+      '@vue/shared': 3.2.47
+      autoprefixer: 10.4.14(postcss@8.4.27)
+      base64url: 3.0.1
+      chokidar: 3.5.3
+      compare-versions: 3.6.0
+      debug: 4.3.4
+      es-module-lexer: 0.9.3
+      esbuild: 0.17.18
+      estree-walker: 2.0.2
+      fast-glob: 3.2.12
+      fs-extra: 10.1.0
+      hash-sum: 2.0.0
+      jsonc-parser: 3.2.0
+      magic-string: 0.27.0
+      merge: 2.1.1
+      mime: 3.0.0
+      module-alias: 2.2.2
+      os-locale-s-fix: 1.0.8-fix-1
+      picocolors: 1.0.0
+      postcss-import: 14.1.0(postcss@8.4.27)
+      postcss-load-config: 3.1.4(postcss@8.4.27)
+      postcss-modules: 4.3.1(postcss@8.4.27)
+      postcss-selector-parser: 6.0.11
+      resolve: 1.22.1
+      tapable: 2.2.1
+      xregexp: 3.1.0
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: true
+
+  /@dcloudio/uni-cli-shared@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-gUgFOT+kJ2wSOUDZG+loqUUEZxsNx13Qmzz/OWI21WB5RxaHz+FYIlsPDUWSfZZIZrkHTiKjcUNPDAUIUfAMXw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    dependencies:
+      '@ampproject/remapping': 2.2.0
+      '@babel/core': 7.21.8
+      '@babel/parser': 7.21.8
+      '@babel/types': 7.21.5
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@intlify/core-base': 9.1.9
+      '@intlify/shared': 9.1.9
+      '@intlify/vue-devtools': 9.1.9
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-core': 3.2.47
+      '@vue/compiler-dom': 3.2.47
+      '@vue/compiler-sfc': 3.2.47
+      '@vue/server-renderer': 3.2.47(vue@3.3.4)
+      '@vue/shared': 3.2.47
+      autoprefixer: 10.4.14(postcss@8.4.27)
+      base64url: 3.0.1
+      chokidar: 3.5.3
+      compare-versions: 3.6.0
+      debug: 4.3.4
+      es-module-lexer: 1.3.0
+      esbuild: 0.17.18
+      estree-walker: 2.0.2
+      fast-glob: 3.2.12
+      fs-extra: 10.1.0
+      hash-sum: 2.0.0
+      jsonc-parser: 3.2.0
+      magic-string: 0.30.0
+      merge: 2.1.1
+      mime: 3.0.0
+      module-alias: 2.2.2
+      os-locale-s-fix: 1.0.8-fix-1
+      picocolors: 1.0.0
+      postcss-import: 14.1.0(postcss@8.4.27)
+      postcss-load-config: 3.1.4(postcss@8.4.27)
+      postcss-modules: 4.3.1(postcss@8.4.27)
+      postcss-selector-parser: 6.0.11
+      resolve: 1.22.1
+      tapable: 2.2.1
+      xregexp: 3.1.0
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+
+  /@dcloudio/uni-cloud@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-4fSp1JwdWsPGVIJnCKBrzwj5WoCgQLpkJa1cSfO2zY2bvjm7IkFfLmx01iHo7TLKfH12Uu8YDRGwW4a0H9j1dA==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/shared': 3.2.47
+      fast-glob: 3.2.12
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-components@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-gUOzYTCYROafVuSfSvU1+Sdz0P4aiNKC6AHC1yH5Z2YKqOEvzHMYOEFBaMlWmK01RH4ZnveEfJ+ztYvOOMw0aA==}
+    dependencies:
+      '@dcloudio/uni-cloud': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-h5': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3081220230802001
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-h5-vite@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-gLnzD3CbaYRiE00usw0zw17HoR0xB8YNzZKT0lpDnPZLLKOsxfS4b2LJP+dy9TCaTz5tvUEkRBkNeWSnSuyd1w==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@rollup/pluginutils': 4.2.1
+      '@vue/compiler-dom': 3.2.47
+      '@vue/compiler-sfc': 3.2.47
+      '@vue/server-renderer': 3.2.47(vue@3.3.4)
+      '@vue/shared': 3.2.47
+      debug: 4.3.4
+      fs-extra: 10.1.0
+      mime: 3.0.0
+      module-alias: 2.2.2
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-h5-vue@3.0.0-alpha-3081220230802001(vue@3.3.4):
+    resolution: {integrity: sha512-+T3lNKz1XkG3al4xzQNMg2AWfnvjVuJaNNl24XQAabrO2E+5Qsyi+GKV/KL1ME6iiT6rKDqNzWPic8DHazVccw==}
+    dependencies:
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/server-renderer': 3.2.47(vue@3.3.4)
+    transitivePeerDependencies:
+      - vue
+    dev: false
+
+  /@dcloudio/uni-h5@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-nsJ4Cbi18WjUa+dGNJmyMFl5BOxyNuQidRUrDESGyUIKwZID58M4c5gq2FEgS4u5REB9CQF1yz9Y7fa9pnhXcA==}
+    dependencies:
+      '@dcloudio/uni-h5-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-h5-vue': 3.0.0-alpha-3081220230802001(vue@3.3.4)
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/server-renderer': 3.2.47(vue@3.3.4)
+      '@vue/shared': 3.2.47
+      debug: 4.3.4
+      localstorage-polyfill: 1.0.1
+      postcss-selector-parser: 6.0.11
+      safe-area-insets: 1.4.1
+      vue-router: 4.1.6(vue@3.3.4)
+      xmlhttprequest: 1.8.0
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-i18n@3.0.0-alpha-3080220230511001:
+    resolution: {integrity: sha512-hii0rSYvE1E7UXcXaFU8T9o2y9ukKAkLcQqwVUb4aUTi7HiRha0lVmifRRitTQ780VkaoVMGtbhZLwFS0L0ArA==}
+    dev: true
+
+  /@dcloudio/uni-i18n@3.0.0-alpha-3081220230802001:
+    resolution: {integrity: sha512-S2MFVeS6igJU1VRzqdC9UDLfI4gdLP7gnI0YVqd4PdetLgy6PS9NgeJ8MLtWKyUlXw/Ykg+Nh7+lv0qf4a5YhQ==}
+
+  /@dcloudio/uni-mp-alipay@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-cEL+WyaCjJeTLZKjs9an1NP0vwH1YA6DPcpdGscd9yu3Zb7FN54xSHPp7COupO5MFMRsdlKSMhAD/akT5zEwKA==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-baidu@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-WL4P7imM1LWaOFjp6tlU0LeLJwcompumybXt0ZzO74V7Uq7DhU6bRcfOuWLRhWmS6DKzrfyIGnu2SRIRZgvugw==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-compiler': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-mp-weixin': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+      jimp: 0.10.3
+      licia: 1.37.0
+      qrcode-reader: 1.0.4
+      qrcode-terminal: 0.12.0
+      ws: 8.11.0
+    transitivePeerDependencies:
+      - bufferutil
+      - postcss
+      - supports-color
+      - ts-node
+      - utf-8-validate
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-compiler@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-DUDVMXUhMC8T8wgsODzAjQe8OEKHpEUkSkyWrtN6WkqB//LUyWiB9cPRXkxKK2MBeonsaajYao/Ezh6dNKychg==}
+    dependencies:
+      '@babel/generator': 7.21.5
+      '@babel/parser': 7.21.8
+      '@babel/types': 7.21.5
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/compiler-core': 3.2.47
+      '@vue/compiler-dom': 3.2.47
+      '@vue/shared': 3.2.47
+      estree-walker: 2.0.2
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-jd@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-84Ua4pIXDjiWMoQ4VLy4ck0Ejk/ZhdsenncKn8kns55xJygb7wP4Ggt3Njx2EVQIa9N1a1vmslPucoI9tDf9vg==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-compiler': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-kuaishou@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-Fx8WiaGReo2UIaYC68DSVJZIS9tH8ZDYS+lPAfjy2dEzwPrhF6ZiJnoIvXv4lRzJhBERqw+SUckkWuOcRSGKPg==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-compiler': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-mp-weixin': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - bufferutil
+      - postcss
+      - supports-color
+      - ts-node
+      - utf-8-validate
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-lark@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-0uGpY92COJ//N7EBHSkaNgctuK4XgFqXAHiiukVf1u2FnTxtAAh9lE7dO9xomPaV/MtMON1TfHw3HrvDcnuCdA==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-compiler': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-toutiao': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-qq@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-WARnOEetYF0ldEXuUq0aimwZkBtat9G6ZUrMHmnp4Vl2TeHWTOpuZcXV7ePZFXZka6Z3TSauMkS/t/xFepZ8DA==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/shared': 3.2.47
+      fs-extra: 10.1.0
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-toutiao@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-mCTwRhSWBeQVZQpMEFY6mG86xkyONIyklo0Mtb1BBPX3EFGEXrNiVgjYsRTAmXePFk4m9Auzr9G9JU3B3btYJA==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-compiler': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-vite@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-jQmdOd0CJ/b65gswS8F2WPnprbihnmEbUeBIo+RYRa7+Yync0+qt2rV8GmB6kP0SusgzD4Ku5IqhNsxhPM0A2Q==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-i18n': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-mp-compiler': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/compiler-sfc': 3.2.47
+      '@vue/shared': 3.2.47
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-vue@3.0.0-alpha-3081220230802001:
+    resolution: {integrity: sha512-BaDy3HJkunEnccdXBYEBlRQ7IuZtlmaRlmsK0UZZtSVERUkQp1/5VfldITvs2aMrFZs5+NqjoaCcH58KY7AgVw==}
+    dependencies:
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/shared': 3.2.47
+    dev: false
+
+  /@dcloudio/uni-mp-weixin@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-+HBTwk9Fndv/YVB8YG+JavtItahDKEj7DK4ih/5Ct2o8u9DRycI6IJhqDUyK4Eks2ZH0TkLAjZPM3KZc87d0RQ==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/shared': 3.2.47
+      jimp: 0.10.3
+      licia: 1.37.0
+      qrcode-reader: 1.0.4
+      qrcode-terminal: 0.12.0
+      ws: 8.11.0
+    transitivePeerDependencies:
+      - bufferutil
+      - postcss
+      - supports-color
+      - ts-node
+      - utf-8-validate
+      - vue
+    dev: false
+
+  /@dcloudio/uni-mp-xhs@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-ndFx+CScnJejXhMmhZK+RCd+JLy6FDJWVypyLymK/BaepdNUE8xkPnB4feVBxnPppR3lNeqk0WUR4qhDFQcDHw==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-compiler': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-nvue-styler@3.0.0-alpha-3081220230802001:
+    resolution: {integrity: sha512-u0EXKPRp0XsSdUs/tvHcQdVGN4pY5wYUFcTrcN6yGjHyzYeOtDzP1NvvuVZEak34EzWFa0CYyTVipI7yEOloxg==}
+    dependencies:
+      '@vue/shared': 3.2.47
+      parse-css-font: 4.0.0
+      postcss: 8.4.27
+    dev: false
+
+  /@dcloudio/uni-push@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-LDdiMUL4Dop9OrjHWPv+t6MAj1pf9ZB0NT9tI+ujSfx4aanKgPYgGpblq3ShiyXuckJTSMz1Tt0cZGolwHO6ZA==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-quickapp-webview@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-u8Vm1gJJdHTpmH3zFkFF0lUtp3DycomBwfv47+Ttoz26uNjEPVKDe/uMWOMAvpSgLVvROnIWHrU+fgs/OBmHug==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vite': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-mp-vue': 3.0.0-alpha-3081220230802001
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@vue/shared': 3.2.47
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-shared@3.0.0-alpha-3080220230511001:
+    resolution: {integrity: sha512-mPLX+dSmOg0+H80+i26YIw1YeUbL1bioug7w8J+0zmf9Qf1u0BCo6CgN7NfrQJkvbSlcef0DA4NhyaDGxZmbXQ==}
+    dependencies:
+      '@vue/shared': 3.2.47
+    dev: true
+
+  /@dcloudio/uni-shared@3.0.0-alpha-3081220230802001:
+    resolution: {integrity: sha512-pxhRZfuU+eT+oHfaBRTjV2XZnBBNAJcqtCnyLppwbeFsDkpvITdSHQVKy0TwDtRhXgJ/TkdR4w458f9Ho7481g==}
+    dependencies:
+      '@vue/shared': 3.2.47
+
+  /@dcloudio/uni-stacktracey@3.0.0-alpha-3081220230802001:
+    resolution: {integrity: sha512-atcvxe41RZ+vyxoA8lSfl3zykUcMW3sTLhIpFBhwGZ7ifwWDNSJ/hWsEJKkdfWMcOW7zutTk132evDCH713Bmw==}
+    dev: true
+
+  /@dcloudio/uni-stat@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-A3tfKOkdbdbFlTStA9rWkwHp7OxyXXHmowGPvnxD/xoKippEWg9n3SOsh98ow1HNc9mcha1i8thBHawUnsna6g==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: false
+
+  /@dcloudio/uni-ui@1.4.23:
+    resolution: {integrity: sha512-FJRkhL240gin9WvOunY38Yi2/0FzTLVlEI3dxcjzaV1oYa81o+IWEf/29YDiRrsHuvYDk9g600QgiZLC+A6ycA==}
+    dev: false
+
+  /@dcloudio/uni-vue-devtools@3.0.0-alpha-3080220230511001(postcss@8.4.27)(vue@3.3.4):
+    resolution: {integrity: sha512-dpDEnWF5hTsPve2pII/srsQYc6wdZbUt9Ejwy57TfuRz6JmPv+IBLTlGkIiZFo1RgQ3GmYyyFzKPuKiMfeLjIg==}
+    dependencies:
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3080220230511001(postcss@8.4.27)(vue@3.3.4)
+      detect-port: 1.5.1
+      express: 4.18.2
+      open: 8.4.2
+      socket.io: 4.6.1
+    transitivePeerDependencies:
+      - bufferutil
+      - postcss
+      - supports-color
+      - ts-node
+      - utf-8-validate
+      - vue
+    dev: true
+
+  /@dcloudio/vite-plugin-uni@3.0.0-alpha-3081220230802001(postcss@8.4.27)(vite@4.4.9)(vue@3.3.4):
+    resolution: {integrity: sha512-Ez7O42zUTuExxpgM92BYofXTY2NJ/fzIlRS2rWeK6bLCKXOKqJ30JUv20rQNu6AwLWRaSMh1us7cZNkbKhZW+g==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    hasBin: true
+    peerDependencies:
+      vite: ^4.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.8)
+      '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.21.8)
+      '@dcloudio/uni-cli-shared': 3.0.0-alpha-3081220230802001(postcss@8.4.27)(vue@3.3.4)
+      '@dcloudio/uni-shared': 3.0.0-alpha-3081220230802001
+      '@rollup/pluginutils': 4.2.1
+      '@vitejs/plugin-legacy': 4.0.3(terser@5.16.0)(vite@4.4.9)
+      '@vitejs/plugin-vue': 4.2.1(vite@4.4.9)(vue@3.3.4)
+      '@vitejs/plugin-vue-jsx': 3.0.1(vite@4.4.9)(vue@3.3.4)
+      '@vue/compiler-core': 3.2.47
+      '@vue/compiler-dom': 3.2.47
+      '@vue/compiler-sfc': 3.2.47
+      '@vue/shared': 3.2.47
+      cac: 6.7.9
+      debug: 4.3.4
+      estree-walker: 2.0.2
+      express: 4.18.2
+      fast-glob: 3.2.12
+      fs-extra: 10.1.0
+      hash-sum: 2.0.0
+      jsonc-parser: 3.2.0
+      magic-string: 0.30.0
+      picocolors: 1.0.0
+      terser: 5.16.0
+      vite: 4.4.9(@types/node@18.11.10)(sass@1.56.1)(terser@5.16.0)
+    transitivePeerDependencies:
+      - postcss
+      - supports-color
+      - ts-node
+      - vue
+    dev: true
+
+  /@esbuild/android-arm64@0.17.18:
+    resolution: {integrity: sha512-/iq0aK0eeHgSC3z55ucMAHO05OIqmQehiGay8eP5l/5l+iEr4EIbh4/MI8xD9qRFjqzgkc0JkX0LculNC9mXBw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/android-arm64@0.18.20:
+    resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [android]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/android-arm@0.17.18:
+    resolution: {integrity: sha512-EmwL+vUBZJ7mhFCs5lA4ZimpUH3WMAoqvOIYhVQwdIgSpHC8ImHdsRyhHAVxpDYUSm0lWvd63z0XH1IlImS2Qw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/android-arm@0.18.20:
+    resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [android]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/android-x64@0.17.18:
+    resolution: {integrity: sha512-x+0efYNBF3NPW2Xc5bFOSFW7tTXdAcpfEg2nXmxegm4mJuVeS+i109m/7HMiOQ6M12aVGGFlqJX3RhNdYM2lWg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/android-x64@0.18.20:
+    resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [android]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/darwin-arm64@0.17.18:
+    resolution: {integrity: sha512-6tY+djEAdF48M1ONWnQb1C+6LiXrKjmqjzPNPWXhu/GzOHTHX2nh8Mo2ZAmBFg0kIodHhciEgUBtcYCAIjGbjQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/darwin-arm64@0.18.20:
+    resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [darwin]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/darwin-x64@0.17.18:
+    resolution: {integrity: sha512-Qq84ykvLvya3dO49wVC9FFCNUfSrQJLbxhoQk/TE1r6MjHo3sFF2tlJCwMjhkBVq3/ahUisj7+EpRSz0/+8+9A==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/darwin-x64@0.18.20:
+    resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [darwin]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/freebsd-arm64@0.17.18:
+    resolution: {integrity: sha512-fw/ZfxfAzuHfaQeMDhbzxp9mc+mHn1Y94VDHFHjGvt2Uxl10mT4CDavHm+/L9KG441t1QdABqkVYwakMUeyLRA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/freebsd-arm64@0.18.20:
+    resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [freebsd]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/freebsd-x64@0.17.18:
+    resolution: {integrity: sha512-FQFbRtTaEi8ZBi/A6kxOC0V0E9B/97vPdYjY9NdawyLd4Qk5VD5g2pbWN2VR1c0xhzcJm74HWpObPszWC+qTew==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/freebsd-x64@0.18.20:
+    resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [freebsd]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-arm64@0.17.18:
+    resolution: {integrity: sha512-R7pZvQZFOY2sxUG8P6A21eq6q+eBv7JPQYIybHVf1XkQYC+lT7nDBdC7wWKTrbvMXKRaGudp/dzZCwL/863mZQ==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-arm64@0.18.20:
+    resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-arm@0.17.18:
+    resolution: {integrity: sha512-jW+UCM40LzHcouIaqv3e/oRs0JM76JfhHjCavPxMUti7VAPh8CaGSlS7cmyrdpzSk7A+8f0hiedHqr/LMnfijg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-arm@0.18.20:
+    resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==}
+    engines: {node: '>=12'}
+    cpu: [arm]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-ia32@0.17.18:
+    resolution: {integrity: sha512-ygIMc3I7wxgXIxk6j3V00VlABIjq260i967Cp9BNAk5pOOpIXmd1RFQJQX9Io7KRsthDrQYrtcx7QCof4o3ZoQ==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-ia32@0.18.20:
+    resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-loong64@0.17.18:
+    resolution: {integrity: sha512-bvPG+MyFs5ZlwYclCG1D744oHk1Pv7j8psF5TfYx7otCVmcJsEXgFEhQkbhNW8otDHL1a2KDINW20cfCgnzgMQ==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-loong64@0.18.20:
+    resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==}
+    engines: {node: '>=12'}
+    cpu: [loong64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-mips64el@0.17.18:
+    resolution: {integrity: sha512-oVqckATOAGuiUOa6wr8TXaVPSa+6IwVJrGidmNZS1cZVx0HqkTMkqFGD2HIx9H1RvOwFeWYdaYbdY6B89KUMxA==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-mips64el@0.18.20:
+    resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==}
+    engines: {node: '>=12'}
+    cpu: [mips64el]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-ppc64@0.17.18:
+    resolution: {integrity: sha512-3dLlQO+b/LnQNxgH4l9rqa2/IwRJVN9u/bK63FhOPB4xqiRqlQAU0qDU3JJuf0BmaH0yytTBdoSBHrb2jqc5qQ==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-ppc64@0.18.20:
+    resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==}
+    engines: {node: '>=12'}
+    cpu: [ppc64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-riscv64@0.17.18:
+    resolution: {integrity: sha512-/x7leOyDPjZV3TcsdfrSI107zItVnsX1q2nho7hbbQoKnmoeUWjs+08rKKt4AUXju7+3aRZSsKrJtaRmsdL1xA==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-riscv64@0.18.20:
+    resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==}
+    engines: {node: '>=12'}
+    cpu: [riscv64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-s390x@0.17.18:
+    resolution: {integrity: sha512-cX0I8Q9xQkL/6F5zWdYmVf5JSQt+ZfZD2bJudZrWD+4mnUvoZ3TDDXtDX2mUaq6upMFv9FlfIh4Gfun0tbGzuw==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-s390x@0.18.20:
+    resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==}
+    engines: {node: '>=12'}
+    cpu: [s390x]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-x64@0.17.18:
+    resolution: {integrity: sha512-66RmRsPlYy4jFl0vG80GcNRdirx4nVWAzJmXkevgphP1qf4dsLQCpSKGM3DUQCojwU1hnepI63gNZdrr02wHUA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/linux-x64@0.18.20:
+    resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [linux]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/netbsd-x64@0.17.18:
+    resolution: {integrity: sha512-95IRY7mI2yrkLlTLb1gpDxdC5WLC5mZDi+kA9dmM5XAGxCME0F8i4bYH4jZreaJ6lIZ0B8hTrweqG1fUyW7jbg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/netbsd-x64@0.18.20:
+    resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [netbsd]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/openbsd-x64@0.17.18:
+    resolution: {integrity: sha512-WevVOgcng+8hSZ4Q3BKL3n1xTv5H6Nb53cBrtzzEjDbbnOmucEVcZeGCsCOi9bAOcDYEeBZbD2SJNBxlfP3qiA==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/openbsd-x64@0.18.20:
+    resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [openbsd]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/sunos-x64@0.17.18:
+    resolution: {integrity: sha512-Rzf4QfQagnwhQXVBS3BYUlxmEbcV7MY+BH5vfDZekU5eYpcffHSyjU8T0xucKVuOcdCsMo+Ur5wmgQJH2GfNrg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/sunos-x64@0.18.20:
+    resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [sunos]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/win32-arm64@0.17.18:
+    resolution: {integrity: sha512-Kb3Ko/KKaWhjeAm2YoT/cNZaHaD1Yk/pa3FTsmqo9uFh1D1Rfco7BBLIPdDOozrObj2sahslFuAQGvWbgWldAg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/win32-arm64@0.18.20:
+    resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==}
+    engines: {node: '>=12'}
+    cpu: [arm64]
+    os: [win32]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/win32-ia32@0.17.18:
+    resolution: {integrity: sha512-0/xUMIdkVHwkvxfbd5+lfG7mHOf2FRrxNbPiKWg9C4fFrB8H0guClmaM3BFiRUYrznVoyxTIyC/Ou2B7QQSwmw==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/win32-ia32@0.18.20:
+    resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==}
+    engines: {node: '>=12'}
+    cpu: [ia32]
+    os: [win32]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/win32-x64@0.17.18:
+    resolution: {integrity: sha512-qU25Ma1I3NqTSHJUOKi9sAH1/Mzuvlke0ioMJRthLXKm7JiSKVwFghlGbDLOO2sARECGhja4xYfRAZNPAkooYg==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    optional: true
+
+  /@esbuild/win32-x64@0.18.20:
+    resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==}
+    engines: {node: '>=12'}
+    cpu: [x64]
+    os: [win32]
+    requiresBuild: true
+    optional: true
+
+  /@eslint/eslintrc@1.3.3:
+    resolution: {integrity: sha512-uj3pT6Mg+3t39fvLrj8iuCIJ38zKO9FpGtJ4BBJebJhEwjoT+KLVNCcHT5QC9NGRIEi7fZ0ZR8YRb884auB4Lg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      ajv: 6.12.6
+      debug: 4.3.4
+      espree: 9.4.1
+      globals: 13.18.0
+      ignore: 5.2.1
+      import-fresh: 3.3.0
+      js-yaml: 4.1.0
+      minimatch: 3.1.2
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@humanwhocodes/config-array@0.11.7:
+    resolution: {integrity: sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==}
+    engines: {node: '>=10.10.0'}
+    dependencies:
+      '@humanwhocodes/object-schema': 1.2.1
+      debug: 4.3.4
+      minimatch: 3.1.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@humanwhocodes/module-importer@1.0.1:
+    resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+    engines: {node: '>=12.22'}
+    dev: true
+
+  /@humanwhocodes/object-schema@1.2.1:
+    resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
+    dev: true
+
+  /@intlify/core-base@9.1.9:
+    resolution: {integrity: sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==}
+    engines: {node: '>= 10'}
+    dependencies:
+      '@intlify/devtools-if': 9.1.9
+      '@intlify/message-compiler': 9.1.9
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/runtime': 9.1.9
+      '@intlify/shared': 9.1.9
+      '@intlify/vue-devtools': 9.1.9
+
+  /@intlify/core-base@9.2.2:
+    resolution: {integrity: sha512-JjUpQtNfn+joMbrXvpR4hTF8iJQ2sEFzzK3KIESOx+f+uwIjgw20igOyaIdhfsVVBCds8ZM64MoeNSx+PHQMkA==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/devtools-if': 9.2.2
+      '@intlify/message-compiler': 9.2.2
+      '@intlify/shared': 9.2.2
+      '@intlify/vue-devtools': 9.2.2
+    dev: false
+
+  /@intlify/devtools-if@9.1.9:
+    resolution: {integrity: sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==}
+    engines: {node: '>= 10'}
+    dependencies:
+      '@intlify/shared': 9.1.9
+
+  /@intlify/devtools-if@9.2.2:
+    resolution: {integrity: sha512-4ttr/FNO29w+kBbU7HZ/U0Lzuh2cRDhP8UlWOtV9ERcjHzuyXVZmjyleESK6eVP60tGC9QtQW9yZE+JeRhDHkg==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/shared': 9.2.2
+    dev: false
+
+  /@intlify/message-compiler@9.1.9:
+    resolution: {integrity: sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==}
+    engines: {node: '>= 10'}
+    dependencies:
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/shared': 9.1.9
+      source-map: 0.6.1
+
+  /@intlify/message-compiler@9.2.2:
+    resolution: {integrity: sha512-IUrQW7byAKN2fMBe8z6sK6riG1pue95e5jfokn8hA5Q3Bqy4MBJ5lJAofUsawQJYHeoPJ7svMDyBaVJ4d0GTtA==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/shared': 9.2.2
+      source-map: 0.6.1
+    dev: false
+
+  /@intlify/message-resolver@9.1.9:
+    resolution: {integrity: sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==}
+    engines: {node: '>= 10'}
+
+  /@intlify/runtime@9.1.9:
+    resolution: {integrity: sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==}
+    engines: {node: '>= 10'}
+    dependencies:
+      '@intlify/message-compiler': 9.1.9
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/shared': 9.1.9
+
+  /@intlify/shared@9.1.9:
+    resolution: {integrity: sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==}
+    engines: {node: '>= 10'}
+
+  /@intlify/shared@9.2.2:
+    resolution: {integrity: sha512-wRwTpsslgZS5HNyM7uDQYZtxnbI12aGiBZURX3BTR9RFIKKRWpllTsgzHWvj3HKm3Y2Sh5LPC1r0PDCKEhVn9Q==}
+    engines: {node: '>= 14'}
+    dev: false
+
+  /@intlify/vue-devtools@9.1.9:
+    resolution: {integrity: sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==}
+    engines: {node: '>= 10'}
+    dependencies:
+      '@intlify/message-resolver': 9.1.9
+      '@intlify/runtime': 9.1.9
+      '@intlify/shared': 9.1.9
+
+  /@intlify/vue-devtools@9.2.2:
+    resolution: {integrity: sha512-+dUyqyCHWHb/UcvY1MlIpO87munedm3Gn6E9WWYdWrMuYLcoIoOEVDWSS8xSwtlPU+kA+MEQTP6Q1iI/ocusJg==}
+    engines: {node: '>= 14'}
+    dependencies:
+      '@intlify/core-base': 9.2.2
+      '@intlify/shared': 9.2.2
+    dev: false
+
+  /@istanbuljs/load-nyc-config@1.1.0:
+    resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      camelcase: 5.3.1
+      find-up: 4.1.0
+      get-package-type: 0.1.0
+      js-yaml: 3.14.1
+      resolve-from: 5.0.0
+    dev: true
+
+  /@istanbuljs/schema@0.1.3:
+    resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /@jest/console@27.5.1:
+    resolution: {integrity: sha512-kZ/tNpS3NXn0mlXXXPNuDZnb4c0oZ20r4K5eemM2k30ZC3G0T02nXUvyhf5YdbXWHPEJLc9qGLxEZ216MdL+Zg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      chalk: 4.1.2
+      jest-message-util: 27.5.1
+      jest-util: 27.5.1
+      slash: 3.0.0
+    dev: true
+
+  /@jest/core@27.5.1:
+    resolution: {integrity: sha512-AK6/UTrvQD0Cd24NSqmIA6rKsu0tKIxfiCducZvqxYdmMisOYAsdItspT+fQDQYARPf8XgjAFZi0ogW2agH5nQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+    dependencies:
+      '@jest/console': 27.5.1
+      '@jest/reporters': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      ansi-escapes: 4.3.2
+      chalk: 4.1.2
+      emittery: 0.8.1
+      exit: 0.1.2
+      graceful-fs: 4.2.10
+      jest-changed-files: 27.5.1
+      jest-config: 27.5.1
+      jest-haste-map: 27.5.1
+      jest-message-util: 27.5.1
+      jest-regex-util: 27.5.1
+      jest-resolve: 27.5.1
+      jest-resolve-dependencies: 27.5.1
+      jest-runner: 27.5.1
+      jest-runtime: 27.5.1
+      jest-snapshot: 27.5.1
+      jest-util: 27.5.1
+      jest-validate: 27.5.1
+      jest-watcher: 27.5.1
+      micromatch: 4.0.5
+      rimraf: 3.0.2
+      slash: 3.0.0
+      strip-ansi: 6.0.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /@jest/environment@27.5.1:
+    resolution: {integrity: sha512-/WQjhPJe3/ghaol/4Bq480JKXV/Rfw8nQdN7f41fM8VDHLcxKXou6QyXAh3EFr9/bVG3x74z1NWDkP87EiY8gA==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/fake-timers': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      jest-mock: 27.5.1
+    dev: true
+
+  /@jest/fake-timers@27.5.1:
+    resolution: {integrity: sha512-/aPowoolwa07k7/oM3aASneNeBGCmGQsc3ugN4u6s4C/+s5M64MFo/+djTdiwcbQlRfFElGuDXWzaWj6QgKObQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      '@sinonjs/fake-timers': 8.1.0
+      '@types/node': 18.11.10
+      jest-message-util: 27.5.1
+      jest-mock: 27.5.1
+      jest-util: 27.5.1
+    dev: true
+
+  /@jest/globals@27.5.1:
+    resolution: {integrity: sha512-ZEJNB41OBQQgGzgyInAv0UUfDDj3upmHydjieSxFvTRuZElrx7tXg/uVQ5hYVEwiXs3+aMsAeEc9X7xiSKCm4Q==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/types': 27.5.1
+      expect: 27.5.1
+    dev: true
+
+  /@jest/reporters@27.5.1:
+    resolution: {integrity: sha512-cPXh9hWIlVJMQkVk84aIvXuBB4uQQmFqZiacloFuGiP3ah1sbCxCosidXFDfqG8+6fO1oR2dTJTlsOy4VFmUfw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+    dependencies:
+      '@bcoe/v8-coverage': 0.2.3
+      '@jest/console': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      chalk: 4.1.2
+      collect-v8-coverage: 1.0.1
+      exit: 0.1.2
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      istanbul-lib-coverage: 3.2.0
+      istanbul-lib-instrument: 5.2.1
+      istanbul-lib-report: 3.0.0
+      istanbul-lib-source-maps: 4.0.1
+      istanbul-reports: 3.1.5
+      jest-haste-map: 27.5.1
+      jest-resolve: 27.5.1
+      jest-util: 27.5.1
+      jest-worker: 27.5.1
+      slash: 3.0.0
+      source-map: 0.6.1
+      string-length: 4.0.2
+      terminal-link: 2.1.1
+      v8-to-istanbul: 8.1.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@jest/source-map@27.5.1:
+    resolution: {integrity: sha512-y9NIHUYF3PJRlHk98NdC/N1gl88BL08aQQgu4k4ZopQkCw9t9cV8mtl3TV8b/YCB8XaVTFrmUTAJvjsntDireg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      callsites: 3.1.0
+      graceful-fs: 4.2.10
+      source-map: 0.6.1
+    dev: true
+
+  /@jest/test-result@27.5.1:
+    resolution: {integrity: sha512-EW35l2RYFUcUQxFJz5Cv5MTOxlJIQs4I7gxzi2zVU7PJhOwfYq1MdC5nhSmYjX1gmMmLPvB3sIaC+BkcHRBfag==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/console': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/istanbul-lib-coverage': 2.0.4
+      collect-v8-coverage: 1.0.1
+    dev: true
+
+  /@jest/test-sequencer@27.5.1:
+    resolution: {integrity: sha512-LCheJF7WB2+9JuCS7VB/EmGIdQuhtqjRNI9A43idHv3E4KltCTsPsLxvdaubFHSYwY/fNjMWjl6vNRhDiN7vpQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/test-result': 27.5.1
+      graceful-fs: 4.2.10
+      jest-haste-map: 27.5.1
+      jest-runtime: 27.5.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@jest/transform@27.5.1:
+    resolution: {integrity: sha512-ipON6WtYgl/1329g5AIJVbUuEh0wZVbdpGwC99Jw4LwuoBNS95MVphU6zOeD9pDkon+LLbFL7lOQRapbB8SCHw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@babel/core': 7.21.8
+      '@jest/types': 27.5.1
+      babel-plugin-istanbul: 6.1.1
+      chalk: 4.1.2
+      convert-source-map: 1.9.0
+      fast-json-stable-stringify: 2.1.0
+      graceful-fs: 4.2.10
+      jest-haste-map: 27.5.1
+      jest-regex-util: 27.5.1
+      jest-util: 27.5.1
+      micromatch: 4.0.5
+      pirates: 4.0.5
+      slash: 3.0.0
+      source-map: 0.6.1
+      write-file-atomic: 3.0.3
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@jest/types@27.5.1:
+    resolution: {integrity: sha512-Cx46iJ9QpwQTjIdq5VJu2QTMMs3QlEjI0x1QbBP5W1+nMzyc2XmimiRR/CbX9TO0cPTeUlxWMOu8mslYsJ8DEw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.4
+      '@types/istanbul-reports': 3.0.1
+      '@types/node': 18.11.10
+      '@types/yargs': 16.0.5
+      chalk: 4.1.2
+    dev: true
+
+  /@jimp/bmp@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-keMOc5woiDmONXsB/6aXLR4Z5Q+v8lFq3EY2rcj2FmstbDMhRuGbmcBxlEgOqfRjwvtf/wOtJ3Of37oAWtVfLg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      bmp-js: 0.1.0
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/core@0.10.3:
+    resolution: {integrity: sha512-Gd5IpL3U2bFIO57Fh/OA3HCpWm4uW/pU01E75rI03BXfTdz3T+J7TwvyG1XaqsQ7/DSlS99GXtLQPlfFIe28UA==}
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/utils': 0.10.3
+      any-base: 1.1.0
+      buffer: 5.7.1
+      core-js: 3.30.1
+      exif-parser: 0.1.12
+      file-type: 9.0.0
+      load-bmfont: 1.4.1
+      mkdirp: 0.5.6
+      phin: 2.9.3
+      pixelmatch: 4.0.2
+      tinycolor2: 1.5.2
+    dev: false
+
+  /@jimp/custom@0.10.3:
+    resolution: {integrity: sha512-nZmSI+jwTi5IRyNLbKSXQovoeqsw+D0Jn0SxW08wYQvdkiWA8bTlDQFgQ7HVwCAKBm8oKkDB/ZEo9qvHJ+1gAQ==}
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/core': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/gif@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-vjlRodSfz1CrUvvrnUuD/DsLK1GHB/yDZXHthVdZu23zYJIW7/WrIiD1IgQ5wOMV7NocfrvPn2iqUfBP81/WWA==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+      omggif: 1.0.10
+    dev: false
+
+  /@jimp/jpeg@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-AAANwgUZOt6f6P7LZxY9lyJ9xclqutYJlsxt3JbriXUGJgrrFAIkcKcqv1nObgmQASSAQKYaMV9KdHjMlWFKlQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+      jpeg-js: 0.3.7
+    dev: false
+
+  /@jimp/plugin-blit@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-5zlKlCfx4JWw9qUVC7GI4DzXyxDWyFvgZLaoGFoT00mlXlN75SarlDwc9iZ/2e2kp4bJWxz3cGgG4G/WXrbg3Q==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-blur@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-cTOK3rjh1Yjh23jSfA6EHCHjsPJDEGLC8K2y9gM7dnTUK1y9NNmkFS23uHpyjgsWFIoH9oRh2SpEs3INjCpZhQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-circle@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-51GAPIVelqAcfuUpaM5JWJ0iWl4vEjNXB7p4P7SX5udugK5bxXUjO6KA2qgWmdpHuCKtoNgkzWU9fNSuYp7tCA==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-color@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-RgeHUElmlTH7vpI4WyQrz6u59spiKfVQbsG/XUzfWGamFSixa24ZDwX/yV/Ts+eNaz7pZeIuv533qmKPvw2ujg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+      tinycolor2: 1.5.2
+    dev: false
+
+  /@jimp/plugin-contain@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3)(@jimp/plugin-resize@0.10.3)(@jimp/plugin-scale@0.10.3):
+    resolution: {integrity: sha512-bYJKW9dqzcB0Ihc6u7jSyKa3juStzbLs2LFr6fu8TzA2WkMS/R8h+ddkiO36+F9ILTWHP0CIA3HFe5OdOGcigw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-blit': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+      '@jimp/plugin-scale': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-cover@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-crop@0.10.3)(@jimp/plugin-resize@0.10.3)(@jimp/plugin-scale@0.10.3):
+    resolution: {integrity: sha512-pOxu0cM0BRPzdV468n4dMocJXoMbTnARDY/EpC3ZW15SpMuc/dr1KhWQHgoQX5kVW1Wt8zgqREAJJCQ5KuPKDA==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-crop': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+      '@jimp/plugin-scale': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-crop@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-nB7HgOjjl9PgdHr076xZ3Sr6qHYzeBYBs9qvs3tfEEUeYMNnvzgCCGtUl6eMakazZFCMk3mhKmcB9zQuHFOvkg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-displace@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-8t3fVKCH5IVqI4lewe4lFFjpxxr69SQCz5/tlpDLQZsrNScNJivHdQ09zljTrVTCSgeCqQJIKgH2Q7Sk/pAZ0w==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-dither@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-JCX/oNSnEg1kGQ8ffZ66bEgQOLCY3Rn+lrd6v1jjLy/mn9YVZTMsxLtGCXpiCDC2wG/KTmi4862ysmP9do9dAQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-fisheye@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-RRZb1wqe+xdocGcFtj2xHU7sF7xmEZmIa6BmrfSchjyA2b32TGPWKnP3qyj7p6LWEsXn+19hRYbjfyzyebPElQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-flip@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-rotate@0.10.3):
+    resolution: {integrity: sha512-0epbi8XEzp0wmSjoW9IB0iMu0yNF17aZOxLdURCN3Zr+8nWPs5VNIMqSVa1Y62GSyiMDpVpKF/ITiXre+EqrPg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-rotate': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-rotate': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3)(@jimp/plugin-crop@0.10.3)(@jimp/plugin-resize@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-gaussian@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-25eHlFbHUDnMMGpgRBBeQ2AMI4wsqCg46sue0KklI+c2BaZ+dGXmJA5uT8RTOrt64/K9Wz5E+2n7eBnny4dfpQ==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-invert@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-effYSApWY/FbtlzqsKXlTLkgloKUiHBKjkQnqh5RL4oQxh/33j6aX+HFdDyQKtsXb8CMd4xd7wyiD2YYabTa0g==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-mask@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-twrg8q8TIhM9Z6Jcu9/5f+OCAPaECb0eKrrbbIajJqJ3bCUlj5zbfgIhiQIzjPJ6KjpnFPSqHQfHkU1Vvk/nVw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-normalize@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-xkb5eZI/mMlbwKkDN79+1/t/+DBo8bBXZUMsT4gkFgMRKNRZ6NQPxlv1d3QpRzlocsl6UMxrHnhgnXdLAcgrXw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-print@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3):
+    resolution: {integrity: sha512-wjRiI6yjXsAgMe6kVjizP+RgleUCLkH256dskjoNvJzmzbEfO7xQw9g6M02VET+emnbY0CO83IkrGm2q43VRyg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-blit': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+      load-bmfont: 1.4.1
+    dev: false
+
+  /@jimp/plugin-resize@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-rf8YmEB1d7Sg+g4LpqF0Mp+dfXfb6JFJkwlAIWPUOR7lGsPWALavEwTW91c0etEdnp0+JB9AFpy6zqq7Lwkq6w==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-rotate@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3)(@jimp/plugin-crop@0.10.3)(@jimp/plugin-resize@0.10.3):
+    resolution: {integrity: sha512-YXLlRjm18fkW9MOHUaVAxWjvgZM851ofOipytz5FyKp4KZWDLk+dZK1JNmVmK7MyVmAzZ5jsgSLhIgj+GgN0Eg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-blit': '>=0.3.5'
+      '@jimp/plugin-crop': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-scale@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3):
+    resolution: {integrity: sha512-5DXD7x7WVcX1gUgnlFXQa8F+Q3ThRYwJm+aesgrYvDOY+xzRoRSdQvhmdd4JEEue3lyX44DvBSgCIHPtGcEPaw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-shadow@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blur@0.10.3)(@jimp/plugin-resize@0.10.3):
+    resolution: {integrity: sha512-/nkFXpt2zVcdP4ETdkAUL0fSzyrC5ZFxdcphbYBodqD7fXNqChS/Un1eD4xCXWEpW8cnG9dixZgQgStjywH0Mg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-blur': '>=0.3.5'
+      '@jimp/plugin-resize': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blur': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugin-threshold@0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-color@0.10.3)(@jimp/plugin-resize@0.10.3):
+    resolution: {integrity: sha512-Dzh0Yq2wXP2SOnxcbbiyA4LJ2luwrdf1MghNIt9H+NX7B+IWw/N8qA2GuSm9n4BPGSLluuhdAWJqHcTiREriVA==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+      '@jimp/plugin-color': '>=0.8.0'
+      '@jimp/plugin-resize': '>=0.8.0'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-color': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+    dev: false
+
+  /@jimp/plugins@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-jTT3/7hOScf0EIKiAXmxwayHhryhc1wWuIe3FrchjDjr9wgIGNN2a7XwCgPl3fML17DXK1x8EzDneCdh261bkw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugin-blit': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-blur': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-circle': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-color': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-contain': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3)(@jimp/plugin-resize@0.10.3)(@jimp/plugin-scale@0.10.3)
+      '@jimp/plugin-cover': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-crop@0.10.3)(@jimp/plugin-resize@0.10.3)(@jimp/plugin-scale@0.10.3)
+      '@jimp/plugin-crop': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-displace': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-dither': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-fisheye': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-flip': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-rotate@0.10.3)
+      '@jimp/plugin-gaussian': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-invert': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-mask': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-normalize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-print': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3)
+      '@jimp/plugin-resize': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/plugin-rotate': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blit@0.10.3)(@jimp/plugin-crop@0.10.3)(@jimp/plugin-resize@0.10.3)
+      '@jimp/plugin-scale': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-resize@0.10.3)
+      '@jimp/plugin-shadow': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-blur@0.10.3)(@jimp/plugin-resize@0.10.3)
+      '@jimp/plugin-threshold': 0.10.3(@jimp/custom@0.10.3)(@jimp/plugin-color@0.10.3)(@jimp/plugin-resize@0.10.3)
+      core-js: 3.30.1
+      timm: 1.7.1
+    dev: false
+
+  /@jimp/png@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-YKqk/dkl+nGZxSYIDQrqhmaP8tC3IK8H7dFPnnzFVvbhDnyYunqBZZO3SaZUKTichClRw8k/CjBhbc+hifSGWg==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/utils': 0.10.3
+      core-js: 3.30.1
+      pngjs: 3.4.0
+    dev: false
+
+  /@jimp/tiff@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-7EsJzZ5Y/EtinkBGuwX3Bi4S+zgbKouxjt9c82VJTRJOQgLWsE/RHqcyRCOQBhHAZ9QexYmDz34medfLKdoX0g==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      core-js: 3.30.1
+      utif: 2.0.1
+    dev: false
+
+  /@jimp/types@0.10.3(@jimp/custom@0.10.3):
+    resolution: {integrity: sha512-XGmBakiHZqseSWr/puGN+CHzx0IKBSpsKlmEmsNV96HKDiP6eu8NSnwdGCEq2mmIHe0JNcg1hqg59hpwtQ7Tiw==}
+    peerDependencies:
+      '@jimp/custom': '>=0.3.5'
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/bmp': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/custom': 0.10.3
+      '@jimp/gif': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/jpeg': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/png': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/tiff': 0.10.3(@jimp/custom@0.10.3)
+      core-js: 3.30.1
+      timm: 1.7.1
+    dev: false
+
+  /@jimp/utils@0.10.3:
+    resolution: {integrity: sha512-VcSlQhkil4ReYmg1KkN+WqHyYfZ2XfZxDsKAHSfST1GEz/RQHxKZbX+KhFKtKflnL0F4e6DlNQj3vznMNXCR2w==}
+    dependencies:
+      '@babel/runtime': 7.20.13
+      core-js: 3.30.1
+      regenerator-runtime: 0.13.11
+    dev: false
+
+  /@jridgewell/gen-mapping@0.1.1:
+    resolution: {integrity: sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/set-array': 1.1.2
+      '@jridgewell/sourcemap-codec': 1.4.14
+
+  /@jridgewell/gen-mapping@0.3.2:
+    resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      '@jridgewell/set-array': 1.1.2
+      '@jridgewell/sourcemap-codec': 1.4.14
+      '@jridgewell/trace-mapping': 0.3.17
+
+  /@jridgewell/resolve-uri@3.1.0:
+    resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==}
+    engines: {node: '>=6.0.0'}
+
+  /@jridgewell/set-array@1.1.2:
+    resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==}
+    engines: {node: '>=6.0.0'}
+
+  /@jridgewell/source-map@0.3.2:
+    resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==}
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.2
+      '@jridgewell/trace-mapping': 0.3.17
+
+  /@jridgewell/sourcemap-codec@1.4.14:
+    resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==}
+
+  /@jridgewell/trace-mapping@0.3.17:
+    resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==}
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.0
+      '@jridgewell/sourcemap-codec': 1.4.14
+
+  /@nodelib/fs.scandir@2.1.5:
+    resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      run-parallel: 1.2.0
+
+  /@nodelib/fs.stat@2.0.5:
+    resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+    engines: {node: '>= 8'}
+
+  /@nodelib/fs.walk@1.2.8:
+    resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+    engines: {node: '>= 8'}
+    dependencies:
+      '@nodelib/fs.scandir': 2.1.5
+      fastq: 1.13.0
+
+  /@rollup/pluginutils@4.2.1:
+    resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==}
+    engines: {node: '>= 8.0.0'}
+    dependencies:
+      estree-walker: 2.0.2
+      picomatch: 2.3.1
+
+  /@rushstack/eslint-patch@1.2.0:
+    resolution: {integrity: sha512-sXo/qW2/pAcmT43VoRKOJbDOfV3cYpq3szSVfIThQXNt+E4DfKj361vaAt3c88U5tPUxzEswam7GW48PJqtKAg==}
+    dev: true
+
+  /@sinonjs/commons@1.8.6:
+    resolution: {integrity: sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==}
+    dependencies:
+      type-detect: 4.0.8
+    dev: true
+
+  /@sinonjs/fake-timers@8.1.0:
+    resolution: {integrity: sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==}
+    dependencies:
+      '@sinonjs/commons': 1.8.6
+    dev: true
+
+  /@socket.io/component-emitter@3.1.0:
+    resolution: {integrity: sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg==}
+    dev: true
+
+  /@tootallnate/once@1.1.2:
+    resolution: {integrity: sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /@types/babel__core@7.20.0:
+    resolution: {integrity: sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@babel/types': 7.21.5
+      '@types/babel__generator': 7.6.4
+      '@types/babel__template': 7.4.1
+      '@types/babel__traverse': 7.18.5
+    dev: true
+
+  /@types/babel__generator@7.6.4:
+    resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==}
+    dependencies:
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@types/babel__template@7.4.1:
+    resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@types/babel__traverse@7.18.5:
+    resolution: {integrity: sha512-enCvTL8m/EHS/zIvJno9nE+ndYPh1/oNFzRYRmtUqJICG2VnCSBzMLW5VN2KCQU91f23tsNKR8v7VJJQMatl7Q==}
+    dependencies:
+      '@babel/types': 7.21.5
+    dev: true
+
+  /@types/cookie@0.4.1:
+    resolution: {integrity: sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==}
+    dev: true
+
+  /@types/cors@2.8.13:
+    resolution: {integrity: sha512-RG8AStHlUiV5ysZQKq97copd2UmVYw3/pRMLefISZ3S1hK104Cwm7iLQ3fTKx+lsUH2CE8FlLaYeEA2LSeqYUA==}
+    dependencies:
+      '@types/node': 18.11.10
+    dev: true
+
+  /@types/graceful-fs@4.1.6:
+    resolution: {integrity: sha512-Sig0SNORX9fdW+bQuTEovKj3uHcUL6LQKbCrrqb1X7J6/ReAbhCXRAhc+SMejhLELFj2QcyuxmUooZ4bt5ReSw==}
+    dependencies:
+      '@types/node': 18.11.10
+    dev: true
+
+  /@types/istanbul-lib-coverage@2.0.4:
+    resolution: {integrity: sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==}
+    dev: true
+
+  /@types/istanbul-lib-report@3.0.0:
+    resolution: {integrity: sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==}
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.4
+    dev: true
+
+  /@types/istanbul-reports@3.0.1:
+    resolution: {integrity: sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==}
+    dependencies:
+      '@types/istanbul-lib-report': 3.0.0
+    dev: true
+
+  /@types/json-schema@7.0.11:
+    resolution: {integrity: sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==}
+    dev: true
+
+  /@types/node@18.11.10:
+    resolution: {integrity: sha512-juG3RWMBOqcOuXC643OAdSA525V44cVgGV6dUDuiFtss+8Fk5x1hI93Rsld43VeJVIeqlP9I7Fn9/qaVqoEAuQ==}
+
+  /@types/prettier@2.7.2:
+    resolution: {integrity: sha512-KufADq8uQqo1pYKVIYzfKbJfBAc0sOeXqGbFaSpv8MRmC/zXgowNZmFcbngndGk922QDmOASEXUZCaY48gs4cg==}
+    dev: true
+
+  /@types/semver@7.3.13:
+    resolution: {integrity: sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==}
+    dev: true
+
+  /@types/stack-utils@2.0.1:
+    resolution: {integrity: sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==}
+    dev: true
+
+  /@types/yargs-parser@21.0.0:
+    resolution: {integrity: sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==}
+    dev: true
+
+  /@types/yargs@16.0.5:
+    resolution: {integrity: sha512-AxO/ADJOBFJScHbWhq2xAhlWP24rY4aCEG/NFaMvbT3X2MgRsLjhjQwsn0Zi5zn0LG9jUhCCZMeX9Dkuw6k+vQ==}
+    dependencies:
+      '@types/yargs-parser': 21.0.0
+    dev: true
+
+  /@typescript-eslint/eslint-plugin@5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.1.6):
+    resolution: {integrity: sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      '@typescript-eslint/parser': ^5.0.0
+      eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.1.6)
+      '@typescript-eslint/scope-manager': 5.45.0
+      '@typescript-eslint/type-utils': 5.45.0(eslint@8.28.0)(typescript@5.1.6)
+      '@typescript-eslint/utils': 5.45.0(eslint@8.28.0)(typescript@5.1.6)
+      debug: 4.3.4
+      eslint: 8.28.0
+      ignore: 5.2.1
+      natural-compare-lite: 1.4.0
+      regexpp: 3.2.0
+      semver: 7.3.8
+      tsutils: 3.21.0(typescript@5.1.6)
+      typescript: 5.1.6
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/parser@5.45.0(eslint@8.28.0)(typescript@5.1.6):
+    resolution: {integrity: sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/scope-manager': 5.45.0
+      '@typescript-eslint/types': 5.45.0
+      '@typescript-eslint/typescript-estree': 5.45.0(typescript@5.1.6)
+      debug: 4.3.4
+      eslint: 8.28.0
+      typescript: 5.1.6
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/scope-manager@5.45.0:
+    resolution: {integrity: sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      '@typescript-eslint/types': 5.45.0
+      '@typescript-eslint/visitor-keys': 5.45.0
+    dev: true
+
+  /@typescript-eslint/type-utils@5.45.0(eslint@8.28.0)(typescript@5.1.6):
+    resolution: {integrity: sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '*'
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/typescript-estree': 5.45.0(typescript@5.1.6)
+      '@typescript-eslint/utils': 5.45.0(eslint@8.28.0)(typescript@5.1.6)
+      debug: 4.3.4
+      eslint: 8.28.0
+      tsutils: 3.21.0(typescript@5.1.6)
+      typescript: 5.1.6
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/types@5.45.0:
+    resolution: {integrity: sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
+  /@typescript-eslint/typescript-estree@5.45.0(typescript@5.1.6):
+    resolution: {integrity: sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/types': 5.45.0
+      '@typescript-eslint/visitor-keys': 5.45.0
+      debug: 4.3.4
+      globby: 11.1.0
+      is-glob: 4.0.3
+      semver: 7.3.8
+      tsutils: 3.21.0(typescript@5.1.6)
+      typescript: 5.1.6
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@typescript-eslint/utils@5.45.0(eslint@8.28.0)(typescript@5.1.6):
+    resolution: {integrity: sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.0.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      '@types/json-schema': 7.0.11
+      '@types/semver': 7.3.13
+      '@typescript-eslint/scope-manager': 5.45.0
+      '@typescript-eslint/types': 5.45.0
+      '@typescript-eslint/typescript-estree': 5.45.0(typescript@5.1.6)
+      eslint: 8.28.0
+      eslint-scope: 5.1.1
+      eslint-utils: 3.0.0(eslint@8.28.0)
+      semver: 7.3.8
+    transitivePeerDependencies:
+      - supports-color
+      - typescript
+    dev: true
+
+  /@typescript-eslint/visitor-keys@5.45.0:
+    resolution: {integrity: sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      '@typescript-eslint/types': 5.45.0
+      eslint-visitor-keys: 3.3.0
+    dev: true
+
+  /@uni-helper/uni-app-types@0.5.8(typescript@5.1.6):
+    resolution: {integrity: sha512-iB2JG6ygBjGcUvSAKpuxrK8JfPBYuLXf3p8fMTM3Mtwoi+VvcyU4hJJwbndX3mqqVJw/lGpsOlPIBozp8NZ3Cg==}
+    engines: {node: '>=14.18'}
+    peerDependencies:
+      typescript: ^4.8.0 || ^5.0.0
+    dependencies:
+      '@dcloudio/types': 3.3.3
+      typescript: 5.1.6
+      vue3: /vue@3.3.4
+    dev: true
+
+  /@uni-helper/uni-ui-types@0.5.11(@uni-helper/uni-app-types@0.5.8):
+    resolution: {integrity: sha512-tGX4llyJBdq9TmNjmZYKu4eueUWUU0F/YgZeTWDgVCmlgYGavhcUL7R+I0Uxe5hGoqFXkNf0ZnmG/WFZWfK/HQ==}
+    engines: {node: '>=14.18'}
+    peerDependencies:
+      '@uni-helper/uni-app-types': ^0.5.1
+    peerDependenciesMeta:
+      '@uni-helper/uni-app-types':
+        optional: true
+    dependencies:
+      '@dcloudio/types': 3.3.3
+      '@uni-helper/uni-app-types': 0.5.8(typescript@5.1.6)
+      vue3: /vue@3.3.4
+    dev: true
+
+  /@vitejs/plugin-legacy@4.0.3(terser@5.16.0)(vite@4.4.9):
+    resolution: {integrity: sha512-RqDQOSEmFSNL42vITkNp8HE8Ak1yjGgaav4B6BGcZ8/URK0wikzwSyhNRirHDkp+snflEEk7iPZXTXUYA9exbg==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      terser: ^5.4.0
+      vite: ^4.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/preset-env': 7.20.2(@babel/core@7.21.8)
+      browserslist: 4.21.5
+      core-js: 3.30.1
+      magic-string: 0.30.0
+      regenerator-runtime: 0.13.11
+      systemjs: 6.14.1
+      terser: 5.16.0
+      vite: 4.4.9(@types/node@18.11.10)(sass@1.56.1)(terser@5.16.0)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@vitejs/plugin-vue-jsx@3.0.1(vite@4.4.9)(vue@3.3.4):
+    resolution: {integrity: sha512-+Jb7ggL48FSPS1uhPnJbJwWa9Sr90vQ+d0InW+AhBM22n+cfuYqJZDckBc+W3QSHe1WDvewMZfa4wZOtk5pRgw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      vite: ^4.0.0
+      vue: ^3.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/plugin-transform-typescript': 7.21.3(@babel/core@7.21.8)
+      '@vue/babel-plugin-jsx': 1.1.1(@babel/core@7.21.8)
+      vite: 4.4.9(@types/node@18.11.10)(sass@1.56.1)(terser@5.16.0)
+      vue: 3.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@vitejs/plugin-vue@4.2.1(vite@4.4.9)(vue@3.3.4):
+    resolution: {integrity: sha512-ZTZjzo7bmxTRTkb8GSTwkPOYDIP7pwuyV+RV53c9PYUouwcbkIZIvWvNWlX2b1dYZqtOv7D6iUAnJLVNGcLrSw==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    peerDependencies:
+      vite: ^4.0.0
+      vue: ^3.2.25
+    dependencies:
+      vite: 4.4.9(@types/node@18.11.10)(sass@1.56.1)(terser@5.16.0)
+      vue: 3.3.4
+
+  /@volar/language-core@1.10.0:
+    resolution: {integrity: sha512-ddyWwSYqcbEZNFHm+Z3NZd6M7Ihjcwl/9B5cZd8kECdimVXUFdFi60XHWD27nrWtUQIsUYIG7Ca1WBwV2u2LSQ==}
+    dependencies:
+      '@volar/source-map': 1.10.0
+    dev: true
+
+  /@volar/language-core@1.10.1:
+    resolution: {integrity: sha512-JnsM1mIPdfGPxmoOcK1c7HYAsL6YOv0TCJ4aW3AXPZN/Jb4R77epDyMZIVudSGjWMbvv/JfUa+rQ+dGKTmgwBA==}
+    dependencies:
+      '@volar/source-map': 1.10.1
+    dev: true
+
+  /@volar/source-map@1.10.0:
+    resolution: {integrity: sha512-/ibWdcOzDGiq/GM1JU2eX8fH1bvAhl66hfe8yEgLEzg9txgr6qb5sQ/DEz5PcDL75tF5H5sCRRwn8Eu8ezi9mw==}
+    dependencies:
+      muggle-string: 0.3.1
+    dev: true
+
+  /@volar/source-map@1.10.1:
+    resolution: {integrity: sha512-3/S6KQbqa7pGC8CxPrg69qHLpOvkiPHGJtWPkI/1AXCsktkJ6gIk/5z4hyuMp8Anvs6eS/Kvp/GZa3ut3votKA==}
+    dependencies:
+      muggle-string: 0.3.1
+    dev: true
+
+  /@volar/typescript@1.10.0:
+    resolution: {integrity: sha512-OtqGtFbUKYC0pLNIk3mHQp5xWnvL1CJIUc9VE39VdZ/oqpoBh5jKfb9uJ45Y4/oP/WYTrif/Uxl1k8VTPz66Gg==}
+    dependencies:
+      '@volar/language-core': 1.10.0
+    dev: true
+
+  /@vue/babel-helper-vue-transform-on@1.0.2:
+    resolution: {integrity: sha512-hz4R8tS5jMn8lDq6iD+yWL6XNB699pGIVLk7WSJnn1dbpjaazsjZQkieJoRX6gW5zpYSCFqQ7jUquPNY65tQYA==}
+    dev: true
+
+  /@vue/babel-plugin-jsx@1.1.1(@babel/core@7.21.8):
+    resolution: {integrity: sha512-j2uVfZjnB5+zkcbc/zsOc0fSNGCMMjaEXP52wdwdIfn0qjFfEYpYZBFKFg+HHnQeJCVrjOeO0YxgaL7DMrym9w==}
+    dependencies:
+      '@babel/helper-module-imports': 7.21.4
+      '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.21.8)
+      '@babel/template': 7.20.7
+      '@babel/traverse': 7.21.5
+      '@babel/types': 7.21.5
+      '@vue/babel-helper-vue-transform-on': 1.0.2
+      camelcase: 6.3.0
+      html-tags: 3.2.0
+      svg-tags: 1.0.0
+    transitivePeerDependencies:
+      - '@babel/core'
+      - supports-color
+    dev: true
+
+  /@vue/compiler-core@3.2.47:
+    resolution: {integrity: sha512-p4D7FDnQb7+YJmO2iPEv0SQNeNzcbHdGByJDsT4lynf63AFkOTFN07HsiRSvjGo0QrxR/o3d0hUyNCUnBU2Tig==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@vue/shared': 3.2.47
+      estree-walker: 2.0.2
+      source-map: 0.6.1
+
+  /@vue/compiler-core@3.3.4:
+    resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@vue/shared': 3.3.4
+      estree-walker: 2.0.2
+      source-map-js: 1.0.2
+
+  /@vue/compiler-dom@3.2.47:
+    resolution: {integrity: sha512-dBBnEHEPoftUiS03a4ggEig74J2YBZ2UIeyfpcRM2tavgMWo4bsEfgCGsu+uJIL/vax9S+JztH8NmQerUo7shQ==}
+    dependencies:
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+
+  /@vue/compiler-dom@3.3.4:
+    resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==}
+    dependencies:
+      '@vue/compiler-core': 3.3.4
+      '@vue/shared': 3.3.4
+
+  /@vue/compiler-sfc@3.2.47:
+    resolution: {integrity: sha512-rog05W+2IFfxjMcFw10tM9+f7i/+FFpZJJ5XHX72NP9eC2uRD+42M3pYcQqDXVYoj74kHMSEdQ/WmCjt8JFksQ==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@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.9
+      postcss: 8.4.27
+      source-map: 0.6.1
+
+  /@vue/compiler-sfc@3.3.4:
+    resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@vue/compiler-core': 3.3.4
+      '@vue/compiler-dom': 3.3.4
+      '@vue/compiler-ssr': 3.3.4
+      '@vue/reactivity-transform': 3.3.4
+      '@vue/shared': 3.3.4
+      estree-walker: 2.0.2
+      magic-string: 0.30.0
+      postcss: 8.4.27
+      source-map-js: 1.0.2
+
+  /@vue/compiler-ssr@3.2.47:
+    resolution: {integrity: sha512-wVXC+gszhulcMD8wpxMsqSOpvDZ6xKXSVWkf50Guf/S+28hTAXPDYRTbLQ3EDkOP5Xz/+SY37YiwDquKbJOgZw==}
+    dependencies:
+      '@vue/compiler-dom': 3.2.47
+      '@vue/shared': 3.2.47
+
+  /@vue/compiler-ssr@3.3.4:
+    resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==}
+    dependencies:
+      '@vue/compiler-dom': 3.3.4
+      '@vue/shared': 3.3.4
+
+  /@vue/devtools-api@6.4.5:
+    resolution: {integrity: sha512-JD5fcdIuFxU4fQyXUu3w2KpAJHzTVdN+p4iOX2lMWSHMOoQdMAcpFLZzm9Z/2nmsoZ1a96QEhZ26e50xLBsgOQ==}
+    dev: false
+
+  /@vue/eslint-config-prettier@7.0.0(eslint@8.28.0)(prettier@2.8.0):
+    resolution: {integrity: sha512-/CTc6ML3Wta1tCe1gUeO0EYnVXfo3nJXsIhZ8WJr3sov+cGASr6yuiibJTL6lmIBm7GobopToOuB3B6AWyV0Iw==}
+    peerDependencies:
+      eslint: '>= 7.28.0'
+      prettier: '>= 2.0.0'
+    dependencies:
+      eslint: 8.28.0
+      eslint-config-prettier: 8.5.0(eslint@8.28.0)
+      eslint-plugin-prettier: 4.2.1(eslint-config-prettier@8.5.0)(eslint@8.28.0)(prettier@2.8.0)
+      prettier: 2.8.0
+    dev: true
+
+  /@vue/eslint-config-typescript@11.0.2(eslint-plugin-vue@9.8.0)(eslint@8.28.0)(typescript@5.1.6):
+    resolution: {integrity: sha512-EiKud1NqlWmSapBFkeSrE994qpKx7/27uCGnhdqzllYDpQZroyX/O6bwjEpeuyKamvLbsGdO6PMR2faIf+zFnw==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
+      eslint-plugin-vue: ^9.0.0
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@typescript-eslint/eslint-plugin': 5.45.0(@typescript-eslint/parser@5.45.0)(eslint@8.28.0)(typescript@5.1.6)
+      '@typescript-eslint/parser': 5.45.0(eslint@8.28.0)(typescript@5.1.6)
+      eslint: 8.28.0
+      eslint-plugin-vue: 9.8.0(eslint@8.28.0)
+      typescript: 5.1.6
+      vue-eslint-parser: 9.1.0(eslint@8.28.0)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /@vue/language-core@1.8.8(typescript@5.1.6):
+    resolution: {integrity: sha512-i4KMTuPazf48yMdYoebTkgSOJdFraE4pQf0B+FTOFkbB+6hAfjrSou/UmYWRsWyZV6r4Rc6DDZdI39CJwL0rWw==}
+    peerDependencies:
+      typescript: '*'
+    peerDependenciesMeta:
+      typescript:
+        optional: true
+    dependencies:
+      '@volar/language-core': 1.10.1
+      '@volar/source-map': 1.10.1
+      '@vue/compiler-dom': 3.3.4
+      '@vue/reactivity': 3.3.4
+      '@vue/shared': 3.3.4
+      minimatch: 9.0.3
+      muggle-string: 0.3.1
+      typescript: 5.1.6
+      vue-template-compiler: 2.7.14
+    dev: true
+
+  /@vue/reactivity-transform@3.2.47:
+    resolution: {integrity: sha512-m8lGXw8rdnPVVIdIFhf0LeQ/ixyHkH5plYuS83yop5n7ggVJU+z5v0zecwEnX7fa7HNLBhh2qngJJkxpwEEmYA==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@vue/compiler-core': 3.2.47
+      '@vue/shared': 3.2.47
+      estree-walker: 2.0.2
+      magic-string: 0.25.9
+
+  /@vue/reactivity-transform@3.3.4:
+    resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==}
+    dependencies:
+      '@babel/parser': 7.21.8
+      '@vue/compiler-core': 3.3.4
+      '@vue/shared': 3.3.4
+      estree-walker: 2.0.2
+      magic-string: 0.30.0
+
+  /@vue/reactivity@3.2.45:
+    resolution: {integrity: sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A==}
+    dependencies:
+      '@vue/shared': 3.2.45
+    dev: true
+
+  /@vue/reactivity@3.3.4:
+    resolution: {integrity: sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==}
+    dependencies:
+      '@vue/shared': 3.3.4
+
+  /@vue/runtime-core@3.2.45:
+    resolution: {integrity: sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A==}
+    dependencies:
+      '@vue/reactivity': 3.2.45
+      '@vue/shared': 3.2.45
+    dev: true
+
+  /@vue/runtime-core@3.3.4:
+    resolution: {integrity: sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==}
+    dependencies:
+      '@vue/reactivity': 3.3.4
+      '@vue/shared': 3.3.4
+
+  /@vue/runtime-dom@3.3.4:
+    resolution: {integrity: sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==}
+    dependencies:
+      '@vue/runtime-core': 3.3.4
+      '@vue/shared': 3.3.4
+      csstype: 3.1.2
+
+  /@vue/server-renderer@3.2.47(vue@3.3.4):
+    resolution: {integrity: sha512-dN9gc1i8EvmP9RCzvneONXsKfBRgqFeFZLurmHOveL7oH6HiFXJw5OGu294n1nHc/HMgTy6LulU/tv5/A7f/LA==}
+    peerDependencies:
+      vue: 3.2.47
+    dependencies:
+      '@vue/compiler-ssr': 3.2.47
+      '@vue/shared': 3.2.47
+      vue: 3.3.4
+
+  /@vue/server-renderer@3.3.4(vue@3.3.4):
+    resolution: {integrity: sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==}
+    peerDependencies:
+      vue: 3.3.4
+    dependencies:
+      '@vue/compiler-ssr': 3.3.4
+      '@vue/shared': 3.3.4
+      vue: 3.3.4
+
+  /@vue/shared@3.2.45:
+    resolution: {integrity: sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg==}
+    dev: true
+
+  /@vue/shared@3.2.47:
+    resolution: {integrity: sha512-BHGyyGN3Q97EZx0taMQ+OLNuZcW3d37ZEVmEAyeoA9ERdGvm9Irc/0Fua8SNyOtV1w6BS4q25wbMzJujO9HIfQ==}
+
+  /@vue/shared@3.3.4:
+    resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==}
+
+  /@vue/tsconfig@0.4.0:
+    resolution: {integrity: sha512-CPuIReonid9+zOG/CGTT05FXrPYATEqoDGNrEaqS4hwcw5BUNM2FguC0mOwJD4Jr16UpRVl9N0pY3P+srIbqmg==}
+    dev: true
+
+  /@vue/typescript@1.8.8(typescript@5.1.6):
+    resolution: {integrity: sha512-jUnmMB6egu5wl342eaUH236v8tdcEPXXkPgj+eI/F6JwW/lb+yAU6U07ZbQ3MVabZRlupIlPESB7ajgAGixhow==}
+    dependencies:
+      '@volar/typescript': 1.10.0
+      '@vue/language-core': 1.8.8(typescript@5.1.6)
+    transitivePeerDependencies:
+      - typescript
+    dev: true
+
+  /abab@2.0.6:
+    resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
+    dev: true
+
+  /accepts@1.3.8:
+    resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      mime-types: 2.1.35
+      negotiator: 0.6.3
+    dev: true
+
+  /acorn-globals@6.0.0:
+    resolution: {integrity: sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==}
+    dependencies:
+      acorn: 7.4.1
+      acorn-walk: 7.2.0
+    dev: true
+
+  /acorn-jsx@5.3.2(acorn@8.8.1):
+    resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+    peerDependencies:
+      acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      acorn: 8.8.1
+    dev: true
+
+  /acorn-walk@7.2.0:
+    resolution: {integrity: sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==}
+    engines: {node: '>=0.4.0'}
+    dev: true
+
+  /acorn@7.4.1:
+    resolution: {integrity: sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+    dev: true
+
+  /acorn@8.8.1:
+    resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==}
+    engines: {node: '>=0.4.0'}
+    hasBin: true
+
+  /address@1.2.1:
+    resolution: {integrity: sha512-B+6bi5D34+fDYENiH5qOlA0cV2rAGKuWZ9LeyUUehbXy8e0VS9e498yO0Jeeh+iM+6KbfudHTFjXw2MmJD4QRA==}
+    engines: {node: '>= 10.0.0'}
+    dev: true
+
+  /agent-base@6.0.2:
+    resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==}
+    engines: {node: '>= 6.0.0'}
+    dependencies:
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /aggregate-error@3.1.0:
+    resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==}
+    engines: {node: '>=8'}
+    dependencies:
+      clean-stack: 2.2.0
+      indent-string: 4.0.0
+    dev: true
+
+  /ajv@6.12.6:
+    resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==}
+    dependencies:
+      fast-deep-equal: 3.1.3
+      fast-json-stable-stringify: 2.1.0
+      json-schema-traverse: 0.4.1
+      uri-js: 4.4.1
+    dev: true
+
+  /ansi-escapes@4.3.2:
+    resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      type-fest: 0.21.3
+    dev: true
+
+  /ansi-regex@5.0.1:
+    resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /ansi-regex@6.0.1:
+    resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /ansi-styles@3.2.1:
+    resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==}
+    engines: {node: '>=4'}
+    dependencies:
+      color-convert: 1.9.3
+
+  /ansi-styles@4.3.0:
+    resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      color-convert: 2.0.1
+    dev: true
+
+  /ansi-styles@5.2.0:
+    resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /ansi-styles@6.2.1:
+    resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /any-base@1.1.0:
+    resolution: {integrity: sha512-uMgjozySS8adZZYePpaWs8cxB9/kdzmpX6SgJZ+wbz1K5eYk5QMYDVJaZKhxyIHUdnnJkfR7SVgStgH7LkGUyg==}
+    dev: false
+
+  /anymatch@3.1.3:
+    resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
+    engines: {node: '>= 8'}
+    dependencies:
+      normalize-path: 3.0.0
+      picomatch: 2.3.1
+
+  /argparse@1.0.10:
+    resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
+    dependencies:
+      sprintf-js: 1.0.3
+    dev: true
+
+  /argparse@2.0.1:
+    resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+    dev: true
+
+  /array-flatten@1.1.1:
+    resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==}
+    dev: true
+
+  /array-union@2.1.0:
+    resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /astral-regex@2.0.0:
+    resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /asynckit@0.4.0:
+    resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+    dev: true
+
+  /autoprefixer@10.4.14(postcss@8.4.27):
+    resolution: {integrity: sha512-FQzyfOsTlwVzjHxKEqRIAdJx9niO6VCBCoEwax/VLSoQF29ggECcPuBqUMZ+u8jCZOPSy8b8/8KnuFbp0SaFZQ==}
+    engines: {node: ^10 || ^12 || >=14}
+    hasBin: true
+    peerDependencies:
+      postcss: ^8.1.0
+    dependencies:
+      browserslist: 4.21.5
+      caniuse-lite: 1.0.30001482
+      fraction.js: 4.2.0
+      normalize-range: 0.1.2
+      picocolors: 1.0.0
+      postcss: 8.4.27
+      postcss-value-parser: 4.2.0
+
+  /babel-jest@27.5.1(@babel/core@7.21.8):
+    resolution: {integrity: sha512-cdQ5dXjGRd0IBRATiQ4mZGlGlRE8kJpjPOixdNRdT+m3UcNqmYWN6rK6nvtXYfY3D76cb8s/O1Ss8ea24PIwcg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      '@babel/core': ^7.8.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/babel__core': 7.20.0
+      babel-plugin-istanbul: 6.1.1
+      babel-preset-jest: 27.5.1(@babel/core@7.21.8)
+      chalk: 4.1.2
+      graceful-fs: 4.2.10
+      slash: 3.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /babel-plugin-istanbul@6.1.1:
+    resolution: {integrity: sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@babel/helper-plugin-utils': 7.20.2
+      '@istanbuljs/load-nyc-config': 1.1.0
+      '@istanbuljs/schema': 0.1.3
+      istanbul-lib-instrument: 5.2.1
+      test-exclude: 6.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /babel-plugin-jest-hoist@27.5.1:
+    resolution: {integrity: sha512-50wCwD5EMNW4aRpOwtqzyZHIewTYNxLA4nhB+09d8BIssfNfzBRhkBIHiaPv1Si226TQSvp8gxAJm2iY2qs2hQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@babel/template': 7.20.7
+      '@babel/types': 7.21.5
+      '@types/babel__core': 7.20.0
+      '@types/babel__traverse': 7.18.5
+    dev: true
+
+  /babel-plugin-polyfill-corejs2@0.3.3(@babel/core@7.21.8):
+    resolution: {integrity: sha512-8hOdmFYFSZhqg2C/JgLUQ+t52o5nirNwaWM2B9LWteozwIvM14VSwdsCAUET10qT+kmySAlseadmfeeSWFCy+Q==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/compat-data': 7.21.7
+      '@babel/core': 7.21.8
+      '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.8)
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /babel-plugin-polyfill-corejs3@0.6.0(@babel/core@7.21.8):
+    resolution: {integrity: sha512-+eHqR6OPcBhJOGgsIar7xoAB1GcSwVUA3XjAd7HJNzOXT4wv6/H7KIdA/Nc60cvUlDbKApmqNvD1B1bzOt4nyA==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.8)
+      core-js-compat: 3.28.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /babel-plugin-polyfill-regenerator@0.4.1(@babel/core@7.21.8):
+    resolution: {integrity: sha512-NtQGmyQDXjQqQ+IzRkBVwEOz9lQ4zxAQZgoAYEtU9dJjnl1Oc98qnN7jcp+bE7O7aYzVpavXE3/VKXNzUbh7aw==}
+    peerDependencies:
+      '@babel/core': ^7.0.0-0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/helper-define-polyfill-provider': 0.3.3(@babel/core@7.21.8)
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /babel-preset-current-node-syntax@1.0.1(@babel/core@7.21.8):
+    resolution: {integrity: sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.21.8)
+      '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.21.8)
+      '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.21.8)
+      '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.21.8)
+      '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.21.8)
+      '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.21.8)
+      '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.21.8)
+    dev: true
+
+  /babel-preset-jest@27.5.1(@babel/core@7.21.8):
+    resolution: {integrity: sha512-Nptf2FzlPCWYuJg41HBqXVT8ym6bXOevuCTbhxlUpjwtysGaIWFvDEjp4y+G7fl13FgOdjs7P/DmErqH7da0Ag==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      '@babel/core': ^7.0.0
+    dependencies:
+      '@babel/core': 7.21.8
+      babel-plugin-jest-hoist: 27.5.1
+      babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.8)
+    dev: true
+
+  /balanced-match@1.0.2:
+    resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+    dev: true
+
+  /base64-js@1.5.1:
+    resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
+    dev: false
+
+  /base64id@2.0.0:
+    resolution: {integrity: sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog==}
+    engines: {node: ^4.5.0 || >= 5.9}
+    dev: true
+
+  /base64url@3.0.1:
+    resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==}
+    engines: {node: '>=6.0.0'}
+
+  /binary-extensions@2.2.0:
+    resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
+    engines: {node: '>=8'}
+
+  /bmp-js@0.1.0:
+    resolution: {integrity: sha512-vHdS19CnY3hwiNdkaqk93DvjVLfbEcI8mys4UjuWrlX1haDmroo8o4xCzh4wD6DGV6HxRCyauwhHRqMTfERtjw==}
+    dev: false
+
+  /body-parser@1.20.1:
+    resolution: {integrity: sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+    dependencies:
+      bytes: 3.1.2
+      content-type: 1.0.4
+      debug: 2.6.9
+      depd: 2.0.0
+      destroy: 1.2.0
+      http-errors: 2.0.0
+      iconv-lite: 0.4.24
+      on-finished: 2.4.1
+      qs: 6.11.0
+      raw-body: 2.5.1
+      type-is: 1.6.18
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /boolbase@1.0.0:
+    resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+    dev: true
+
+  /brace-expansion@1.1.11:
+    resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
+    dependencies:
+      balanced-match: 1.0.2
+      concat-map: 0.0.1
+    dev: true
+
+  /brace-expansion@2.0.1:
+    resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==}
+    dependencies:
+      balanced-match: 1.0.2
+    dev: true
+
+  /braces@3.0.2:
+    resolution: {integrity: sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==}
+    engines: {node: '>=8'}
+    dependencies:
+      fill-range: 7.0.1
+
+  /browser-process-hrtime@1.0.0:
+    resolution: {integrity: sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==}
+    dev: true
+
+  /browserslist@4.21.5:
+    resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==}
+    engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+    hasBin: true
+    dependencies:
+      caniuse-lite: 1.0.30001482
+      electron-to-chromium: 1.4.284
+      node-releases: 2.0.10
+      update-browserslist-db: 1.0.10(browserslist@4.21.5)
+
+  /bser@2.1.1:
+    resolution: {integrity: sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==}
+    dependencies:
+      node-int64: 0.4.0
+    dev: true
+
+  /buffer-equal@0.0.1:
+    resolution: {integrity: sha512-RgSV6InVQ9ODPdLWJ5UAqBqJBOg370Nz6ZQtRzpt6nUjc8v0St97uJ4PYC6NztqIScrAXafKM3mZPMygSe1ggA==}
+    engines: {node: '>=0.4.0'}
+    dev: false
+
+  /buffer-from@1.1.2:
+    resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==}
+
+  /buffer@5.7.1:
+    resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
+    dependencies:
+      base64-js: 1.5.1
+      ieee754: 1.2.1
+    dev: false
+
+  /bytes@3.1.2:
+    resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
+  /cac@6.7.9:
+    resolution: {integrity: sha512-XN5qEpfNQCJ8jRaZgitSkkukjMRCGio+X3Ks5KUbGGlPbV+pSem1l9VuzooCBXOiMFshUZgyYqg6rgN8rjkb/w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /call-bind@1.0.2:
+    resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
+    dependencies:
+      function-bind: 1.1.1
+      get-intrinsic: 1.1.3
+    dev: true
+
+  /callsites@3.1.0:
+    resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /camelcase@5.3.1:
+    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /camelcase@6.3.0:
+    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /caniuse-lite@1.0.30001482:
+    resolution: {integrity: sha512-F1ZInsg53cegyjroxLNW9DmrEQ1SuGRTO1QlpA0o2/6OpQ0gFeDRoq1yFmnr8Sakn9qwwt9DmbxHB6w167OSuQ==}
+
+  /chalk@2.4.2:
+    resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==}
+    engines: {node: '>=4'}
+    dependencies:
+      ansi-styles: 3.2.1
+      escape-string-regexp: 1.0.5
+      supports-color: 5.5.0
+
+  /chalk@4.1.2:
+    resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      supports-color: 7.2.0
+    dev: true
+
+  /char-regex@1.0.2:
+    resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /chokidar@3.5.3:
+    resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
+    engines: {node: '>= 8.10.0'}
+    dependencies:
+      anymatch: 3.1.3
+      braces: 3.0.2
+      glob-parent: 5.1.2
+      is-binary-path: 2.1.0
+      is-glob: 4.0.3
+      normalize-path: 3.0.0
+      readdirp: 3.6.0
+    optionalDependencies:
+      fsevents: 2.3.2
+
+  /ci-info@3.8.0:
+    resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /cjs-module-lexer@1.2.2:
+    resolution: {integrity: sha512-cOU9usZw8/dXIXKtwa8pM0OTJQuJkxMN6w30csNRUerHfeQ5R6U3kkU/FtJeIf3M202OHfY2U8ccInBG7/xogA==}
+    dev: true
+
+  /clean-stack@2.2.0:
+    resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /cli-cursor@3.1.0:
+    resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==}
+    engines: {node: '>=8'}
+    dependencies:
+      restore-cursor: 3.1.0
+    dev: true
+
+  /cli-truncate@2.1.0:
+    resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==}
+    engines: {node: '>=8'}
+    dependencies:
+      slice-ansi: 3.0.0
+      string-width: 4.2.3
+    dev: true
+
+  /cli-truncate@3.1.0:
+    resolution: {integrity: sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      slice-ansi: 5.0.0
+      string-width: 5.1.2
+    dev: true
+
+  /cliui@7.0.4:
+    resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 7.0.0
+    dev: true
+
+  /co@4.6.0:
+    resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==}
+    engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'}
+    dev: true
+
+  /collect-v8-coverage@1.0.1:
+    resolution: {integrity: sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==}
+    dev: true
+
+  /color-convert@1.9.3:
+    resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==}
+    dependencies:
+      color-name: 1.1.3
+
+  /color-convert@2.0.1:
+    resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+    engines: {node: '>=7.0.0'}
+    dependencies:
+      color-name: 1.1.4
+    dev: true
+
+  /color-name@1.1.3:
+    resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
+
+  /color-name@1.1.4:
+    resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+    dev: true
+
+  /colorette@2.0.19:
+    resolution: {integrity: sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==}
+    dev: true
+
+  /combined-stream@1.0.8:
+    resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      delayed-stream: 1.0.0
+    dev: true
+
+  /commander@2.20.3:
+    resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
+
+  /commander@9.4.1:
+    resolution: {integrity: sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==}
+    engines: {node: ^12.20.0 || >=14}
+    dev: true
+
+  /compare-versions@3.6.0:
+    resolution: {integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==}
+
+  /concat-map@0.0.1:
+    resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+    dev: true
+
+  /content-disposition@0.5.4:
+    resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      safe-buffer: 5.2.1
+    dev: true
+
+  /content-type@1.0.4:
+    resolution: {integrity: sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /convert-source-map@1.9.0:
+    resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==}
+
+  /cookie-signature@1.0.6:
+    resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==}
+    dev: true
+
+  /cookie@0.4.2:
+    resolution: {integrity: sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /cookie@0.5.0:
+    resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /core-js-compat@3.28.0:
+    resolution: {integrity: sha512-myzPgE7QodMg4nnd3K1TDoES/nADRStM8Gpz0D6nhkwbmwEnE0ZGJgoWsvQ722FR8D7xS0n0LV556RcEicjTyg==}
+    dependencies:
+      browserslist: 4.21.5
+    dev: true
+
+  /core-js@3.30.1:
+    resolution: {integrity: sha512-ZNS5nbiSwDTq4hFosEDqm65izl2CWmLz0hARJMyNQBgkUZMIF51cQiMvIQKA6hvuaeWxQDP3hEedM1JZIgTldQ==}
+    requiresBuild: true
+
+  /cors@2.8.5:
+    resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
+    engines: {node: '>= 0.10'}
+    dependencies:
+      object-assign: 4.1.1
+      vary: 1.1.2
+    dev: true
+
+  /cross-env@7.0.3:
+    resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
+    engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
+    hasBin: true
+    dependencies:
+      cross-spawn: 7.0.3
+    dev: true
+
+  /cross-spawn@7.0.3:
+    resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
+    engines: {node: '>= 8'}
+    dependencies:
+      path-key: 3.1.1
+      shebang-command: 2.0.0
+      which: 2.0.2
+    dev: true
+
+  /css-font-size-keywords@1.0.0:
+    resolution: {integrity: sha512-Q+svMDbMlelgCfH/RVDKtTDaf5021O486ZThQPIpahnIjUkMUslC+WuOQSWTgGSrNCH08Y7tYNEmmy0hkfMI8Q==}
+    dev: false
+
+  /css-font-stretch-keywords@1.0.1:
+    resolution: {integrity: sha512-KmugPO2BNqoyp9zmBIUGwt58UQSfyk1X5DbOlkb2pckDXFSAfjsD5wenb88fNrD6fvS+vu90a/tsPpb9vb0SLg==}
+    dev: false
+
+  /css-font-style-keywords@1.0.1:
+    resolution: {integrity: sha512-0Fn0aTpcDktnR1RzaBYorIxQily85M2KXRpzmxQPgh8pxUN9Fcn00I8u9I3grNr1QXVgCl9T5Imx0ZwKU973Vg==}
+    dev: false
+
+  /css-font-weight-keywords@1.0.0:
+    resolution: {integrity: sha512-5So8/NH+oDD+EzsnF4iaG4ZFHQ3vaViePkL1ZbZ5iC/KrsCY+WHq/lvOgrtmuOQ9pBBZ1ADGpaf+A4lj1Z9eYA==}
+    dev: false
+
+  /css-list-helpers@2.0.0:
+    resolution: {integrity: sha512-9Bj8tZ0jWbAM3u/U6m/boAzAwLPwtjzFvwivr2piSvyVa3K3rChJzQy4RIHkNkKiZCHrEMWDJWtTR8UyVhdDnQ==}
+    dev: false
+
+  /css-system-font-keywords@1.0.0:
+    resolution: {integrity: sha512-1umTtVd/fXS25ftfjB71eASCrYhilmEsvDEI6wG/QplnmlfmVM5HkZ/ZX46DT5K3eblFPgLUHt5BRCb0YXkSFA==}
+    dev: false
+
+  /cssesc@3.0.0:
+    resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  /cssom@0.3.8:
+    resolution: {integrity: sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==}
+    dev: true
+
+  /cssom@0.4.4:
+    resolution: {integrity: sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==}
+    dev: true
+
+  /cssstyle@2.3.0:
+    resolution: {integrity: sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==}
+    engines: {node: '>=8'}
+    dependencies:
+      cssom: 0.3.8
+    dev: true
+
+  /csstype@3.1.2:
+    resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
+
+  /data-urls@2.0.0:
+    resolution: {integrity: sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      abab: 2.0.6
+      whatwg-mimetype: 2.3.0
+      whatwg-url: 8.7.0
+    dev: true
+
+  /de-indent@1.0.2:
+    resolution: {integrity: sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==}
+    dev: true
+
+  /debug@2.6.9:
+    resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.0.0
+    dev: true
+
+  /debug@4.3.4:
+    resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.2
+
+  /decimal.js@10.4.3:
+    resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==}
+    dev: true
+
+  /dedent@0.7.0:
+    resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==}
+    dev: true
+
+  /deep-is@0.1.4:
+    resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+    dev: true
+
+  /deepmerge@4.3.1:
+    resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /default-gateway@6.0.3:
+    resolution: {integrity: sha512-fwSOJsbbNzZ/CUFpqFBqYfYNLj1NbMPm8MMCIzHjC83iSJRBEGmDUxU+WP661BaBQImeC2yHwXtz+P/O9o+XEg==}
+    engines: {node: '>= 10'}
+    dependencies:
+      execa: 5.1.1
+    dev: true
+
+  /define-lazy-prop@2.0.0:
+    resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /delayed-stream@1.0.0:
+    resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+    engines: {node: '>=0.4.0'}
+    dev: true
+
+  /depd@2.0.0:
+    resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
+  /destroy@1.2.0:
+    resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
+    engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
+    dev: true
+
+  /detect-newline@3.1.0:
+    resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /detect-port@1.5.1:
+    resolution: {integrity: sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==}
+    hasBin: true
+    dependencies:
+      address: 1.2.1
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /diff-sequences@27.5.1:
+    resolution: {integrity: sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dev: true
+
+  /dir-glob@3.0.1:
+    resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==}
+    engines: {node: '>=8'}
+    dependencies:
+      path-type: 4.0.0
+    dev: true
+
+  /doctrine@3.0.0:
+    resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      esutils: 2.0.3
+    dev: true
+
+  /dom-walk@0.1.2:
+    resolution: {integrity: sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==}
+    dev: false
+
+  /domexception@2.0.1:
+    resolution: {integrity: sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==}
+    engines: {node: '>=8'}
+    dependencies:
+      webidl-conversions: 5.0.0
+    dev: true
+
+  /eastasianwidth@0.2.0:
+    resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==}
+    dev: true
+
+  /ee-first@1.1.1:
+    resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==}
+    dev: true
+
+  /electron-to-chromium@1.4.284:
+    resolution: {integrity: sha512-M8WEXFuKXMYMVr45fo8mq0wUrrJHheiKZf6BArTKk9ZBYCKJEOU5H8cdWgDT+qCVZf7Na4lVUaZsA+h6uA9+PA==}
+
+  /emittery@0.8.1:
+    resolution: {integrity: sha512-uDfvUjVrfGJJhymx/kz6prltenw1u7WrCg1oa94zYY8xxVpLLUu045LAT0dhDZdXG58/EpPL/5kA180fQ/qudg==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /emoji-regex@8.0.0:
+    resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
+    dev: true
+
+  /emoji-regex@9.2.2:
+    resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+    dev: true
+
+  /encodeurl@1.0.2:
+    resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
+  /engine.io-parser@5.0.6:
+    resolution: {integrity: sha512-tjuoZDMAdEhVnSFleYPCtdL2GXwVTGtNjoeJd9IhIG3C1xs9uwxqRNEu5WpnDZCaozwVlK/nuQhpodhXSIMaxw==}
+    engines: {node: '>=10.0.0'}
+    dev: true
+
+  /engine.io@6.4.2:
+    resolution: {integrity: sha512-FKn/3oMiJjrOEOeUub2WCox6JhxBXq/Zn3fZOMCBxKnNYtsdKjxhl7yR3fZhM9PV+rdE75SU5SYMc+2PGzo+Tg==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      '@types/cookie': 0.4.1
+      '@types/cors': 2.8.13
+      '@types/node': 18.11.10
+      accepts: 1.3.8
+      base64id: 2.0.0
+      cookie: 0.4.2
+      cors: 2.8.5
+      debug: 4.3.4
+      engine.io-parser: 5.0.6
+      ws: 8.11.0
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /error-ex@1.3.2:
+    resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
+    dependencies:
+      is-arrayish: 0.2.1
+    dev: true
+
+  /es-module-lexer@0.9.3:
+    resolution: {integrity: sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==}
+    dev: true
+
+  /es-module-lexer@1.3.0:
+    resolution: {integrity: sha512-vZK7T0N2CBmBOixhmjdqx2gWVbFZ4DXZ/NyRMZVlJXPa7CyFS+/a4QQsDGDQy9ZfEzxFuNEsMLeQJnKP2p5/JA==}
+
+  /esbuild@0.17.18:
+    resolution: {integrity: sha512-z1lix43jBs6UKjcZVKOw2xx69ffE2aG0PygLL5qJ9OS/gy0Ewd1gW/PUQIOIQGXBHWNywSc0floSKoMFF8aK2w==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/android-arm': 0.17.18
+      '@esbuild/android-arm64': 0.17.18
+      '@esbuild/android-x64': 0.17.18
+      '@esbuild/darwin-arm64': 0.17.18
+      '@esbuild/darwin-x64': 0.17.18
+      '@esbuild/freebsd-arm64': 0.17.18
+      '@esbuild/freebsd-x64': 0.17.18
+      '@esbuild/linux-arm': 0.17.18
+      '@esbuild/linux-arm64': 0.17.18
+      '@esbuild/linux-ia32': 0.17.18
+      '@esbuild/linux-loong64': 0.17.18
+      '@esbuild/linux-mips64el': 0.17.18
+      '@esbuild/linux-ppc64': 0.17.18
+      '@esbuild/linux-riscv64': 0.17.18
+      '@esbuild/linux-s390x': 0.17.18
+      '@esbuild/linux-x64': 0.17.18
+      '@esbuild/netbsd-x64': 0.17.18
+      '@esbuild/openbsd-x64': 0.17.18
+      '@esbuild/sunos-x64': 0.17.18
+      '@esbuild/win32-arm64': 0.17.18
+      '@esbuild/win32-ia32': 0.17.18
+      '@esbuild/win32-x64': 0.17.18
+
+  /esbuild@0.18.20:
+    resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    optionalDependencies:
+      '@esbuild/android-arm': 0.18.20
+      '@esbuild/android-arm64': 0.18.20
+      '@esbuild/android-x64': 0.18.20
+      '@esbuild/darwin-arm64': 0.18.20
+      '@esbuild/darwin-x64': 0.18.20
+      '@esbuild/freebsd-arm64': 0.18.20
+      '@esbuild/freebsd-x64': 0.18.20
+      '@esbuild/linux-arm': 0.18.20
+      '@esbuild/linux-arm64': 0.18.20
+      '@esbuild/linux-ia32': 0.18.20
+      '@esbuild/linux-loong64': 0.18.20
+      '@esbuild/linux-mips64el': 0.18.20
+      '@esbuild/linux-ppc64': 0.18.20
+      '@esbuild/linux-riscv64': 0.18.20
+      '@esbuild/linux-s390x': 0.18.20
+      '@esbuild/linux-x64': 0.18.20
+      '@esbuild/netbsd-x64': 0.18.20
+      '@esbuild/openbsd-x64': 0.18.20
+      '@esbuild/sunos-x64': 0.18.20
+      '@esbuild/win32-arm64': 0.18.20
+      '@esbuild/win32-ia32': 0.18.20
+      '@esbuild/win32-x64': 0.18.20
+
+  /escalade@3.1.1:
+    resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
+    engines: {node: '>=6'}
+
+  /escape-html@1.0.3:
+    resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==}
+    dev: true
+
+  /escape-string-regexp@1.0.5:
+    resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==}
+    engines: {node: '>=0.8.0'}
+
+  /escape-string-regexp@2.0.0:
+    resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /escape-string-regexp@4.0.0:
+    resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /escodegen@2.0.0:
+    resolution: {integrity: sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==}
+    engines: {node: '>=6.0'}
+    hasBin: true
+    dependencies:
+      esprima: 4.0.1
+      estraverse: 5.3.0
+      esutils: 2.0.3
+      optionator: 0.8.3
+    optionalDependencies:
+      source-map: 0.6.1
+    dev: true
+
+  /eslint-config-prettier@8.5.0(eslint@8.28.0):
+    resolution: {integrity: sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==}
+    hasBin: true
+    peerDependencies:
+      eslint: '>=7.0.0'
+    dependencies:
+      eslint: 8.28.0
+    dev: true
+
+  /eslint-plugin-prettier@4.2.1(eslint-config-prettier@8.5.0)(eslint@8.28.0)(prettier@2.8.0):
+    resolution: {integrity: sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==}
+    engines: {node: '>=12.0.0'}
+    peerDependencies:
+      eslint: '>=7.28.0'
+      eslint-config-prettier: '*'
+      prettier: '>=2.0.0'
+    peerDependenciesMeta:
+      eslint-config-prettier:
+        optional: true
+    dependencies:
+      eslint: 8.28.0
+      eslint-config-prettier: 8.5.0(eslint@8.28.0)
+      prettier: 2.8.0
+      prettier-linter-helpers: 1.0.0
+    dev: true
+
+  /eslint-plugin-vue@9.8.0(eslint@8.28.0):
+    resolution: {integrity: sha512-E/AXwcTzunyzM83C2QqDHxepMzvI2y6x+mmeYHbVDQlKFqmKYvRrhaVixEeeG27uI44p9oKDFiyCRw4XxgtfHA==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: ^6.2.0 || ^7.0.0 || ^8.0.0
+    dependencies:
+      eslint: 8.28.0
+      eslint-utils: 3.0.0(eslint@8.28.0)
+      natural-compare: 1.4.0
+      nth-check: 2.1.1
+      postcss-selector-parser: 6.0.11
+      semver: 7.3.8
+      vue-eslint-parser: 9.1.0(eslint@8.28.0)
+      xml-name-validator: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /eslint-scope@5.1.1:
+    resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
+    engines: {node: '>=8.0.0'}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 4.3.0
+    dev: true
+
+  /eslint-scope@7.1.1:
+    resolution: {integrity: sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      esrecurse: 4.3.0
+      estraverse: 5.3.0
+    dev: true
+
+  /eslint-utils@3.0.0(eslint@8.28.0):
+    resolution: {integrity: sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==}
+    engines: {node: ^10.0.0 || ^12.0.0 || >= 14.0.0}
+    peerDependencies:
+      eslint: '>=5'
+    dependencies:
+      eslint: 8.28.0
+      eslint-visitor-keys: 2.1.0
+    dev: true
+
+  /eslint-visitor-keys@2.1.0:
+    resolution: {integrity: sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /eslint-visitor-keys@3.3.0:
+    resolution: {integrity: sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dev: true
+
+  /eslint@8.28.0:
+    resolution: {integrity: sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    hasBin: true
+    dependencies:
+      '@eslint/eslintrc': 1.3.3
+      '@humanwhocodes/config-array': 0.11.7
+      '@humanwhocodes/module-importer': 1.0.1
+      '@nodelib/fs.walk': 1.2.8
+      ajv: 6.12.6
+      chalk: 4.1.2
+      cross-spawn: 7.0.3
+      debug: 4.3.4
+      doctrine: 3.0.0
+      escape-string-regexp: 4.0.0
+      eslint-scope: 7.1.1
+      eslint-utils: 3.0.0(eslint@8.28.0)
+      eslint-visitor-keys: 3.3.0
+      espree: 9.4.1
+      esquery: 1.4.0
+      esutils: 2.0.3
+      fast-deep-equal: 3.1.3
+      file-entry-cache: 6.0.1
+      find-up: 5.0.0
+      glob-parent: 6.0.2
+      globals: 13.18.0
+      grapheme-splitter: 1.0.4
+      ignore: 5.2.1
+      import-fresh: 3.3.0
+      imurmurhash: 0.1.4
+      is-glob: 4.0.3
+      is-path-inside: 3.0.3
+      js-sdsl: 4.2.0
+      js-yaml: 4.1.0
+      json-stable-stringify-without-jsonify: 1.0.1
+      levn: 0.4.1
+      lodash.merge: 4.6.2
+      minimatch: 3.1.2
+      natural-compare: 1.4.0
+      optionator: 0.9.1
+      regexpp: 3.2.0
+      strip-ansi: 6.0.1
+      strip-json-comments: 3.1.1
+      text-table: 0.2.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /espree@9.4.1:
+    resolution: {integrity: sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==}
+    engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+    dependencies:
+      acorn: 8.8.1
+      acorn-jsx: 5.3.2(acorn@8.8.1)
+      eslint-visitor-keys: 3.3.0
+    dev: true
+
+  /esprima@4.0.1:
+    resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
+    engines: {node: '>=4'}
+    hasBin: true
+    dev: true
+
+  /esquery@1.4.0:
+    resolution: {integrity: sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==}
+    engines: {node: '>=0.10'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /esrecurse@4.3.0:
+    resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+    engines: {node: '>=4.0'}
+    dependencies:
+      estraverse: 5.3.0
+    dev: true
+
+  /estraverse@4.3.0:
+    resolution: {integrity: sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==}
+    engines: {node: '>=4.0'}
+    dev: true
+
+  /estraverse@5.3.0:
+    resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+    engines: {node: '>=4.0'}
+    dev: true
+
+  /estree-walker@2.0.2:
+    resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
+
+  /esutils@2.0.3:
+    resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /etag@1.8.1:
+    resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /execa@5.1.1:
+    resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==}
+    engines: {node: '>=10'}
+    dependencies:
+      cross-spawn: 7.0.3
+      get-stream: 6.0.1
+      human-signals: 2.1.0
+      is-stream: 2.0.1
+      merge-stream: 2.0.0
+      npm-run-path: 4.0.1
+      onetime: 5.1.2
+      signal-exit: 3.0.7
+      strip-final-newline: 2.0.0
+    dev: true
+
+  /execa@6.1.0:
+    resolution: {integrity: sha512-QVWlX2e50heYJcCPG0iWtf8r0xjEYfz/OYLGDYH+IyjWezzPNxz63qNFOu0l4YftGWuizFVZHHs8PrLU5p2IDA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      cross-spawn: 7.0.3
+      get-stream: 6.0.1
+      human-signals: 3.0.1
+      is-stream: 3.0.0
+      merge-stream: 2.0.0
+      npm-run-path: 5.1.0
+      onetime: 6.0.0
+      signal-exit: 3.0.7
+      strip-final-newline: 3.0.0
+    dev: true
+
+  /exif-parser@0.1.12:
+    resolution: {integrity: sha512-c2bQfLNbMzLPmzQuOr8fy0csy84WmwnER81W88DzTp9CYNPJ6yzOj2EZAh9pywYpqHnshVLHQJ8WzldAyfY+Iw==}
+    dev: false
+
+  /exit@0.1.2:
+    resolution: {integrity: sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /expect@27.5.1:
+    resolution: {integrity: sha512-E1q5hSUG2AmYQwQJ041nvgpkODHQvB+RKlB4IYdru6uJsyFTRyZAP463M+1lINorwbqAmUggi6+WwkD8lCS/Dw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      jest-get-type: 27.5.1
+      jest-matcher-utils: 27.5.1
+      jest-message-util: 27.5.1
+    dev: true
+
+  /express@4.18.2:
+    resolution: {integrity: sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==}
+    engines: {node: '>= 0.10.0'}
+    dependencies:
+      accepts: 1.3.8
+      array-flatten: 1.1.1
+      body-parser: 1.20.1
+      content-disposition: 0.5.4
+      content-type: 1.0.4
+      cookie: 0.5.0
+      cookie-signature: 1.0.6
+      debug: 2.6.9
+      depd: 2.0.0
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      etag: 1.8.1
+      finalhandler: 1.2.0
+      fresh: 0.5.2
+      http-errors: 2.0.0
+      merge-descriptors: 1.0.1
+      methods: 1.1.2
+      on-finished: 2.4.1
+      parseurl: 1.3.3
+      path-to-regexp: 0.1.7
+      proxy-addr: 2.0.7
+      qs: 6.11.0
+      range-parser: 1.2.1
+      safe-buffer: 5.2.1
+      send: 0.18.0
+      serve-static: 1.15.0
+      setprototypeof: 1.2.0
+      statuses: 2.0.1
+      type-is: 1.6.18
+      utils-merge: 1.0.1
+      vary: 1.1.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /fast-deep-equal@3.1.3:
+    resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+    dev: true
+
+  /fast-diff@1.2.0:
+    resolution: {integrity: sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==}
+    dev: true
+
+  /fast-glob@3.2.12:
+    resolution: {integrity: sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==}
+    engines: {node: '>=8.6.0'}
+    dependencies:
+      '@nodelib/fs.stat': 2.0.5
+      '@nodelib/fs.walk': 1.2.8
+      glob-parent: 5.1.2
+      merge2: 1.4.1
+      micromatch: 4.0.5
+
+  /fast-json-stable-stringify@2.1.0:
+    resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+    dev: true
+
+  /fast-levenshtein@2.0.6:
+    resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+    dev: true
+
+  /fastq@1.13.0:
+    resolution: {integrity: sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==}
+    dependencies:
+      reusify: 1.0.4
+
+  /fb-watchman@2.0.2:
+    resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==}
+    dependencies:
+      bser: 2.1.1
+    dev: true
+
+  /file-entry-cache@6.0.1:
+    resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      flat-cache: 3.0.4
+    dev: true
+
+  /file-type@9.0.0:
+    resolution: {integrity: sha512-Qe/5NJrgIOlwijpq3B7BEpzPFcgzggOTagZmkXQY4LA6bsXKTUstK7Wp12lEJ/mLKTpvIZxmIuRcLYWT6ov9lw==}
+    engines: {node: '>=6'}
+    dev: false
+
+  /fill-range@7.0.1:
+    resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      to-regex-range: 5.0.1
+
+  /finalhandler@1.2.0:
+    resolution: {integrity: sha512-5uXcUVftlQMFnWC9qu/svkWv3GTd2PfUhK/3PLkYNAe7FbqJMt3515HaxE6eRL74GdsriiwujiawdaB1BpEISg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      debug: 2.6.9
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      on-finished: 2.4.1
+      parseurl: 1.3.3
+      statuses: 2.0.1
+      unpipe: 1.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /find-up@4.1.0:
+    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+    engines: {node: '>=8'}
+    dependencies:
+      locate-path: 5.0.0
+      path-exists: 4.0.0
+    dev: true
+
+  /find-up@5.0.0:
+    resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+    engines: {node: '>=10'}
+    dependencies:
+      locate-path: 6.0.0
+      path-exists: 4.0.0
+    dev: true
+
+  /flat-cache@3.0.4:
+    resolution: {integrity: sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==}
+    engines: {node: ^10.12.0 || >=12.0.0}
+    dependencies:
+      flatted: 3.2.7
+      rimraf: 3.0.2
+    dev: true
+
+  /flatted@3.2.7:
+    resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
+    dev: true
+
+  /form-data@3.0.1:
+    resolution: {integrity: sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==}
+    engines: {node: '>= 6'}
+    dependencies:
+      asynckit: 0.4.0
+      combined-stream: 1.0.8
+      mime-types: 2.1.35
+    dev: true
+
+  /forwarded@0.2.0:
+    resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /fraction.js@4.2.0:
+    resolution: {integrity: sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==}
+
+  /fresh@0.5.2:
+    resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /fs-extra@10.1.0:
+    resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      graceful-fs: 4.2.10
+      jsonfile: 6.1.0
+      universalify: 2.0.0
+
+  /fs.realpath@1.0.0:
+    resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+    dev: true
+
+  /fsevents@2.3.2:
+    resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==}
+    engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
+    os: [darwin]
+    requiresBuild: true
+    optional: true
+
+  /function-bind@1.1.1:
+    resolution: {integrity: sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==}
+
+  /generic-names@4.0.0:
+    resolution: {integrity: sha512-ySFolZQfw9FoDb3ed9d80Cm9f0+r7qj+HJkWjeD9RBfpxEVTlVhol+gvaQB/78WbwYfbnNh8nWHHBSlg072y6A==}
+    dependencies:
+      loader-utils: 3.2.1
+
+  /gensync@1.0.0-beta.2:
+    resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+    engines: {node: '>=6.9.0'}
+
+  /get-caller-file@2.0.5:
+    resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
+    engines: {node: 6.* || 8.* || >= 10.*}
+    dev: true
+
+  /get-intrinsic@1.1.3:
+    resolution: {integrity: sha512-QJVz1Tj7MS099PevUG5jvnt9tSkXN8K14dxQlikJuPt4uD9hHAHjLyLBiLR5zELelBdD9QNRAXZzsJx0WaDL9A==}
+    dependencies:
+      function-bind: 1.1.1
+      has: 1.0.3
+      has-symbols: 1.0.3
+    dev: true
+
+  /get-package-type@0.1.0:
+    resolution: {integrity: sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==}
+    engines: {node: '>=8.0.0'}
+    dev: true
+
+  /get-stream@6.0.1:
+    resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /glob-parent@5.1.2:
+    resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+    engines: {node: '>= 6'}
+    dependencies:
+      is-glob: 4.0.3
+
+  /glob-parent@6.0.2:
+    resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+    engines: {node: '>=10.13.0'}
+    dependencies:
+      is-glob: 4.0.3
+    dev: true
+
+  /glob@7.2.3:
+    resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==}
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 3.1.2
+      once: 1.4.0
+      path-is-absolute: 1.0.1
+    dev: true
+
+  /global@4.4.0:
+    resolution: {integrity: sha512-wv/LAoHdRE3BeTGz53FAamhGlPLhlssK45usmGFThIi4XqnBmjKQ16u+RNbP7WvigRZDxUsM0J3gcQ5yicaL0w==}
+    dependencies:
+      min-document: 2.19.0
+      process: 0.11.10
+    dev: false
+
+  /globals@11.12.0:
+    resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
+    engines: {node: '>=4'}
+
+  /globals@13.18.0:
+    resolution: {integrity: sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==}
+    engines: {node: '>=8'}
+    dependencies:
+      type-fest: 0.20.2
+    dev: true
+
+  /globby@11.1.0:
+    resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==}
+    engines: {node: '>=10'}
+    dependencies:
+      array-union: 2.1.0
+      dir-glob: 3.0.1
+      fast-glob: 3.2.12
+      ignore: 5.2.1
+      merge2: 1.4.1
+      slash: 3.0.0
+    dev: true
+
+  /graceful-fs@4.2.10:
+    resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==}
+
+  /grapheme-splitter@1.0.4:
+    resolution: {integrity: sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==}
+    dev: true
+
+  /has-flag@3.0.0:
+    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
+    engines: {node: '>=4'}
+
+  /has-flag@4.0.0:
+    resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /has-symbols@1.0.3:
+    resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==}
+    engines: {node: '>= 0.4'}
+    dev: true
+
+  /has@1.0.3:
+    resolution: {integrity: sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==}
+    engines: {node: '>= 0.4.0'}
+    dependencies:
+      function-bind: 1.1.1
+
+  /hash-sum@2.0.0:
+    resolution: {integrity: sha512-WdZTbAByD+pHfl/g9QSsBIIwy8IT+EsPiKDs0KNX+zSHhdDLFKdZu0BQHljvO+0QI/BasbMSUa8wYNCZTvhslg==}
+
+  /he@1.2.0:
+    resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
+    hasBin: true
+    dev: true
+
+  /html-encoding-sniffer@2.0.1:
+    resolution: {integrity: sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      whatwg-encoding: 1.0.5
+    dev: true
+
+  /html-escaper@2.0.2:
+    resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+    dev: true
+
+  /html-tags@3.2.0:
+    resolution: {integrity: sha512-vy7ClnArOZwCnqZgvv+ddgHgJiAFXe3Ge9ML5/mBctVJoUoYPCdxVucOywjDARn6CVoh3dRSFdPHy2sX80L0Wg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /http-errors@2.0.0:
+    resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      depd: 2.0.0
+      inherits: 2.0.4
+      setprototypeof: 1.2.0
+      statuses: 2.0.1
+      toidentifier: 1.0.1
+    dev: true
+
+  /http-proxy-agent@4.0.1:
+    resolution: {integrity: sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==}
+    engines: {node: '>= 6'}
+    dependencies:
+      '@tootallnate/once': 1.1.2
+      agent-base: 6.0.2
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /https-proxy-agent@5.0.1:
+    resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==}
+    engines: {node: '>= 6'}
+    dependencies:
+      agent-base: 6.0.2
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /human-signals@2.1.0:
+    resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==}
+    engines: {node: '>=10.17.0'}
+    dev: true
+
+  /human-signals@3.0.1:
+    resolution: {integrity: sha512-rQLskxnM/5OCldHo+wNXbpVgDn5A17CUoKX+7Sokwaknlq7CdSnphy0W39GU8dw59XiCXmFXDg4fRuckQRKewQ==}
+    engines: {node: '>=12.20.0'}
+    dev: true
+
+  /husky@8.0.2:
+    resolution: {integrity: sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg==}
+    engines: {node: '>=14'}
+    hasBin: true
+    dev: true
+
+  /iconv-lite@0.4.24:
+    resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      safer-buffer: 2.1.2
+    dev: true
+
+  /icss-replace-symbols@1.1.0:
+    resolution: {integrity: sha512-chIaY3Vh2mh2Q3RGXttaDIzeiPvaVXJ+C4DAh/w3c37SKZ/U6PGMmuicR2EQQp9bKG8zLMCl7I+PtIoOOPp8Gg==}
+
+  /icss-utils@5.1.0(postcss@8.4.27):
+    resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+    dependencies:
+      postcss: 8.4.27
+
+  /ieee754@1.2.1:
+    resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
+    dev: false
+
+  /ignore@5.2.1:
+    resolution: {integrity: sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==}
+    engines: {node: '>= 4'}
+    dev: true
+
+  /immutable@4.1.0:
+    resolution: {integrity: sha512-oNkuqVTA8jqG1Q6c+UglTOD1xhC1BtjKI7XkCXRkZHrN5m18/XsnUp8Q89GkQO/z+0WjonSvl0FLhDYftp46nQ==}
+
+  /import-fresh@3.3.0:
+    resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
+    engines: {node: '>=6'}
+    dependencies:
+      parent-module: 1.0.1
+      resolve-from: 4.0.0
+    dev: true
+
+  /import-local@3.1.0:
+    resolution: {integrity: sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==}
+    engines: {node: '>=8'}
+    hasBin: true
+    dependencies:
+      pkg-dir: 4.2.0
+      resolve-cwd: 3.0.0
+    dev: true
+
+  /imurmurhash@0.1.4:
+    resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+    engines: {node: '>=0.8.19'}
+    dev: true
+
+  /indent-string@4.0.0:
+    resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /inflight@1.0.6:
+    resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+    dependencies:
+      once: 1.4.0
+      wrappy: 1.0.2
+    dev: true
+
+  /inherits@2.0.4:
+    resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==}
+    dev: true
+
+  /invert-kv@3.0.1:
+    resolution: {integrity: sha512-CYdFeFexxhv/Bcny+Q0BfOV+ltRlJcd4BBZBYFX/O0u4npJrgZtIcjokegtiSMAvlMTJ+Koq0GBCc//3bueQxw==}
+    engines: {node: '>=8'}
+
+  /ipaddr.js@1.9.1:
+    resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==}
+    engines: {node: '>= 0.10'}
+    dev: true
+
+  /is-arrayish@0.2.1:
+    resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+    dev: true
+
+  /is-binary-path@2.1.0:
+    resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==}
+    engines: {node: '>=8'}
+    dependencies:
+      binary-extensions: 2.2.0
+
+  /is-core-module@2.11.0:
+    resolution: {integrity: sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==}
+    dependencies:
+      has: 1.0.3
+
+  /is-docker@2.2.1:
+    resolution: {integrity: sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==}
+    engines: {node: '>=8'}
+    hasBin: true
+    dev: true
+
+  /is-extglob@2.1.1:
+    resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+    engines: {node: '>=0.10.0'}
+
+  /is-fullwidth-code-point@3.0.0:
+    resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-fullwidth-code-point@4.0.0:
+    resolution: {integrity: sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /is-function@1.0.2:
+    resolution: {integrity: sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==}
+    dev: false
+
+  /is-generator-fn@2.1.0:
+    resolution: {integrity: sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /is-glob@4.0.3:
+    resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+    engines: {node: '>=0.10.0'}
+    dependencies:
+      is-extglob: 2.1.1
+
+  /is-number@7.0.0:
+    resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+    engines: {node: '>=0.12.0'}
+
+  /is-path-inside@3.0.3:
+    resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-potential-custom-element-name@1.0.1:
+    resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+    dev: true
+
+  /is-stream@2.0.1:
+    resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /is-stream@3.0.0:
+    resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dev: true
+
+  /is-typedarray@1.0.0:
+    resolution: {integrity: sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==}
+    dev: true
+
+  /is-wsl@2.2.0:
+    resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==}
+    engines: {node: '>=8'}
+    dependencies:
+      is-docker: 2.2.1
+    dev: true
+
+  /isexe@2.0.0:
+    resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+    dev: true
+
+  /istanbul-lib-coverage@3.2.0:
+    resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /istanbul-lib-instrument@5.2.1:
+    resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/parser': 7.21.8
+      '@istanbuljs/schema': 0.1.3
+      istanbul-lib-coverage: 3.2.0
+      semver: 6.3.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /istanbul-lib-report@3.0.0:
+    resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==}
+    engines: {node: '>=8'}
+    dependencies:
+      istanbul-lib-coverage: 3.2.0
+      make-dir: 3.1.0
+      supports-color: 7.2.0
+    dev: true
+
+  /istanbul-lib-source-maps@4.0.1:
+    resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==}
+    engines: {node: '>=10'}
+    dependencies:
+      debug: 4.3.4
+      istanbul-lib-coverage: 3.2.0
+      source-map: 0.6.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /istanbul-reports@3.1.5:
+    resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==}
+    engines: {node: '>=8'}
+    dependencies:
+      html-escaper: 2.0.2
+      istanbul-lib-report: 3.0.0
+    dev: true
+
+  /jest-changed-files@27.5.1:
+    resolution: {integrity: sha512-buBLMiByfWGCoMsLLzGUUSpAmIAGnbR2KJoMN10ziLhOLvP4e0SlypHnAel8iqQXTrcbmfEY9sSqae5sgUsTvw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      execa: 5.1.1
+      throat: 6.0.2
+    dev: true
+
+  /jest-circus@27.5.1:
+    resolution: {integrity: sha512-D95R7x5UtlMA5iBYsOHFFbMD/GVA4R/Kdq15f7xYWUfWHBto9NYRsOvnSauTgdF+ogCpJ4tyKOXhUifxS65gdw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      chalk: 4.1.2
+      co: 4.6.0
+      dedent: 0.7.0
+      expect: 27.5.1
+      is-generator-fn: 2.1.0
+      jest-each: 27.5.1
+      jest-matcher-utils: 27.5.1
+      jest-message-util: 27.5.1
+      jest-runtime: 27.5.1
+      jest-snapshot: 27.5.1
+      jest-util: 27.5.1
+      pretty-format: 27.5.1
+      slash: 3.0.0
+      stack-utils: 2.0.6
+      throat: 6.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /jest-cli@27.5.1:
+    resolution: {integrity: sha512-Hc6HOOwYq4/74/c62dEE3r5elx8wjYqxY0r0G/nFrLDPMFRu6RA/u8qINOIkvhxG7mMQ5EJsOGfRpI8L6eFUVw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    hasBin: true
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0 || ^10.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+    dependencies:
+      '@jest/core': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/types': 27.5.1
+      chalk: 4.1.2
+      exit: 0.1.2
+      graceful-fs: 4.2.10
+      import-local: 3.1.0
+      jest-config: 27.5.1
+      jest-util: 27.5.1
+      jest-validate: 27.5.1
+      prompts: 2.4.2
+      yargs: 16.2.0
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /jest-config@27.5.1:
+    resolution: {integrity: sha512-5sAsjm6tGdsVbW9ahcChPAFCk4IlkQUknH5AvKjuLTSlcO/wCZKyFdn7Rg0EkC+OGgWODEy2hDpWB1PgzH0JNA==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    peerDependencies:
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      ts-node:
+        optional: true
+    dependencies:
+      '@babel/core': 7.21.8
+      '@jest/test-sequencer': 27.5.1
+      '@jest/types': 27.5.1
+      babel-jest: 27.5.1(@babel/core@7.21.8)
+      chalk: 4.1.2
+      ci-info: 3.8.0
+      deepmerge: 4.3.1
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      jest-circus: 27.5.1
+      jest-environment-jsdom: 27.5.1
+      jest-environment-node: 27.5.1
+      jest-get-type: 27.5.1
+      jest-jasmine2: 27.5.1
+      jest-regex-util: 27.5.1
+      jest-resolve: 27.5.1
+      jest-runner: 27.5.1
+      jest-util: 27.5.1
+      jest-validate: 27.5.1
+      micromatch: 4.0.5
+      parse-json: 5.2.0
+      pretty-format: 27.5.1
+      slash: 3.0.0
+      strip-json-comments: 3.1.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /jest-diff@27.5.1:
+    resolution: {integrity: sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      chalk: 4.1.2
+      diff-sequences: 27.5.1
+      jest-get-type: 27.5.1
+      pretty-format: 27.5.1
+    dev: true
+
+  /jest-docblock@27.5.1:
+    resolution: {integrity: sha512-rl7hlABeTsRYxKiUfpHrQrG4e2obOiTQWfMEH3PxPjOtdsfLQO4ReWSZaQ7DETm4xu07rl4q/h4zcKXyU0/OzQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      detect-newline: 3.1.0
+    dev: true
+
+  /jest-each@27.5.1:
+    resolution: {integrity: sha512-1Ff6p+FbhT/bXQnEouYy00bkNSY7OUpfIcmdl8vZ31A1UUaurOLPA8a8BbJOF2RDUElwJhmeaV7LnagI+5UwNQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      chalk: 4.1.2
+      jest-get-type: 27.5.1
+      jest-util: 27.5.1
+      pretty-format: 27.5.1
+    dev: true
+
+  /jest-environment-jsdom@27.5.1:
+    resolution: {integrity: sha512-TFBvkTC1Hnnnrka/fUb56atfDtJ9VMZ94JkjTbggl1PEpwrYtUBKMezB3inLmWqQsXYLcMwNoDQwoBTAvFfsfw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/fake-timers': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      jest-mock: 27.5.1
+      jest-util: 27.5.1
+      jsdom: 16.7.0
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /jest-environment-node@27.5.1:
+    resolution: {integrity: sha512-Jt4ZUnxdOsTGwSRAfKEnE6BcwsSPNOijjwifq5sDFSA2kesnXTvNqKHYgM0hDq3549Uf/KzdXNYn4wMZJPlFLw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/fake-timers': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      jest-mock: 27.5.1
+      jest-util: 27.5.1
+    dev: true
+
+  /jest-get-type@27.5.1:
+    resolution: {integrity: sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dev: true
+
+  /jest-haste-map@27.5.1:
+    resolution: {integrity: sha512-7GgkZ4Fw4NFbMSDSpZwXeBiIbx+t/46nJ2QitkOjvwPYyZmqttu2TDSimMHP1EkPOi4xUZAN1doE5Vd25H4Jng==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      '@types/graceful-fs': 4.1.6
+      '@types/node': 18.11.10
+      anymatch: 3.1.3
+      fb-watchman: 2.0.2
+      graceful-fs: 4.2.10
+      jest-regex-util: 27.5.1
+      jest-serializer: 27.5.1
+      jest-util: 27.5.1
+      jest-worker: 27.5.1
+      micromatch: 4.0.5
+      walker: 1.0.8
+    optionalDependencies:
+      fsevents: 2.3.2
+    dev: true
+
+  /jest-jasmine2@27.5.1:
+    resolution: {integrity: sha512-jtq7VVyG8SqAorDpApwiJJImd0V2wv1xzdheGHRGyuT7gZm6gG47QEskOlzsN1PG/6WNaCo5pmwMHDf3AkG2pQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/source-map': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      chalk: 4.1.2
+      co: 4.6.0
+      expect: 27.5.1
+      is-generator-fn: 2.1.0
+      jest-each: 27.5.1
+      jest-matcher-utils: 27.5.1
+      jest-message-util: 27.5.1
+      jest-runtime: 27.5.1
+      jest-snapshot: 27.5.1
+      jest-util: 27.5.1
+      pretty-format: 27.5.1
+      throat: 6.0.2
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /jest-leak-detector@27.5.1:
+    resolution: {integrity: sha512-POXfWAMvfU6WMUXftV4HolnJfnPOGEu10fscNCA76KBpRRhcMN2c8d3iT2pxQS3HLbA+5X4sOUPzYO2NUyIlHQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      jest-get-type: 27.5.1
+      pretty-format: 27.5.1
+    dev: true
+
+  /jest-matcher-utils@27.5.1:
+    resolution: {integrity: sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      chalk: 4.1.2
+      jest-diff: 27.5.1
+      jest-get-type: 27.5.1
+      pretty-format: 27.5.1
+    dev: true
+
+  /jest-message-util@27.5.1:
+    resolution: {integrity: sha512-rMyFe1+jnyAAf+NHwTclDz0eAaLkVDdKVHHBFWsBWHnnh5YeJMNWWsv7AbFYXfK3oTqvL7VTWkhNLu1jX24D+g==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@babel/code-frame': 7.21.4
+      '@jest/types': 27.5.1
+      '@types/stack-utils': 2.0.1
+      chalk: 4.1.2
+      graceful-fs: 4.2.10
+      micromatch: 4.0.5
+      pretty-format: 27.5.1
+      slash: 3.0.0
+      stack-utils: 2.0.6
+    dev: true
+
+  /jest-mock@27.5.1:
+    resolution: {integrity: sha512-K4jKbY1d4ENhbrG2zuPWaQBvDly+iZ2yAW+T1fATN78hc0sInwn7wZB8XtlNnvHug5RMwV897Xm4LqmPM4e2Og==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+    dev: true
+
+  /jest-pnp-resolver@1.2.3(jest-resolve@27.5.1):
+    resolution: {integrity: sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==}
+    engines: {node: '>=6'}
+    peerDependencies:
+      jest-resolve: '*'
+    peerDependenciesMeta:
+      jest-resolve:
+        optional: true
+    dependencies:
+      jest-resolve: 27.5.1
+    dev: true
+
+  /jest-regex-util@27.5.1:
+    resolution: {integrity: sha512-4bfKq2zie+x16okqDXjXn9ql2B0dScQu+vcwe4TvFVhkVyuWLqpZrZtXxLLWoXYgn0E87I6r6GRYHF7wFZBUvg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dev: true
+
+  /jest-resolve-dependencies@27.5.1:
+    resolution: {integrity: sha512-QQOOdY4PE39iawDn5rzbIePNigfe5B9Z91GDD1ae/xNDlu9kaat8QQ5EKnNmVWPV54hUdxCVwwj6YMgR2O7IOg==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      jest-regex-util: 27.5.1
+      jest-snapshot: 27.5.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /jest-resolve@27.5.1:
+    resolution: {integrity: sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      chalk: 4.1.2
+      graceful-fs: 4.2.10
+      jest-haste-map: 27.5.1
+      jest-pnp-resolver: 1.2.3(jest-resolve@27.5.1)
+      jest-util: 27.5.1
+      jest-validate: 27.5.1
+      resolve: 1.22.1
+      resolve.exports: 1.1.1
+      slash: 3.0.0
+    dev: true
+
+  /jest-runner@27.5.1:
+    resolution: {integrity: sha512-g4NPsM4mFCOwFKXO4p/H/kWGdJp9V8kURY2lX8Me2drgXqG7rrZAx5kv+5H7wtt/cdFIjhqYx1HrlqWHaOvDaQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/console': 27.5.1
+      '@jest/environment': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      chalk: 4.1.2
+      emittery: 0.8.1
+      graceful-fs: 4.2.10
+      jest-docblock: 27.5.1
+      jest-environment-jsdom: 27.5.1
+      jest-environment-node: 27.5.1
+      jest-haste-map: 27.5.1
+      jest-leak-detector: 27.5.1
+      jest-message-util: 27.5.1
+      jest-resolve: 27.5.1
+      jest-runtime: 27.5.1
+      jest-util: 27.5.1
+      jest-worker: 27.5.1
+      source-map-support: 0.5.21
+      throat: 6.0.2
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /jest-runtime@27.5.1:
+    resolution: {integrity: sha512-o7gxw3Gf+H2IGt8fv0RiyE1+r83FJBRruoA+FXrlHw6xEyBsU8ugA6IPfTdVyA0w8HClpbK+DGJxH59UrNMx8A==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/environment': 27.5.1
+      '@jest/fake-timers': 27.5.1
+      '@jest/globals': 27.5.1
+      '@jest/source-map': 27.5.1
+      '@jest/test-result': 27.5.1
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      chalk: 4.1.2
+      cjs-module-lexer: 1.2.2
+      collect-v8-coverage: 1.0.1
+      execa: 5.1.1
+      glob: 7.2.3
+      graceful-fs: 4.2.10
+      jest-haste-map: 27.5.1
+      jest-message-util: 27.5.1
+      jest-mock: 27.5.1
+      jest-regex-util: 27.5.1
+      jest-resolve: 27.5.1
+      jest-snapshot: 27.5.1
+      jest-util: 27.5.1
+      slash: 3.0.0
+      strip-bom: 4.0.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /jest-serializer@27.5.1:
+    resolution: {integrity: sha512-jZCyo6iIxO1aqUxpuBlwTDMkzOAJS4a3eYz3YzgxxVQFwLeSA7Jfq5cbqCY+JLvTDrWirgusI/0KwxKMgrdf7w==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@types/node': 18.11.10
+      graceful-fs: 4.2.10
+    dev: true
+
+  /jest-snapshot@27.5.1:
+    resolution: {integrity: sha512-yYykXI5a0I31xX67mgeLw1DZ0bJB+gpq5IpSuCAoyDi0+BhgU/RIrL+RTzDmkNTchvDFWKP8lp+w/42Z3us5sA==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@babel/core': 7.21.8
+      '@babel/generator': 7.21.5
+      '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.21.8)
+      '@babel/traverse': 7.21.5
+      '@babel/types': 7.21.5
+      '@jest/transform': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/babel__traverse': 7.18.5
+      '@types/prettier': 2.7.2
+      babel-preset-current-node-syntax: 1.0.1(@babel/core@7.21.8)
+      chalk: 4.1.2
+      expect: 27.5.1
+      graceful-fs: 4.2.10
+      jest-diff: 27.5.1
+      jest-get-type: 27.5.1
+      jest-haste-map: 27.5.1
+      jest-matcher-utils: 27.5.1
+      jest-message-util: 27.5.1
+      jest-util: 27.5.1
+      natural-compare: 1.4.0
+      pretty-format: 27.5.1
+      semver: 7.3.8
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /jest-util@27.5.1:
+    resolution: {integrity: sha512-Kv2o/8jNvX1MQ0KGtw480E/w4fBCDOnH6+6DmeKi6LZUIlKA5kwY0YNdlzaWTiVgxqAqik11QyxDOKk543aKXw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      chalk: 4.1.2
+      ci-info: 3.8.0
+      graceful-fs: 4.2.10
+      picomatch: 2.3.1
+    dev: true
+
+  /jest-validate@27.5.1:
+    resolution: {integrity: sha512-thkNli0LYTmOI1tDB3FI1S1RTp/Bqyd9pTarJwL87OIBFuqEb5Apv5EaApEudYg4g86e3CT6kM0RowkhtEnCBQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/types': 27.5.1
+      camelcase: 6.3.0
+      chalk: 4.1.2
+      jest-get-type: 27.5.1
+      leven: 3.1.0
+      pretty-format: 27.5.1
+    dev: true
+
+  /jest-watcher@27.5.1:
+    resolution: {integrity: sha512-z676SuD6Z8o8qbmEGhoEUFOM1+jfEiL3DXHK/xgEiG2EyNYfFG60jluWcupY6dATjfEsKQuibReS1djInQnoVw==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      '@jest/test-result': 27.5.1
+      '@jest/types': 27.5.1
+      '@types/node': 18.11.10
+      ansi-escapes: 4.3.2
+      chalk: 4.1.2
+      jest-util: 27.5.1
+      string-length: 4.0.2
+    dev: true
+
+  /jest-worker@27.5.1:
+    resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==}
+    engines: {node: '>= 10.13.0'}
+    dependencies:
+      '@types/node': 18.11.10
+      merge-stream: 2.0.0
+      supports-color: 8.1.1
+    dev: true
+
+  /jest@27.0.4:
+    resolution: {integrity: sha512-Px1iKFooXgGSkk1H8dJxxBIrM3tsc5SIuI4kfKYK2J+4rvCvPGr/cXktxh0e9zIPQ5g09kOMNfHQEmusBUf/ZA==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    hasBin: true
+    peerDependencies:
+      node-notifier: ^8.0.1 || ^9.0.0
+    peerDependenciesMeta:
+      node-notifier:
+        optional: true
+    dependencies:
+      '@jest/core': 27.5.1
+      import-local: 3.1.0
+      jest-cli: 27.5.1
+    transitivePeerDependencies:
+      - bufferutil
+      - canvas
+      - supports-color
+      - ts-node
+      - utf-8-validate
+    dev: true
+
+  /jimp@0.10.3:
+    resolution: {integrity: sha512-meVWmDMtyUG5uYjFkmzu0zBgnCvvxwWNi27c4cg55vWNVC9ES4Lcwb+ogx+uBBQE3Q+dLKjXaLl0JVW+nUNwbQ==}
+    dependencies:
+      '@babel/runtime': 7.20.13
+      '@jimp/custom': 0.10.3
+      '@jimp/plugins': 0.10.3(@jimp/custom@0.10.3)
+      '@jimp/types': 0.10.3(@jimp/custom@0.10.3)
+      core-js: 3.30.1
+      regenerator-runtime: 0.13.11
+    dev: false
+
+  /jpeg-js@0.3.7:
+    resolution: {integrity: sha512-9IXdWudL61npZjvLuVe/ktHiA41iE8qFyLB+4VDTblEsWBzeg8WQTlktdUK4CdncUqtUgUg0bbOmTE2bKBKaBQ==}
+    dev: false
+
+  /js-sdsl@4.2.0:
+    resolution: {integrity: sha512-dyBIzQBDkCqCu+0upx25Y2jGdbTGxE9fshMsCdK0ViOongpV+n5tXRcZY9v7CaVQ79AGS9KA1KHtojxiM7aXSQ==}
+    dev: true
+
+  /js-tokens@4.0.0:
+    resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+  /js-yaml@3.14.1:
+    resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==}
+    hasBin: true
+    dependencies:
+      argparse: 1.0.10
+      esprima: 4.0.1
+    dev: true
+
+  /js-yaml@4.1.0:
+    resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+    hasBin: true
+    dependencies:
+      argparse: 2.0.1
+    dev: true
+
+  /jsdom@16.7.0:
+    resolution: {integrity: sha512-u9Smc2G1USStM+s/x1ru5Sxrl6mPYCbByG1U/hUmqaVsm4tbNyS7CicOSRyuGQYZhTu0h84qkZZQ/I+dzizSVw==}
+    engines: {node: '>=10'}
+    peerDependencies:
+      canvas: ^2.5.0
+    peerDependenciesMeta:
+      canvas:
+        optional: true
+    dependencies:
+      abab: 2.0.6
+      acorn: 8.8.1
+      acorn-globals: 6.0.0
+      cssom: 0.4.4
+      cssstyle: 2.3.0
+      data-urls: 2.0.0
+      decimal.js: 10.4.3
+      domexception: 2.0.1
+      escodegen: 2.0.0
+      form-data: 3.0.1
+      html-encoding-sniffer: 2.0.1
+      http-proxy-agent: 4.0.1
+      https-proxy-agent: 5.0.1
+      is-potential-custom-element-name: 1.0.1
+      nwsapi: 2.2.4
+      parse5: 6.0.1
+      saxes: 5.0.1
+      symbol-tree: 3.2.4
+      tough-cookie: 4.1.2
+      w3c-hr-time: 1.0.2
+      w3c-xmlserializer: 2.0.0
+      webidl-conversions: 6.1.0
+      whatwg-encoding: 1.0.5
+      whatwg-mimetype: 2.3.0
+      whatwg-url: 8.7.0
+      ws: 7.5.9
+      xml-name-validator: 3.0.0
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /jsesc@0.5.0:
+    resolution: {integrity: sha512-uZz5UnB7u4T9LvwmFqXii7pZSouaRPorGs5who1Ip7VO0wxanFvBL7GkM6dTHlgX+jhBApRetaWpnDabOeTcnA==}
+    hasBin: true
+    dev: true
+
+  /jsesc@2.5.2:
+    resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  /json-parse-even-better-errors@2.3.1:
+    resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+    dev: true
+
+  /json-schema-traverse@0.4.1:
+    resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+    dev: true
+
+  /json-stable-stringify-without-jsonify@1.0.1:
+    resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+    dev: true
+
+  /json5@2.2.3:
+    resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
+  /jsonc-parser@3.2.0:
+    resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
+
+  /jsonfile@6.1.0:
+    resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
+    dependencies:
+      universalify: 2.0.0
+    optionalDependencies:
+      graceful-fs: 4.2.10
+
+  /kleur@3.0.3:
+    resolution: {integrity: sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /lcid@3.1.1:
+    resolution: {integrity: sha512-M6T051+5QCGLBQb8id3hdvIW8+zeFV2FyBGFS9IEK5H9Wt4MueD4bW1eWikpHgZp+5xR3l5c8pZUkQsIA0BFZg==}
+    engines: {node: '>=8'}
+    dependencies:
+      invert-kv: 3.0.1
+
+  /leven@3.1.0:
+    resolution: {integrity: sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /levn@0.3.0:
+    resolution: {integrity: sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.1.2
+      type-check: 0.3.2
+    dev: true
+
+  /levn@0.4.1:
+    resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+    dev: true
+
+  /licia@1.37.0:
+    resolution: {integrity: sha512-jX49+WmzikOPGNrcy/giS23HCI8Pb7RF585Ei5d7oWF4WMelaZWv4odqQNdT0jtHkoUxqSvPz67Jvyq06xamUA==}
+
+  /lilconfig@2.0.6:
+    resolution: {integrity: sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==}
+    engines: {node: '>=10'}
+
+  /lines-and-columns@1.2.4:
+    resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+    dev: true
+
+  /lint-staged@13.0.4:
+    resolution: {integrity: sha512-HxlHCXoYRsq9QCby5wFozmZW00hMs/9e3l+/dz6Qr8Kle4UH0kJTdABAbqhzG+3pcG6QjL9kz7NgGBfph+a5dw==}
+    engines: {node: ^14.13.1 || >=16.0.0}
+    hasBin: true
+    dependencies:
+      cli-truncate: 3.1.0
+      colorette: 2.0.19
+      commander: 9.4.1
+      debug: 4.3.4
+      execa: 6.1.0
+      lilconfig: 2.0.6
+      listr2: 5.0.6
+      micromatch: 4.0.5
+      normalize-path: 3.0.0
+      object-inspect: 1.12.2
+      pidtree: 0.6.0
+      string-argv: 0.3.1
+      yaml: 2.1.3
+    transitivePeerDependencies:
+      - enquirer
+      - supports-color
+    dev: true
+
+  /listr2@5.0.6:
+    resolution: {integrity: sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==}
+    engines: {node: ^14.13.1 || >=16.0.0}
+    peerDependencies:
+      enquirer: '>= 2.3.0 < 3'
+    peerDependenciesMeta:
+      enquirer:
+        optional: true
+    dependencies:
+      cli-truncate: 2.1.0
+      colorette: 2.0.19
+      log-update: 4.0.0
+      p-map: 4.0.0
+      rfdc: 1.3.0
+      rxjs: 7.5.7
+      through: 2.3.8
+      wrap-ansi: 7.0.0
+    dev: true
+
+  /load-bmfont@1.4.1:
+    resolution: {integrity: sha512-8UyQoYmdRDy81Brz6aLAUhfZLwr5zV0L3taTQ4hju7m6biuwiWiJXjPhBJxbUQJA8PrkvJ/7Enqmwk2sM14soA==}
+    dependencies:
+      buffer-equal: 0.0.1
+      mime: 1.6.0
+      parse-bmfont-ascii: 1.0.6
+      parse-bmfont-binary: 1.0.6
+      parse-bmfont-xml: 1.1.4
+      phin: 2.9.3
+      xhr: 2.6.0
+      xtend: 4.0.2
+    dev: false
+
+  /loader-utils@3.2.1:
+    resolution: {integrity: sha512-ZvFw1KWS3GVyYBYb7qkmRM/WwL2TQQBxgCK62rlvm4WpVQ23Nb4tYjApUlfjrEGvOs7KHEsmyUn75OHZrJMWPw==}
+    engines: {node: '>= 12.13.0'}
+
+  /localstorage-polyfill@1.0.1:
+    resolution: {integrity: sha512-m4iHVZxFH5734oQcPKU08025gIz2+4bjWR9lulP8ZYxEJR0BpA0w32oJmkzh8y3UI9ci7xCBehQDc3oA1X+VHw==}
+    engines: {node: '>=6'}
+    dev: false
+
+  /locate-path@5.0.0:
+    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-locate: 4.1.0
+    dev: true
+
+  /locate-path@6.0.0:
+    resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+    engines: {node: '>=10'}
+    dependencies:
+      p-locate: 5.0.0
+    dev: true
+
+  /lodash.camelcase@4.3.0:
+    resolution: {integrity: sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==}
+
+  /lodash.debounce@4.0.8:
+    resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+    dev: true
+
+  /lodash.merge@4.6.2:
+    resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+    dev: true
+
+  /lodash@4.17.21:
+    resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==}
+    dev: true
+
+  /log-update@4.0.0:
+    resolution: {integrity: sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-escapes: 4.3.2
+      cli-cursor: 3.1.0
+      slice-ansi: 4.0.0
+      wrap-ansi: 6.2.0
+    dev: true
+
+  /lru-cache@5.1.1:
+    resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+    dependencies:
+      yallist: 3.1.1
+
+  /lru-cache@6.0.0:
+    resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
+    engines: {node: '>=10'}
+    dependencies:
+      yallist: 4.0.0
+    dev: true
+
+  /magic-string@0.25.9:
+    resolution: {integrity: sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==}
+    dependencies:
+      sourcemap-codec: 1.4.8
+
+  /magic-string@0.27.0:
+    resolution: {integrity: sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==}
+    engines: {node: '>=12'}
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.4.14
+    dev: true
+
+  /magic-string@0.30.0:
+    resolution: {integrity: sha512-LA+31JYDJLs82r2ScLrlz1GjSgu66ZV518eyWT+S8VhyQn/JL0u9MeBOvQMGYiPk1DBiSN9DDMOcXvigJZaViQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.4.14
+
+  /make-dir@3.1.0:
+    resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==}
+    engines: {node: '>=8'}
+    dependencies:
+      semver: 6.3.0
+    dev: true
+
+  /makeerror@1.0.12:
+    resolution: {integrity: sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==}
+    dependencies:
+      tmpl: 1.0.5
+    dev: true
+
+  /media-typer@0.3.0:
+    resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /merge-descriptors@1.0.1:
+    resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==}
+    dev: true
+
+  /merge-stream@2.0.0:
+    resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==}
+    dev: true
+
+  /merge2@1.4.1:
+    resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+    engines: {node: '>= 8'}
+
+  /merge@2.1.1:
+    resolution: {integrity: sha512-jz+Cfrg9GWOZbQAnDQ4hlVnQky+341Yk5ru8bZSe6sIDTCIg8n9i/u7hSQGSVOF3C7lH6mGtqjkiT9G4wFLL0w==}
+
+  /methods@1.1.2:
+    resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /micromatch@4.0.5:
+    resolution: {integrity: sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==}
+    engines: {node: '>=8.6'}
+    dependencies:
+      braces: 3.0.2
+      picomatch: 2.3.1
+
+  /mime-db@1.52.0:
+    resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /mime-types@2.1.35:
+    resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      mime-db: 1.52.0
+    dev: true
+
+  /mime@1.6.0:
+    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
+  /mime@3.0.0:
+    resolution: {integrity: sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+
+  /mimic-fn@2.1.0:
+    resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /mimic-fn@4.0.0:
+    resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /min-document@2.19.0:
+    resolution: {integrity: sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==}
+    dependencies:
+      dom-walk: 0.1.2
+    dev: false
+
+  /minimatch@3.1.2:
+    resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
+    dependencies:
+      brace-expansion: 1.1.11
+    dev: true
+
+  /minimatch@9.0.3:
+    resolution: {integrity: sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==}
+    engines: {node: '>=16 || 14 >=14.17'}
+    dependencies:
+      brace-expansion: 2.0.1
+    dev: true
+
+  /minimist@1.2.7:
+    resolution: {integrity: sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==}
+    dev: false
+
+  /miniprogram-api-typings@3.12.0:
+    resolution: {integrity: sha512-tE0zjq+3bFEgUROzCjXTdtLY+FmqiTX4kZWXc8bCC7M9wSHCriBz+ga1E2i1PcI+DQJoz2cyK8Cv2exJv6XEzA==}
+    dev: true
+
+  /mkdirp@0.5.6:
+    resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==}
+    hasBin: true
+    dependencies:
+      minimist: 1.2.7
+    dev: false
+
+  /module-alias@2.2.2:
+    resolution: {integrity: sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==}
+
+  /ms@2.0.0:
+    resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
+    dev: true
+
+  /ms@2.1.2:
+    resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
+
+  /ms@2.1.3:
+    resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+    dev: true
+
+  /muggle-string@0.3.1:
+    resolution: {integrity: sha512-ckmWDJjphvd/FvZawgygcUeQCxzvohjFO5RxTjj4eq8kw359gFF3E1brjfI+viLMxss5JrHTDRHZvu2/tuy0Qg==}
+    dev: true
+
+  /nanoid@3.3.6:
+    resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==}
+    engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+    hasBin: true
+
+  /natural-compare-lite@1.4.0:
+    resolution: {integrity: sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==}
+    dev: true
+
+  /natural-compare@1.4.0:
+    resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+    dev: true
+
+  /negotiator@0.6.3:
+    resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /node-int64@0.4.0:
+    resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+    dev: true
+
+  /node-releases@2.0.10:
+    resolution: {integrity: sha512-5GFldHPXVG/YZmFzJvKK2zDSzPKhEp0+ZR5SVaoSag9fsL5YgHbUHDfnG5494ISANDcK4KwPXAx2xqVEydmd7w==}
+
+  /normalize-path@3.0.0:
+    resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
+    engines: {node: '>=0.10.0'}
+
+  /normalize-range@0.1.2:
+    resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==}
+    engines: {node: '>=0.10.0'}
+
+  /npm-run-path@4.0.1:
+    resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==}
+    engines: {node: '>=8'}
+    dependencies:
+      path-key: 3.1.1
+    dev: true
+
+  /npm-run-path@5.1.0:
+    resolution: {integrity: sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==}
+    engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
+    dependencies:
+      path-key: 4.0.0
+    dev: true
+
+  /nth-check@2.1.1:
+    resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+    dependencies:
+      boolbase: 1.0.0
+    dev: true
+
+  /nwsapi@2.2.4:
+    resolution: {integrity: sha512-NHj4rzRo0tQdijE9ZqAx6kYDcoRwYwSYzCA8MY3JzfxlrvEU0jhnhJT9BhqhJs7I/dKcrDm6TyulaRqZPIhN5g==}
+    dev: true
+
+  /object-assign@4.1.1:
+    resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /object-inspect@1.12.2:
+    resolution: {integrity: sha512-z+cPxW0QGUp0mcqcsgQyLVRDoXFQbXOwBaqyF7VIgI4TWNQsDHrBpUQslRmIfAoYWdYzs6UlKJtB2XJpTaNSpQ==}
+    dev: true
+
+  /omggif@1.0.10:
+    resolution: {integrity: sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==}
+    dev: false
+
+  /on-finished@2.4.1:
+    resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      ee-first: 1.1.1
+    dev: true
+
+  /once@1.4.0:
+    resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+    dependencies:
+      wrappy: 1.0.2
+    dev: true
+
+  /onetime@5.1.2:
+    resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==}
+    engines: {node: '>=6'}
+    dependencies:
+      mimic-fn: 2.1.0
+    dev: true
+
+  /onetime@6.0.0:
+    resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      mimic-fn: 4.0.0
+    dev: true
+
+  /open@8.4.2:
+    resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      define-lazy-prop: 2.0.0
+      is-docker: 2.2.1
+      is-wsl: 2.2.0
+    dev: true
+
+  /optionator@0.8.3:
+    resolution: {integrity: sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.3.0
+      prelude-ls: 1.1.2
+      type-check: 0.3.2
+      word-wrap: 1.2.3
+    dev: true
+
+  /optionator@0.9.1:
+    resolution: {integrity: sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      deep-is: 0.1.4
+      fast-levenshtein: 2.0.6
+      levn: 0.4.1
+      prelude-ls: 1.2.1
+      type-check: 0.4.0
+      word-wrap: 1.2.3
+    dev: true
+
+  /os-locale-s-fix@1.0.8-fix-1:
+    resolution: {integrity: sha512-Sv0OvhPiMutICiwORAUefv02DCPb62IelBmo8ZsSrRHyI3FStqIWZvjqDkvtjU+lcujo7UNir+dCwKSqlEQ/5w==}
+    engines: {node: '>=10', yarn: ^1.22.4}
+    dependencies:
+      lcid: 3.1.1
+
+  /p-limit@2.3.0:
+    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+    engines: {node: '>=6'}
+    dependencies:
+      p-try: 2.2.0
+    dev: true
+
+  /p-limit@3.1.0:
+    resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      yocto-queue: 0.1.0
+    dev: true
+
+  /p-locate@4.1.0:
+    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-limit: 2.3.0
+    dev: true
+
+  /p-locate@5.0.0:
+    resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+    engines: {node: '>=10'}
+    dependencies:
+      p-limit: 3.1.0
+    dev: true
+
+  /p-map@4.0.0:
+    resolution: {integrity: sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      aggregate-error: 3.1.0
+    dev: true
+
+  /p-try@2.2.0:
+    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /pako@1.0.11:
+    resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==}
+    dev: false
+
+  /parent-module@1.0.1:
+    resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+    engines: {node: '>=6'}
+    dependencies:
+      callsites: 3.1.0
+    dev: true
+
+  /parse-bmfont-ascii@1.0.6:
+    resolution: {integrity: sha512-U4RrVsUFCleIOBsIGYOMKjn9PavsGOXxbvYGtMOEfnId0SVNsgehXh1DxUdVPLoxd5mvcEtvmKs2Mmf0Mpa1ZA==}
+    dev: false
+
+  /parse-bmfont-binary@1.0.6:
+    resolution: {integrity: sha512-GxmsRea0wdGdYthjuUeWTMWPqm2+FAd4GI8vCvhgJsFnoGhTrLhXDDupwTo7rXVAgaLIGoVHDZS9p/5XbSqeWA==}
+    dev: false
+
+  /parse-bmfont-xml@1.1.4:
+    resolution: {integrity: sha512-bjnliEOmGv3y1aMEfREMBJ9tfL3WR0i0CKPj61DnSLaoxWR3nLrsQrEbCId/8rF4NyRF0cCqisSVXyQYWM+mCQ==}
+    dependencies:
+      xml-parse-from-string: 1.0.1
+      xml2js: 0.4.23
+    dev: false
+
+  /parse-css-font@4.0.0:
+    resolution: {integrity: sha512-lnY7dTUfjRXsSo5G5C639L8RaBBaVSgL+5hacIFKsNHzeCJQ5SFSZv1DZmc7+wZv/22PFGOq2YbaEHLdaCS/mQ==}
+    dependencies:
+      css-font-size-keywords: 1.0.0
+      css-font-stretch-keywords: 1.0.1
+      css-font-style-keywords: 1.0.1
+      css-font-weight-keywords: 1.0.0
+      css-list-helpers: 2.0.0
+      css-system-font-keywords: 1.0.0
+      unquote: 1.1.1
+    dev: false
+
+  /parse-headers@2.0.5:
+    resolution: {integrity: sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA==}
+    dev: false
+
+  /parse-json@5.2.0:
+    resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@babel/code-frame': 7.21.4
+      error-ex: 1.3.2
+      json-parse-even-better-errors: 2.3.1
+      lines-and-columns: 1.2.4
+    dev: true
+
+  /parse5@6.0.1:
+    resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==}
+    dev: true
+
+  /parseurl@1.3.3:
+    resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
+  /path-exists@4.0.0:
+    resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-is-absolute@1.0.1:
+    resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /path-key@3.1.1:
+    resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /path-key@4.0.0:
+    resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /path-parse@1.0.7:
+    resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+  /path-to-regexp@0.1.7:
+    resolution: {integrity: sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ==}
+    dev: true
+
+  /path-type@4.0.0:
+    resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /phin@2.9.3:
+    resolution: {integrity: sha512-CzFr90qM24ju5f88quFC/6qohjC144rehe5n6DH900lgXmUe86+xCKc10ev56gRKC4/BkHUoG4uSiQgBiIXwDA==}
+    dev: false
+
+  /picocolors@1.0.0:
+    resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==}
+
+  /picomatch@2.3.1:
+    resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+    engines: {node: '>=8.6'}
+
+  /pidtree@0.6.0:
+    resolution: {integrity: sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==}
+    engines: {node: '>=0.10'}
+    hasBin: true
+    dev: true
+
+  /pify@2.3.0:
+    resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==}
+    engines: {node: '>=0.10.0'}
+
+  /pinia-plugin-persistedstate@3.2.0(pinia@2.0.27):
+    resolution: {integrity: sha512-tZbNGf2vjAQcIm7alK40sE51Qu/m9oWr+rEgNm/2AWr1huFxj72CjvpQcIQzMknDBJEkQznCLAGtJTIcLKrKdw==}
+    peerDependencies:
+      pinia: ^2.0.0
+    dependencies:
+      pinia: 2.0.27(typescript@5.1.6)(vue@3.3.4)
+    dev: false
+
+  /pinia@2.0.27(typescript@5.1.6)(vue@3.3.4):
+    resolution: {integrity: sha512-nOnXP0OFeL8R4WjAHsterU+11vptda643gH02xKNtSCDPiRzVfRYodOLihLDoa0gL1KKuQKV+KOzEgdt3YvqEw==}
+    peerDependencies:
+      '@vue/composition-api': ^1.4.0
+      typescript: '>=4.4.4'
+      vue: ^2.6.14 || ^3.2.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+      typescript:
+        optional: true
+    dependencies:
+      '@vue/devtools-api': 6.4.5
+      typescript: 5.1.6
+      vue: 3.3.4
+      vue-demi: 0.13.11(vue@3.3.4)
+    dev: false
+
+  /pirates@4.0.5:
+    resolution: {integrity: sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==}
+    engines: {node: '>= 6'}
+    dev: true
+
+  /pixelmatch@4.0.2:
+    resolution: {integrity: sha512-J8B6xqiO37sU/gkcMglv6h5Jbd9xNER7aHzpfRdNmV4IbQBzBpe4l9XmbG+xPF/znacgu2jfEw+wHffaq/YkXA==}
+    hasBin: true
+    dependencies:
+      pngjs: 3.4.0
+    dev: false
+
+  /pkg-dir@4.2.0:
+    resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      find-up: 4.1.0
+    dev: true
+
+  /pngjs@3.4.0:
+    resolution: {integrity: sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==}
+    engines: {node: '>=4.0.0'}
+    dev: false
+
+  /postcss-import@14.1.0(postcss@8.4.27):
+    resolution: {integrity: sha512-flwI+Vgm4SElObFVPpTIT7SU7R3qk2L7PyduMcokiaVKuWv9d/U+Gm/QAd8NDLuykTWTkcrjOeD2Pp1rMeBTGw==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      postcss: ^8.0.0
+    dependencies:
+      postcss: 8.4.27
+      postcss-value-parser: 4.2.0
+      read-cache: 1.0.0
+      resolve: 1.22.1
+
+  /postcss-load-config@3.1.4(postcss@8.4.27):
+    resolution: {integrity: sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==}
+    engines: {node: '>= 10'}
+    peerDependencies:
+      postcss: '>=8.0.9'
+      ts-node: '>=9.0.0'
+    peerDependenciesMeta:
+      postcss:
+        optional: true
+      ts-node:
+        optional: true
+    dependencies:
+      lilconfig: 2.0.6
+      postcss: 8.4.27
+      yaml: 1.10.2
+
+  /postcss-modules-extract-imports@3.0.0(postcss@8.4.27):
+    resolution: {integrity: sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+    dependencies:
+      postcss: 8.4.27
+
+  /postcss-modules-local-by-default@4.0.0(postcss@8.4.27):
+    resolution: {integrity: sha512-sT7ihtmGSF9yhm6ggikHdV0hlziDTX7oFoXtuVWeDd3hHObNkcHRo9V3yg7vCAY7cONyxJC/XXCmmiHHcvX7bQ==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+    dependencies:
+      icss-utils: 5.1.0(postcss@8.4.27)
+      postcss: 8.4.27
+      postcss-selector-parser: 6.0.11
+      postcss-value-parser: 4.2.0
+
+  /postcss-modules-scope@3.0.0(postcss@8.4.27):
+    resolution: {integrity: sha512-hncihwFA2yPath8oZ15PZqvWGkWf+XUfQgUGamS4LqoP1anQLOsOJw0vr7J7IwLpoY9fatA2qiGUGmuZL0Iqlg==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+    dependencies:
+      postcss: 8.4.27
+      postcss-selector-parser: 6.0.11
+
+  /postcss-modules-values@4.0.0(postcss@8.4.27):
+    resolution: {integrity: sha512-RDxHkAiEGI78gS2ofyvCsu7iycRv7oqw5xMWn9iMoR0N/7mf9D50ecQqUo5BZ9Zh2vH4bCUR/ktCqbB9m8vJjQ==}
+    engines: {node: ^10 || ^12 || >= 14}
+    peerDependencies:
+      postcss: ^8.1.0
+    dependencies:
+      icss-utils: 5.1.0(postcss@8.4.27)
+      postcss: 8.4.27
+
+  /postcss-modules@4.3.1(postcss@8.4.27):
+    resolution: {integrity: sha512-ItUhSUxBBdNamkT3KzIZwYNNRFKmkJrofvC2nWab3CPKhYBQ1f27XXh1PAPE27Psx58jeelPsxWB/+og+KEH0Q==}
+    peerDependencies:
+      postcss: ^8.0.0
+    dependencies:
+      generic-names: 4.0.0
+      icss-replace-symbols: 1.1.0
+      lodash.camelcase: 4.3.0
+      postcss: 8.4.27
+      postcss-modules-extract-imports: 3.0.0(postcss@8.4.27)
+      postcss-modules-local-by-default: 4.0.0(postcss@8.4.27)
+      postcss-modules-scope: 3.0.0(postcss@8.4.27)
+      postcss-modules-values: 4.0.0(postcss@8.4.27)
+      string-hash: 1.1.3
+
+  /postcss-selector-parser@6.0.11:
+    resolution: {integrity: sha512-zbARubNdogI9j7WY4nQJBiNqQf3sLS3wCP4WfOidu+p28LofJqDH1tcXypGrcmMHhDk2t9wGhCsYe/+szLTy1g==}
+    engines: {node: '>=4'}
+    dependencies:
+      cssesc: 3.0.0
+      util-deprecate: 1.0.2
+
+  /postcss-value-parser@4.2.0:
+    resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==}
+
+  /postcss@8.4.27:
+    resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==}
+    engines: {node: ^10 || ^12 || >=14}
+    dependencies:
+      nanoid: 3.3.6
+      picocolors: 1.0.0
+      source-map-js: 1.0.2
+
+  /prelude-ls@1.1.2:
+    resolution: {integrity: sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /prelude-ls@1.2.1:
+    resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+    engines: {node: '>= 0.8.0'}
+    dev: true
+
+  /prettier-linter-helpers@1.0.0:
+    resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
+    engines: {node: '>=6.0.0'}
+    dependencies:
+      fast-diff: 1.2.0
+    dev: true
+
+  /prettier@2.8.0:
+    resolution: {integrity: sha512-9Lmg8hTFZKG0Asr/kW9Bp8tJjRVluO8EJQVfY2T7FMw9T5jy4I/Uvx0Rca/XWf50QQ1/SS48+6IJWnrb+2yemA==}
+    engines: {node: '>=10.13.0'}
+    hasBin: true
+    dev: true
+
+  /pretty-format@27.5.1:
+    resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==}
+    engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0}
+    dependencies:
+      ansi-regex: 5.0.1
+      ansi-styles: 5.2.0
+      react-is: 17.0.2
+    dev: true
+
+  /process@0.11.10:
+    resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
+    engines: {node: '>= 0.6.0'}
+    dev: false
+
+  /prompts@2.4.2:
+    resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==}
+    engines: {node: '>= 6'}
+    dependencies:
+      kleur: 3.0.3
+      sisteransi: 1.0.5
+    dev: true
+
+  /proxy-addr@2.0.7:
+    resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==}
+    engines: {node: '>= 0.10'}
+    dependencies:
+      forwarded: 0.2.0
+      ipaddr.js: 1.9.1
+    dev: true
+
+  /psl@1.9.0:
+    resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==}
+    dev: true
+
+  /punycode@2.1.1:
+    resolution: {integrity: sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /qrcode-reader@1.0.4:
+    resolution: {integrity: sha512-rRjALGNh9zVqvweg1j5OKIQKNsw3bLC+7qwlnead5K/9cb1cEIAGkwikt/09U0K+2IDWGD9CC6SP7tHAjUeqvQ==}
+
+  /qrcode-terminal@0.12.0:
+    resolution: {integrity: sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==}
+    hasBin: true
+
+  /qs@6.11.0:
+    resolution: {integrity: sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==}
+    engines: {node: '>=0.6'}
+    dependencies:
+      side-channel: 1.0.4
+    dev: true
+
+  /querystringify@2.2.0:
+    resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==}
+    dev: true
+
+  /queue-microtask@1.2.3:
+    resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+  /range-parser@1.2.1:
+    resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==}
+    engines: {node: '>= 0.6'}
+    dev: true
+
+  /raw-body@2.5.1:
+    resolution: {integrity: sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==}
+    engines: {node: '>= 0.8'}
+    dependencies:
+      bytes: 3.1.2
+      http-errors: 2.0.0
+      iconv-lite: 0.4.24
+      unpipe: 1.0.0
+    dev: true
+
+  /react-is@17.0.2:
+    resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
+    dev: true
+
+  /read-cache@1.0.0:
+    resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==}
+    dependencies:
+      pify: 2.3.0
+
+  /readdirp@3.6.0:
+    resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==}
+    engines: {node: '>=8.10.0'}
+    dependencies:
+      picomatch: 2.3.1
+
+  /regenerate-unicode-properties@10.1.0:
+    resolution: {integrity: sha512-d1VudCLoIGitcU/hEg2QqvyGZQmdC0Lf8BqdOMXGFSvJP4bNV1+XqbPQeHHLD51Jh4QJJ225dlIFvY4Ly6MXmQ==}
+    engines: {node: '>=4'}
+    dependencies:
+      regenerate: 1.4.2
+    dev: true
+
+  /regenerate@1.4.2:
+    resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
+    dev: true
+
+  /regenerator-runtime@0.13.11:
+    resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==}
+
+  /regenerator-transform@0.15.1:
+    resolution: {integrity: sha512-knzmNAcuyxV+gQCufkYcvOqX/qIIfHLv0u5x79kRxuGojfYVky1f15TzZEu2Avte8QGepvUNTnLskf8E6X6Vyg==}
+    dependencies:
+      '@babel/runtime': 7.20.13
+    dev: true
+
+  /regexpp@3.2.0:
+    resolution: {integrity: sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /regexpu-core@5.3.0:
+    resolution: {integrity: sha512-ZdhUQlng0RoscyW7jADnUZ25F5eVtHdMyXSb2PiwafvteRAOJUjFoUPEYZSIfP99fBIs3maLIRfpEddT78wAAQ==}
+    engines: {node: '>=4'}
+    dependencies:
+      '@babel/regjsgen': 0.8.0
+      regenerate: 1.4.2
+      regenerate-unicode-properties: 10.1.0
+      regjsparser: 0.9.1
+      unicode-match-property-ecmascript: 2.0.0
+      unicode-match-property-value-ecmascript: 2.1.0
+    dev: true
+
+  /regjsparser@0.9.1:
+    resolution: {integrity: sha512-dQUtn90WanSNl+7mQKcXAgZxvUe7Z0SqXlgzv0za4LwiUhyzBC58yQO3liFoUgu8GiJVInAhJjkj1N0EtQ5nkQ==}
+    hasBin: true
+    dependencies:
+      jsesc: 0.5.0
+    dev: true
+
+  /require-directory@2.1.1:
+    resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /requires-port@1.0.0:
+    resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
+    dev: true
+
+  /resolve-cwd@3.0.0:
+    resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==}
+    engines: {node: '>=8'}
+    dependencies:
+      resolve-from: 5.0.0
+    dev: true
+
+  /resolve-from@4.0.0:
+    resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /resolve-from@5.0.0:
+    resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /resolve.exports@1.1.1:
+    resolution: {integrity: sha512-/NtpHNDN7jWhAaQ9BvBUYZ6YTXsRBgfqWFWP7BZBaoMJO/I3G5OFzvTuWNlZC3aPjins1F+TNrLKsGbH4rfsRQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /resolve@1.22.1:
+    resolution: {integrity: sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==}
+    hasBin: true
+    dependencies:
+      is-core-module: 2.11.0
+      path-parse: 1.0.7
+      supports-preserve-symlinks-flag: 1.0.0
+
+  /restore-cursor@3.1.0:
+    resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
+    engines: {node: '>=8'}
+    dependencies:
+      onetime: 5.1.2
+      signal-exit: 3.0.7
+    dev: true
+
+  /reusify@1.0.4:
+    resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
+    engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+  /rfdc@1.3.0:
+    resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
+    dev: true
+
+  /rimraf@3.0.2:
+    resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
+    hasBin: true
+    dependencies:
+      glob: 7.2.3
+    dev: true
+
+  /rollup@3.28.0:
+    resolution: {integrity: sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==}
+    engines: {node: '>=14.18.0', npm: '>=8.0.0'}
+    hasBin: true
+    optionalDependencies:
+      fsevents: 2.3.2
+
+  /run-parallel@1.2.0:
+    resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+    dependencies:
+      queue-microtask: 1.2.3
+
+  /rxjs@7.5.7:
+    resolution: {integrity: sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==}
+    dependencies:
+      tslib: 2.4.1
+    dev: true
+
+  /safe-area-insets@1.4.1:
+    resolution: {integrity: sha512-r/nRWTjFGhhm3w1Z6Kd/jY11srN+lHt2mNl1E/emQGW8ic7n3Avu4noibklfSM+Y34peNphHD/BSZecav0sXYQ==}
+    dev: false
+
+  /safe-buffer@5.2.1:
+    resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==}
+    dev: true
+
+  /safer-buffer@2.1.2:
+    resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+    dev: true
+
+  /sass@1.56.1:
+    resolution: {integrity: sha512-VpEyKpyBPCxE7qGDtOcdJ6fFbcpOM+Emu7uZLxVrkX8KVU/Dp5UF7WLvzqRuUhB6mqqQt1xffLoG+AndxTZrCQ==}
+    engines: {node: '>=12.0.0'}
+    hasBin: true
+    dependencies:
+      chokidar: 3.5.3
+      immutable: 4.1.0
+      source-map-js: 1.0.2
+
+  /sax@1.2.4:
+    resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
+    dev: false
+
+  /saxes@5.0.1:
+    resolution: {integrity: sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==}
+    engines: {node: '>=10'}
+    dependencies:
+      xmlchars: 2.2.0
+    dev: true
+
+  /semver@6.3.0:
+    resolution: {integrity: sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==}
+    hasBin: true
+
+  /semver@7.3.8:
+    resolution: {integrity: sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      lru-cache: 6.0.0
+    dev: true
+
+  /send@0.18.0:
+    resolution: {integrity: sha512-qqWzuOjSFOuqPjFe4NOsMLafToQQwBSOEpS+FwEt3A2V3vKubTquT3vmLTQpFgMXp8AlFWFuP1qKaJZOtPpVXg==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      debug: 2.6.9
+      depd: 2.0.0
+      destroy: 1.2.0
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      etag: 1.8.1
+      fresh: 0.5.2
+      http-errors: 2.0.0
+      mime: 1.6.0
+      ms: 2.1.3
+      on-finished: 2.4.1
+      range-parser: 1.2.1
+      statuses: 2.0.1
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /serve-static@1.15.0:
+    resolution: {integrity: sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      encodeurl: 1.0.2
+      escape-html: 1.0.3
+      parseurl: 1.3.3
+      send: 0.18.0
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /setprototypeof@1.2.0:
+    resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==}
+    dev: true
+
+  /shebang-command@2.0.0:
+    resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+    engines: {node: '>=8'}
+    dependencies:
+      shebang-regex: 3.0.0
+    dev: true
+
+  /shebang-regex@3.0.0:
+    resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /side-channel@1.0.4:
+    resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
+    dependencies:
+      call-bind: 1.0.2
+      get-intrinsic: 1.1.3
+      object-inspect: 1.12.2
+    dev: true
+
+  /signal-exit@3.0.7:
+    resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==}
+    dev: true
+
+  /sisteransi@1.0.5:
+    resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==}
+    dev: true
+
+  /slash@3.0.0:
+    resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /slice-ansi@3.0.0:
+    resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-styles: 4.3.0
+      astral-regex: 2.0.0
+      is-fullwidth-code-point: 3.0.0
+    dev: true
+
+  /slice-ansi@4.0.0:
+    resolution: {integrity: sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      astral-regex: 2.0.0
+      is-fullwidth-code-point: 3.0.0
+    dev: true
+
+  /slice-ansi@5.0.0:
+    resolution: {integrity: sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-styles: 6.2.1
+      is-fullwidth-code-point: 4.0.0
+    dev: true
+
+  /socket.io-adapter@2.5.2:
+    resolution: {integrity: sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA==}
+    dependencies:
+      ws: 8.11.0
+    transitivePeerDependencies:
+      - bufferutil
+      - utf-8-validate
+    dev: true
+
+  /socket.io-parser@4.2.2:
+    resolution: {integrity: sha512-DJtziuKypFkMMHCm2uIshOYC7QaylbtzQwiMYDuCKy3OPkjLzu4B2vAhTlqipRHHzrI0NJeBAizTK7X+6m1jVw==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      '@socket.io/component-emitter': 3.1.0
+      debug: 4.3.4
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /socket.io@4.6.1:
+    resolution: {integrity: sha512-KMcaAi4l/8+xEjkRICl6ak8ySoxsYG+gG6/XfRCPJPQ/haCRIJBTL4wIl8YCsmtaBovcAXGLOShyVWQ/FG8GZA==}
+    engines: {node: '>=10.0.0'}
+    dependencies:
+      accepts: 1.3.8
+      base64id: 2.0.0
+      debug: 4.3.4
+      engine.io: 6.4.2
+      socket.io-adapter: 2.5.2
+      socket.io-parser: 4.2.2
+    transitivePeerDependencies:
+      - bufferutil
+      - supports-color
+      - utf-8-validate
+    dev: true
+
+  /source-map-js@1.0.2:
+    resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==}
+    engines: {node: '>=0.10.0'}
+
+  /source-map-support@0.5.21:
+    resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==}
+    dependencies:
+      buffer-from: 1.1.2
+      source-map: 0.6.1
+
+  /source-map@0.6.1:
+    resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
+    engines: {node: '>=0.10.0'}
+
+  /source-map@0.7.4:
+    resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==}
+    engines: {node: '>= 8'}
+    dev: true
+
+  /sourcemap-codec@1.4.8:
+    resolution: {integrity: sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==}
+    deprecated: Please use @jridgewell/sourcemap-codec instead
+
+  /sprintf-js@1.0.3:
+    resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
+    dev: true
+
+  /stack-utils@2.0.6:
+    resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      escape-string-regexp: 2.0.0
+    dev: true
+
+  /statuses@2.0.1:
+    resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
+  /string-argv@0.3.1:
+    resolution: {integrity: sha512-a1uQGz7IyVy9YwhqjZIZu1c8JO8dNIe20xBmSS6qu9kv++k3JGzCVmprbNN5Kn+BgzD5E7YYwg1CcjuJMRNsvg==}
+    engines: {node: '>=0.6.19'}
+    dev: true
+
+  /string-hash@1.1.3:
+    resolution: {integrity: sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==}
+
+  /string-length@4.0.2:
+    resolution: {integrity: sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==}
+    engines: {node: '>=10'}
+    dependencies:
+      char-regex: 1.0.2
+      strip-ansi: 6.0.1
+    dev: true
+
+  /string-width@4.2.3:
+    resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==}
+    engines: {node: '>=8'}
+    dependencies:
+      emoji-regex: 8.0.0
+      is-fullwidth-code-point: 3.0.0
+      strip-ansi: 6.0.1
+    dev: true
+
+  /string-width@5.1.2:
+    resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==}
+    engines: {node: '>=12'}
+    dependencies:
+      eastasianwidth: 0.2.0
+      emoji-regex: 9.2.2
+      strip-ansi: 7.0.1
+    dev: true
+
+  /strip-ansi@6.0.1:
+    resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-regex: 5.0.1
+    dev: true
+
+  /strip-ansi@7.0.1:
+    resolution: {integrity: sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==}
+    engines: {node: '>=12'}
+    dependencies:
+      ansi-regex: 6.0.1
+    dev: true
+
+  /strip-bom@4.0.0:
+    resolution: {integrity: sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /strip-final-newline@2.0.0:
+    resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /strip-final-newline@3.0.0:
+    resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /strip-json-comments@3.1.1:
+    resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /supports-color@5.5.0:
+    resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==}
+    engines: {node: '>=4'}
+    dependencies:
+      has-flag: 3.0.0
+
+  /supports-color@7.2.0:
+    resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+    dev: true
+
+  /supports-color@8.1.1:
+    resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==}
+    engines: {node: '>=10'}
+    dependencies:
+      has-flag: 4.0.0
+    dev: true
+
+  /supports-hyperlinks@2.3.0:
+    resolution: {integrity: sha512-RpsAZlpWcDwOPQA22aCH4J0t7L8JmAvsCxfOSEwm7cQs3LshN36QaTkwd70DnBOXDWGssw2eUoc8CaRWT0XunA==}
+    engines: {node: '>=8'}
+    dependencies:
+      has-flag: 4.0.0
+      supports-color: 7.2.0
+    dev: true
+
+  /supports-preserve-symlinks-flag@1.0.0:
+    resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+    engines: {node: '>= 0.4'}
+
+  /svg-tags@1.0.0:
+    resolution: {integrity: sha512-ovssysQTa+luh7A5Weu3Rta6FJlFBBbInjOh722LIt6klpU2/HtdUbszju/G4devcvk8PGt7FCLv5wftu3THUA==}
+    dev: true
+
+  /symbol-tree@3.2.4:
+    resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+    dev: true
+
+  /systemjs@6.14.1:
+    resolution: {integrity: sha512-8ftwWd+XnQtZ/aGbatrN4QFNGrKJzmbtixW+ODpci7pyoTajg4sonPP8aFLESAcuVxaC1FyDESt+SpfFCH9rZQ==}
+    dev: true
+
+  /tapable@2.2.1:
+    resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
+    engines: {node: '>=6'}
+
+  /terminal-link@2.1.1:
+    resolution: {integrity: sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-escapes: 4.3.2
+      supports-hyperlinks: 2.3.0
+    dev: true
+
+  /terser@5.16.0:
+    resolution: {integrity: sha512-KjTV81QKStSfwbNiwlBXfcgMcOloyuRdb62/iLFPGBcVNF4EXjhdYBhYHmbJpiBrVxZhDvltE11j+LBQUxEEJg==}
+    engines: {node: '>=10'}
+    hasBin: true
+    dependencies:
+      '@jridgewell/source-map': 0.3.2
+      acorn: 8.8.1
+      commander: 2.20.3
+      source-map-support: 0.5.21
+
+  /test-exclude@6.0.0:
+    resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==}
+    engines: {node: '>=8'}
+    dependencies:
+      '@istanbuljs/schema': 0.1.3
+      glob: 7.2.3
+      minimatch: 3.1.2
+    dev: true
+
+  /text-table@0.2.0:
+    resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==}
+    dev: true
+
+  /throat@6.0.2:
+    resolution: {integrity: sha512-WKexMoJj3vEuK0yFEapj8y64V0A6xcuPuK9Gt1d0R+dzCSJc0lHqQytAbSB4cDAK0dWh4T0E2ETkoLE2WZ41OQ==}
+    dev: true
+
+  /through@2.3.8:
+    resolution: {integrity: sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==}
+    dev: true
+
+  /timm@1.7.1:
+    resolution: {integrity: sha512-IjZc9KIotudix8bMaBW6QvMuq64BrJWFs1+4V0lXwWGQZwH+LnX87doAYhem4caOEusRP9/g6jVDQmZ8XOk1nw==}
+    dev: false
+
+  /tinycolor2@1.5.2:
+    resolution: {integrity: sha512-h80m9GPFGbcLzZByXlNSEhp1gf8Dy+VX/2JCGUZsWLo7lV1mnE/XlxGYgRBoMLJh1lIDXP0EMC4RPTjlRaV+Bg==}
+    dev: false
+
+  /tmpl@1.0.5:
+    resolution: {integrity: sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==}
+    dev: true
+
+  /to-fast-properties@2.0.0:
+    resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
+    engines: {node: '>=4'}
+
+  /to-regex-range@5.0.1:
+    resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+    engines: {node: '>=8.0'}
+    dependencies:
+      is-number: 7.0.0
+
+  /toidentifier@1.0.1:
+    resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==}
+    engines: {node: '>=0.6'}
+    dev: true
+
+  /tough-cookie@4.1.2:
+    resolution: {integrity: sha512-G9fqXWoYFZgTc2z8Q5zaHy/vJMjm+WV0AkAeHxVCQiEB1b+dGvWzFW6QV07cY5jQ5gRkeid2qIkzkxUnmoQZUQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      psl: 1.9.0
+      punycode: 2.1.1
+      universalify: 0.2.0
+      url-parse: 1.5.10
+    dev: true
+
+  /tr46@2.1.0:
+    resolution: {integrity: sha512-15Ih7phfcdP5YxqiB+iDtLoaTz4Nd35+IiAv0kQ5FNKHzXgdWqPoTIqEDDJmXceQt4JZk6lVPT8lnDlPpGDppw==}
+    engines: {node: '>=8'}
+    dependencies:
+      punycode: 2.1.1
+    dev: true
+
+  /tslib@1.14.1:
+    resolution: {integrity: sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==}
+    dev: true
+
+  /tslib@2.4.1:
+    resolution: {integrity: sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==}
+    dev: true
+
+  /tsutils@3.21.0(typescript@5.1.6):
+    resolution: {integrity: sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==}
+    engines: {node: '>= 6'}
+    peerDependencies:
+      typescript: '>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta'
+    dependencies:
+      tslib: 1.14.1
+      typescript: 5.1.6
+    dev: true
+
+  /type-check@0.3.2:
+    resolution: {integrity: sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.1.2
+    dev: true
+
+  /type-check@0.4.0:
+    resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+    engines: {node: '>= 0.8.0'}
+    dependencies:
+      prelude-ls: 1.2.1
+    dev: true
+
+  /type-detect@4.0.8:
+    resolution: {integrity: sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /type-fest@0.20.2:
+    resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /type-fest@0.21.3:
+    resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /type-is@1.6.18:
+    resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
+    engines: {node: '>= 0.6'}
+    dependencies:
+      media-typer: 0.3.0
+      mime-types: 2.1.35
+    dev: true
+
+  /typedarray-to-buffer@3.1.5:
+    resolution: {integrity: sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==}
+    dependencies:
+      is-typedarray: 1.0.0
+    dev: true
+
+  /typescript@5.1.6:
+    resolution: {integrity: sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==}
+    engines: {node: '>=14.17'}
+    hasBin: true
+
+  /unicode-canonical-property-names-ecmascript@2.0.0:
+    resolution: {integrity: sha512-yY5PpDlfVIU5+y/BSCxAJRBIS1Zc2dDG3Ujq+sR0U+JjUevW2JhocOF+soROYDSaAezOzOKuyyixhD6mBknSmQ==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /unicode-match-property-ecmascript@2.0.0:
+    resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
+    engines: {node: '>=4'}
+    dependencies:
+      unicode-canonical-property-names-ecmascript: 2.0.0
+      unicode-property-aliases-ecmascript: 2.1.0
+    dev: true
+
+  /unicode-match-property-value-ecmascript@2.1.0:
+    resolution: {integrity: sha512-qxkjQt6qjg/mYscYMC0XKRn3Rh0wFPlfxB0xkt9CfyTvpX1Ra0+rAmdX2QyAobptSEvuy4RtpPRui6XkV+8wjA==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /unicode-property-aliases-ecmascript@2.1.0:
+    resolution: {integrity: sha512-6t3foTQI9qne+OZoVQB/8x8rk2k1eVy1gRXhV3oFQ5T6R1dqQ1xtin3XqSlx3+ATBkliTaR/hHyJBm+LVPNM8w==}
+    engines: {node: '>=4'}
+    dev: true
+
+  /universalify@0.2.0:
+    resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
+    engines: {node: '>= 4.0.0'}
+    dev: true
+
+  /universalify@2.0.0:
+    resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
+    engines: {node: '>= 10.0.0'}
+
+  /unpipe@1.0.0:
+    resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
+  /unquote@1.1.1:
+    resolution: {integrity: sha512-vRCqFv6UhXpWxZPyGDh/F3ZpNv8/qo7w6iufLpQg9aKnQ71qM4B5KiI7Mia9COcjEhrO9LueHpMYjYzsWH3OIg==}
+    dev: false
+
+  /update-browserslist-db@1.0.10(browserslist@4.21.5):
+    resolution: {integrity: sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==}
+    hasBin: true
+    peerDependencies:
+      browserslist: '>= 4.21.0'
+    dependencies:
+      browserslist: 4.21.5
+      escalade: 3.1.1
+      picocolors: 1.0.0
+
+  /uri-js@4.4.1:
+    resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+    dependencies:
+      punycode: 2.1.1
+    dev: true
+
+  /url-parse@1.5.10:
+    resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==}
+    dependencies:
+      querystringify: 2.2.0
+      requires-port: 1.0.0
+    dev: true
+
+  /utif@2.0.1:
+    resolution: {integrity: sha512-Z/S1fNKCicQTf375lIP9G8Sa1H/phcysstNrrSdZKj1f9g58J4NMgb5IgiEZN9/nLMPDwF0W7hdOe9Qq2IYoLg==}
+    dependencies:
+      pako: 1.0.11
+    dev: false
+
+  /util-deprecate@1.0.2:
+    resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==}
+
+  /utils-merge@1.0.1:
+    resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
+    engines: {node: '>= 0.4.0'}
+    dev: true
+
+  /v8-to-istanbul@8.1.1:
+    resolution: {integrity: sha512-FGtKtv3xIpR6BYhvgH8MI/y78oT7d8Au3ww4QIxymrCtZEh5b8gCw2siywE+puhEmuWKDtmfrvF5UlB298ut3w==}
+    engines: {node: '>=10.12.0'}
+    dependencies:
+      '@types/istanbul-lib-coverage': 2.0.4
+      convert-source-map: 1.9.0
+      source-map: 0.7.4
+    dev: true
+
+  /vary@1.1.2:
+    resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
+    engines: {node: '>= 0.8'}
+    dev: true
+
+  /vite@4.4.9(@types/node@18.11.10)(sass@1.56.1)(terser@5.16.0):
+    resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==}
+    engines: {node: ^14.18.0 || >=16.0.0}
+    hasBin: true
+    peerDependencies:
+      '@types/node': '>= 14'
+      less: '*'
+      lightningcss: ^1.21.0
+      sass: '*'
+      stylus: '*'
+      sugarss: '*'
+      terser: ^5.4.0
+    peerDependenciesMeta:
+      '@types/node':
+        optional: true
+      less:
+        optional: true
+      lightningcss:
+        optional: true
+      sass:
+        optional: true
+      stylus:
+        optional: true
+      sugarss:
+        optional: true
+      terser:
+        optional: true
+    dependencies:
+      '@types/node': 18.11.10
+      esbuild: 0.18.20
+      postcss: 8.4.27
+      rollup: 3.28.0
+      sass: 1.56.1
+      terser: 5.16.0
+    optionalDependencies:
+      fsevents: 2.3.2
+
+  /vue-demi@0.13.11(vue@3.3.4):
+    resolution: {integrity: sha512-IR8HoEEGM65YY3ZJYAjMlKygDQn25D5ajNFNoKh9RSDMQtlzCxtfQjdQgv9jjK+m3377SsJXY8ysq8kLCZL25A==}
+    engines: {node: '>=12'}
+    hasBin: true
+    requiresBuild: true
+    peerDependencies:
+      '@vue/composition-api': ^1.0.0-rc.1
+      vue: ^3.0.0-0 || ^2.6.0
+    peerDependenciesMeta:
+      '@vue/composition-api':
+        optional: true
+    dependencies:
+      vue: 3.3.4
+    dev: false
+
+  /vue-eslint-parser@9.1.0(eslint@8.28.0):
+    resolution: {integrity: sha512-NGn/iQy8/Wb7RrRa4aRkokyCZfOUWk19OP5HP6JEozQFX5AoS/t+Z0ZN7FY4LlmWc4FNI922V7cvX28zctN8dQ==}
+    engines: {node: ^14.17.0 || >=16.0.0}
+    peerDependencies:
+      eslint: '>=6.0.0'
+    dependencies:
+      debug: 4.3.4
+      eslint: 8.28.0
+      eslint-scope: 7.1.1
+      eslint-visitor-keys: 3.3.0
+      espree: 9.4.1
+      esquery: 1.4.0
+      lodash: 4.17.21
+      semver: 7.3.8
+    transitivePeerDependencies:
+      - supports-color
+    dev: true
+
+  /vue-i18n@9.2.2(vue@3.3.4):
+    resolution: {integrity: sha512-yswpwtj89rTBhegUAv9Mu37LNznyu3NpyLQmozF3i1hYOhwpG8RjcjIFIIfnu+2MDZJGSZPXaKWvnQA71Yv9TQ==}
+    engines: {node: '>= 14'}
+    peerDependencies:
+      vue: ^3.0.0
+    dependencies:
+      '@intlify/core-base': 9.2.2
+      '@intlify/shared': 9.2.2
+      '@intlify/vue-devtools': 9.2.2
+      '@vue/devtools-api': 6.4.5
+      vue: 3.3.4
+    dev: false
+
+  /vue-router@4.1.6(vue@3.3.4):
+    resolution: {integrity: sha512-DYWYwsG6xNPmLq/FmZn8Ip+qrhFEzA14EI12MsMgVxvHFDYvlr4NXpVF5hrRH1wVcDP8fGi5F4rxuJSl8/r+EQ==}
+    peerDependencies:
+      vue: ^3.2.0
+    dependencies:
+      '@vue/devtools-api': 6.4.5
+      vue: 3.3.4
+    dev: false
+
+  /vue-template-compiler@2.7.14:
+    resolution: {integrity: sha512-zyA5Y3ArvVG0NacJDkkzJuPQDF8RFeRlzV2vLeSnhSpieO6LK2OVbdLPi5MPPs09Ii+gMO8nY4S3iKQxBxDmWQ==}
+    dependencies:
+      de-indent: 1.0.2
+      he: 1.2.0
+    dev: true
+
+  /vue-tsc@1.8.8(typescript@5.1.6):
+    resolution: {integrity: sha512-bSydNFQsF7AMvwWsRXD7cBIXaNs/KSjvzWLymq/UtKE36697sboX4EccSHFVxvgdBlI1frYPc/VMKJNB7DFeDQ==}
+    hasBin: true
+    peerDependencies:
+      typescript: '*'
+    dependencies:
+      '@vue/language-core': 1.8.8(typescript@5.1.6)
+      '@vue/typescript': 1.8.8(typescript@5.1.6)
+      semver: 7.3.8
+      typescript: 5.1.6
+    dev: true
+
+  /vue@3.3.4:
+    resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==}
+    dependencies:
+      '@vue/compiler-dom': 3.3.4
+      '@vue/compiler-sfc': 3.3.4
+      '@vue/runtime-dom': 3.3.4
+      '@vue/server-renderer': 3.3.4(vue@3.3.4)
+      '@vue/shared': 3.3.4
+
+  /w3c-hr-time@1.0.2:
+    resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
+    deprecated: Use your platform's native performance.now() and performance.timeOrigin.
+    dependencies:
+      browser-process-hrtime: 1.0.0
+    dev: true
+
+  /w3c-xmlserializer@2.0.0:
+    resolution: {integrity: sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==}
+    engines: {node: '>=10'}
+    dependencies:
+      xml-name-validator: 3.0.0
+    dev: true
+
+  /walker@1.0.8:
+    resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
+    dependencies:
+      makeerror: 1.0.12
+    dev: true
+
+  /webidl-conversions@5.0.0:
+    resolution: {integrity: sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==}
+    engines: {node: '>=8'}
+    dev: true
+
+  /webidl-conversions@6.1.0:
+    resolution: {integrity: sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==}
+    engines: {node: '>=10.4'}
+    dev: true
+
+  /whatwg-encoding@1.0.5:
+    resolution: {integrity: sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==}
+    dependencies:
+      iconv-lite: 0.4.24
+    dev: true
+
+  /whatwg-mimetype@2.3.0:
+    resolution: {integrity: sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==}
+    dev: true
+
+  /whatwg-url@8.7.0:
+    resolution: {integrity: sha512-gAojqb/m9Q8a5IV96E3fHJM70AzCkgt4uXYX2O7EmuyOnLrViCQlsEBmF9UQIu3/aeAIp2U17rtbpZWNntQqdg==}
+    engines: {node: '>=10'}
+    dependencies:
+      lodash: 4.17.21
+      tr46: 2.1.0
+      webidl-conversions: 6.1.0
+    dev: true
+
+  /which@2.0.2:
+    resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+    engines: {node: '>= 8'}
+    hasBin: true
+    dependencies:
+      isexe: 2.0.0
+    dev: true
+
+  /word-wrap@1.2.3:
+    resolution: {integrity: sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /wrap-ansi@6.2.0:
+    resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
+    engines: {node: '>=8'}
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+    dev: true
+
+  /wrap-ansi@7.0.0:
+    resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
+    engines: {node: '>=10'}
+    dependencies:
+      ansi-styles: 4.3.0
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+    dev: true
+
+  /wrappy@1.0.2:
+    resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+    dev: true
+
+  /write-file-atomic@3.0.3:
+    resolution: {integrity: sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==}
+    dependencies:
+      imurmurhash: 0.1.4
+      is-typedarray: 1.0.0
+      signal-exit: 3.0.7
+      typedarray-to-buffer: 3.1.5
+    dev: true
+
+  /ws@7.5.9:
+    resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==}
+    engines: {node: '>=8.3.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: ^5.0.2
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+    dev: true
+
+  /ws@8.11.0:
+    resolution: {integrity: sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==}
+    engines: {node: '>=10.0.0'}
+    peerDependencies:
+      bufferutil: ^4.0.1
+      utf-8-validate: ^5.0.2
+    peerDependenciesMeta:
+      bufferutil:
+        optional: true
+      utf-8-validate:
+        optional: true
+
+  /xhr@2.6.0:
+    resolution: {integrity: sha512-/eCGLb5rxjx5e3mF1A7s+pLlR6CGyqWN91fv1JgER5mVWg1MZmlhBvy9kjcsOdRk8RrIujotWyJamfyrp+WIcA==}
+    dependencies:
+      global: 4.4.0
+      is-function: 1.0.2
+      parse-headers: 2.0.5
+      xtend: 4.0.2
+    dev: false
+
+  /xml-name-validator@3.0.0:
+    resolution: {integrity: sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==}
+    dev: true
+
+  /xml-name-validator@4.0.0:
+    resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
+    engines: {node: '>=12'}
+    dev: true
+
+  /xml-parse-from-string@1.0.1:
+    resolution: {integrity: sha512-ErcKwJTF54uRzzNMXq2X5sMIy88zJvfN2DmdoQvy7PAFJ+tPRU6ydWuOKNMyfmOjdyBQTFREi60s0Y0SyI0G0g==}
+    dev: false
+
+  /xml2js@0.4.23:
+    resolution: {integrity: sha512-ySPiMjM0+pLDftHgXY4By0uswI3SPKLDw/i3UXbnO8M/p28zqexCUoPmQFrYD+/1BzhGJSs2i1ERWKJAtiLrug==}
+    engines: {node: '>=4.0.0'}
+    dependencies:
+      sax: 1.2.4
+      xmlbuilder: 11.0.1
+    dev: false
+
+  /xmlbuilder@11.0.1:
+    resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==}
+    engines: {node: '>=4.0'}
+    dev: false
+
+  /xmlchars@2.2.0:
+    resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+    dev: true
+
+  /xmlhttprequest@1.8.0:
+    resolution: {integrity: sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==}
+    engines: {node: '>=0.4.0'}
+    dev: false
+
+  /xregexp@3.1.0:
+    resolution: {integrity: sha512-4Y1x6DyB8xRoxosooa6PlGWqmmSKatbzhrftZ7Purmm4B8R4qIEJG1A2hZsdz5DhmIqS0msC0I7KEq93GphEVg==}
+
+  /xtend@4.0.2:
+    resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==}
+    engines: {node: '>=0.4'}
+    dev: false
+
+  /y18n@5.0.8:
+    resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /yallist@3.1.1:
+    resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+  /yallist@4.0.0:
+    resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
+    dev: true
+
+  /yaml@1.10.2:
+    resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==}
+    engines: {node: '>= 6'}
+
+  /yaml@2.1.3:
+    resolution: {integrity: sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==}
+    engines: {node: '>= 14'}
+    dev: true
+
+  /yargs-parser@20.2.9:
+    resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+    engines: {node: '>=10'}
+    dev: true
+
+  /yargs@16.2.0:
+    resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+    engines: {node: '>=10'}
+    dependencies:
+      cliui: 7.0.4
+      escalade: 3.1.1
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      string-width: 4.2.3
+      y18n: 5.0.8
+      yargs-parser: 20.2.9
+    dev: true
+
+  /yocto-queue@0.1.0:
+    resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+    engines: {node: '>=10'}
+    dev: true

+ 51 - 0
src/App.vue

@@ -0,0 +1,51 @@
+<script setup lang="ts">
+import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
+
+onLaunch(() => {
+  console.log('App Launch')
+})
+onShow(() => {
+  console.log('App Show')
+})
+onHide(() => {
+  console.log('App Hide')
+})
+</script>
+
+<style lang="scss">
+// 字体图标
+@import '@/styles/fonts.scss';
+
+view,
+navigator,
+input,
+scroll-view {
+  box-sizing: border-box;
+}
+
+button::after {
+  border: none;
+}
+
+swiper,
+scroll-view {
+  flex: 1;
+  height: 100%;
+  overflow: hidden;
+}
+
+image {
+  width: 100%;
+  height: 100%;
+  vertical-align: middle;
+}
+
+// 两行省略
+.ellipsis {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  display: -webkit-box;
+  -webkit-line-clamp: 2;
+  -webkit-box-orient: vertical;
+}
+</style>

+ 152 - 0
src/components/XtxGuess.vue

@@ -0,0 +1,152 @@
+<script setup lang="ts">
+import { getHomeGoodsGuessLikeAPI } from '@/services/home'
+import type { PageParams } from '@/types/global'
+import type { GuessItem } from '@/types/home'
+import { onMounted, ref } from 'vue'
+
+// 分页参数
+const pageParams: Required<PageParams> = {
+  page: 1,
+  pageSize: 10,
+}
+// 猜你喜欢的列表
+const guessList = ref<GuessItem[]>([])
+// 已结束标记
+const finish = ref(false)
+// 获取猜你喜欢数据
+const getHomeGoodsGuessLikeData = async () => {
+  // 退出分页判断
+  if (finish.value === true) {
+    return uni.showToast({ icon: 'none', title: '没有更多数据~' })
+  }
+  const res = await getHomeGoodsGuessLikeAPI(pageParams)
+  // guessList.value = res.result.items
+  // 数组追加
+  guessList.value.push(...res.result.items)
+  // 分页条件
+  if (pageParams.page < res.result.pages) {
+    // 页码累加
+    pageParams.page++
+  } else {
+    finish.value = true
+  }
+}
+// 重置数据
+const resetData = () => {
+  pageParams.page = 1
+  guessList.value = []
+  finish.value = false
+}
+// 组件挂载完毕
+onMounted(() => {
+  getHomeGoodsGuessLikeData()
+})
+// 暴露方法
+defineExpose({
+  resetData,
+  getMore: getHomeGoodsGuessLikeData,
+})
+</script>
+
+<template>
+  <!-- 猜你喜欢 -->
+  <view class="caption">
+    <text class="text">猜你喜欢</text>
+  </view>
+  <view class="guess">
+    <navigator
+      class="guess-item"
+      v-for="item in guessList"
+      :key="item.id"
+      :url="`/pages/goods/goods?id=${item.id}`"
+    >
+      <image class="image" mode="aspectFill" :src="item.picture"></image>
+      <view class="name"> {{ item.name }} </view>
+      <view class="price">
+        <text class="small">¥</text>
+        <text>{{ item.price }}</text>
+      </view>
+    </navigator>
+  </view>
+  <view class="loading-text">
+    {{ finish ? '没有更多数据~' : '正在加载...' }}
+  </view>
+</template>
+
+<style lang="scss">
+:host {
+  display: block;
+}
+/* 分类标题 */
+.caption {
+  display: flex;
+  justify-content: center;
+  line-height: 1;
+  padding: 36rpx 0 40rpx;
+  font-size: 32rpx;
+  color: #262626;
+  .text {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    padding: 0 28rpx 0 30rpx;
+
+    &::before,
+    &::after {
+      content: '';
+      width: 20rpx;
+      height: 20rpx;
+      background-image: url(@/static/images/bubble.png);
+      background-size: contain;
+      margin: 0 10rpx;
+    }
+  }
+}
+
+/* 猜你喜欢 */
+.guess {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  padding: 0 20rpx;
+  .guess-item {
+    width: 345rpx;
+    padding: 24rpx 20rpx 20rpx;
+    margin-bottom: 20rpx;
+    border-radius: 10rpx;
+    overflow: hidden;
+    background-color: #fff;
+  }
+  .image {
+    width: 304rpx;
+    height: 304rpx;
+  }
+  .name {
+    height: 75rpx;
+    margin: 10rpx 0;
+    font-size: 26rpx;
+    color: #262626;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    display: -webkit-box;
+    -webkit-line-clamp: 2;
+    -webkit-box-orient: vertical;
+  }
+  .price {
+    line-height: 1;
+    padding-top: 4rpx;
+    color: #cf4444;
+    font-size: 26rpx;
+  }
+  .small {
+    font-size: 80%;
+  }
+}
+// 加载提示文字
+.loading-text {
+  text-align: center;
+  font-size: 28rpx;
+  color: #666;
+  padding: 20rpx 0;
+}
+</style>

+ 40 - 0
src/components/XtxSwiper.vue

@@ -0,0 +1,40 @@
+<script setup lang="ts">
+import type { BannerItem } from '@/types/home'
+import { ref } from 'vue'
+
+const activeIndex = ref(0)
+
+// 当 swiper 下标发生变化时触发
+const onChange: UniHelper.SwiperOnChange = (ev) => {
+  activeIndex.value = ev.detail.current
+}
+// 定义 props 接收
+defineProps<{
+  list: BannerItem[]
+}>()
+</script>
+
+<template>
+  <view class="carousel">
+    <swiper :circular="true" :autoplay="false" :interval="3000" @change="onChange">
+      <swiper-item v-for="item in list" :key="item.id">
+        <navigator url="/pages/index/index" hover-class="none" class="navigator">
+          <image mode="aspectFill" class="image" :src="item.imgUrl"></image>
+        </navigator>
+      </swiper-item>
+    </swiper>
+    <!-- 指示点 -->
+    <view class="indicator">
+      <text
+        v-for="(item, index) in list"
+        :key="item.id"
+        class="dot"
+        :class="{ active: index === activeIndex }"
+      ></text>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+@import './styles/XtxSwiper.scss';
+</style>

+ 31 - 0
src/components/styles/XtxSwiper.scss

@@ -0,0 +1,31 @@
+/* 轮播图 */
+.carousel {
+  height: 280rpx;
+  position: relative;
+  overflow: hidden;
+  transform: translateY(0);
+  background-color: #efefef;
+  .indicator {
+    position: absolute;
+    left: 0;
+    right: 0;
+    bottom: 16rpx;
+    display: flex;
+    justify-content: center;
+    .dot {
+      width: 30rpx;
+      height: 6rpx;
+      margin: 0 8rpx;
+      border-radius: 6rpx;
+      background-color: rgba(255, 255, 255, 0.4);
+    }
+    .active {
+      background-color: #fff;
+    }
+  }
+  .navigator,
+  .image {
+    width: 100%;
+    height: 100%;
+  }
+}

+ 145 - 0
src/components/vk-data-goods-sku-popup/vk-data-goods-sku-popup.d.ts

@@ -0,0 +1,145 @@
+import { Component } from '@uni-helper/uni-app-types'
+
+/** SKU 弹出层 */
+export type SkuPopup = Component<SkuPopupProps>
+
+/** SKU 弹出层实例 */
+export type SkuPopupInstance = InstanceType<SkuPopup>
+
+/** SKU 弹出层属性 */
+export type SkuPopupProps = {
+  /** 双向绑定,true 为打开组件,false 为关闭组件 */
+  modelValue: boolean
+  /** 商品信息本地数据源 */
+  localdata: SkuPopupLocaldata
+  /** 按钮模式 1:都显示 2:只显示购物车 3:只显示立即购买 */
+  mode?: 1 | 2 | 3
+  /** 该商品已抢完时的按钮文字 */
+  noStockText?: string
+  /** 库存文字 */
+  stockText?: string
+  /** 点击遮罩是否关闭组件 */
+  maskCloseAble?: boolean
+  /** 顶部圆角值 */
+  borderRadius?: string | number
+  /** 最小购买数量 */
+  minBuyNum?: number
+  /** 最大购买数量 */
+  maxBuyNum?: number
+  /** 每次点击后的数量 */
+  stepBuyNum?: number
+  /** 是否只能输入 step 的倍数 */
+  stepStrictly?: boolean
+  /** 是否隐藏库存的显示 */
+  hideStock?: false
+  /** 主题风格 */
+  theme?: 'default' | 'red-black' | 'black-white' | 'coffee' | 'green'
+  /** 默认金额会除以100(即100=1元),若设置为0,则不会除以100(即1=1元) */
+  amountType?: 1 | 0
+  /** 自定义获取商品信息的函数(已知支付宝不支持,支付宝请改用localdata属性) */
+  customAction?: () => void
+  /** 是否显示右上角关闭按钮 */
+  showClose?: boolean
+  /** 关闭按钮的图片地址 */
+  closeImage?: string
+  /** 价格的字体颜色 */
+  priceColor?: string
+  /** 立即购买 - 按钮的文字 */
+  buyNowText?: string
+  /** 立即购买 - 按钮的字体颜色 */
+  buyNowColor?: string
+  /** 立即购买 - 按钮的背景颜色 */
+  buyNowBackgroundColor?: string
+  /** 加入购物车 - 按钮的文字 */
+  addCartText?: string
+  /** 加入购物车 - 按钮的字体颜色 */
+  addCartColor?: string
+  /** 加入购物车 - 按钮的背景颜色 */
+  addCartBackgroundColor?: string
+  /** 商品缩略图背景颜色 */
+  goodsThumbBackgroundColor?: string
+  /** 样式 - 不可点击时,按钮的样式 */
+  disableStyle?: object
+  /** 样式 - 按钮点击时的样式 */
+  activedStyle?: object
+  /** 样式 - 按钮常态的样式 */
+  btnStyle?: object
+  /** 字段名 - 商品表id的字段名 */
+  goodsIdName?: string
+  /** 字段名 - sku表id的字段名 */
+  skuIdName?: string
+  /** 字段名 - 商品对应的sku列表的字段名 */
+  skuListName?: string
+  /** 字段名 - 商品规格名称的字段名 */
+  specListName?: string
+  /** 字段名 - sku库存的字段名 */
+  stockName?: string
+  /** 字段名 - sku组合路径的字段名 */
+  skuArrName?: string
+  /** 字段名 - 商品缩略图字段名(未选择sku时) */
+  goodsThumbName?: string
+  /** 被选中的值 */
+  selectArr?: string[]
+
+  /** 打开弹出层 */
+  onOpen: () => void
+  /** 关闭弹出层 */
+  onClose: () => void
+  /** 点击加入购物车时(需选择完SKU才会触发)*/
+  onAddCart: (event: SkuPopupEvent) => void
+  /** 点击立即购买时(需选择完SKU才会触发)*/
+  onBuyNow: (event: SkuPopupEvent) => void
+}
+
+/**  商品信息本地数据源 */
+export type SkuPopupLocaldata = {
+  /** 商品 ID */
+  _id: string
+  /** 商品名称 */
+  name: string
+  /** 商品图片 */
+  goods_thumb: string
+  /** 商品规格列表 */
+  spec_list: SkuPopupSpecItem[]
+  /** 商品SKU列表 */
+  sku_list: SkuPopupSkuItem[]
+}
+
+/** 商品规格名称的集合 */
+export type SkuPopupSpecItem = {
+  /** 规格名称 */
+  name: string
+  /** 规格集合 */
+  list: { name: string }[]
+}
+
+/** 商品SKU列表 */
+export type SkuPopupSkuItem = {
+  /** SKU ID */
+  _id: string
+  /**  商品 ID */
+  goods_id: string
+  /** 商品名称 */
+  goods_name: string
+  /** 商品图片 */
+  image: string
+  /** SKU 价格 * 100, 注意:需要乘以 100 */
+  price: number
+  /** SKU 规格组成, 注意:需要与 spec_list 数组顺序对应 */
+  sku_name_arr: string[]
+  /** SKU 库存 */
+  stock: number
+}
+
+/** 当前选择的sku数据 */
+export type SkuPopupEvent = SkuPopupSkuItem & {
+  /** 商品购买数量 */
+  buy_num: number
+}
+
+/** 全局组件类型声明 */
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    'vk-data-goods-sku-popup': SkuPopup
+  }
+}

+ 1426 - 0
src/components/vk-data-goods-sku-popup/vk-data-goods-sku-popup.vue

@@ -0,0 +1,1426 @@
+<template>
+  <view
+    class="vk-data-goods-sku-popup"
+    catchtouchmove="true"
+    :class="valueCom && complete ? 'show' : 'none'"
+    @touchmove.stop.prevent="moveHandle"
+    @click.stop="stop"
+  >
+    <!-- 页面内容开始 -->
+    <view class="mask" @click="close('mask')"></view>
+    <view
+      class="layer attr-content"
+      :class="{ 'safe-area-inset-bottom': safeAreaInsetBottom }"
+      :style="{
+        borderRadius: borderRadius + 'rpx ' + borderRadius + 'rpx 0 0',
+        paddingBottom: safeBottom + 'px',
+      }"
+    >
+      <view class="specification-wrapper">
+        <scroll-view class="specification-wrapper-content" scroll-y="true">
+          <view class="specification-header">
+            <view class="specification-left">
+              <image
+                class="product-img"
+                :src="selectShop.image ? selectShop.image : goodsInfo[goodsThumbName]"
+                :style="{ backgroundColor: goodsThumbBackgroundColor }"
+                mode="aspectFill"
+                @click="previewImage"
+              ></image>
+            </view>
+            <view class="specification-right">
+              <view class="price-content" :style="{ color: themeColorFn('priceColor') }">
+                <text class="sign">¥</text>
+                <text class="price" :class="priceCom.length > 16 ? 'price2' : ''">{{
+                  priceCom
+                }}</text>
+              </view>
+              <view class="inventory" v-if="!hideStock">{{ stockText }}:{{ stockCom }}</view>
+              <view class="inventory" v-else></view>
+              <view class="choose" v-show="isManyCom">已选:{{ selectArr.join(' ') }}</view>
+            </view>
+          </view>
+
+          <view class="specification-content">
+            <view
+              v-show="isManyCom"
+              class="specification-item"
+              v-for="(item, index1) in goodsInfo[specListName]"
+              :key="index1"
+            >
+              <view class="item-title">{{ item.name }}</view>
+              <view class="item-wrapper">
+                <view
+                  class="item-content"
+                  v-for="(item_value, index2) in item.list"
+                  :key="index2"
+                  :class="[
+                    item_value.ishow ? '' : 'noactived',
+                    subIndex[index1] == index2 ? 'actived' : '',
+                  ]"
+                  :style="[
+                    item_value.ishow ? '' : themeColorFn('disableStyle'),
+                    item_value.ishow ? themeColorFn('btnStyle') : '',
+                    subIndex[index1] == index2 ? themeColorFn('activedStyle') : '',
+                  ]"
+                  @click="skuClick(item_value, index1, index2)"
+                >
+                  {{ item_value.name }}
+                </view>
+              </view>
+            </view>
+            <view class="number-box-view">
+              <view style="flex: 1">数量</view>
+              <view style="flex: 4; text-align: right">
+                <vk-data-input-number-box
+                  v-model="selectNum"
+                  :min="minBuyNum || 1"
+                  :max="maxBuyNumCom"
+                  :step="stepBuyNum || 1"
+                  :step-strictly="stepStrictly"
+                  :positive-integer="true"
+                  @change="numChange"
+                ></vk-data-input-number-box>
+              </view>
+            </view>
+          </view>
+        </scroll-view>
+        <view class="close" @click="close('close')" v-if="showClose != false"
+          ><image class="close-item" :src="closeImage"></image
+        ></view>
+      </view>
+
+      <view class="btn-wrapper" v-if="outFoStock || mode == 4">
+        <view class="sure" style="color: #ffffff; background-color: #cccccc">{{
+          noStockText
+        }}</view>
+      </view>
+      <view class="btn-wrapper" v-else-if="mode == 1">
+        <view
+          class="sure add-cart"
+          style="border-radius: 38rpx 0rpx 0rpx 38rpx"
+          :style="{
+            color: themeColorFn('addCartColor'),
+            backgroundColor: themeColorFn('addCartBackgroundColor'),
+          }"
+          @click="addCart"
+        >
+          {{ addCartText }}
+        </view>
+
+        <view
+          class="sure"
+          style="border-radius: 0rpx 38rpx 38rpx 0rpx"
+          :style="{
+            color: themeColorFn('buyNowColor'),
+            backgroundColor: themeColorFn('buyNowBackgroundColor'),
+          }"
+          @click="buyNow"
+        >
+          {{ buyNowText }}
+        </view>
+      </view>
+      <view class="btn-wrapper" v-else-if="mode == 2">
+        <view
+          class="sure add-cart"
+          :style="{
+            color: themeColorFn('addCartColor'),
+            backgroundColor: themeColorFn('addCartBackgroundColor'),
+          }"
+          @click="addCart"
+        >
+          {{ addCartText }}
+        </view>
+      </view>
+      <view class="btn-wrapper" v-else-if="mode == 3">
+        <view
+          class="sure"
+          :style="{
+            color: themeColorFn('buyNowColor'),
+            backgroundColor: themeColorFn('buyNowBackgroundColor'),
+          }"
+          @click="buyNow"
+        >
+          {{ buyNowText }}
+        </view>
+      </view>
+    </view>
+    <!-- 页面内容结束 -->
+  </view>
+</template>
+
+<script>
+/* eslint-disable */
+var vk // vk依赖
+var goodsCache = {} // 本地商品缓存
+export default {
+  name: 'vk-data-goods-sku-popup',
+  emits: [
+    'update:modelValue',
+    'input',
+    'update-goods',
+    'open',
+    'close',
+    'add-cart',
+    'buy-now',
+    'cart',
+    'buy',
+    'num-change',
+  ],
+  props: {
+    // true 组件显示 false 组件隐藏
+    value: {
+      Type: Boolean,
+      default: false,
+    },
+    modelValue: {
+      Type: Boolean,
+      default: false,
+    },
+    // vk云函数路由模式参数开始-----------------------------------------------------------
+    // 商品id
+    goodsId: {
+      Type: String,
+      default: '',
+    },
+    // vk路由模式框架下的云函数地址
+    action: {
+      Type: String,
+      default: '',
+    },
+    // vk云函数路由模式参数结束-----------------------------------------------------------
+    // 该商品已抢完时的按钮文字
+    noStockText: {
+      Type: String,
+      default: '该商品已抢完',
+    },
+    // 库存文字
+    stockText: {
+      Type: String,
+      default: '库存',
+    },
+    // 商品表id的字段名
+    goodsIdName: {
+      Type: String,
+      default: '_id',
+    },
+    // sku表id的字段名
+    skuIdName: {
+      Type: String,
+      default: '_id',
+    },
+    // sku_list的字段名
+    skuListName: {
+      Type: String,
+      default: 'sku_list',
+    },
+    // spec_list的字段名
+    specListName: {
+      Type: String,
+      default: 'spec_list',
+    },
+    // 库存的字段名 默认 stock
+    stockName: {
+      Type: String,
+      default: 'stock',
+    },
+    // sku组合路径的字段名
+    skuArrName: {
+      Type: String,
+      default: 'sku_name_arr',
+    },
+    // 默认单规格时的规格组名称
+    defaultSingleSkuName: {
+      Type: String,
+      default: '默认',
+    },
+    // 模式 1:都显示  2:只显示购物车 3:只显示立即购买 4:显示缺货按钮 默认 1
+    mode: {
+      Type: Number,
+      default: 1,
+    },
+    // 点击遮罩是否关闭组件 true 关闭 false 不关闭 默认true
+    maskCloseAble: {
+      Type: Boolean,
+      default: true,
+    },
+    // 顶部圆角值
+    borderRadius: {
+      Type: [String, Number],
+      default: 0,
+    },
+    // 商品缩略图字段名(未选择sku时)
+    goodsThumbName: {
+      Type: [String],
+      default: 'goods_thumb',
+    },
+    // 商品缩略图背景颜色,如#999999
+    goodsThumbBackgroundColor: {
+      Type: String,
+      default: 'transparent',
+    },
+    // 最小购买数量 默认 1
+    minBuyNum: {
+      Type: [Number, String],
+      default: 1,
+    },
+    // 最大购买数量 默认 100000
+    maxBuyNum: {
+      Type: [Number, String],
+      default: 100000,
+    },
+    // 步进器步长 默认 1
+    stepBuyNum: {
+      Type: [Number, String],
+      default: 1,
+    },
+    // 是否只能输入 step 的倍数
+    stepStrictly: {
+      Type: Boolean,
+      default: false,
+    },
+    // 自定义获取商品信息的函数,支付宝小程序不支持该属性,请使用localdata属性
+    customAction: {
+      Type: [Function],
+      default: null,
+    },
+    // 本地数据源
+    localdata: {
+      type: Object,
+    },
+    // 价格的字体颜色
+    priceColor: {
+      Type: String,
+    },
+    // 立即购买按钮的文字
+    buyNowText: {
+      Type: String,
+      default: '立即购买',
+    },
+    // 立即购买按钮的字体颜色
+    buyNowColor: {
+      Type: String,
+    },
+    // 立即购买按钮的背景颜色
+    buyNowBackgroundColor: {
+      Type: String,
+    },
+    // 加入购物车按钮的文字
+    addCartText: {
+      Type: String,
+      default: '加入购物车',
+    },
+    // 加入购物车按钮的字体颜色
+    addCartColor: {
+      Type: String,
+    },
+    // 加入购物车按钮的背景颜色
+    addCartBackgroundColor: {
+      Type: String,
+    },
+    // 不可点击时,按钮的样式
+    disableStyle: {
+      Type: Object,
+      default: null,
+    },
+    // 按钮点击时的样式
+    activedStyle: {
+      Type: Object,
+      default: null,
+    },
+    // 按钮常态的样式
+    btnStyle: {
+      Type: Object,
+      default: null,
+    },
+    // 是否显示右上角关闭按钮
+    showClose: {
+      Type: Boolean,
+      default: true,
+    },
+    // 关闭按钮的图片地址 https://img.alicdn.com/imgextra/i1/121022687/O1CN01ImN0O11VigqwzpLiK_!!121022687.png
+    closeImage: {
+      Type: String,
+      default:
+        'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACoAAAAqCAYAAADFw8lbAAAEyUlEQVR42sSZeWwNURTGp4OqtBo7sSXELragdkpQsRRJ1Zr4hyJiJ9YgxNIg1qANiT+E1i5IY0kVVWtQEbuEKLFGUSH27/ANN5PXmTvzupzkl/tm8t6b7517lnvvC0lKSjJ8WmnQAUSDFqABqALKgl8gD7wE90E2SAeXwFf1SxISErQeVtKHwCgwFsSDSIf3hYFKoCkYDBaDdyAViHdueHmoF6FtwDLQ23b/E7gM7oIcejIERIDaoBFoC8qA8mA8SQNz6W1XC9GY+nCQCCYAk/c+gF0gBZwH312+IxR0BCPBUIaH2A+wHsxHCHxx+gLT5QGN6a2JfG8uvVCDws9oiDQYlxkMGfHyQvARlADTwcXk5OT6foV2kS8ATXidymlcyen1a/Jjl9IJh3hPkjELYqO8Cu0KjjNZvtETw5jFBWXPmGSTGQKSeOn5iQ0kVLL0CINfPNcPbDMKyRCbGzEMBJ+ZD8cChYFdqGTqfsWT8otPGoVsEHsMwxDFs3shNsxJ6BrQ0Po8OGUUkVHsNCVml+cntB1jUWwn2GEUsTEMrASbDK+2CCQ0kYX6nfLLisMmKqUr0S60M+jG10vAm+JSCa8+x7CKlzHwaktV6DiObzUzPJIxFO1BQ12wGtTReO9GetVgY/kjNJzZbcWmTjHfxw51AsRqvL8eOAtmsJuFu3g1l+1ZLB5eDTVZ3K0P7tL0TkWOpSg61kVkBtuuNRthGs+wtJST5aQI7cEbkkRXNYVKgX6kIdYuUhYzMQwxN8tiExCLFqHNeSF9/aem0BzGp5PYQCJ7c/Gsk1RfuSD6U1dNpcDf9ZigTmKbMRZ9iVTsHscGJluW2FMf1SSQWGnBmaB6kCJVTVVNJZE++Cx9drEllS1KMCINpURFmEbBWA63Fz9s95cGIdJgp/zXmT4pZcOvSUzuZttTbblmnc3PIjjmidDXvKgdhMh0JdbzuCjWrbNOVovjS5P7bkPJ/mBESkz2BO0166ybNeJ431S2q+01NntuIq3E0amzjiZtk9tssWyTDzO4525bACK9NAUn68TtkNhpEXpOSagRml+S6iLSSeweHv242Qhl13rRyvoDvDlKyTQny/ZQJ+1iH7vVbEx7OR5UiKVIO7VicgvHCtwrudloMIV7/0uadVYW57O4Wvvi8v4pymlKkrpwvsDeLLZAY2pkwbAB3PSQfC+4cH7l4k1ZH8zkZRq8ecO+Z5rN40JJqnXFuGfaxPCTLjcn0OZOpnArXw8HY4paIbw5CcMgXq6HN2/mt6+XGLrN15tBryIUGavMpCTrfKcDCKkAceA9S8nhAOehhSUyhXpkBxxnP4YM1InugP7cBkjBPcqVUWFYCEROxXiQz5JlXV+IfKh7mpfJac+lZ6V87QXVClBkTc7YWsWTPSDyitfzUTlJlj8TbvE6jluDOdwZ+jX57GLO3ADeuyZrDYi86vV81FD2UVGsmT+5Zl0BnkhoseOEaogL46pqO4v/IqUEyalIR4h85BgjHv6+aUWRMbb7EstX6O0cpT1Gco0ry8fWygLDMjmDnQeBt3Qe7uVfkeugDwVLcsVzGsuwLXbV+I63XNAkG5r/hvgRqgqWs6pJPKrsbvz/Q6yyun0w/h6lP+BnzrCpfPMT2L8FGAA7k1GZ/vnaqAAAAABJRU5ErkJggg==',
+    },
+    // 是否隐藏库存显示
+    hideStock: {
+      Type: Boolean,
+      default: false,
+    },
+    // 颜色主题
+    theme: {
+      Type: String,
+      default: 'default',
+    },
+    // 请求中的提示
+    actionTips: {
+      Type: String,
+      default: '请求中...',
+    },
+    // 默认选中的SKU
+    defaultSelect: {
+      Type: Object,
+    },
+    // 是否使用缓存
+    useCache: {
+      Type: Boolean,
+      default: true,
+    },
+    /**
+     * 默认商品,设置该值可快速展示商品
+     * 逻辑: 先展示 defaultGoods 信息,再取数据库,再更新页面(通常为更新库存)
+     */
+    defaultGoods: {
+      Type: Object,
+    },
+    /**
+     * 金额是否需要除以100
+     * 1:金额会除以100
+     * 0:金额不会除以100
+     */
+    amountType: {
+      Type: Number,
+      default: 1,
+    },
+    // 每次选择完SKU后,购买数量归1,如果有最小购买数量,则设置为最小购买数量
+    selectedInit: {
+      Type: Boolean,
+      default: false,
+    },
+    // 是否开启底部安全区适配,默认true
+    safeAreaInsetBottom: {
+      Type: Boolean,
+      default: true,
+    },
+  },
+  data() {
+    return {
+      safeBottom: 0,  // 留出底部安全距离
+      complete: false, // 组件是否加载完成
+      goodsInfo: {}, // 商品信息
+      isShow: false, // true 显示 false 隐藏
+      initKey: true, // 是否需要初始化 true 是 false 否
+      // #ifndef MP-BAIDU
+      shopItemInfo: {}, // 存放要和选中的值进行匹配的数据(因百度小程序setData不支持中文字段,故不编译shopItemInfo变量)
+      // #endif
+      selectArr: [], // 存放被选中的值
+      subIndex: [], // 是否选中 因为不确定是多规格还是单规格,所以这里定义数组来判断
+      selectShop: {}, // 存放最后选中的商品
+      selectNum: this.minBuyNum || 1, // 选中数量
+      outFoStock: false, // 是否全部sku都缺货
+      openTime: 0,
+      themeColor: {
+        // 默认主题
+        default: {
+          priceColor: 'rgb(254, 86, 10)',
+          buyNowColor: '#ffffff',
+          buyNowBackgroundColor: 'rgb(254, 86, 10)',
+          addCartColor: '#ffffff',
+          addCartBackgroundColor: 'rgb(255, 148, 2)',
+          btnStyle: {
+            color: '#333333',
+            borderColor: '#f4f4f4',
+            backgroundColor: '#ffffff',
+          },
+          activedStyle: {
+            color: 'rgb(254, 86, 10)',
+            borderColor: 'rgb(254, 86, 10)',
+            backgroundColor: 'rgba(254,86,10,0.1)',
+          },
+          disableStyle: {
+            color: '#c3c3c3',
+            borderColor: '#f6f6f6',
+            backgroundColor: '#f6f6f6',
+          },
+        },
+        // 红黑主题
+        'red-black': {
+          priceColor: 'rgb(255, 68, 68)',
+          buyNowColor: '#ffffff',
+          buyNowBackgroundColor: 'rgb(255, 68, 68)',
+          addCartColor: '#ffffff',
+          addCartBackgroundColor: 'rgb(85, 85, 85)',
+          activedStyle: {
+            color: 'rgb(255, 68, 68)',
+            borderColor: 'rgb(255, 68, 68)',
+            backgroundColor: 'rgba(255,68,68,0.1)',
+          },
+        },
+        // 黑白主题
+        'black-white': {
+          priceColor: 'rgb(47, 47, 52)',
+          buyNowColor: '#ffffff',
+          buyNowBackgroundColor: 'rgb(47, 47, 52)',
+          addCartColor: 'rgb(47, 47, 52)',
+          addCartBackgroundColor: 'rgb(235, 236, 242)',
+          // btnStyle:{
+          // 	color:"rgb(47, 47, 52)",
+          // 	borderColor:"rgba(235,236,242,0.5)",
+          // 	backgroundColor:"rgba(235,236,242,0.5)",
+          // },
+          activedStyle: {
+            color: 'rgb(47, 47, 52)',
+            borderColor: 'rgba(47,47,52,0.12)',
+            backgroundColor: 'rgba(47,47,52,0.12)',
+          },
+        },
+        // 咖啡色主题
+        coffee: {
+          priceColor: 'rgb(195, 167, 105)',
+          buyNowColor: '#ffffff',
+          buyNowBackgroundColor: 'rgb(195, 167, 105)',
+          addCartColor: 'rgb(195, 167, 105)',
+          addCartBackgroundColor: 'rgb(243, 238, 225)',
+          activedStyle: {
+            color: 'rgb(195, 167, 105)',
+            borderColor: 'rgb(195, 167, 105)',
+            backgroundColor: 'rgba(195, 167, 105,0.1)',
+          },
+        },
+        // 浅绿色主题
+        green: {
+          priceColor: 'rgb(99, 190, 114)',
+          buyNowColor: '#ffffff',
+          buyNowBackgroundColor: 'rgb(99, 190, 114)',
+          addCartColor: 'rgb(99, 190, 114)',
+          addCartBackgroundColor: 'rgb(225, 244, 227)',
+          activedStyle: {
+            color: 'rgb(99, 190, 114)',
+            borderColor: 'rgb(99, 190, 114)',
+            backgroundColor: 'rgba(99, 190, 114,0.1)',
+          },
+        },
+      },
+    }
+  },
+  created() {
+    let that = this
+    vk = that.vk
+    if (that.valueCom) {
+      that.open()
+    }
+    // 获取屏幕边界到安全区域距离
+    const {safeAreaInsets} = uni.getSystemInfoSync()
+    // 底部安全距离
+    that.safeBottom = safeAreaInsets.bottom
+  },
+  mounted() {},
+  methods: {
+    // 初始化
+    init(notAutoClick) {
+      let that = this
+      // 清空之前的数据
+      that.selectArr = []
+      that.subIndex = []
+      that.selectShop = {}
+      that.selectNum = that.minBuyNum || 1
+      that.outFoStock = false
+      that.shopItemInfo = {}
+      let specListName = that.specListName
+      that.goodsInfo[specListName].map((item) => {
+        that.selectArr.push('')
+        that.subIndex.push(-1)
+      })
+      that.checkItem() // 计算sku里面规格形成路径
+      that.checkInpath(-1) // 传-1是为了不跳过循环
+      if (!notAutoClick) that.autoClickSku() // 自动选择sku策略
+    },
+    // 使用vk路由模式框架获取商品信息
+    findGoodsInfo(obj = {}) {
+      let that = this
+      let { useCache } = obj
+      if (typeof vk == 'undefined') {
+        that.toast('custom-action必须是function', 'none')
+        return false
+      }
+      let { actionTips } = that
+      let actionTitle = ''
+      let actionAoading = false
+      if (actionTips !== 'custom') {
+        actionTitle = useCache ? '' : '请求中...'
+      } else {
+        actionAoading = useCache ? false : true
+      }
+      vk.callFunction({
+        url: that.action,
+        title: actionTitle,
+        loading: actionAoading,
+        data: {
+          goods_id: that.goodsId,
+        },
+        success(data) {
+          that.updateGoodsInfo(data.goodsInfo)
+          // 更新缓存
+          goodsCache[that.goodsId] = data.goodsInfo
+          that.$emit('update-goods', data.goodsInfo)
+        },
+        fail() {
+          that.updateValue(false)
+        },
+      })
+    },
+    updateValue(value) {
+      let that = this
+      if (value) {
+        that.$emit('open', true)
+        that.$emit('input', true)
+        that.$emit('update:modelValue', true)
+      } else {
+        that.$emit('input', false)
+        that.$emit('close', 'close')
+        that.$emit('update:modelValue', false)
+      }
+    },
+    // 更新商品信息(库存、名称、图片)
+    updateGoodsInfo(goodsInfo) {
+      let that = this
+      // goodsInfo.sku_list.map((item, index) => {
+      // 	item.sku_name_arr = ["20ml/瓶"];
+      // });
+      let { skuListName } = that
+      if (
+        JSON.stringify(that.goodsInfo) === '{}' ||
+        that.goodsInfo[that.goodsIdName] !== goodsInfo[that.goodsIdName]
+      ) {
+        that.goodsInfo = goodsInfo
+        that.initKey = true
+      } else {
+        that.goodsInfo[skuListName] = goodsInfo[skuListName]
+      }
+      if (that.initKey) {
+        that.initKey = false
+        that.init()
+      }
+      // 更新选中sku的库存信息
+      let select_sku_info = that.getListItem(
+        that.goodsInfo[skuListName],
+        that.skuIdName,
+        that.selectShop[that.skuIdName],
+      )
+      Object.assign(that.selectShop, select_sku_info)
+      that.defaultSelectSku()
+      that.complete = true
+    },
+    async open() {
+      let that = this
+      that.openTime = new Date().getTime()
+      let findGoodsInfoRun = true
+      let skuListName = that.skuListName
+      // 先获取缓存中的商品信息
+      let useCache = false
+      let goodsInfo = goodsCache[that.goodsId]
+      if (goodsInfo && that.useCache) {
+        useCache = true
+        that.updateGoodsInfo(goodsInfo)
+      } else {
+        that.complete = false
+      }
+      if (that.customAction && typeof that.customAction === 'function') {
+        try {
+          goodsInfo = await that
+            .customAction({
+              useCache,
+              goodsId: that.goodsId,
+              goodsInfo,
+              close: function () {
+                setTimeout(function () {
+                  that.close()
+                }, 500)
+              },
+            })
+            .catch((err) => {
+              setTimeout(function () {
+                that.close()
+              }, 500)
+            })
+        } catch (err) {
+          let { message = '' } = err
+          if (message.indexOf('.catch is not a function') > -1) {
+            that.toast('custom-action必须返回一个Promise', 'none')
+            setTimeout(function () {
+              that.close()
+            }, 500)
+            return false
+          }
+        }
+        // 更新缓存
+        goodsCache[that.goodsId] = goodsInfo
+        if (goodsInfo && typeof goodsInfo == 'object' && JSON.stringify(goodsInfo) != '{}') {
+          findGoodsInfoRun = false
+          that.updateGoodsInfo(goodsInfo)
+          that.updateValue(true)
+        } else {
+          that.toast('未获取到商品信息', 'none')
+          that.$emit('input', false)
+          return false
+        }
+      } else if (typeof that.localdata !== 'undefined' && that.localdata !== null) {
+        goodsInfo = that.localdata
+        if (goodsInfo && typeof goodsInfo == 'object' && JSON.stringify(goodsInfo) != '{}') {
+          findGoodsInfoRun = false
+          that.updateGoodsInfo(goodsInfo)
+          that.updateValue(true)
+        } else {
+          that.toast('未获取到商品信息', 'none')
+          that.$emit('input', false)
+          return false
+        }
+      } else {
+        if (findGoodsInfoRun) that.findGoodsInfo({ useCache })
+      }
+    },
+    // 监听 - 弹出层收起
+    close(s) {
+      let that = this
+      if (new Date().getTime() - that.openTime < 400) {
+        return false
+      }
+      if (s == 'mask') {
+        if (that.maskCloseAble !== false) {
+          that.$emit('input', false)
+          that.$emit('close', 'mask')
+          that.$emit('update:modelValue', false)
+        }
+      } else {
+        that.$emit('input', false)
+        that.$emit('close', 'close')
+        that.$emit('update:modelValue', false)
+      }
+    },
+    moveHandle() {
+      //禁止父元素滑动
+    },
+    // sku按钮的点击事件
+    skuClick(value, index1, index2) {
+      let that = this
+      if (value.ishow) {
+        if (that.selectArr[index1] != value.name) {
+          that.$set(that.selectArr, index1, value.name)
+          that.$set(that.subIndex, index1, index2)
+        } else {
+          that.$set(that.selectArr, index1, '')
+          that.$set(that.subIndex, index1, -1)
+        }
+        that.checkInpath(index1)
+        // 如果全部选完
+        that.checkSelectShop()
+      }
+    },
+    // 检测是否已经选完sku
+    checkSelectShop() {
+      let that = this
+      // 如果全部选完
+      if (that.selectArr.every((item) => item != '')) {
+        that.selectShop = that.shopItemInfo[that.getArrayToSting(that.selectArr)]
+        let stock = that.selectShop[that.stockName]
+        if (typeof stock !== 'undefined' && that.selectNum > stock) {
+          that.selectNum = stock
+        }
+        if (that.selectNum > that.maxBuyNum) {
+          that.selectNum = that.maxBuyNum
+        }
+        if (that.selectNum < that.minBuyNum) {
+          that.selectNum = that.minBuyNum
+        }
+        if (that.selectedInit) {
+          that.selectNum = that.minBuyNum || 1
+        }
+      } else {
+        that.selectShop = {}
+      }
+    },
+    // 检查路径
+    checkInpath(clickIndex) {
+      let that = this
+      let specListName = that.specListName
+      //console.time('筛选可选路径需要的时间是');
+      //循环所有属性判断哪些属性可选
+      //当前选中的兄弟节点和已选中属性不需要循环
+      let specList = that.goodsInfo[specListName]
+      for (let i = 0, len = specList.length; i < len; i++) {
+        if (i == clickIndex) {
+          continue
+        }
+        let len2 = specList[i].list.length
+        for (let j = 0; j < len2; j++) {
+          if (that.subIndex[i] != -1 && j == that.subIndex[i]) {
+            continue
+          }
+          let choosed_copy = [...that.selectArr]
+          that.$set(choosed_copy, i, specList[i].list[j].name)
+          let choosed_copy2 = choosed_copy.filter(
+            (item) => item !== '' && typeof item !== 'undefined',
+          )
+          if (that.shopItemInfo.hasOwnProperty(that.getArrayToSting(choosed_copy2))) {
+            specList[i].list[j].ishow = true
+          } else {
+            specList[i].list[j].ishow = false
+          }
+        }
+      }
+      that.$set(that.goodsInfo, specListName, specList)
+      // console.timeEnd('筛选可选路径需要的时间是');
+    },
+    // 计算sku里面规格形成路径
+    checkItem() {
+      let that = this
+      // console.time('计算有多小种可选路径需要的时间是');
+      let { stockName } = that
+      let skuListName = that.skuListName
+      // 去除库存小于等于0的商品sku
+      let originalSkuList = that.goodsInfo[skuListName]
+      let skuList = []
+      let stockNum = 0
+      originalSkuList.map((skuItem, index) => {
+        if (skuItem[stockName] > 0) {
+          skuList.push(skuItem)
+          stockNum += skuItem[stockName]
+        }
+      })
+      if (stockNum <= 0) {
+        that.outFoStock = true
+      }
+      // 计算有多小种可选路径
+      let result = skuList.reduce(
+        (arrs, items) => {
+          return arrs.concat(
+            items[that.skuArrName].reduce(
+              (arr, item) => {
+                return arr.concat(
+                  arr.map((item2) => {
+                    // 利用对象属性的唯一性实现二维数组去重
+                    //console.log(1,that.shopItemInfo,that.getArrayToSting([...item2, item]),item2,item,items);
+                    if (!that.shopItemInfo.hasOwnProperty(that.getArrayToSting([...item2, item]))) {
+                      that.shopItemInfo[that.getArrayToSting([...item2, item])] = items
+                    }
+                    return [...item2, item]
+                  }),
+                )
+              },
+              [[]],
+            ),
+          )
+        },
+        [[]],
+      )
+      // console.timeEnd('计算有多小种可选路径需要的时间是');
+    },
+    getArrayToSting(arr) {
+      let str = ''
+      arr.map((item, index) => {
+        item = item.replace(/\./g, '。')
+        if (index == 0) {
+          str += item
+        } else {
+          str += ',' + item
+        }
+      })
+      return str
+    },
+    // 检测sku选项是否已全部选完,且有库存
+    checkSelectComplete(obj = {}) {
+      let that = this
+      let clickTime = new Date().getTime()
+      if (that.clickTime && clickTime - that.clickTime < 400) {
+        return false
+      }
+      that.clickTime = clickTime
+      let { selectShop, selectNum, stockText, stockName } = that
+      if (!selectShop || !selectShop[that.skuIdName]) {
+        that.toast('请先选择对应规格', 'none')
+        return false
+      }
+      if (selectNum <= 0) {
+        that.toast('购买数量必须>0', 'none')
+        return false
+      }
+      // 判断库存
+      if (selectNum > selectShop[stockName]) {
+        that.toast(stockText + '不足', 'none')
+        return false
+      }
+      if (typeof obj.success == 'function') obj.success(selectShop)
+    },
+    // 加入购物车
+    addCart() {
+      let that = this
+      that.checkSelectComplete({
+        success: function (selectShop) {
+          selectShop.buy_num = that.selectNum
+          that.$emit('add-cart', selectShop)
+          that.$emit('cart', selectShop)
+          // setTimeout(function() {
+          // 	that.init();
+          // }, 300);
+        },
+      })
+    },
+    // 立即购买
+    buyNow() {
+      let that = this
+      that.checkSelectComplete({
+        success: function (selectShop) {
+          selectShop.buy_num = that.selectNum
+          that.$emit('buy-now', selectShop)
+          that.$emit('buy', selectShop)
+        },
+      })
+    },
+    // 弹窗
+    toast(title, icon) {
+      uni.showToast({
+        title: title,
+        icon: icon,
+      })
+    },
+    // 获取对象数组中的某一个item,根据指定的键值
+    getListItem(list, key, value) {
+      let that = this
+      let item
+      for (let i in list) {
+        if (typeof value == 'object') {
+          if (JSON.stringify(list[i][key]) === JSON.stringify(value)) {
+            item = list[i]
+            break
+          }
+        } else {
+          if (list[i][key] === value) {
+            item = list[i]
+            break
+          }
+        }
+      }
+      return item
+    },
+    getListIndex(list, key, value) {
+      let that = this
+      let index = -1
+      for (let i = 0; i < list.length; i++) {
+        if (list[i][key] === value) {
+          index = i
+          break
+        }
+      }
+      return index
+    },
+    // 自动选择sku前提是只有一组sku,默认自动选择最前面的有库存的sku
+    autoClickSku() {
+      let that = this
+      let { stockName } = that
+      let skuList = that.goodsInfo[that.skuListName]
+      let specListArr = that.goodsInfo[that.specListName]
+      if (specListArr.length == 1) {
+        let specList = specListArr[0].list
+        for (let i = 0; i < specList.length; i++) {
+          let sku = that.getListItem(skuList, that.skuArrName, [specList[i].name])
+          if (sku && sku[stockName] > 0) {
+            that.skuClick(specList[i], 0, i)
+            break
+          }
+        }
+      }
+    },
+    // 主题颜色
+    themeColorFn(name) {
+      let that = this
+      let { theme, themeColor } = that
+      let color = that[name] ? that[name] : themeColor[theme][name]
+      return color
+    },
+    defaultSelectSku() {
+      let that = this
+      let { defaultSelect } = that
+      if (defaultSelect && defaultSelect.sku && defaultSelect.sku.length > 0) {
+        that.selectSku(defaultSelect)
+      }
+    },
+    /**
+			 * 主动方法 - 设置sku
+			that.$refs.skuPopup.selectSku({
+				sku:["红色","256G","公开版"],
+				num:5
+			});
+			 */
+    selectSku(obj = {}) {
+      let that = this
+      let { sku: skuArr, num: selectNum } = obj
+      let specListArr = that.goodsInfo[that.specListName]
+      if (skuArr && specListArr.length === skuArr.length) {
+        // 先清空
+        let skuClickArr = []
+        let clickKey = true
+        for (let index = 0; index < skuArr.length; index++) {
+          let skuName = skuArr[index]
+          let specList = specListArr[index].list
+          let index1 = index
+          let index2 = that.getListIndex(specList, 'name', skuName)
+          if (index2 == -1) {
+            clickKey = false
+            break
+          }
+          skuClickArr.push({
+            spec: specList[index2],
+            index1: index1,
+            index2: index2,
+          })
+        }
+        if (clickKey) {
+          that.init(true)
+          skuClickArr.map((item) => {
+            that.skuClick(item.spec, item.index1, item.index2)
+          })
+        }
+      }
+      if (selectNum > 0) that.selectNum = selectNum
+    },
+    priceFilter(n = 0) {
+      let that = this
+      if (typeof n == 'string') {
+        n = parseFloat(n)
+      }
+      if (that.amountType === 0) {
+        return n.toFixed(2)
+      } else {
+        return (n / 100).toFixed(2)
+      }
+    },
+    pushGoodsCache(goodsInfo) {
+      let that = this
+      let { goodsIdName } = that
+      goodsCache[goodsInfo[goodsIdName]] = goodsInfo
+    },
+    // 用于阻止冒泡
+    stop() {},
+    // 图片预览
+    previewImage() {
+      let that = this
+      let { selectShop, goodsInfo, goodsThumbName } = that
+      let src = selectShop.image ? selectShop.image : goodsInfo[goodsThumbName]
+      if (src) {
+        uni.previewImage({
+          urls: [src],
+        })
+      }
+    },
+    getMaxStock() {
+      let maxStock = 0
+      let that = this
+      let { selectShop = {}, goodsInfo = {}, skuListName, stockName } = that
+      if (selectShop[stockName]) {
+        maxStock = selectShop[stockName]
+      } else {
+        let skuList = goodsInfo[skuListName]
+        if (skuList && skuList.length > 0) {
+          let valueArr = []
+          skuList.map((skuItem, index) => {
+            valueArr.push(skuItem[stockName])
+          })
+          let max = Math.max(...valueArr)
+          maxStock = max
+        }
+      }
+      return maxStock
+    },
+    numChange(e) {
+      this.$emit('num-change', e.value)
+    },
+  },
+  // 计算属性
+  computed: {
+    valueCom() {
+      // #ifndef VUE3
+      return this.value
+      // #endif
+
+      // #ifdef VUE3
+      return this.modelValue
+      // #endif
+    },
+    // 最大购买数量
+    maxBuyNumCom() {
+      let that = this
+      let maxStock = that.getMaxStock()
+      let max = that.maxBuyNum || 100000
+      // 最大购买量不能超过当前商品的库存
+      if (max > maxStock) {
+        max = maxStock
+      }
+      return max
+    },
+    // 是否是多规格
+    isManyCom() {
+      let that = this
+      let { goodsInfo, defaultSingleSkuName, specListName } = that
+      let isMany = true
+      if (
+        goodsInfo[specListName] &&
+        goodsInfo[specListName].length === 1 &&
+        goodsInfo[specListName][0].list.length === 1 &&
+        goodsInfo[specListName][0].name === defaultSingleSkuName
+      ) {
+        isMany = false
+      }
+      return isMany
+    },
+    // 默认价格区间计算
+    priceCom() {
+      let str = ''
+      let that = this
+      let { selectShop = {}, goodsInfo = {}, skuListName, skuIdName } = that
+      if (selectShop[skuIdName]) {
+        str = that.priceFilter(selectShop.price)
+      } else {
+        let skuList = goodsInfo[skuListName]
+        if (skuList && skuList.length > 0) {
+          let valueArr = []
+          skuList.map((skuItem, index) => {
+            valueArr.push(skuItem.price)
+          })
+          let min = that.priceFilter(Math.min(...valueArr))
+          let max = that.priceFilter(Math.max(...valueArr))
+          if (min === max) {
+            str = min + ''
+          } else {
+            str = `${min} - ${max}`
+          }
+        }
+      }
+      return str
+    },
+    // 库存显示
+    stockCom() {
+      let str = ''
+      let that = this
+      let { selectShop = {}, goodsInfo = {}, skuListName, stockName } = that
+      if (selectShop[stockName]) {
+        str = selectShop[stockName]
+      } else {
+        let skuList = goodsInfo[skuListName]
+        if (skuList && skuList.length > 0) {
+          let valueArr = []
+          skuList.map((skuItem, index) => {
+            valueArr.push(skuItem[stockName])
+          })
+          let min = Math.min(...valueArr)
+          let max = Math.max(...valueArr)
+          if (min === max) {
+            str = min
+          } else {
+            str = `${min} - ${max}`
+          }
+        }
+      }
+      return str
+    },
+  },
+  watch: {
+    valueCom(newVal, oldValue) {
+      let that = this
+      if (newVal) {
+        that.open()
+      }
+    },
+    defaultGoods: {
+      immediate: true,
+      handler: function (newVal, oldValue) {
+        let that = this
+        let { goodsIdName } = that
+        if (
+          typeof newVal === 'object' &&
+          newVal &&
+          newVal[goodsIdName] &&
+          !goodsCache[newVal[goodsIdName]]
+        ) {
+          that.pushGoodsCache(newVal)
+        }
+      },
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+/*  sku弹出层 */
+.vk-data-goods-sku-popup {
+  position: fixed;
+  left: 0;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  z-index: 990;
+  overflow: hidden;
+  &.show {
+    display: block;
+
+    .mask {
+      animation: showPopup 0.2s linear both;
+    }
+
+    .layer {
+      animation: showLayer 0.2s linear both;
+      bottom: var(--window-bottom);
+    }
+  }
+
+  &.hide {
+    .mask {
+      animation: hidePopup 0.2s linear both;
+    }
+
+    .layer {
+      animation: hideLayer 0.2s linear both;
+    }
+  }
+
+  &.none {
+    display: none;
+  }
+  .mask {
+    position: fixed;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 1;
+    background-color: rgba(0, 0, 0, 0.3);
+  }
+  .layer {
+    display: flex;
+    width: 100%;
+    // height: 1014rpx;
+    flex-direction: column;
+    // min-height: 40vh;
+    // max-height: 1014rpx;
+    position: fixed;
+    z-index: 99;
+    bottom: 0;
+    border-radius: 10rpx 10rpx 0 0;
+    background-color: #fff;
+
+    .specification-wrapper {
+      width: 100%;
+      padding: 30rpx 25rpx;
+      box-sizing: border-box;
+      .specification-wrapper-content {
+        width: 100%;
+        max-height: 900rpx;
+        min-height: 300rpx;
+        &::-webkit-scrollbar {
+          /*隐藏滚轮*/
+          display: none;
+        }
+
+        .specification-header {
+          width: 100%;
+          display: flex;
+          flex-direction: row;
+          position: relative;
+          margin-bottom: 40rpx;
+
+          .specification-left {
+            width: 180rpx;
+            height: 180rpx;
+            flex: 0 0 180rpx;
+
+            .product-img {
+              width: 180rpx;
+              height: 180rpx;
+            }
+          }
+
+          .specification-right {
+            flex: 1;
+            padding: 0 35rpx 0 28rpx;
+            box-sizing: border-box;
+            display: flex;
+            flex-direction: column;
+            justify-content: flex-end;
+            font-weight: 500;
+
+            .price-content {
+              color: #fe560a;
+              margin-bottom: 20rpx;
+
+              .sign {
+                font-size: 28rpx;
+              }
+
+              .price {
+                margin-left: 4rpx;
+                font-size: 48rpx;
+              }
+              .price2 {
+                margin-left: 4rpx;
+                font-size: 36rpx;
+              }
+            }
+
+            .inventory {
+              font-size: 24rpx;
+              color: #999999;
+              margin-bottom: 14rpx;
+            }
+
+            .choose {
+              font-size: 28rpx;
+              color: #333333;
+            }
+          }
+        }
+
+        .specification-content {
+          font-weight: 500;
+
+          .specification-item {
+            margin-bottom: 40rpx;
+
+            &:last-child {
+              margin-bottom: 0;
+            }
+
+            .item-title {
+              margin-bottom: 20rpx;
+              font-size: 28rpx;
+              color: #999999;
+            }
+
+            .item-wrapper {
+              display: flex;
+              flex-direction: row;
+              flex-flow: wrap;
+
+              .item-content {
+                display: inline-block;
+                padding: 10rpx 35rpx;
+                font-size: 24rpx;
+                border-radius: 10rpx;
+                background-color: #ffffff;
+                color: #333333;
+                margin-right: 20rpx;
+                margin-bottom: 16rpx;
+                border: 1px solid #f4f4f4;
+                box-sizing: border-box;
+                &.actived {
+                  border-color: #fe560a;
+                  color: #fe560a;
+                }
+
+                &.noactived {
+                  background-color: #f6f6f6;
+                  border-color: #f6f6f6;
+                  color: #c3c3c3;
+                }
+              }
+            }
+          }
+          .number-box-view {
+            display: flex;
+            padding-top: 30rpx;
+          }
+        }
+      }
+      .close {
+        position: absolute;
+        top: 30rpx;
+        right: 25rpx;
+        width: 50rpx;
+        height: 50rpx;
+        text-align: center;
+        line-height: 50rpx;
+        .close-item {
+          width: 50rpx;
+          height: 50rpx;
+        }
+      }
+    }
+    .btn-wrapper {
+      display: flex;
+      width: 100%;
+      height: 120rpx;
+      flex: 0 0 120rpx;
+      align-items: center;
+      justify-content: space-between;
+      padding: 0 26rpx;
+      box-sizing: border-box;
+      .layer-btn {
+        width: 335rpx;
+        height: 76rpx;
+        border-radius: 38rpx;
+        color: #fff;
+        line-height: 76rpx;
+        text-align: center;
+        font-weight: 500;
+        font-size: 28rpx;
+
+        &.add-cart {
+          background: #ffbe46;
+        }
+
+        &.buy {
+          background: #fe560a;
+        }
+      }
+      .sure {
+        width: 698rpx;
+        height: 68rpx;
+        border-radius: 38rpx;
+        color: #fff;
+        line-height: 68rpx;
+        text-align: center;
+        font-weight: 500;
+        font-size: 28rpx;
+        background: #fe560a;
+      }
+      .sure.add-cart {
+        background: #ff9402;
+      }
+    }
+    .btn-wrapper.safe-area-inset-bottom {
+      padding-bottom: 0;
+      padding-bottom: constant(safe-area-inset-bottom);
+      padding-bottom: env(safe-area-inset-bottom);
+    }
+  }
+
+  @keyframes showPopup {
+    0% {
+      opacity: 0;
+    }
+
+    100% {
+      opacity: 1;
+    }
+  }
+
+  @keyframes hidePopup {
+    0% {
+      opacity: 1;
+    }
+
+    100% {
+      opacity: 0;
+    }
+  }
+
+  @keyframes showLayer {
+    0% {
+      transform: translateY(120%);
+    }
+
+    100% {
+      transform: translateY(0%);
+    }
+  }
+
+  @keyframes hideLayer {
+    0% {
+      transform: translateY(0);
+    }
+
+    100% {
+      transform: translateY(120%);
+    }
+  }
+}
+</style>

+ 52 - 0
src/components/vk-data-input-number-box/vk-data-input-number-box.d.ts

@@ -0,0 +1,52 @@
+import { Component } from '@uni-helper/uni-app-types'
+
+/** 步进器 */
+export type InputNumberBox = Component<InputNumberBoxProps>
+
+/** 步进器实例 */
+export type InputNumberBoxInstance = InstanceType<InputNumberBox>
+
+/** 步进器属性 */
+export type InputNumberBoxProps = {
+  /** 输入框初始值(默认1) */
+  modelValue: number
+  /** 用户可输入的最小值(默认0) */
+  min: number
+  /** 用户可输入的最大值(默认99999) */
+  max: number
+  /**  步长,每次加或减的值(默认1) */
+  step: number
+  /** 是否禁用操作,包括输入框,加减按钮 */
+  disabled: boolean
+  /** 输入框宽度,单位rpx(默认80) */
+  inputWidth: string | number
+  /**  输入框和按钮的高度,单位rpx(默认50) */
+  inputHeight: string | number
+  /** 输入框和按钮的背景颜色(默认#F2F3F5) */
+  bgColor: string
+  /** 步进器标识符 */
+  index: string
+  /** 输入框内容发生变化时触发 */
+  onChange: (event: InputNumberBoxEvent) => void
+  /** 输入框失去焦点时触发 */
+  onBlur: (event: InputNumberBoxEvent) => void
+  /** 点击增加按钮时触发 */
+  onPlus: (event: InputNumberBoxEvent) => void
+  /** 点击减少按钮时触发 */
+  onMinus: (event: InputNumberBoxEvent) => void
+}
+
+/** 步进器事件对象 */
+export type InputNumberBoxEvent = {
+  /** 输入框当前值 */
+  value: number
+  /** 步进器标识符 */
+  index: string
+}
+
+/** 全局组件类型声明 */
+declare module '@vue/runtime-core' {
+  export interface GlobalComponents {
+    'vk-data-input-number-box': InputNumberBox
+  }
+}

+ 458 - 0
src/components/vk-data-input-number-box/vk-data-input-number-box.vue

@@ -0,0 +1,458 @@
+<!-- 步进器 -->
+<template>
+  <view class="vk-data-input-number-box">
+    <view
+      class="u-icon-minus"
+      :class="{ 'u-icon-disabled': disabled || inputVal <= min }"
+      :style="{
+        background: bgColor,
+        height: inputHeight + 'rpx',
+        color: color,
+        fontSize: size + 'rpx',
+        minHeight: '1.4em',
+      }"
+      @touchstart.prevent="btnTouchStart('minus')"
+      @touchend.stop.prevent="clearTimer"
+    >
+      <view :style="'font-size:' + (Number(size) + 10) + 'rpx'" class="num-btn">-</view>
+    </view>
+    <input
+      v-model="inputVal"
+      :disabled="disabledInput || disabled"
+      :cursor-spacing="getCursorSpacing"
+      :class="{ 'u-input-disabled': disabled }"
+      class="u-number-input"
+      type="number"
+      :style="{
+        color: color,
+        fontSize: size + 'rpx',
+        background: bgColor,
+        height: inputHeight + 'rpx',
+        width: inputWidth + 'rpx',
+      }"
+      @blur="onBlur"
+    />
+    <view
+      class="u-icon-plus"
+      :class="{ 'u-icon-disabled': disabled || inputVal >= max }"
+      :style="{
+        background: bgColor,
+        height: inputHeight + 'rpx',
+        color: color,
+        fontSize: size + 'rpx',
+        minHeight: '1.4em',
+      }"
+      @touchstart.prevent="btnTouchStart('plus')"
+      @touchend.stop.prevent="clearTimer"
+    >
+      <view :style="'font-size:' + (Number(size) + 10) + 'rpx'" class="num-btn">+</view>
+    </view>
+  </view>
+</template>
+
+<script>
+/* eslint-disable */
+/**
+ * numberBox 步进器(此为uview组件改造)
+ * @description 该组件一般用于商城购物选择物品数量的场景。注意:该输入框只能输入大于或等于0的整数,不支持小数输入
+ * @tutorial https://www.uviewui.com/components/numberBox.html
+ * @property {Number} value 输入框初始值(默认1)
+ * @property {String} bg-color 输入框和按钮的背景颜色(默认#F2F3F5)
+ * @property {Number} min 用户可输入的最小值(默认0)
+ * @property {Number} max 用户可输入的最大值(默认99999)
+ * @property {Number} step 步长,每次加或减的值(默认1)
+ * @property {Number} stepFirst 步进值,首次增加或最后减的值(默认step值和一致)
+ * @property {Boolean} disabled 是否禁用操作,禁用后无法加减或手动修改输入框的值(默认false)
+ * @property {Boolean} disabled-input 是否禁止输入框手动输入值(默认false)
+ * @property {Boolean} positive-integer 是否只能输入正整数(默认true)
+ * @property {String | Number} size 输入框文字和按钮字体大小,单位rpx(默认26)
+ * @property {String} color 输入框文字和加减按钮图标的颜色(默认#323233)
+ * @property {String | Number} input-width 输入框宽度,单位rpx(默认80)
+ * @property {String | Number} input-height 输入框和按钮的高度,单位rpx(默认50)
+ * @property {String | Number} index 事件回调时用以区分当前发生变化的是哪个输入框
+ * @property {Boolean} long-press 是否开启长按连续递增或递减(默认true)
+ * @property {String | Number} press-time 开启长按触发后,每触发一次需要多久,单位ms(默认250)
+ * @property {String | Number} cursor-spacing 指定光标于键盘的距离,避免键盘遮挡输入框,单位rpx(默认200)
+ * @event {Function} change 输入框内容发生变化时触发,对象形式
+ * @event {Function} blur 输入框失去焦点时触发,对象形式
+ * @event {Function} minus 点击减少按钮时触发(按钮可点击情况下),对象形式
+ * @event {Function} plus 点击增加按钮时触发(按钮可点击情况下),对象形式
+ * @example <vk-data-input-number-box :min="1" :max="100"></vk-data-input-number-box>
+ */
+export default {
+  name: 'vk-data-input-number-box',
+  emits: ['update:modelValue', 'input', 'change', 'blur', 'plus', 'minus'],
+  props: {
+    // 预显示的数字
+    value: {
+      type: Number,
+      default: 1,
+    },
+    modelValue: {
+      type: Number,
+      default: 1,
+    },
+    // 背景颜色
+    bgColor: {
+      type: String,
+      default: '#F2F3F5',
+    },
+    // 最小值
+    min: {
+      type: Number,
+      default: 0,
+    },
+    // 最大值
+    max: {
+      type: Number,
+      default: 99999,
+    },
+    // 步进值,每次加或减的值
+    step: {
+      type: Number,
+      default: 1,
+    },
+    // 步进值,首次增加或最后减的值
+    stepFirst: {
+      type: Number,
+      default: 0,
+    },
+    // 是否只能输入 step 的倍数
+    stepStrictly: {
+      type: Boolean,
+      default: false,
+    },
+    // 是否禁用加减操作
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
+    // input的字体大小,单位rpx
+    size: {
+      type: [Number, String],
+      default: 26,
+    },
+    // 加减图标的颜色
+    color: {
+      type: String,
+      default: '#323233',
+    },
+    // input宽度,单位rpx
+    inputWidth: {
+      type: [Number, String],
+      default: 80,
+    },
+    // input高度,单位rpx
+    inputHeight: {
+      type: [Number, String],
+      default: 50,
+    },
+    // index索引,用于列表中使用,让用户知道是哪个numberbox发生了变化,一般使用for循环出来的index值即可
+    index: {
+      type: [Number, String],
+      default: '',
+    },
+    // 是否禁用输入框,与disabled作用于输入框时,为OR的关系,即想要禁用输入框,又可以加减的话
+    // 设置disabled为false,disabledInput为true即可
+    disabledInput: {
+      type: Boolean,
+      default: false,
+    },
+    // 输入框于键盘之间的距离
+    cursorSpacing: {
+      type: [Number, String],
+      default: 100,
+    },
+    // 是否开启长按连续递增或递减
+    longPress: {
+      type: Boolean,
+      default: true,
+    },
+    // 开启长按触发后,每触发一次需要多久
+    pressTime: {
+      type: [Number, String],
+      default: 250,
+    },
+    // 是否只能输入大于或等于0的整数(正整数)
+    positiveInteger: {
+      type: Boolean,
+      default: true,
+    },
+  },
+  watch: {
+    valueCom(v1, v2) {
+      // 只有value的改变是来自外部的时候,才去同步inputVal的值,否则会造成循环错误
+      if (!this.changeFromInner) {
+        this.inputVal = v1
+        // 因为inputVal变化后,会触发this.handleChange(),在其中changeFromInner会再次被设置为true,
+        // 造成外面修改值,也导致被认为是内部修改的混乱,这里进行this.$nextTick延时,保证在运行周期的最后处
+        // 将changeFromInner设置为false
+        this.$nextTick(function () {
+          this.changeFromInner = false
+        })
+      }
+    },
+    inputVal(v1, v2) {
+      // 为了让用户能够删除所有输入值,重新输入内容,删除所有值后,内容为空字符串
+      if (v1 == '') return
+      let value = 0
+      // 首先判断是否数值,并且在min和max之间,如果不是,使用原来值
+      let tmp = this.isNumber(v1)
+      if (tmp && v1 >= this.min && v1 <= this.max) value = v1
+      else value = v2
+      // 判断是否只能输入大于等于0的整数
+      if (this.positiveInteger) {
+        // 小于0,或者带有小数点,
+        if (v1 < 0 || String(v1).indexOf('.') !== -1) {
+          value = v2
+          // 双向绑定input的值,必须要使用$nextTick修改显示的值
+          this.$nextTick(() => {
+            this.inputVal = v2
+          })
+        }
+      }
+      // 发出change事件
+      this.handleChange(value, 'change')
+    },
+    min(v1) {
+      if (v1 !== undefined && v1 != '' && this.valueCom < v1) {
+        this.$emit('input', v1)
+        this.$emit('update:modelValue', v1)
+      }
+    },
+    max(v1) {
+      if (v1 !== undefined && v1 != '' && this.valueCom > v1) {
+        this.$emit('input', v1)
+        this.$emit('update:modelValue', v1)
+      }
+    },
+  },
+  data() {
+    return {
+      inputVal: 1, // 输入框中的值,不能直接使用props中的value,因为应该改变props的状态
+      timer: null, // 用作长按的定时器
+      changeFromInner: false, // 值发生变化,是来自内部还是外部
+      innerChangeTimer: null, // 内部定时器
+    }
+  },
+  created() {
+    this.inputVal = Number(this.valueCom)
+  },
+  computed: {
+    valueCom() {
+      // #ifndef VUE3
+      return this.value
+      // #endif
+
+      // #ifdef VUE3
+      return this.modelValue
+      // #endif
+    },
+    getCursorSpacing() {
+      // 先将值转为px单位,再转为数值
+      return Number(uni.upx2px(this.cursorSpacing))
+    },
+  },
+  methods: {
+    // 点击退格键
+    btnTouchStart(callback) {
+      // 先执行一遍方法,否则会造成松开手时,就执行了clearTimer,导致无法实现功能
+      this[callback]()
+      // 如果没开启长按功能,直接返回
+      if (!this.longPress) return
+      clearInterval(this.timer) //再次清空定时器,防止重复注册定时器
+      this.timer = null
+      this.timer = setInterval(() => {
+        // 执行加或减函数
+        this[callback]()
+      }, this.pressTime)
+    },
+    clearTimer() {
+      this.$nextTick(() => {
+        clearInterval(this.timer)
+        this.timer = null
+      })
+    },
+    minus() {
+      this.computeVal('minus')
+    },
+    plus() {
+      this.computeVal('plus')
+    },
+    // 为了保证小数相加减出现精度溢出的问题
+    calcPlus(num1, num2) {
+      let baseNum, baseNum1, baseNum2
+      try {
+        baseNum1 = num1.toString().split('.')[1].length
+      } catch (e) {
+        baseNum1 = 0
+      }
+      try {
+        baseNum2 = num2.toString().split('.')[1].length
+      } catch (e) {
+        baseNum2 = 0
+      }
+      baseNum = Math.pow(10, Math.max(baseNum1, baseNum2))
+      let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2 //精度
+      return ((num1 * baseNum + num2 * baseNum) / baseNum).toFixed(precision)
+    },
+    // 为了保证小数相加减出现精度溢出的问题
+    calcMinus(num1, num2) {
+      let baseNum, baseNum1, baseNum2
+      try {
+        baseNum1 = num1.toString().split('.')[1].length
+      } catch (e) {
+        baseNum1 = 0
+      }
+      try {
+        baseNum2 = num2.toString().split('.')[1].length
+      } catch (e) {
+        baseNum2 = 0
+      }
+      baseNum = Math.pow(10, Math.max(baseNum1, baseNum2))
+      let precision = baseNum1 >= baseNum2 ? baseNum1 : baseNum2
+      return ((num1 * baseNum - num2 * baseNum) / baseNum).toFixed(precision)
+    },
+    computeVal(type) {
+      uni.hideKeyboard()
+      if (this.disabled) return
+      let value = 0
+      // 新增stepFirst开始
+      // 减
+      if (type === 'minus') {
+        if (this.stepFirst > 0 && this.inputVal == this.stepFirst) {
+          value = this.min
+        } else {
+          value = this.calcMinus(this.inputVal, this.step)
+        }
+      } else if (type === 'plus') {
+        if (this.stepFirst > 0 && this.inputVal < this.stepFirst) {
+          value = this.stepFirst
+        } else {
+          value = this.calcPlus(this.inputVal, this.step)
+        }
+      }
+      if (this.stepStrictly) {
+        let strictly = value % this.step
+        if (strictly > 0) {
+          value -= strictly
+        }
+      }
+      if (value > this.max) {
+        value = this.max
+      } else if (value < this.min) {
+        value = this.min
+      }
+      // 新增stepFirst结束
+      this.inputVal = value
+      this.handleChange(value, type)
+    },
+    // 处理用户手动输入的情况
+    onBlur(event) {
+      let val = 0
+      let value = event.detail.value
+      // 如果为非0-9数字组成,或者其第一位数值为0,直接让其等于min值
+      // 这里不直接判断是否正整数,是因为用户传递的props min值可能为0
+      if (!/(^\d+$)/.test(value) || value[0] == 0) val = this.min
+      val = +value
+
+      // 新增stepFirst开始
+      if (this.stepFirst > 0 && this.inputVal < this.stepFirst && this.inputVal > 0) {
+        val = this.stepFirst
+      }
+      // 新增stepFirst结束
+      if (this.stepStrictly) {
+        let strictly = val % this.step
+        if (strictly > 0) {
+          val -= strictly
+        }
+      }
+      if (val > this.max) {
+        val = this.max
+      } else if (val < this.min) {
+        val = this.min
+      }
+      this.$nextTick(() => {
+        this.inputVal = val
+      })
+      this.handleChange(val, 'blur')
+    },
+    handleChange(value, type) {
+      if (this.disabled) return
+      // 清除定时器,避免造成混乱
+      if (this.innerChangeTimer) {
+        clearTimeout(this.innerChangeTimer)
+        this.innerChangeTimer = null
+      }
+      // 发出input事件,修改通过v-model绑定的值,达到双向绑定的效果
+      this.changeFromInner = true
+      // 一定时间内,清除changeFromInner标记,否则内部值改变后
+      // 外部通过程序修改value值,将会无效
+      this.innerChangeTimer = setTimeout(() => {
+        this.changeFromInner = false
+      }, 150)
+      this.$emit('input', Number(value))
+      this.$emit('update:modelValue', Number(value))
+      this.$emit(type, {
+        // 转为Number类型
+        value: Number(value),
+        index: this.index,
+      })
+    },
+    /**
+     * 验证十进制数字
+     */
+    isNumber(value) {
+      return /^(?:-?\d+|-?\d{1,3}(?:,\d{3})+)?(?:\.\d+)?$/.test(value)
+    },
+  },
+}
+</script>
+
+<style lang="scss" scoped>
+.vk-data-input-number-box {
+  display: inline-flex;
+  align-items: center;
+}
+
+.u-number-input {
+  position: relative;
+  text-align: center;
+  padding: 0;
+  margin: 0 6rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+}
+
+.u-icon-plus,
+.u-icon-minus {
+  width: 60rpx;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.u-icon-plus {
+  border-radius: 0 8rpx 8rpx 0;
+}
+
+.u-icon-minus {
+  border-radius: 8rpx 0 0 8rpx;
+}
+
+.u-icon-disabled {
+  color: #c8c9cc !important;
+  background: #f7f8fa !important;
+}
+
+.u-input-disabled {
+  color: #c8c9cc !important;
+  background-color: #f2f3f5 !important;
+}
+.num-btn {
+  font-weight: 550;
+  position: relative;
+  top: -4rpx;
+}
+</style>

+ 21 - 0
src/composables/index.ts

@@ -0,0 +1,21 @@
+import type { XtxGuessInstance } from '@/types/components'
+import { ref } from 'vue'
+
+/**
+ * 猜你喜欢组合式函数
+ */
+export const useGuessList = () => {
+  // 获取猜你喜欢组件实例
+  const guessRef = ref<XtxGuessInstance>()
+
+  // 滚动触底事件
+  const onScrolltolower = () => {
+    guessRef.value?.getMore()
+  }
+
+  // 返回 ref 和事件处理函数
+  return {
+    guessRef,
+    onScrolltolower,
+  }
+}

+ 8 - 0
src/env.d.ts

@@ -0,0 +1,8 @@
+/// <reference types="vite/client" />
+
+declare module '*.vue' {
+  import { DefineComponent } from 'vue'
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
+  const component: DefineComponent<{}, {}, any>
+  export default component
+}

+ 17 - 0
src/main.ts

@@ -0,0 +1,17 @@
+import { createSSRApp } from 'vue'
+import App from './App.vue'
+
+// 导入 pinia 实例
+import pinia from './stores'
+
+export function createApp() {
+  // 创建 vue 实例
+  const app = createSSRApp(App)
+
+  // 使用 pinia
+  app.use(pinia)
+
+  return {
+    app,
+  }
+}

+ 119 - 0
src/manifest.json

@@ -0,0 +1,119 @@
+{
+    "name" : "小兔鲜儿",
+    "appid" : "__UNI__1018704",
+    "description" : "",
+    "versionName" : "1.0.0",
+    "versionCode" : "100",
+    "transformPx" : false,
+    /* 5+App特有相关 */
+    "app-plus" : {
+        // 忽略版本检查提示框
+        "compatible" : {
+            "ignoreVersion" : true
+        },
+        "usingComponents" : true,
+        "nvueStyleCompiler" : "uni-app",
+        "compilerVersion" : 3,
+        "splashscreen" : {
+            "alwaysShowBeforeRender" : true,
+            "waiting" : true,
+            "autoclose" : true,
+            "delay" : 0
+        },
+        /* 模块配置 */
+        "modules" : {},
+        /* 应用发布信息 */
+        "distribute" : {
+            /* android打包配置 */
+            "android" : {
+                "permissions" : [
+                    "<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
+                    "<uses-permission android:name=\"android.permission.VIBRATE\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
+                    "<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CAMERA\"/>",
+                    "<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
+                    "<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
+                    "<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
+                    "<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
+                    "<uses-feature android:name=\"android.hardware.camera\"/>",
+                    "<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
+                ]
+            },
+            /* ios打包配置 */
+            "ios" : {
+                "dSYMs" : false
+            },
+            /* SDK配置 */
+            "sdkConfigs" : {
+                "ad" : {}
+            },
+            "icons" : {
+                "android" : {
+                    "hdpi" : "unpackage/res/icons/72x72.png",
+                    "xhdpi" : "unpackage/res/icons/96x96.png",
+                    "xxhdpi" : "unpackage/res/icons/144x144.png",
+                    "xxxhdpi" : "unpackage/res/icons/192x192.png"
+                },
+                "ios" : {
+                    "appstore" : "unpackage/res/icons/1024x1024.png",
+                    "ipad" : {
+                        "app" : "unpackage/res/icons/76x76.png",
+                        "app@2x" : "unpackage/res/icons/152x152.png",
+                        "notification" : "unpackage/res/icons/20x20.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "proapp@2x" : "unpackage/res/icons/167x167.png",
+                        "settings" : "unpackage/res/icons/29x29.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "spotlight" : "unpackage/res/icons/40x40.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png"
+                    },
+                    "iphone" : {
+                        "app@2x" : "unpackage/res/icons/120x120.png",
+                        "app@3x" : "unpackage/res/icons/180x180.png",
+                        "notification@2x" : "unpackage/res/icons/40x40.png",
+                        "notification@3x" : "unpackage/res/icons/60x60.png",
+                        "settings@2x" : "unpackage/res/icons/58x58.png",
+                        "settings@3x" : "unpackage/res/icons/87x87.png",
+                        "spotlight@2x" : "unpackage/res/icons/80x80.png",
+                        "spotlight@3x" : "unpackage/res/icons/120x120.png"
+                    }
+                }
+            }
+        }
+    },
+    /* 快应用特有相关 */
+    "quickapp" : {},
+    /* 网页端特有配置 */
+    "h5" : {
+        "router" : {
+            // 路由基础路径。默认为 /
+            "base" : "./"
+        }
+    },
+    /* 小程序特有相关 */
+    "mp-weixin" : {
+        "appid" : "wx26729f20b9efae3a",
+        "setting" : {
+            "urlCheck" : true
+        },
+        "usingComponents" : true
+    },
+    "mp-alipay" : {
+        "usingComponents" : true
+    },
+    "mp-baidu" : {
+        "usingComponents" : true
+    },
+    "mp-toutiao" : {
+        "usingComponents" : true
+    },
+    "uniStatistics" : {
+        "enable" : false
+    },
+    "vueVersion" : "3"
+}

+ 183 - 0
src/pages.json

@@ -0,0 +1,183 @@
+{
+	// 组件自动引入规则
+	"easycom": {
+		// 是否开启自动扫描
+		"autoscan": true,
+		// 以正则方式自定义组件匹配规则
+		"custom": {
+			// uni-ui 规则如下配置
+			"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
+			// 以 Xtx 开头的组件,在 components 文件夹中查找引入(需要重启服务器)
+			"^Xtx(.*)": "@/components/Xtx$1.vue"
+		}
+	},
+	"pages": [
+		//pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		{
+			"path": "pages/index/index",
+			"style": {
+				"navigationStyle": "custom",
+				"navigationBarTextStyle": "white",
+				"navigationBarTitleText": "首页"
+			}
+		},
+		{
+			"path": "pages/category/category",
+			"style": {
+				"navigationBarTitleText": "商品分类"
+			}
+		},
+		{
+			"path": "pages/cart/cart",
+			"style": {
+				"navigationBarTitleText": "购物车"
+			}
+		},
+		{
+			"path": "pages/cart/cart2",
+			"style": {
+				"navigationBarTitleText": "购物车"
+			}
+		},
+		{
+			"path": "pages/my/my",
+			"style": {
+				"navigationStyle": "custom",
+				"navigationBarTextStyle": "white",
+				"navigationBarTitleText": "我的"
+			}
+		},
+		{
+			"path": "pages/login/login",
+			"style": {
+				"navigationBarTitleText": "登录"
+			}
+		},
+		{
+			"path": "pages/hot/hot",
+			"style": {
+				"navigationBarTitleText": "热门推荐"
+			}
+		},
+		{
+			"path": "pages/goods/goods",
+			"style": {
+				"navigationBarTitleText": "商品详情"
+			}
+		}
+	],
+	"globalStyle": {
+		"navigationBarTextStyle": "black",
+		"navigationBarTitleText": "",
+		"navigationBarBackgroundColor": "#F8F8F8",
+		"backgroundColor": "#F8F8F8"
+	},
+	// 设置 TabBar
+	"tabBar": {
+		"color": "#333",
+		"selectedColor": "#27ba9b",
+		"backgroundColor": "#fff",
+		"borderStyle": "white",
+		"list": [
+			{
+				"text": "首页",
+				"pagePath": "pages/index/index",
+				"iconPath": "static/tabs/home_default.png",
+				"selectedIconPath": "static/tabs/home_selected.png"
+			},
+			{
+				"text": "分类",
+				"pagePath": "pages/category/category",
+				"iconPath": "static/tabs/category_default.png",
+				"selectedIconPath": "static/tabs/category_selected.png"
+			},
+			{
+				"text": "购物车",
+				"pagePath": "pages/cart/cart",
+				"iconPath": "static/tabs/cart_default.png",
+				"selectedIconPath": "static/tabs/cart_selected.png"
+			},
+			{
+				"text": "我的",
+				"pagePath": "pages/my/my",
+				"iconPath": "static/tabs/user_default.png",
+				"selectedIconPath": "static/tabs/user_selected.png"
+			}
+		]
+	},
+	// 分包加载规则
+	"subPackages": [
+		{
+			// 子包的根目录
+			"root": "pagesMember",
+			// 页面路径和窗口表现
+			"pages": [
+				{
+					"path": "settings/settings",
+					"style": {
+						"navigationBarTitleText": "设置"
+					}
+				},
+				{
+					"path": "profile/profile",
+					"style": {
+						"navigationStyle": "custom",
+						"navigationBarTextStyle": "white",
+						"navigationBarTitleText": "个人信息"
+					}
+				},
+				{
+					"path": "address/address",
+					"style": {
+						"navigationBarTitleText": "地址管理"
+					}
+				},
+				{
+					"path": "address-form/address-form",
+					"style": {
+						"navigationBarTitleText": ""
+					}
+				}
+			]
+		},
+		{
+			"root": "pagesOrder",
+			"pages": [
+				{
+					"path": "create/create",
+					"style": {
+						"navigationBarTitleText": "填写订单"
+					}
+				},
+				{
+					"path": "detail/detail",
+					"style": {
+						"navigationBarTitleText": "订单详情",
+						"navigationStyle": "custom"
+					}
+				},
+				{
+					"path": "payment/payment",
+					"style": {
+						"navigationBarTitleText": "支付结果"
+					}
+				},
+				{
+					"path": "list/list",
+					"style": {
+						"navigationBarTitleText": "订单列表"
+					}
+				}
+			]
+		}
+	],
+	// 分包预下载规则
+	"preloadRule": {
+		"pages/my/my": {
+			"network": "all",
+			"packages": [
+				"pagesMember"
+			]
+		}
+	}
+}

+ 13 - 0
src/pages/cart/cart.vue

@@ -0,0 +1,13 @@
+<script setup lang="ts">
+import CartMain from './components/CartMain.vue'
+</script>
+
+<template>
+  <CartMain />
+</template>
+
+<style lang="scss">
+page {
+  height: 100%;
+}
+</style>

+ 14 - 0
src/pages/cart/cart2.vue

@@ -0,0 +1,14 @@
+<script setup lang="ts">
+import CartMain from './components/CartMain.vue'
+</script>
+
+<template>
+  <!-- 适配底部安全区 -->
+  <CartMain safe-area-inset-bottom />
+</template>
+
+<style lang="scss">
+page {
+  height: 100%;
+}
+</style>

+ 509 - 0
src/pages/cart/components/CartMain.vue

@@ -0,0 +1,509 @@
+<script setup lang="ts">
+import type { InputNumberBoxEvent } from '@/components/vk-data-input-number-box/vk-data-input-number-box'
+import { useGuessList } from '@/composables'
+import {
+  deleteMemberCartAPI,
+  getMemberCartAPI,
+  putMemberCartBySkuIdAPI,
+  putMemberCartSelectedAPI,
+} from '@/services/cart'
+import { useMemberStore } from '@/stores'
+import type { CartItem } from '@/types/cart'
+import { onShow } from '@dcloudio/uni-app'
+import { computed, ref } from 'vue'
+
+// 是否适配底部安全区域
+defineProps<{
+  safeAreaInsetBottom?: boolean
+}>()
+
+// 获取屏幕边界到安全区域距离
+const { safeAreaInsets } = uni.getSystemInfoSync()
+
+// 获取会员Store
+const memberStore = useMemberStore()
+
+// 获取购物车数据
+const cartList = ref<CartItem[]>([])
+// 优化购物车空列表状态,默认展示列表
+const showCartList = ref(true)
+const getMemberCartData = async () => {
+  const res = await getMemberCartAPI()
+  cartList.value = res.result
+  showCartList.value = res.result.length > 0
+}
+
+// 初始化调用: 页面显示触发
+onShow(() => {
+  if (memberStore.profile) {
+    getMemberCartData()
+  }
+})
+
+// 点击删除按钮
+const onDeleteCart = (skuId: string) => {
+  // 弹窗二次确认
+  uni.showModal({
+    content: '是否删除',
+    confirmColor: '#27BA9B',
+    success: async (res) => {
+      if (res.confirm) {
+        // 后端删除单品
+        await deleteMemberCartAPI({ ids: [skuId] })
+        // 重新获取列表
+        getMemberCartData()
+      }
+    },
+  })
+}
+
+// 修改商品数量
+const onChangeCount = (ev: InputNumberBoxEvent) => {
+  putMemberCartBySkuIdAPI(ev.index, { count: ev.value })
+}
+
+// 修改选中状态-单品修改
+const onChangeSelected = (item: CartItem) => {
+  // 前端数据更新-是否选中取反
+  item.selected = !item.selected
+  // 后端数据更新
+  putMemberCartBySkuIdAPI(item.skuId, { selected: item.selected })
+}
+
+// 计算全选状态
+const isSelectedAll = computed(() => {
+  return cartList.value.length && cartList.value.every((v) => v.selected)
+})
+
+// 修改选中状态-全选修改
+const onChangeSelectedAll = () => {
+  // 全选状态取反
+  const _isSelectedAll = !isSelectedAll.value
+  // 前端数据更新
+  cartList.value.forEach((item) => {
+    item.selected = _isSelectedAll
+  })
+  // 后端数据更新
+  putMemberCartSelectedAPI({ selected: _isSelectedAll })
+}
+
+// 计算选中单品列表
+const selectedCartList = computed(() => {
+  return cartList.value.filter((v) => v.selected)
+})
+
+// 计算选中总件数
+const selectedCartListCount = computed(() => {
+  return selectedCartList.value.reduce((sum, item) => sum + item.count, 0)
+})
+
+// 计算选中总金额
+const selectedCartListMoney = computed(() => {
+  return selectedCartList.value
+    .reduce((sum, item) => sum + item.count * item.nowPrice, 0)
+    .toFixed(2)
+})
+
+// 结算按钮
+const gotoPayment = () => {
+  if (selectedCartListCount.value === 0) {
+    return uni.showToast({
+      icon: 'none',
+      title: '请选择商品',
+    })
+  }
+  // 跳转到结算页
+  uni.navigateTo({ url: '/pagesOrder/create/create' })
+}
+
+// 猜你喜欢
+const { guessRef, onScrolltolower } = useGuessList()
+</script>
+
+<template>
+  <scroll-view enable-back-to-top scroll-y class="scroll-view" @scrolltolower="onScrolltolower">
+    <!-- 已登录: 显示购物车 -->
+    <template v-if="memberStore.profile">
+      <!-- 购物车列表 -->
+      <view class="cart-list" v-if="showCartList">
+        <!-- 优惠提示 -->
+        <view class="tips">
+          <text class="label">满减</text>
+          <text class="desc">满1件, 即可享受9折优惠</text>
+        </view>
+        <!-- 滑动操作分区 -->
+        <uni-swipe-action>
+          <!-- 滑动操作项 -->
+          <uni-swipe-action-item v-for="item in cartList" :key="item.skuId" class="cart-swipe">
+            <!-- 商品信息 -->
+            <view class="goods">
+              <!-- 选中状态 -->
+              <text
+                @tap="onChangeSelected(item)"
+                class="checkbox"
+                :class="{ checked: item.selected }"
+              ></text>
+              <navigator
+                :url="`/pages/goods/goods?id=${item.id}`"
+                hover-class="none"
+                class="navigator"
+              >
+                <image mode="aspectFill" class="picture" :src="item.picture"></image>
+                <view class="meta">
+                  <view class="name ellipsis">{{ item.name }}</view>
+                  <view class="attrsText ellipsis">{{ item.attrsText }}</view>
+                  <view class="price">{{ item.nowPrice }}</view>
+                </view>
+              </navigator>
+              <!-- 商品数量 -->
+              <view class="count">
+                <vk-data-input-number-box
+                  v-model="item.count"
+                  :min="1"
+                  :max="item.stock"
+                  :index="item.skuId"
+                  @change="onChangeCount"
+                />
+              </view>
+            </view>
+            <!-- 右侧删除按钮 -->
+            <template #right>
+              <view class="cart-swipe-right">
+                <button @tap="onDeleteCart(item.skuId)" class="button delete-button">删除</button>
+              </view>
+            </template>
+          </uni-swipe-action-item>
+        </uni-swipe-action>
+      </view>
+      <!-- 购物车空状态 -->
+      <view class="cart-blank" v-else>
+        <image src="/static/images/blank_cart.png" class="image" />
+        <text class="text">购物车还是空的,快来挑选好货吧</text>
+        <navigator url="/pages/index/index" hover-class="none">
+          <button class="button">去首页看看</button>
+        </navigator>
+      </view>
+      <!-- 吸底工具栏 -->
+      <view
+        v-if="showCartList"
+        class="toolbar"
+        :style="{ paddingBottom: safeAreaInsetBottom ? safeAreaInsets?.bottom + 'px' : 0 }"
+      >
+        <text @tap="onChangeSelectedAll" class="all" :class="{ checked: isSelectedAll }">全选</text>
+        <text class="text">合计:</text>
+        <text class="amount">{{ selectedCartListMoney }}</text>
+        <view class="button-grounp">
+          <view
+            @tap="gotoPayment"
+            class="button payment-button"
+            :class="{ disabled: selectedCartListCount === 0 }"
+          >
+            去结算({{ selectedCartListCount }})
+          </view>
+        </view>
+      </view>
+    </template>
+    <!-- 未登录: 提示登录 -->
+    <view class="login-blank" v-else>
+      <text class="text">登录后可查看购物车中的商品</text>
+      <navigator url="/pages/login/login" hover-class="none">
+        <button class="button">去登录</button>
+      </navigator>
+    </view>
+    <!-- 猜你喜欢 -->
+    <XtxGuess ref="guessRef" />
+    <!-- 底部占位空盒子 -->
+    <view class="toolbar-height"></view>
+  </scroll-view>
+</template>
+
+<style lang="scss">
+// 根元素
+:host {
+  height: 100vh;
+  display: flex;
+  flex-direction: column;
+  overflow: hidden;
+  background-color: #f7f7f8;
+}
+
+// 滚动容器
+.scroll-view {
+  flex: 1;
+  background-color: #f7f7f8;
+}
+
+// 购物车列表
+.cart-list {
+  padding: 0 20rpx;
+
+  // 优惠提示
+  .tips {
+    display: flex;
+    align-items: center;
+    line-height: 1;
+    margin: 30rpx 10rpx;
+    font-size: 26rpx;
+    color: #666;
+
+    .label {
+      color: #fff;
+      padding: 7rpx 15rpx 5rpx;
+      border-radius: 4rpx;
+      font-size: 24rpx;
+      background-color: #27ba9b;
+      margin-right: 10rpx;
+    }
+  }
+
+  // 购物车商品
+  .goods {
+    display: flex;
+    padding: 20rpx 20rpx 20rpx 80rpx;
+    border-radius: 10rpx;
+    background-color: #fff;
+    position: relative;
+
+    .navigator {
+      display: flex;
+    }
+
+    .checkbox {
+      position: absolute;
+      top: 0;
+      left: 0;
+
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      width: 80rpx;
+      height: 100%;
+
+      &::before {
+        content: '\e6cd';
+        font-family: 'erabbit' !important;
+        font-size: 40rpx;
+        color: #444;
+      }
+
+      &.checked::before {
+        content: '\e6cc';
+        color: #27ba9b;
+      }
+    }
+
+    .picture {
+      width: 170rpx;
+      height: 170rpx;
+    }
+
+    .meta {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      justify-content: space-between;
+      margin-left: 20rpx;
+    }
+
+    .name {
+      height: 72rpx;
+      font-size: 26rpx;
+      color: #444;
+    }
+
+    .attrsText {
+      line-height: 1.8;
+      padding: 0 15rpx;
+      font-size: 24rpx;
+      align-self: flex-start;
+      border-radius: 4rpx;
+      color: #888;
+      background-color: #f7f7f8;
+    }
+
+    .price {
+      line-height: 1;
+      font-size: 26rpx;
+      color: #444;
+      margin-bottom: 2rpx;
+      color: #cf4444;
+
+      &::before {
+        content: '¥';
+        font-size: 80%;
+      }
+    }
+
+    // 商品数量
+    .count {
+      position: absolute;
+      bottom: 20rpx;
+      right: 5rpx;
+
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      width: 220rpx;
+      height: 48rpx;
+
+      .text {
+        height: 100%;
+        padding: 0 20rpx;
+        font-size: 32rpx;
+        color: #444;
+      }
+
+      .input {
+        height: 100%;
+        text-align: center;
+        border-radius: 4rpx;
+        font-size: 24rpx;
+        color: #444;
+        background-color: #f6f6f6;
+      }
+    }
+  }
+
+  .cart-swipe {
+    display: block;
+    margin: 20rpx 0;
+  }
+
+  .cart-swipe-right {
+    display: flex;
+    height: 100%;
+
+    .button {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      width: 50px;
+      padding: 6px;
+      line-height: 1.5;
+      color: #fff;
+      font-size: 26rpx;
+      border-radius: 0;
+    }
+
+    .delete-button {
+      background-color: #cf4444;
+    }
+  }
+}
+
+// 空状态
+.cart-blank,
+.login-blank {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  flex-direction: column;
+  height: 60vh;
+  .image {
+    width: 400rpx;
+    height: 281rpx;
+  }
+  .text {
+    color: #444;
+    font-size: 26rpx;
+    margin: 20rpx 0;
+  }
+  .button {
+    width: 240rpx !important;
+    height: 60rpx;
+    line-height: 60rpx;
+    margin-top: 20rpx;
+    font-size: 26rpx;
+    border-radius: 60rpx;
+    color: #fff;
+    background-color: #27ba9b;
+  }
+}
+
+// 吸底工具栏
+.toolbar {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: calc(var(--window-bottom));
+  z-index: 1;
+
+  height: 100rpx;
+  padding: 0 20rpx;
+  display: flex;
+  align-items: center;
+  border-top: 1rpx solid #ededed;
+  border-bottom: 1rpx solid #ededed;
+  background-color: #fff;
+  box-sizing: content-box;
+
+  .all {
+    margin-left: 25rpx;
+    font-size: 14px;
+    color: #444;
+    display: flex;
+    align-items: center;
+  }
+
+  .all::before {
+    font-family: 'erabbit' !important;
+    content: '\e6cd';
+    font-size: 40rpx;
+    margin-right: 8rpx;
+  }
+
+  .checked::before {
+    content: '\e6cc';
+    color: #27ba9b;
+  }
+
+  .text {
+    margin-right: 8rpx;
+    margin-left: 32rpx;
+    color: #444;
+    font-size: 14px;
+  }
+
+  .amount {
+    font-size: 20px;
+    color: #cf4444;
+
+    .decimal {
+      font-size: 12px;
+    }
+
+    &::before {
+      content: '¥';
+      font-size: 12px;
+    }
+  }
+
+  .button-grounp {
+    margin-left: auto;
+    display: flex;
+    justify-content: space-between;
+    text-align: center;
+    line-height: 72rpx;
+    font-size: 13px;
+    color: #fff;
+
+    .button {
+      width: 240rpx;
+      margin: 0 10rpx;
+      border-radius: 72rpx;
+    }
+
+    .payment-button {
+      background-color: #27ba9b;
+
+      &.disabled {
+        opacity: 0.6;
+      }
+    }
+  }
+}
+// 底部占位空盒子
+.toolbar-height {
+  height: 100rpx;
+}
+</style>

+ 98 - 0
src/pages/category/category.vue

@@ -0,0 +1,98 @@
+<script setup lang="ts">
+import { getCategoryTopAPI } from '@/services/category'
+import { getHomeBannerAPI } from '@/services/home'
+import type { CategoryTopItem } from '@/types/category'
+import type { BannerItem } from '@/types/home'
+import { onLoad } from '@dcloudio/uni-app'
+import { computed, ref } from 'vue'
+import PageSkeleton from './components/PageSkeleton.vue'
+
+// 获取轮播图数据
+const bannerList = ref<BannerItem[]>([])
+const getBannerData = async () => {
+  const res = await getHomeBannerAPI(2)
+  bannerList.value = res.result
+}
+
+// 获取分类列表数据
+const categoryList = ref<CategoryTopItem[]>([])
+const activeIndex = ref(0)
+const getCategoryTopData = async () => {
+  const res = await getCategoryTopAPI()
+  categoryList.value = res.result
+}
+
+// 是否数据加载完毕
+const isFinish = ref(false)
+// 页面加载
+onLoad(async () => {
+  await Promise.all([getBannerData(), getCategoryTopData()])
+  isFinish.value = true
+})
+
+// 提取当前二级分类数据
+const subCategoryList = computed(() => {
+  return categoryList.value[activeIndex.value]?.children || []
+})
+</script>
+
+<template>
+  <view class="viewport" v-if="isFinish">
+    <!-- 搜索框 -->
+    <view class="search">
+      <view class="input">
+        <text class="icon-search">女靴</text>
+      </view>
+    </view>
+    <!-- 分类 -->
+    <view class="categories">
+      <!-- 左侧:一级分类 -->
+      <scroll-view class="primary" scroll-y>
+        <view
+          v-for="(item, index) in categoryList"
+          :key="item.id"
+          class="item"
+          :class="{ active: index === activeIndex }"
+          @tap="activeIndex = index"
+        >
+          <text class="name">
+            {{ item.name }}
+          </text>
+        </view>
+      </scroll-view>
+      <!-- 右侧:二级分类 -->
+      <scroll-view enable-back-to-top class="secondary" scroll-y>
+        <!-- 焦点图 -->
+        <XtxSwiper class="banner" :list="bannerList" />
+        <!-- 内容区域 -->
+        <view class="panel" v-for="item in subCategoryList" :key="item.id">
+          <view class="title">
+            <text class="name">{{ item.name }}</text>
+            <navigator class="more" hover-class="none">全部</navigator>
+          </view>
+          <view class="section">
+            <navigator
+              v-for="goods in item.goods"
+              :key="goods.id"
+              class="goods"
+              hover-class="none"
+              :url="`/pages/goods/goods?id=${goods.id}`"
+            >
+              <image class="image" :src="goods.picture"></image>
+              <view class="name ellipsis">{{ goods.name }}</view>
+              <view class="price">
+                <text class="symbol">¥</text>
+                <text class="number">{{ goods.price }}</text>
+              </view>
+            </navigator>
+          </view>
+        </view>
+      </scroll-view>
+    </view>
+  </view>
+  <PageSkeleton v-else />
+</template>
+
+<style lang="scss">
+@import './styles/category.scss';
+</style>

+ 510 - 0
src/pages/category/components/PageSkeleton.vue

@@ -0,0 +1,510 @@
+<template name="skeleton">
+  <view class="sk-container">
+    <view class="viewport">
+      <view class="search">
+        <view class="input">
+          <text
+            class="icon-search sk-transparent sk-text-14-2857-225 sk-text sk-pseudo sk-pseudo-circle"
+            >女靴</text
+          >
+        </view>
+      </view>
+      <view class="categories">
+        <scroll-view :scroll-y="true" class="primary">
+          <view class="item active sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-218 sk-text">居家</text>
+          </view>
+          <view class="item sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-495 sk-text">美食</text>
+          </view>
+          <view class="item sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-628 sk-text">服饰</text>
+          </view>
+          <view class="item sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-163 sk-text">母婴</text>
+          </view>
+          <view class="item sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-690 sk-text">个护</text>
+          </view>
+          <view class="item sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-302 sk-text">严选</text>
+          </view>
+          <view class="item sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-730 sk-text">数码</text>
+          </view>
+          <view class="item sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-584 sk-text">运动</text>
+          </view>
+          <view class="item sk-pseudo sk-pseudo-circle">
+            <text class="name sk-transparent sk-text-14-2857-895 sk-text">杂项</text>
+          </view>
+        </scroll-view>
+        <scroll-view :scroll-y="true" class="secondary">
+          <view is="components/XtxSwiper" class="banner">
+            <view class="carousel XtxSwiper--carousel">
+              <view class="indicator XtxSwiper--indicator">
+                <text class="dot XtxSwiper--dot active XtxSwiper--active"></text>
+                <text class="dot XtxSwiper--dot"></text>
+                <text class="dot XtxSwiper--dot"></text>
+                <text class="dot XtxSwiper--dot"></text>
+                <text class="dot XtxSwiper--dot"></text>
+              </view>
+            </view>
+          </view>
+          <view class="panel">
+            <view class="title">
+              <text class="name sk-transparent sk-text-26-6667-885 sk-text">居家生活用品</text>
+              <navigator
+                class="more sk-transparent sk-text-30-0000-892 sk-text sk-pseudo sk-pseudo-circle"
+                hover-class="none"
+                >全部</navigator
+              >
+            </view>
+            <view class="section">
+              <navigator class="goods" hover-class="none">
+                <image class="image sk-image"></image>
+                <view class="name ellipsis sk-transparent sk-text-14-2857-648 sk-text"
+                  >极光限定 珠光蓝珐琅锅</view
+                >
+                <view class="price">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="number sk-transparent sk-text-14-2857-708 sk-text">199.00</text>
+                </view>
+              </navigator>
+              <navigator class="goods" hover-class="none">
+                <image class="image sk-image"></image>
+                <view class="name ellipsis sk-transparent sk-text-14-2857-832 sk-text"
+                  >钻石陶瓷涂层多用锅18cm 小奶锅</view
+                >
+                <view class="price">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="number sk-transparent sk-text-14-2857-349 sk-text">149.00</text>
+                </view>
+              </navigator>
+            </view>
+          </view>
+          <view class="panel">
+            <view class="title">
+              <text class="name sk-transparent sk-text-26-6667-486 sk-text">收纳</text>
+              <navigator
+                class="more sk-transparent sk-text-30-0000-520 sk-text sk-pseudo sk-pseudo-circle"
+                hover-class="none"
+                >全部</navigator
+              >
+            </view>
+            <view class="section">
+              <navigator class="goods" hover-class="none">
+                <image class="image sk-image"></image>
+                <view class="name ellipsis sk-transparent sk-text-14-2857-582 sk-text"
+                  >开发员自留款,带滚轮双层脏衣篓</view
+                >
+                <view class="price">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="number sk-transparent sk-text-14-2857-938 sk-text">125.00</text>
+                </view>
+              </navigator>
+              <navigator class="goods" hover-class="none">
+                <image class="image sk-image"></image>
+                <view class="name ellipsis sk-transparent sk-text-14-2857-108 sk-text"
+                  >换季好帮手,大容量防尘衣物收纳袋</view
+                >
+                <view class="price">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="number sk-transparent sk-text-14-2857-564 sk-text">69.00</text>
+                </view>
+              </navigator>
+              <navigator class="goods" hover-class="none">
+                <image class="image sk-image"></image>
+                <view class="name ellipsis sk-transparent sk-text-14-2857-507 sk-text"
+                  >可水洗的布艺收纳盒</view
+                >
+                <view class="price">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="number sk-transparent sk-text-14-2857-503 sk-text">29.90</text>
+                </view>
+              </navigator>
+              <navigator class="goods" hover-class="none">
+                <image class="image sk-image"></image>
+                <view class="name ellipsis sk-transparent sk-text-14-2857-75 sk-text"
+                  >爆款明星好物,抽屉式透明储物柜</view
+                >
+                <view class="price">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="number sk-transparent sk-text-14-2857-965 sk-text">129.00</text>
+                </view>
+              </navigator>
+              <navigator class="goods" hover-class="none">
+                <image class="image sk-image"></image>
+                <view class="name ellipsis sk-transparent sk-text-14-2857-71 sk-text"
+                  >给衣柜减减肥,真空防潮压缩袋</view
+                >
+                <view class="price">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="number sk-transparent sk-text-14-2857-530 sk-text">79.00</text>
+                </view>
+              </navigator>
+              <navigator class="goods" hover-class="none">
+                <image class="image sk-image"></image>
+                <view class="name ellipsis sk-transparent sk-text-14-2857-151 sk-text"
+                  >拉开抽屉不凌乱,磨砂抽屉整理盒套装</view
+                >
+                <view class="price">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="number sk-transparent sk-text-14-2857-641 sk-text">49.00</text>
+                </view>
+              </navigator>
+            </view>
+          </view>
+        </scroll-view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+/* #ifdef H5 || APP-PLUS */
+/* 修复 H5 端骨架屏样式异常 */
+@import '@/components/styles/XtxSwiper.scss';
+@import '../styles/category.scss';
+/* #endif */
+.sk-transparent {
+  color: transparent !important;
+}
+.sk-text-14-2857-225 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 39.2rpx;
+  position: relative !important;
+}
+.sk-text {
+  background-origin: content-box !important;
+  background-clip: content-box !important;
+  background-color: transparent !important;
+  color: transparent !important;
+  background-repeat: repeat-y !important;
+}
+.sk-text-14-2857-218 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-495 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-628 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-163 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-690 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-302 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-730 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-584 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-895 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-26-6667-885 {
+  background-image: linear-gradient(
+    transparent 26.6667%,
+    #eeeeee 0%,
+    #eeeeee 73.3333%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 60rpx;
+  position: relative !important;
+}
+.sk-text-30-0000-892 {
+  background-image: linear-gradient(
+    transparent 30%,
+    #eeeeee 0%,
+    #eeeeee 70%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 60rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-648 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30.8rpx;
+  position: relative !important;
+}
+.sk-opacity {
+  opacity: 0 !important;
+}
+.sk-text-14-2857-708 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-832 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-349 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-26-6667-486 {
+  background-image: linear-gradient(
+    transparent 26.6667%,
+    #eeeeee 0%,
+    #eeeeee 73.3333%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 60rpx;
+  position: relative !important;
+}
+.sk-text-30-0000-520 {
+  background-image: linear-gradient(
+    transparent 30%,
+    #eeeeee 0%,
+    #eeeeee 70%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 60rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-582 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-938 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-108 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-564 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-507 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-503 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-75 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-965 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-71 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-530 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-151 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-641 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-image {
+  background: #efefef !important;
+}
+.sk-pseudo::before,
+.sk-pseudo::after {
+  background: #efefef !important;
+  background-image: none !important;
+  color: transparent !important;
+  border-color: transparent !important;
+}
+.sk-pseudo-rect::before,
+.sk-pseudo-rect::after {
+  border-radius: 0 !important;
+}
+.sk-pseudo-circle::before,
+.sk-pseudo-circle::after {
+  border-radius: 50% !important;
+}
+.sk-container {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  background-color: transparent;
+}
+</style>

+ 138 - 0
src/pages/category/styles/category.scss

@@ -0,0 +1,138 @@
+page {
+  height: 100%;
+  overflow: hidden;
+}
+.viewport {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+.search {
+  padding: 20rpx 30rpx;
+  background-color: #fff;
+  .input {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    height: 64rpx;
+    padding-left: 26rpx;
+    color: #8b8b8b;
+    font-size: 28rpx;
+    border-radius: 32rpx;
+    background-color: #f3f4f4;
+  }
+}
+.icon-search {
+  &::before {
+    margin-right: 10rpx;
+  }
+}
+/* 分类 */
+.categories {
+  flex: 1;
+  min-height: 400rpx;
+  display: flex;
+}
+/* 一级分类 */
+.primary {
+  overflow: hidden;
+  width: 180rpx;
+  flex: none;
+  background-color: #f6f6f6;
+  .item {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    height: 96rpx;
+    font-size: 26rpx;
+    color: #595c63;
+    position: relative;
+    &::after {
+      content: '';
+      position: absolute;
+      left: 42rpx;
+      bottom: 0;
+      width: 96rpx;
+      border-top: 1rpx solid #e3e4e7;
+    }
+  }
+  .active {
+    background-color: #fff;
+    &::before {
+      content: '';
+      position: absolute;
+      left: 0;
+      top: 0;
+      width: 8rpx;
+      height: 100%;
+      background-color: #27ba9b;
+    }
+  }
+}
+.primary .item:last-child::after,
+.primary .active::after {
+  display: none;
+}
+/* 二级分类 */
+.secondary {
+  background-color: #fff;
+  .carousel {
+    height: 200rpx;
+    margin: 0 30rpx 20rpx;
+    border-radius: 4rpx;
+    overflow: hidden;
+  }
+  .panel {
+    margin: 0 30rpx 0rpx;
+  }
+  .title {
+    height: 60rpx;
+    line-height: 60rpx;
+    color: #333;
+    font-size: 28rpx;
+    border-bottom: 1rpx solid #f7f7f8;
+    .more {
+      float: right;
+      padding-left: 20rpx;
+      font-size: 24rpx;
+      color: #999;
+    }
+  }
+  .more {
+    &::after {
+      font-family: 'erabbit' !important;
+      content: '\e6c2';
+    }
+  }
+  .section {
+    width: 100%;
+    display: flex;
+    flex-wrap: wrap;
+    padding: 20rpx 0;
+    .goods {
+      width: 150rpx;
+      margin: 0rpx 20rpx 20rpx 0;
+      &:nth-child(3n) {
+        margin-right: 0;
+      }
+      .image {
+        width: 150rpx;
+        height: 150rpx;
+      }
+      .name {
+        padding: 5rpx;
+        font-size: 22rpx;
+        color: #333;
+      }
+      .price {
+        padding: 5rpx;
+        font-size: 18rpx;
+        color: #cf4444;
+      }
+      .number {
+        font-size: 24rpx;
+        margin-left: 2rpx;
+      }
+    }
+  }
+}

+ 124 - 0
src/pages/goods/components/AddressPanel.vue

@@ -0,0 +1,124 @@
+<script setup lang="ts">
+//
+</script>
+
+<template>
+  <view class="address-panel">
+    <!-- 关闭按钮 -->
+    <text class="close icon-close"></text>
+    <!-- 标题 -->
+    <view class="title">配送至</view>
+    <!-- 内容 -->
+    <view class="content">
+      <view class="item">
+        <view class="user">李明 13824686868</view>
+        <view class="address">北京市顺义区后沙峪地区安平北街6号院</view>
+        <text class="icon icon-checked"></text>
+      </view>
+      <view class="item">
+        <view class="user">王东 13824686868</view>
+        <view class="address">北京市顺义区后沙峪地区安平北街6号院</view>
+        <text class="icon icon-ring"></text>
+      </view>
+      <view class="item">
+        <view class="user">张三 13824686868</view>
+        <view class="address">北京市朝阳区孙河安平北街6号院</view>
+        <text class="icon icon-ring"></text>
+      </view>
+    </view>
+    <view class="footer">
+      <view class="button primary"> 新建地址 </view>
+      <view v-if="false" class="button primary">确定</view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+.address-panel {
+  padding: 0 30rpx;
+  border-radius: 10rpx 10rpx 0 0;
+  position: relative;
+  background-color: #fff;
+}
+
+.title {
+  line-height: 1;
+  padding: 40rpx 0;
+  text-align: center;
+  font-size: 32rpx;
+  font-weight: normal;
+  border-bottom: 1rpx solid #ddd;
+  color: #444;
+}
+
+.close {
+  position: absolute;
+  right: 24rpx;
+  top: 24rpx;
+}
+
+.content {
+  min-height: 300rpx;
+  max-height: 540rpx;
+  overflow: auto;
+  padding: 20rpx;
+  .item {
+    padding: 30rpx 50rpx 30rpx 60rpx;
+    background-size: 40rpx;
+    background-repeat: no-repeat;
+    background-position: 0 center;
+    background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/locate.png);
+    position: relative;
+  }
+  .icon {
+    color: #999;
+    font-size: 40rpx;
+    transform: translateY(-50%);
+    position: absolute;
+    top: 50%;
+    right: 0;
+  }
+  .icon-checked {
+    color: #27ba9b;
+  }
+  .icon-ring {
+    color: #444;
+  }
+  .user {
+    font-size: 28rpx;
+    color: #444;
+    font-weight: 500;
+  }
+  .address {
+    font-size: 26rpx;
+    color: #666;
+  }
+}
+
+.footer {
+  display: flex;
+  justify-content: space-between;
+  padding: 20rpx 0 40rpx;
+  font-size: 28rpx;
+  color: #444;
+
+  .button {
+    flex: 1;
+    height: 72rpx;
+    text-align: center;
+    line-height: 72rpx;
+    margin: 0 20rpx;
+    color: #fff;
+    border-radius: 72rpx;
+  }
+
+  .primary {
+    color: #fff;
+    background-color: #27ba9b;
+  }
+
+  .secondary {
+    background-color: #ffa868;
+  }
+}
+</style>

+ 96 - 0
src/pages/goods/components/ServicePanel.vue

@@ -0,0 +1,96 @@
+<script setup lang="ts">
+// 子调父
+const emit = defineEmits<{
+  (event: 'close'): void
+}>()
+</script>
+
+<template>
+  <view class="service-panel">
+    <!-- 关闭按钮 -->
+    <text class="close icon-close" @tap="emit('close')"></text>
+    <!-- 标题 -->
+    <view class="title">服务说明</view>
+    <!-- 内容 -->
+    <view class="content">
+      <view class="item">
+        <view class="dt">无忧退货</view>
+        <view class="dd">
+          自收到商品之日起30天内,可在线申请无忧退货服务(食品等特殊商品除外)
+        </view>
+      </view>
+      <view class="item">
+        <view class="dt">快速退款</view>
+        <view class="dd">
+          收到退货包裹并确认无误后,将在48小时内办理退款,
+          退款将原路返回,不同银行处理时间不同,预计1-5个工作日到账
+        </view>
+      </view>
+      <view class="item">
+        <view class="dt">满88元免邮费</view>
+        <view class="dd">
+          单笔订单金额(不含运费)满88元可免邮费,不满88元, 单笔订单收取10元邮费
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+.service-panel {
+  padding: 0 30rpx;
+  border-radius: 10rpx 10rpx 0 0;
+  position: relative;
+  background-color: #fff;
+}
+
+.title {
+  line-height: 1;
+  padding: 40rpx 0;
+  text-align: center;
+  font-size: 32rpx;
+  font-weight: normal;
+  border-bottom: 1rpx solid #ddd;
+  color: #444;
+}
+
+.close {
+  position: absolute;
+  right: 24rpx;
+  top: 24rpx;
+}
+
+.content {
+  padding: 20rpx 20rpx 100rpx 20rpx;
+
+  .item {
+    margin-top: 20rpx;
+  }
+
+  .dt {
+    margin-bottom: 10rpx;
+    font-size: 28rpx;
+    color: #333;
+    font-weight: 500;
+    position: relative;
+
+    &::before {
+      content: '';
+      width: 10rpx;
+      height: 10rpx;
+      border-radius: 50%;
+      background-color: #eaeaea;
+      transform: translateY(-50%);
+      position: absolute;
+      top: 50%;
+      left: -20rpx;
+    }
+  }
+
+  .dd {
+    line-height: 1.6;
+    font-size: 26rpx;
+    color: #999;
+  }
+}
+</style>

+ 533 - 0
src/pages/goods/goods.vue

@@ -0,0 +1,533 @@
+<script setup lang="ts">
+import type {
+  SkuPopupEvent,
+  SkuPopupInstance,
+  SkuPopupLocaldata,
+} from '@/components/vk-data-goods-sku-popup/vk-data-goods-sku-popup'
+import { postMemberCartAPI } from '@/services/cart'
+import { getGoodsByIdAPI } from '@/services/goods'
+import type { GoodsResult } from '@/types/goods'
+import { onLoad } from '@dcloudio/uni-app'
+import { computed, ref } from 'vue'
+import AddressPanel from './components/AddressPanel.vue'
+import ServicePanel from './components/ServicePanel.vue'
+
+// 获取屏幕边界到安全区域距离
+const { safeAreaInsets } = uni.getSystemInfoSync()
+
+// 接收页面参数
+const query = defineProps<{
+  id: string
+}>()
+
+// 获取商品详情信息
+const goods = ref<GoodsResult>()
+const getGoodsByIdData = async () => {
+  const res = await getGoodsByIdAPI(query.id)
+  goods.value = res.result
+  // SKU组件所需格式
+  localdata.value = {
+    _id: res.result.id,
+    name: res.result.name,
+    goods_thumb: res.result.mainPictures[0],
+    spec_list: res.result.specs.map((v) => {
+      return {
+        name: v.name,
+        list: v.values,
+      }
+    }),
+    sku_list: res.result.skus.map((v) => {
+      return {
+        _id: v.id,
+        goods_id: res.result.id,
+        goods_name: res.result.name,
+        image: v.picture,
+        price: v.price * 100, // 注意:需要乘以 100
+        stock: v.inventory,
+        sku_name_arr: v.specs.map((vv) => vv.valueName),
+      }
+    }),
+  }
+}
+
+// 页面加载
+onLoad(() => {
+  getGoodsByIdData()
+})
+
+// 轮播图变化时
+const currentIndex = ref(0)
+const onChange: UniHelper.SwiperOnChange = (ev) => {
+  currentIndex.value = ev.detail.current
+}
+
+// 点击图片时
+const onTapImage = (url: string) => {
+  // 大图预览
+  uni.previewImage({
+    current: url,
+    urls: goods.value!.mainPictures,
+  })
+}
+
+// uni-ui 弹出层组件 ref
+const popup = ref<{
+  open: (type?: UniHelper.UniPopupType) => void
+  close: () => void
+}>()
+
+// 弹出层条件渲染
+const popupName = ref<'address' | 'service'>()
+const openPopup = (name: typeof popupName.value) => {
+  // 修改弹出层名称
+  popupName.value = name
+  popup.value?.open()
+}
+// 是否显示SKU组件
+const isShowSku = ref(false)
+// 商品信息
+const localdata = ref({} as SkuPopupLocaldata)
+// 按钮模式
+enum SkuMode {
+  Both = 1,
+  Cart = 2,
+  Buy = 3,
+}
+const mode = ref<SkuMode>(SkuMode.Cart)
+// 打开SKU弹窗修改按钮模式
+const openSkuPopup = (val: SkuMode) => {
+  // 显示SKU弹窗
+  isShowSku.value = true
+  // 修改按钮模式
+  mode.value = val
+}
+// SKU组件实例
+const skuPopupRef = ref<SkuPopupInstance>()
+// 计算被选中的值
+const selectArrText = computed(() => {
+  return skuPopupRef.value?.selectArr?.join(' ').trim() || '请选择商品规格'
+})
+// 加入购物车事件
+const onAddCart = async (ev: SkuPopupEvent) => {
+  await postMemberCartAPI({ skuId: ev._id, count: ev.buy_num })
+  uni.showToast({ title: '添加成功' })
+  isShowSku.value = false
+}
+// 立即购买
+const onBuyNow = (ev: SkuPopupEvent) => {
+  uni.navigateTo({ url: `/pagesOrder/create/create?skuId=${ev._id}&count=${ev.buy_num}` })
+}
+</script>
+
+<template>
+  <!-- SKU弹窗组件 -->
+  <vk-data-goods-sku-popup
+    v-model="isShowSku"
+    :localdata="localdata"
+    :mode="mode"
+    add-cart-background-color="#FFA868"
+    buy-now-background-color="#27BA9B"
+    ref="skuPopupRef"
+    :actived-style="{
+      color: '#27BA9B',
+      borderColor: '#27BA9B',
+      backgroundColor: '#E9F8F5',
+    }"
+    @add-cart="onAddCart"
+    @buy-now="onBuyNow"
+  />
+  <scroll-view enable-back-to-top scroll-y class="viewport">
+    <!-- 基本信息 -->
+    <view class="goods">
+      <!-- 商品主图 -->
+      <view class="preview">
+        <swiper @change="onChange" circular>
+          <swiper-item v-for="item in goods?.mainPictures" :key="item">
+            <image class="image" @tap="onTapImage(item)" mode="aspectFill" :src="item" />
+          </swiper-item>
+        </swiper>
+        <view class="indicator">
+          <text class="current">{{ currentIndex + 1 }}</text>
+          <text class="split">/</text>
+          <text class="total">{{ goods?.mainPictures.length }}</text>
+        </view>
+      </view>
+
+      <!-- 商品简介 -->
+      <view class="meta">
+        <view class="price">
+          <text class="symbol">¥</text>
+          <text class="number">{{ goods?.price }}</text>
+        </view>
+        <view class="name ellipsis">{{ goods?.name }}</view>
+        <view class="desc"> {{ goods?.desc }} </view>
+      </view>
+
+      <!-- 操作面板 -->
+      <view class="action">
+        <view @tap="openSkuPopup(SkuMode.Both)" class="item arrow">
+          <text class="label">选择</text>
+          <text class="text ellipsis"> {{ selectArrText }} </text>
+        </view>
+        <view @tap="openPopup('address')" class="item arrow">
+          <text class="label">送至</text>
+          <text class="text ellipsis"> 请选择收获地址 </text>
+        </view>
+        <view @tap="openPopup('service')" class="item arrow">
+          <text class="label">服务</text>
+          <text class="text ellipsis"> 无忧退 快速退款 免费包邮 </text>
+        </view>
+      </view>
+    </view>
+
+    <!-- 商品详情 -->
+    <view class="detail panel">
+      <view class="title">
+        <text>详情</text>
+      </view>
+      <view class="content">
+        <view class="properties">
+          <!-- 属性详情 -->
+          <view class="item" v-for="item in goods?.details.properties" :key="item.name">
+            <text class="label">{{ item.name }}</text>
+            <text class="value">{{ item.value }}</text>
+          </view>
+        </view>
+        <!-- 图片详情 -->
+        <image
+          class="image"
+          v-for="item in goods?.details.pictures"
+          :key="item"
+          mode="widthFix"
+          :src="item"
+        ></image>
+      </view>
+    </view>
+
+    <!-- 同类推荐 -->
+    <view class="similar panel">
+      <view class="title">
+        <text>同类推荐</text>
+      </view>
+      <view class="content">
+        <navigator
+          v-for="item in goods?.similarProducts"
+          :key="item.id"
+          class="goods"
+          hover-class="none"
+          :url="`/pages/goods/goods?id=${item.id}`"
+        >
+          <image class="image" mode="aspectFill" :src="item.picture"></image>
+          <view class="name ellipsis">{{ item.name }}</view>
+          <view class="price">
+            <text class="symbol">¥</text>
+            <text class="number">{{ item.price }}</text>
+          </view>
+        </navigator>
+      </view>
+    </view>
+  </scroll-view>
+
+  <!-- 用户操作 -->
+  <view v-if="goods" class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
+    <view class="icons">
+      <button class="icons-button"><text class="icon-heart"></text>收藏</button>
+      <!-- #ifdef MP-WEIXIN -->
+      <button class="icons-button" open-type="contact">
+        <text class="icon-handset"></text>客服
+      </button>
+      <!-- #endif -->
+      <navigator class="icons-button" url="/pages/cart/cart2" open-type="navigate">
+        <text class="icon-cart"></text>购物车
+      </navigator>
+    </view>
+    <view class="buttons">
+      <view @tap="openSkuPopup(SkuMode.Cart)" class="addcart"> 加入购物车 </view>
+      <view @tap="openSkuPopup(SkuMode.Buy)" class="payment"> 立即购买 </view>
+    </view>
+  </view>
+
+  <!-- uni-ui 弹出层 -->
+  <uni-popup ref="popup" type="bottom" background-color="#fff">
+    <AddressPanel v-if="popupName === 'address'" @close="popup?.close()" />
+    <ServicePanel v-if="popupName === 'service'" @close="popup?.close()" />
+  </uni-popup>
+</template>
+
+<style lang="scss">
+page {
+  height: 100%;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+}
+
+.viewport {
+  background-color: #f4f4f4;
+}
+
+.panel {
+  margin-top: 20rpx;
+  background-color: #fff;
+  .title {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    height: 90rpx;
+    line-height: 1;
+    padding: 30rpx 60rpx 30rpx 6rpx;
+    position: relative;
+    text {
+      padding-left: 10rpx;
+      font-size: 28rpx;
+      color: #333;
+      font-weight: 600;
+      border-left: 4rpx solid #27ba9b;
+    }
+    navigator {
+      font-size: 24rpx;
+      color: #666;
+    }
+  }
+}
+
+.arrow {
+  &::after {
+    position: absolute;
+    top: 50%;
+    right: 30rpx;
+    content: '\e6c2';
+    color: #ccc;
+    font-family: 'erabbit' !important;
+    font-size: 32rpx;
+    transform: translateY(-50%);
+  }
+}
+
+/* 商品信息 */
+.goods {
+  background-color: #fff;
+  .preview {
+    height: 750rpx;
+    position: relative;
+    .image {
+      width: 750rpx;
+      height: 750rpx;
+    }
+    .indicator {
+      height: 40rpx;
+      padding: 0 24rpx;
+      line-height: 40rpx;
+      border-radius: 30rpx;
+      color: #fff;
+      font-family: Arial, Helvetica, sans-serif;
+      background-color: rgba(0, 0, 0, 0.3);
+      position: absolute;
+      bottom: 30rpx;
+      right: 30rpx;
+      .current {
+        font-size: 26rpx;
+      }
+      .split {
+        font-size: 24rpx;
+        margin: 0 1rpx 0 2rpx;
+      }
+      .total {
+        font-size: 24rpx;
+      }
+    }
+  }
+  .meta {
+    position: relative;
+    border-bottom: 1rpx solid #eaeaea;
+    .price {
+      height: 130rpx;
+      padding: 25rpx 30rpx 0;
+      color: #fff;
+      font-size: 34rpx;
+      box-sizing: border-box;
+      background-color: #35c8a9;
+    }
+    .number {
+      font-size: 56rpx;
+    }
+    .brand {
+      width: 160rpx;
+      height: 80rpx;
+      overflow: hidden;
+      position: absolute;
+      top: 26rpx;
+      right: 30rpx;
+    }
+    .name {
+      max-height: 88rpx;
+      line-height: 1.4;
+      margin: 20rpx;
+      font-size: 32rpx;
+      color: #333;
+    }
+    .desc {
+      line-height: 1;
+      padding: 0 20rpx 30rpx;
+      font-size: 24rpx;
+      color: #cf4444;
+    }
+  }
+  .action {
+    padding-left: 20rpx;
+    .item {
+      height: 90rpx;
+      padding-right: 60rpx;
+      border-bottom: 1rpx solid #eaeaea;
+      font-size: 26rpx;
+      color: #333;
+      position: relative;
+      display: flex;
+      align-items: center;
+      &:last-child {
+        border-bottom: 0 none;
+      }
+    }
+    .label {
+      width: 60rpx;
+      color: #898b94;
+      margin: 0 16rpx 0 10rpx;
+    }
+    .text {
+      flex: 1;
+      -webkit-line-clamp: 1;
+    }
+  }
+}
+
+/* 商品详情 */
+.detail {
+  padding-left: 20rpx;
+  .content {
+    margin-left: -20rpx;
+    .image {
+      width: 100%;
+    }
+  }
+  .properties {
+    padding: 0 20rpx;
+    margin-bottom: 30rpx;
+    .item {
+      display: flex;
+      line-height: 2;
+      padding: 10rpx;
+      font-size: 26rpx;
+      color: #333;
+      border-bottom: 1rpx dashed #ccc;
+    }
+    .label {
+      width: 200rpx;
+    }
+    .value {
+      flex: 1;
+    }
+  }
+}
+
+/* 同类推荐 */
+.similar {
+  .content {
+    padding: 0 20rpx 20rpx;
+    background-color: #f4f4f4;
+    display: flex;
+    flex-wrap: wrap;
+    .goods {
+      width: 340rpx;
+      padding: 24rpx 20rpx 20rpx;
+      margin: 20rpx 7rpx;
+      border-radius: 10rpx;
+      background-color: #fff;
+    }
+    .image {
+      width: 300rpx;
+      height: 260rpx;
+    }
+    .name {
+      height: 80rpx;
+      margin: 10rpx 0;
+      font-size: 26rpx;
+      color: #262626;
+    }
+    .price {
+      line-height: 1;
+      font-size: 20rpx;
+      color: #cf4444;
+    }
+    .number {
+      font-size: 26rpx;
+      margin-left: 2rpx;
+    }
+  }
+  navigator {
+    &:nth-child(even) {
+      margin-right: 0;
+    }
+  }
+}
+
+/* 底部工具栏 */
+.toolbar {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: calc((var(--window-bottom)));
+  z-index: 1;
+  background-color: #fff;
+  height: 100rpx;
+  padding: 0 20rpx;
+  border-top: 1rpx solid #eaeaea;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  box-sizing: content-box;
+  .buttons {
+    display: flex;
+    & > view {
+      width: 220rpx;
+      text-align: center;
+      line-height: 72rpx;
+      font-size: 26rpx;
+      color: #fff;
+      border-radius: 72rpx;
+    }
+    .addcart {
+      background-color: #ffa868;
+    }
+    .payment {
+      background-color: #27ba9b;
+      margin-left: 20rpx;
+    }
+  }
+  .icons {
+    padding-right: 20rpx;
+    display: flex;
+    align-items: center;
+    flex: 1;
+    // 兼容 H5 端和 App 端的导航链接样式
+    .navigator-wrap,
+    .icons-button {
+      flex: 1;
+      text-align: center;
+      line-height: 1.4;
+      padding: 0;
+      margin: 0;
+      border-radius: 0;
+      font-size: 20rpx;
+      color: #333;
+      background-color: #fff;
+      &::after {
+        border: none;
+      }
+    }
+    text {
+      display: block;
+      font-size: 34rpx;
+    }
+  }
+}
+</style>

+ 221 - 0
src/pages/hot/hot.vue

@@ -0,0 +1,221 @@
+<script setup lang="ts">
+import { getHotRecommendAPI } from '@/services/hot'
+import type { SubTypeItem } from '@/types/hot'
+import { onLoad } from '@dcloudio/uni-app'
+import { ref } from 'vue'
+
+// 热门推荐页 标题和url
+const urlMap = [
+  { type: '1', title: '特惠推荐', url: '/hot/preference' },
+  { type: '2', title: '爆款推荐', url: '/hot/inVogue' },
+  { type: '3', title: '一站买全', url: '/hot/oneStop' },
+  { type: '4', title: '新鲜好物', url: '/hot/new' },
+]
+
+// uniapp 获取页面参数
+const query = defineProps<{
+  type: string
+}>()
+// console.log(query)
+const currUrlMap = urlMap.find((v) => v.type === query.type)
+// 动态设置标题
+uni.setNavigationBarTitle({ title: currUrlMap!.title })
+
+// 推荐封面图
+const bannerPicture = ref('')
+// 推荐选项
+const subTypes = ref<(SubTypeItem & { finish?: boolean })[]>([])
+// 高亮的下标
+const activeIndex = ref(0)
+// 获取热门推荐数据
+const getHotRecommendData = async () => {
+  const res = await getHotRecommendAPI(currUrlMap!.url, {
+    // 技巧:环境变量,开发环境,修改初始页面方便测试分页结束
+    page: import.meta.env.DEV ? 30 : 1,
+    pageSize: 10,
+  })
+  // console.log(res.result.title)
+  bannerPicture.value = res.result.bannerPicture
+  subTypes.value = res.result.subTypes
+}
+
+// 页面加载
+onLoad(() => {
+  getHotRecommendData()
+})
+
+// 滚动触底
+const onScrolltolower = async () => {
+  // 获取当前选项
+  const currsubTypes = subTypes.value[activeIndex.value]
+  // 分页条件
+  if (currsubTypes.goodsItems.page < currsubTypes.goodsItems.pages) {
+    // 当前页码累加
+    currsubTypes.goodsItems.page++
+  } else {
+    // 标记已结束
+    currsubTypes.finish = true
+    // 退出并轻提示
+    return uni.showToast({ icon: 'none', title: '没有更多数据了~' })
+  }
+
+  // 调用API传参
+  const res = await getHotRecommendAPI(currUrlMap!.url, {
+    subType: currsubTypes.id,
+    page: currsubTypes.goodsItems.page,
+    pageSize: currsubTypes.goodsItems.pageSize,
+  })
+  // 新的列表选项
+  const newsubTypes = res.result.subTypes[activeIndex.value]
+  // 数组追加
+  currsubTypes.goodsItems.items.push(...newsubTypes.goodsItems.items)
+}
+</script>
+
+<template>
+  <view class="viewport">
+    <!-- 推荐封面图 -->
+    <view class="cover">
+      <image class="image" mode="widthFix" :src="bannerPicture"></image>
+    </view>
+    <!-- 推荐选项 -->
+    <view class="tabs">
+      <text
+        v-for="(item, index) in subTypes"
+        :key="item.id"
+        class="text"
+        :class="{ active: index === activeIndex }"
+        @tap="activeIndex = index"
+        >{{ item.title }}</text
+      >
+    </view>
+    <!-- 推荐列表 -->
+    <scroll-view
+      enable-back-to-top
+      v-for="(item, index) in subTypes"
+      :key="item.id"
+      v-show="activeIndex === index"
+      scroll-y
+      class="scroll-view"
+      @scrolltolower="onScrolltolower"
+    >
+      <view class="goods">
+        <navigator
+          hover-class="none"
+          class="navigator"
+          v-for="goods in item.goodsItems.items"
+          :key="goods.id"
+          :url="`/pages/goods/goods?id=${goods.id}`"
+        >
+          <image class="thumb" :src="goods.picture"></image>
+          <view class="name ellipsis">{{ goods.name }}</view>
+          <view class="price">
+            <text class="symbol">¥</text>
+            <text class="number">{{ goods.price }}</text>
+          </view>
+        </navigator>
+      </view>
+      <view class="loading-text">
+        {{ item.finish ? '没有更多数据了~' : '正在加载...' }}
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<style lang="scss">
+page {
+  height: 100%;
+  background-color: #f4f4f4;
+}
+.viewport {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  padding: 180rpx 0 0;
+  position: relative;
+}
+.cover {
+  width: 750rpx;
+  height: 225rpx;
+  border-radius: 0 0 40rpx 40rpx;
+  overflow: hidden;
+  position: absolute;
+  left: 0;
+  top: 0;
+  .image {
+    width: 750rpx;
+  }
+}
+.scroll-view {
+  flex: 1;
+}
+.tabs {
+  display: flex;
+  justify-content: space-evenly;
+  height: 100rpx;
+  line-height: 90rpx;
+  margin: 0 20rpx;
+  font-size: 28rpx;
+  border-radius: 10rpx;
+  box-shadow: 0 4rpx 5rpx rgba(200, 200, 200, 0.3);
+  color: #333;
+  background-color: #fff;
+  position: relative;
+  z-index: 9;
+  .text {
+    margin: 0 20rpx;
+    position: relative;
+  }
+  .active {
+    &::after {
+      content: '';
+      width: 40rpx;
+      height: 4rpx;
+      transform: translate(-50%);
+      background-color: #27ba9b;
+      position: absolute;
+      left: 50%;
+      bottom: 24rpx;
+    }
+  }
+}
+.goods {
+  display: flex;
+  flex-wrap: wrap;
+  justify-content: space-between;
+  padding: 0 20rpx 20rpx;
+  .navigator {
+    width: 345rpx;
+    padding: 20rpx;
+    margin-top: 20rpx;
+    border-radius: 10rpx;
+    background-color: #fff;
+  }
+  .thumb {
+    width: 305rpx;
+    height: 305rpx;
+  }
+  .name {
+    height: 88rpx;
+    font-size: 26rpx;
+  }
+  .price {
+    line-height: 1;
+    color: #cf4444;
+    font-size: 30rpx;
+  }
+  .symbol {
+    font-size: 70%;
+  }
+  .decimal {
+    font-size: 70%;
+  }
+}
+
+.loading-text {
+  text-align: center;
+  font-size: 28rpx;
+  color: #666;
+  padding: 20rpx 0 50rpx;
+}
+</style>

+ 27 - 0
src/pages/index/components/CategoryPanel.vue

@@ -0,0 +1,27 @@
+<script setup lang="ts">
+import type { CategoryItem } from '@/types/home'
+
+// 定义 props 接收数据
+defineProps<{
+  list: CategoryItem[]
+}>()
+</script>
+
+<template>
+  <view class="category">
+    <navigator
+      class="category-item"
+      hover-class="none"
+      url="/pages/index/index"
+      v-for="item in list"
+      :key="item.id"
+    >
+      <image class="icon" :src="item.icon"></image>
+      <text class="text">{{ item.name }}</text>
+    </navigator>
+  </view>
+</template>
+
+<style lang="scss">
+@import '../styles/category.scss';
+</style>

+ 71 - 0
src/pages/index/components/CustomNavbar.vue

@@ -0,0 +1,71 @@
+<script setup lang="ts">
+// 获取屏幕边界到安全区域距离
+const { safeAreaInsets } = uni.getSystemInfoSync()
+</script>
+
+<template>
+  <view class="navbar" :style="{ paddingTop: safeAreaInsets!.top + 10 + 'px' }">
+    <!-- logo文字 -->
+    <view class="logo">
+      <image class="logo-image" src="@/static/images/logo.png"></image>
+      <text class="logo-text">新鲜 · 亲民 · 快捷</text>
+    </view>
+    <!-- 搜索条 -->
+    <view class="search">
+      <text class="icon-search">搜索商品</text>
+      <text class="icon-scan"></text>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+/* 自定义导航条 */
+.navbar {
+  background-image: url(@/static/images/navigator_bg.png);
+  background-size: cover;
+  position: relative;
+  display: flex;
+  flex-direction: column;
+  padding-top: 20px;
+  .logo {
+    display: flex;
+    align-items: center;
+    height: 64rpx;
+    padding-left: 30rpx;
+    .logo-image {
+      width: 166rpx;
+      height: 39rpx;
+    }
+    .logo-text {
+      flex: 1;
+      line-height: 28rpx;
+      color: #fff;
+      margin: 2rpx 0 0 20rpx;
+      padding-left: 20rpx;
+      border-left: 1rpx solid #fff;
+      font-size: 26rpx;
+    }
+  }
+  .search {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 0 10rpx 0 26rpx;
+    height: 64rpx;
+    margin: 16rpx 20rpx;
+    color: #fff;
+    font-size: 28rpx;
+    border-radius: 32rpx;
+    background-color: rgba(255, 255, 255, 0.5);
+  }
+  .icon-search {
+    &::before {
+      margin-right: 10rpx;
+    }
+  }
+  .icon-scan {
+    font-size: 30rpx;
+    padding: 15rpx;
+  }
+}
+</style>

+ 33 - 0
src/pages/index/components/HotPanel.vue

@@ -0,0 +1,33 @@
+<script setup lang="ts">
+import type { HotItem } from '@/types/home'
+
+// 定义 props 接收数据
+defineProps<{
+  list: HotItem[]
+}>()
+</script>
+
+<template>
+  <!-- 推荐专区 -->
+  <view class="panel hot">
+    <view class="item" v-for="item in list" :key="item.id">
+      <view class="title">
+        <text class="title-text">{{ item.title }}</text>
+        <text class="title-desc">{{ item.alt }}</text>
+      </view>
+      <navigator hover-class="none" :url="`/pages/hot/hot?type=${item.type}`" class="cards">
+        <image
+          v-for="src in item.pictures"
+          :key="src"
+          class="image"
+          mode="aspectFit"
+          :src="src"
+        ></image>
+      </navigator>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+@import '../styles/hot.scss';
+</style>

+ 394 - 0
src/pages/index/components/PageSkeleton.vue

@@ -0,0 +1,394 @@
+<template name="skeleton">
+  <view is="components/XtxSwiper">
+    <view class="carousel XtxSwiper--carousel">
+      <!-- App 端控制台未知报错,删除 swiper 错误消失? -->
+      <view class="indicator XtxSwiper--indicator">
+        <text class="dot XtxSwiper--dot active XtxSwiper--active"></text>
+        <text class="dot XtxSwiper--dot"></text>
+        <text class="dot XtxSwiper--dot"></text>
+        <text class="dot XtxSwiper--dot"></text>
+        <text class="dot XtxSwiper--dot"></text>
+      </view>
+    </view>
+  </view>
+  <view is="pages/index/components/CategoryPanel">
+    <view class="category CategoryPanel--category">
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-382 sk-text"
+          >居家</text
+        >
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-248 sk-text"
+          >锦鲤</text
+        >
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-184 sk-text"
+          >服饰</text
+        >
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-202 sk-text"
+          >母婴</text
+        >
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-664 sk-text"
+          >个护</text
+        >
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-805 sk-text"
+          >严选</text
+        >
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-445 sk-text"
+          >数码</text
+        >
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-512 sk-text"
+          >运动</text
+        >
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-61 sk-text">杂项</text>
+      </navigator>
+      <navigator class="category-item CategoryPanel--category-item" hover-class="none">
+        <image class="icon CategoryPanel--icon sk-image"></image>
+        <text class="text CategoryPanel--text sk-transparent sk-text-14-2857-863 sk-text"
+          >品牌</text
+        >
+      </navigator>
+    </view>
+  </view>
+  <view is="pages/index/components/HotPanel">
+    <view class="panel HotPanel--panel hot HotPanel--hot">
+      <view class="item HotPanel--item">
+        <view class="title HotPanel--title">
+          <text class="title-text HotPanel--title-text sk-transparent sk-text-14-2857-739 sk-text"
+            >特惠推荐</text
+          >
+          <text class="title-desc HotPanel--title-desc sk-transparent sk-text-14-2857-229 sk-text"
+            >精选全攻略</text
+          >
+        </view>
+        <navigator class="cards HotPanel--cards" hover-class="none">
+          <image class="image HotPanel--image sk-image" mode="aspectFit"></image>
+          <image class="image HotPanel--image sk-image" mode="aspectFit"></image>
+        </navigator>
+      </view>
+      <view class="item HotPanel--item">
+        <view class="title HotPanel--title">
+          <text class="title-text HotPanel--title-text sk-transparent sk-text-14-2857-73 sk-text"
+            >爆款推荐</text
+          >
+          <text class="title-desc HotPanel--title-desc sk-transparent sk-text-14-2857-879 sk-text"
+            >最受欢迎</text
+          >
+        </view>
+        <navigator class="cards HotPanel--cards" hover-class="none">
+          <image class="image HotPanel--image sk-image" mode="aspectFit"></image>
+          <image class="image HotPanel--image sk-image" mode="aspectFit"></image>
+        </navigator>
+      </view>
+      <view class="item HotPanel--item">
+        <view class="title HotPanel--title">
+          <text class="title-text HotPanel--title-text sk-transparent sk-text-14-2857-323 sk-text"
+            >一站买全</text
+          >
+          <text class="title-desc HotPanel--title-desc sk-transparent sk-text-14-2857-50 sk-text"
+            >精心优选</text
+          >
+        </view>
+        <navigator class="cards HotPanel--cards" hover-class="none">
+          <image class="image HotPanel--image sk-image" mode="aspectFit"></image>
+          <image class="image HotPanel--image sk-image" mode="aspectFit"></image>
+        </navigator>
+      </view>
+      <view class="item HotPanel--item">
+        <view class="title HotPanel--title">
+          <text class="title-text HotPanel--title-text sk-transparent sk-text-14-2857-70 sk-text"
+            >新鲜好物</text
+          >
+          <text class="title-desc HotPanel--title-desc sk-transparent sk-text-14-2857-877 sk-text"
+            >生活加分项</text
+          >
+        </view>
+        <navigator class="cards HotPanel--cards" hover-class="none">
+          <image class="image HotPanel--image sk-image" mode="aspectFit"></image>
+          <image class="image HotPanel--image sk-image" mode="aspectFit"></image>
+        </navigator>
+      </view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+/* #ifdef H5 || APP-PLUS */
+/* 修复 H5 端骨架屏样式异常 */
+/* 原因:H5 端样式自动开启 scoped 隔离,导致骨架屏的基础样式被隔离 */
+@import '../styles/category.scss';
+@import '../styles/hot.scss';
+@import '@/components/styles/XtxSwiper.scss';
+/* #endif */
+
+.sk-transparent {
+  color: transparent !important;
+}
+.sk-text-3-5714-536 {
+  background-image: linear-gradient(
+    transparent 3.5714%,
+    #eeeeee 0%,
+    #eeeeee 96.4286%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 28rpx;
+  position: relative !important;
+}
+.sk-text {
+  background-origin: content-box !important;
+  background-clip: content-box !important;
+  background-color: transparent !important;
+  color: transparent !important;
+  background-repeat: repeat-y !important;
+}
+.sk-text-14-2857-28 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 39.2rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-382 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-248 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-184 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-202 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-664 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-805 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-445 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-512 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-61 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-863 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-739 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 44.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-229 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-73 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 44.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-879 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-323 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 44.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-50 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-70 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 44.8rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-877 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-363 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 32rpx;
+  position: relative !important;
+}
+.sk-image {
+  background: #efefef !important;
+}
+.sk-pseudo::before,
+.sk-pseudo::after {
+  background: #efefef !important;
+  background-image: none !important;
+  color: transparent !important;
+  border-color: transparent !important;
+}
+.sk-pseudo-rect::before,
+.sk-pseudo-rect::after {
+  border-radius: 0 !important;
+}
+.sk-pseudo-circle::before,
+.sk-pseudo-circle::after {
+  border-radius: 50% !important;
+}
+.sk-container {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  background-color: transparent;
+}
+</style>

+ 114 - 0
src/pages/index/index.vue

@@ -0,0 +1,114 @@
+<script setup lang="ts">
+import { getHomeBannerAPI, getHomeCategoryAPI, getHomeHotAPI } from '@/services/home'
+import type { BannerItem, CategoryItem, HotItem } from '@/types/home'
+import { onLoad } from '@dcloudio/uni-app'
+import { ref } from 'vue'
+import CustomNavbar from './components/CustomNavbar.vue'
+import CategoryPanel from './components/CategoryPanel.vue'
+import HotPanel from './components/HotPanel.vue'
+import PageSkeleton from './components/PageSkeleton.vue'
+import { useGuessList } from '@/composables'
+
+// 获取轮播图数据
+const bannerList = ref<BannerItem[]>([])
+const getHomeBannerData = async () => {
+  const res = await getHomeBannerAPI()
+  bannerList.value = res.result
+}
+
+// 获取前台分类数据
+const categoryList = ref<CategoryItem[]>([])
+const getHomeCategoryData = async () => {
+  const res = await getHomeCategoryAPI()
+  categoryList.value = res.result
+}
+
+// 获取热门推荐数据
+const hotList = ref<HotItem[]>([])
+const getHomeHotData = async () => {
+  const res = await getHomeHotAPI()
+  hotList.value = res.result
+}
+
+// 是否加载中标记
+const isLoading = ref(false)
+
+// 页面加载
+onLoad(async () => {
+  isLoading.value = true
+  await Promise.all([getHomeBannerData(), getHomeCategoryData(), getHomeHotData()])
+  isLoading.value = false
+})
+
+// 猜你喜欢组合式函数调用
+const { guessRef, onScrolltolower } = useGuessList()
+// 当前下拉刷新状态
+const isTriggered = ref(false)
+// 自定义下拉刷新被触发
+const onRefresherrefresh = async () => {
+  // 开始动画
+  isTriggered.value = true
+  // 加载数据
+  // await getHomeBannerData()
+  // await getHomeCategoryData()
+  // await getHomeHotData()
+  // 重置猜你喜欢组件数据
+  guessRef.value?.resetData()
+  await Promise.all([
+    getHomeBannerData(),
+    getHomeCategoryData(),
+    getHomeHotData(),
+    guessRef.value?.getMore(),
+  ])
+  // 关闭动画
+  isTriggered.value = false
+}
+</script>
+
+<template>
+  <view class="viewport">
+    <!-- 自定义导航栏 -->
+    <CustomNavbar />
+    <!-- 滚动容器 -->
+    <scroll-view
+      enable-back-to-top
+      refresher-enabled
+      @refresherrefresh="onRefresherrefresh"
+      :refresher-triggered="isTriggered"
+      @scrolltolower="onScrolltolower"
+      class="scroll-view"
+      scroll-y
+    >
+      <PageSkeleton v-if="isLoading" />
+      <template v-else>
+        <!-- 自定义轮播图 -->
+        <XtxSwiper :list="bannerList" />
+        <!-- 分类面板 -->
+        <CategoryPanel :list="categoryList" />
+        <!-- 热门推荐 -->
+        <HotPanel :list="hotList" />
+        <!-- 猜你喜欢 -->
+        <XtxGuess ref="guessRef" />
+      </template>
+    </scroll-view>
+  </view>
+</template>
+
+<style lang="scss">
+page {
+  background-color: #f7f7f7;
+  height: 100%;
+  overflow: hidden;
+}
+
+.viewport {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+}
+
+.scroll-view {
+  flex: 1;
+  overflow: hidden;
+}
+</style>

+ 26 - 0
src/pages/index/styles/category.scss

@@ -0,0 +1,26 @@
+/* 前台类目 */
+.category {
+  margin: 20rpx 0 0;
+  padding: 10rpx 0;
+  display: flex;
+  flex-wrap: wrap;
+  min-height: 328rpx;
+
+  .category-item {
+    width: 150rpx;
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+    align-items: center;
+    box-sizing: border-box;
+
+    .icon {
+      width: 100rpx;
+      height: 100rpx;
+    }
+    .text {
+      font-size: 26rpx;
+      color: #666;
+    }
+  }
+}

+ 52 - 0
src/pages/index/styles/hot.scss

@@ -0,0 +1,52 @@
+/* 热门推荐 */
+.hot {
+  display: flex;
+  flex-wrap: wrap;
+  min-height: 508rpx;
+  margin: 20rpx 20rpx 0;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .title {
+    display: flex;
+    align-items: center;
+    padding: 24rpx 24rpx 0;
+    font-size: 32rpx;
+    color: #262626;
+    position: relative;
+    .title-desc {
+      font-size: 24rpx;
+      color: #7f7f7f;
+      margin-left: 18rpx;
+    }
+  }
+
+  .item {
+    display: flex;
+    flex-direction: column;
+    width: 50%;
+    height: 254rpx;
+    border-right: 1rpx solid #eee;
+    border-top: 1rpx solid #eee;
+    .title {
+      justify-content: start;
+    }
+    &:nth-child(2n) {
+      border-right: 0 none;
+    }
+    &:nth-child(-n + 2) {
+      border-top: 0 none;
+    }
+    .image {
+      width: 150rpx;
+      height: 150rpx;
+    }
+  }
+  .cards {
+    flex: 1;
+    padding: 15rpx 20rpx;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+}

+ 225 - 0
src/pages/login/login.vue

@@ -0,0 +1,225 @@
+<script setup lang="ts">
+import { postLoginAPI, postLoginWxMinAPI, postLoginWxMinSimpleAPI } from '@/services/login'
+import { useMemberStore } from '@/stores'
+import type { LoginResult } from '@/types/member'
+import { onLoad } from '@dcloudio/uni-app'
+import { ref } from 'vue'
+
+// #ifdef MP-WEIXIN
+// 获取 code 登录凭证
+let code = ''
+onLoad(async () => {
+  const res = await wx.login()
+  code = res.code
+})
+
+// 获取用户手机号码
+const onGetphonenumber: UniHelper.ButtonOnGetphonenumber = async (ev) => {
+  const { encryptedData, iv } = ev.detail
+  const res = await postLoginWxMinAPI({ code, encryptedData, iv })
+  loginSuccess(res.result)
+}
+// #endif
+
+// 模拟手机号码快捷登录(开发练习)
+const onGetphonenumberSimple = async () => {
+  const res = await postLoginWxMinSimpleAPI('13123456789')
+  loginSuccess(res.result)
+}
+
+const loginSuccess = (profile: LoginResult) => {
+  // 保存会员信息
+  const memberStore = useMemberStore()
+  memberStore.setProfile(profile)
+  // 成功提示
+  uni.showToast({ icon: 'success', title: '登录成功' })
+  setTimeout(() => {
+    // 页面跳转
+    // uni.switchTab({ url: '/pages/my/my' })
+    uni.navigateBack()
+  }, 500)
+}
+
+// #ifdef H5
+// 传统表单登录,测试账号:13123456789 密码:123456,测试账号仅开发学习使用。
+const form = ref({
+  account: '13123456789',
+  password: '',
+})
+
+// 表单提交
+const onSubmit = async () => {
+  const res = await postLoginAPI(form.value)
+  loginSuccess(res.result)
+}
+// #endif
+</script>
+
+<template>
+  <view class="viewport">
+    <view class="logo">
+      <image
+        src="https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/logo_icon.png"
+      ></image>
+    </view>
+    <view class="login">
+      <!-- 网页端表单登录 -->
+      <!-- #ifdef H5 -->
+      <input v-model="form.account" class="input" type="text" placeholder="请输入用户名/手机号码" />
+      <input v-model="form.password" class="input" type="text" password placeholder="请输入密码" />
+      <button @tap="onSubmit" class="button phone">登录</button>
+      <!-- #endif -->
+
+      <!-- 小程序端授权登录 -->
+      <!-- #ifdef MP-WEIXIN -->
+      <button class="button phone" open-type="getPhoneNumber" @getphonenumber="onGetphonenumber">
+        <text class="icon icon-phone"></text>
+        手机号快捷登录
+      </button>
+      <!-- #endif -->
+      <view class="extra">
+        <view class="caption">
+          <text>其他登录方式</text>
+        </view>
+        <view class="options">
+          <!-- 通用模拟登录 -->
+          <button @tap="onGetphonenumberSimple">
+            <text class="icon icon-phone">模拟快捷登录</text>
+          </button>
+        </view>
+      </view>
+      <view class="tips">登录/注册即视为你同意《服务条款》和《小兔鲜儿隐私协议》</view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+page {
+  height: 100%;
+}
+
+.viewport {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  padding: 20rpx 40rpx;
+}
+
+.logo {
+  flex: 1;
+  text-align: center;
+  image {
+    width: 220rpx;
+    height: 220rpx;
+    margin-top: 15vh;
+  }
+}
+
+.login {
+  display: flex;
+  flex-direction: column;
+  height: 60vh;
+  padding: 40rpx 20rpx 20rpx;
+
+  .input {
+    width: 100%;
+    height: 80rpx;
+    font-size: 28rpx;
+    border-radius: 72rpx;
+    border: 1px solid #ddd;
+    padding-left: 30rpx;
+    margin-bottom: 20rpx;
+  }
+
+  .button {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    width: 100%;
+    height: 80rpx;
+    font-size: 28rpx;
+    border-radius: 72rpx;
+    color: #fff;
+    .icon {
+      font-size: 40rpx;
+      margin-right: 6rpx;
+    }
+  }
+
+  .phone {
+    background-color: #28bb9c;
+  }
+
+  .wechat {
+    background-color: #06c05f;
+  }
+
+  .extra {
+    flex: 1;
+    padding: 70rpx 70rpx 0;
+    .caption {
+      width: 440rpx;
+      line-height: 1;
+      border-top: 1rpx solid #ddd;
+      font-size: 26rpx;
+      color: #999;
+      position: relative;
+      text {
+        transform: translate(-40%);
+        background-color: #fff;
+        position: absolute;
+        top: -12rpx;
+        left: 50%;
+      }
+    }
+
+    .options {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      margin-top: 70rpx;
+      button {
+        padding: 0;
+        background-color: transparent;
+        &::after {
+          border: none;
+        }
+      }
+    }
+
+    .icon {
+      font-size: 24rpx;
+      color: #444;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+
+      &::before {
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        width: 80rpx;
+        height: 80rpx;
+        margin-bottom: 6rpx;
+        font-size: 40rpx;
+        border: 1rpx solid #444;
+        border-radius: 50%;
+      }
+    }
+    .icon-weixin::before {
+      border-color: #06c05f;
+      color: #06c05f;
+    }
+  }
+}
+
+.tips {
+  position: absolute;
+  bottom: 80rpx;
+  left: 20rpx;
+  right: 20rpx;
+  font-size: 22rpx;
+  color: #999;
+  text-align: center;
+}
+</style>

+ 232 - 0
src/pages/my/my.vue

@@ -0,0 +1,232 @@
+<script setup lang="ts">
+import { useGuessList } from '@/composables'
+import { useMemberStore } from '@/stores'
+// 获取屏幕边界到安全区域距离
+const { safeAreaInsets } = uni.getSystemInfoSync()
+// 订单选项
+const orderTypes = [
+  { type: '1', text: '待付款', icon: 'icon-currency' },
+  { type: '2', text: '待发货', icon: 'icon-gift' },
+  { type: '3', text: '待收货', icon: 'icon-check' },
+  { type: '4', text: '待评价', icon: 'icon-comment' },
+]
+// 获取会员信息
+const memberStore = useMemberStore()
+
+const { guessRef, onScrolltolower } = useGuessList()
+</script>
+
+<template>
+  <scroll-view enable-back-to-top @scrolltolower="onScrolltolower" class="viewport" scroll-y>
+    <!-- 个人资料 -->
+    <view class="profile" :style="{ paddingTop: safeAreaInsets!.top + 'px' }">
+      <!-- 情况1:已登录 -->
+      <view class="overview" v-if="memberStore.profile">
+        <navigator url="/pagesMember/profile/profile" hover-class="none">
+          <image class="avatar" :src="memberStore.profile.avatar" mode="aspectFill"></image>
+        </navigator>
+        <view class="meta">
+          <view class="nickname">
+            {{ memberStore.profile.nickname || memberStore.profile.account }}
+          </view>
+          <navigator class="extra" url="/pagesMember/profile/profile" hover-class="none">
+            <text class="update">更新头像昵称</text>
+          </navigator>
+        </view>
+      </view>
+      <!-- 情况2:未登录 -->
+      <view class="overview" v-else>
+        <navigator url="/pages/login/login" hover-class="none">
+          <image
+            class="avatar gray"
+            mode="aspectFill"
+            src="https://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-06/db628d42-88a7-46e7-abb8-659448c33081.png"
+          ></image>
+        </navigator>
+        <view class="meta">
+          <navigator url="/pages/login/login" hover-class="none" class="nickname">
+            未登录
+          </navigator>
+          <view class="extra">
+            <text class="tips">点击登录账号</text>
+          </view>
+        </view>
+      </view>
+      <navigator class="settings" url="/pagesMember/settings/settings" hover-class="none">
+        设置
+      </navigator>
+    </view>
+    <!-- 我的订单 -->
+    <view class="orders">
+      <view class="title">
+        我的订单
+        <navigator class="navigator" url="/pagesOrder/list/list?type=0" hover-class="none">
+          查看全部订单<text class="icon-right"></text>
+        </navigator>
+      </view>
+      <view class="section">
+        <!-- 订单 -->
+        <navigator
+          v-for="item in orderTypes"
+          :key="item.type"
+          :class="item.icon"
+          :url="`/pagesOrder/list/list?type=${item.type}`"
+          class="navigator"
+          hover-class="none"
+        >
+          {{ item.text }}
+        </navigator>
+        <!-- 客服 -->
+        <!-- #ifdef MP-WEIXIN -->
+        <button class="contact icon-handset" open-type="contact">售后</button>
+        <!-- #endif -->
+      </view>
+    </view>
+    <!-- 猜你喜欢 -->
+    <view class="guess">
+      <XtxGuess ref="guessRef" />
+    </view>
+  </scroll-view>
+</template>
+
+<style lang="scss">
+page {
+  height: 100%;
+  overflow: hidden;
+  background-color: #f7f7f8;
+}
+
+.viewport {
+  height: 100%;
+  background-repeat: no-repeat;
+  background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/center_bg.png);
+  background-size: 100% auto;
+}
+
+/* 用户信息 */
+.profile {
+  margin-top: 30rpx;
+  position: relative;
+
+  .overview {
+    display: flex;
+    height: 120rpx;
+    padding: 0 36rpx;
+    color: #fff;
+  }
+
+  .avatar {
+    width: 120rpx;
+    height: 120rpx;
+    border-radius: 50%;
+    background-color: #eee;
+  }
+
+  .gray {
+    filter: grayscale(100%);
+  }
+
+  .meta {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    align-items: flex-start;
+    line-height: 30rpx;
+    padding: 16rpx 0;
+    margin-left: 20rpx;
+  }
+
+  .nickname {
+    max-width: 180rpx;
+    margin-bottom: 16rpx;
+    font-size: 30rpx;
+
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+  }
+
+  .extra {
+    display: flex;
+    font-size: 20rpx;
+  }
+
+  .tips {
+    font-size: 22rpx;
+  }
+
+  .update {
+    padding: 3rpx 10rpx 1rpx;
+    color: rgba(255, 255, 255, 0.8);
+    border: 1rpx solid rgba(255, 255, 255, 0.8);
+    margin-right: 10rpx;
+    border-radius: 30rpx;
+  }
+
+  .settings {
+    position: absolute;
+    bottom: 0;
+    right: 40rpx;
+    font-size: 30rpx;
+    color: #fff;
+  }
+}
+
+/* 我的订单 */
+.orders {
+  position: relative;
+  z-index: 99;
+  padding: 30rpx;
+  margin: 50rpx 20rpx 0;
+  background-color: #fff;
+  border-radius: 10rpx;
+  box-shadow: 0 4rpx 6rpx rgba(240, 240, 240, 0.6);
+
+  .title {
+    height: 40rpx;
+    line-height: 40rpx;
+    font-size: 28rpx;
+    color: #1e1e1e;
+
+    .navigator {
+      font-size: 24rpx;
+      color: #939393;
+      float: right;
+    }
+  }
+
+  .section {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+    padding: 40rpx 20rpx 10rpx;
+    .navigator,
+    .contact {
+      text-align: center;
+      font-size: 24rpx;
+      color: #333;
+      &::before {
+        display: block;
+        font-size: 60rpx;
+        color: #ff9545;
+      }
+      &::after {
+        border: none;
+      }
+    }
+    .contact {
+      padding: 0;
+      margin: 0;
+      border: 0;
+      background-color: transparent;
+      line-height: inherit;
+    }
+  }
+}
+
+/* 猜你喜欢 */
+.guess {
+  background-color: #f7f7f8;
+  margin-top: 20rpx;
+}
+</style>

+ 263 - 0
src/pagesMember/address-form/address-form.vue

@@ -0,0 +1,263 @@
+<script setup lang="ts">
+import {
+  getMemberAddressByIdAPI,
+  postMemberAddressAPI,
+  putMemberAddressByIdAPI,
+} from '@/services/address'
+import { onLoad } from '@dcloudio/uni-app'
+import { ref } from 'vue'
+
+// 表单数据
+const form = ref({
+  receiver: '', // 收货人
+  contact: '', // 联系方式
+  fullLocation: '', // 省市区(前端展示)
+  provinceCode: '', // 省份编码(后端参数)
+  cityCode: '', // 城市编码(后端参数)
+  countyCode: '', // 区/县编码(后端参数)
+  address: '', // 详细地址
+  isDefault: 0, // 默认地址,1为是,0为否
+})
+
+// 获取页面参数
+const query = defineProps<{
+  id?: string
+}>()
+
+// 获取收货地址详情数据
+const getMemberAddressByIdData = async () => {
+  if (query.id) {
+    // 发送请求
+    const res = await getMemberAddressByIdAPI(query.id)
+    // 把数据合并到表单中
+    Object.assign(form.value, res.result)
+  }
+}
+
+// 页面加载
+onLoad(() => {
+  getMemberAddressByIdData()
+})
+
+// 动态设置标题
+uni.setNavigationBarTitle({ title: query.id ? '修改地址' : '新建地址' })
+
+// 收集所在地区
+const onRegionChange: UniHelper.RegionPickerOnChange = (ev) => {
+  // 省市区(前端展示)
+  form.value.fullLocation = ev.detail.value.join(' ')
+  // 省市区(后端参数)
+  const [provinceCode, cityCode, countyCode] = ev.detail.code!
+  // form.value.provinceCode = provinceCode
+  Object.assign(form.value, { provinceCode, cityCode, countyCode })
+}
+
+// 收集是否默认收货地址
+const onSwitchChange: UniHelper.SwitchOnChange = (ev) => {
+  form.value.isDefault = ev.detail.value ? 1 : 0
+}
+
+// 定义校验规则
+const rules: UniHelper.UniFormsRules = {
+  receiver: {
+    rules: [{ required: true, errorMessage: '请输入收货人姓名' }],
+  },
+  contact: {
+    rules: [
+      { required: true, errorMessage: '请输入联系方式' },
+      { pattern: /^1[3-9]\d{9}$/, errorMessage: '手机号格式不正确' },
+    ],
+  },
+  countyCode: {
+    rules: [{ required: true, errorMessage: '请选择所在地区' }],
+  },
+  address: {
+    rules: [{ required: true, errorMessage: '请选择详细地址' }],
+  },
+}
+
+// 表单组件实例
+const formRef = ref<UniHelper.UniFormsInstance>()
+
+// 提交表单
+const onSubmit = async () => {
+  try {
+    // 表单校验
+    await formRef.value?.validate?.()
+    // 校验通过后再发送请求
+    if (query.id) {
+      // 修改地址请求
+      await putMemberAddressByIdAPI(query.id, form.value)
+    } else {
+      // 新建地址请求
+      await postMemberAddressAPI(form.value)
+    }
+    // 成功提示
+    uni.showToast({ icon: 'success', title: query.id ? '修改成功' : '添加成功' })
+    // 返回上一页
+    setTimeout(() => {
+      uni.navigateBack()
+    }, 400)
+  } catch (error) {
+    uni.showToast({ icon: 'error', title: '请填写完整信息' })
+  }
+}
+
+// #ifdef H5 || APP-PLUS
+const onCityChange: UniHelper.UniDataPickerOnChange = (ev) => {
+  // 省市区
+  const [province, city, county] = ev.detail.value
+  // 收集后端所需的 code 数据
+  Object.assign(form.value, {
+    provinceCode: province.value,
+    cityCode: city.value,
+    countyCode: county.value,
+  })
+}
+// #endif
+</script>
+
+<template>
+  <view class="content">
+    <uni-forms :rules="rules" :model="form" ref="formRef">
+      <!-- 表单内容 -->
+      <uni-forms-item name="receiver" class="form-item">
+        <text class="label">收货人</text>
+        <input class="input" placeholder="请填写收货人姓名" v-model="form.receiver" />
+      </uni-forms-item>
+      <uni-forms-item name="contact" class="form-item">
+        <text class="label">手机号码</text>
+        <input
+          class="input"
+          placeholder="请填写收货人手机号码"
+          :maxlength="11"
+          v-model="form.contact"
+        />
+      </uni-forms-item>
+      <uni-forms-item name="countyCode" class="form-item">
+        <text class="label">所在地区</text>
+        <!-- #ifdef MP-WEIXIN -->
+        <picker
+          @change="onRegionChange"
+          class="picker"
+          mode="region"
+          :value="form.fullLocation.split(' ')"
+        >
+          <view v-if="form.fullLocation">{{ form.fullLocation }}</view>
+          <view v-else class="placeholder">请选择省/市/区(县)</view>
+        </picker>
+        <!-- #endif -->
+
+        <!-- #ifdef H5 || APP-PLUS -->
+        <uni-data-picker
+          placeholder="请选择地址"
+          popup-title="请选择城市"
+          collection="opendb-city-china"
+          field="code as value, name as text"
+          orderby="value asc"
+          :step-searh="true"
+          self-field="code"
+          parent-field="parent_code"
+          @change="onCityChange"
+          :clear-icon="false"
+          v-model="form.countyCode"
+        />
+        <!-- #endif -->
+      </uni-forms-item>
+      <uni-forms-item name="address" class="form-item">
+        <text class="label">详细地址</text>
+        <input class="input" placeholder="街道、楼牌号等信息" v-model="form.address" />
+      </uni-forms-item>
+      <view class="form-item">
+        <label class="label">设为默认地址</label>
+        <switch
+          @change="onSwitchChange"
+          class="switch"
+          color="#27ba9b"
+          :checked="form.isDefault === 1"
+        />
+      </view>
+    </uni-forms>
+  </view>
+  <!-- 提交按钮 -->
+  <button @tap="onSubmit" class="button">保存并使用</button>
+</template>
+
+<style lang="scss">
+// 深度选择器修改 uni-data-picker 组件样式
+:deep(.selected-area) {
+  flex: 0 1 auto;
+  height: auto;
+}
+
+page {
+  background-color: #f4f4f4;
+}
+
+.content {
+  margin: 20rpx 20rpx 0;
+  padding: 0 20rpx;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .form-item,
+  .uni-forms-item {
+    display: flex;
+    align-items: center;
+    min-height: 96rpx;
+    padding: 25rpx 10rpx;
+    background-color: #fff;
+    font-size: 28rpx;
+    border-bottom: 1rpx solid #ddd;
+    position: relative;
+    margin-bottom: 0;
+
+    // 调整 uni-forms 样式
+    .uni-forms-item__content {
+      display: flex;
+    }
+
+    .uni-forms-item__error {
+      margin-left: 200rpx;
+    }
+
+    &:last-child {
+      border: none;
+    }
+
+    .label {
+      width: 200rpx;
+      color: #333;
+    }
+
+    .input {
+      flex: 1;
+      display: block;
+      height: 46rpx;
+    }
+
+    .switch {
+      position: absolute;
+      right: -20rpx;
+      transform: scale(0.8);
+    }
+
+    .picker {
+      flex: 1;
+    }
+
+    .placeholder {
+      color: #808080;
+    }
+  }
+}
+
+.button {
+  height: 80rpx;
+  margin: 30rpx 20rpx;
+  color: #fff;
+  border-radius: 80rpx;
+  font-size: 30rpx;
+  background-color: #27ba9b;
+}
+</style>

+ 194 - 0
src/pagesMember/address/address.vue

@@ -0,0 +1,194 @@
+<script setup lang="ts">
+import { deleteMemberAddressByIdAPI, getMemberAddressAPI } from '@/services/address'
+import { useAddressStore } from '@/stores/modules/address'
+import type { AddressItem } from '@/types/address'
+import { onShow } from '@dcloudio/uni-app'
+import { ref } from 'vue'
+
+// 获取收货地址列表数据
+const addressList = ref<AddressItem[]>([])
+const getMemberAddressData = async () => {
+  const res = await getMemberAddressAPI()
+  addressList.value = res.result
+}
+
+// 初始化调用(页面显示)
+onShow(() => {
+  getMemberAddressData()
+})
+
+// 删除收货地址
+const onDeleteAddress = (id: string) => {
+  // 二次确认
+  uni.showModal({
+    content: '删除地址?',
+    confirmColor: '#27BA9B',
+    success: async (res) => {
+      if (res.confirm) {
+        // 根据id删除收货地址
+        await deleteMemberAddressByIdAPI(id)
+        // 重新获取收货地址列表
+        getMemberAddressData()
+      }
+    },
+  })
+}
+
+// 修改收货地址
+const onChangeAddress = (item: AddressItem) => {
+  // 修改地址
+  const addressStore = useAddressStore()
+  addressStore.changeSelectedAddress(item)
+  // 返回上一页
+  uni.navigateBack()
+}
+</script>
+
+<template>
+  <view class="viewport">
+    <!-- 地址列表 -->
+    <scroll-view enable-back-to-top class="scroll-view" scroll-y>
+      <view v-if="addressList.length" class="address">
+        <uni-swipe-action class="address-list">
+          <!-- 收货地址项 -->
+          <uni-swipe-action-item class="item" v-for="item in addressList" :key="item.id">
+            <view class="item-content" @tap="onChangeAddress(item)">
+              <view class="user">
+                {{ item.receiver }}
+                <text class="contact">{{ item.contact }}</text>
+                <text v-if="item.isDefault" class="badge">默认</text>
+              </view>
+              <view class="locate">{{ item.fullLocation }} {{ item.address }}</view>
+              <!-- H5 端需添加 .prevent 阻止链接的默认行为 -->
+              <navigator
+                class="edit"
+                hover-class="none"
+                :url="`/pagesMember/address-form/address-form?id=${item.id}`"
+                @tap.stop="() => {}"
+                @tap.prevent="() => {}"
+              >
+                修改
+              </navigator>
+            </view>
+            <!-- 右侧插槽 -->
+            <template #right>
+              <button @tap="onDeleteAddress(item.id)" class="delete-button">删除</button>
+            </template>
+          </uni-swipe-action-item>
+        </uni-swipe-action>
+      </view>
+      <view v-else class="blank">暂无收货地址</view>
+    </scroll-view>
+    <!-- 添加按钮 -->
+    <view class="add-btn">
+      <navigator hover-class="none" url="/pagesMember/address-form/address-form">
+        新建地址
+      </navigator>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+page {
+  height: 100%;
+  overflow: hidden;
+}
+
+/* 删除按钮 */
+.delete-button {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  width: 50px;
+  height: 100%;
+  font-size: 28rpx;
+  color: #fff;
+  border-radius: 0;
+  padding: 0;
+  background-color: #cf4444;
+}
+
+.viewport {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background-color: #f4f4f4;
+
+  .scroll-view {
+    padding-top: 20rpx;
+  }
+}
+
+.address {
+  padding: 0 20rpx;
+  margin: 0 20rpx;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .item-content {
+    line-height: 1;
+    padding: 40rpx 10rpx 38rpx;
+    border-bottom: 1rpx solid #ddd;
+    position: relative;
+
+    .edit {
+      position: absolute;
+      top: 36rpx;
+      right: 30rpx;
+      padding: 2rpx 0 2rpx 20rpx;
+      border-left: 1rpx solid #666;
+      font-size: 26rpx;
+      color: #666;
+      line-height: 1;
+    }
+  }
+
+  .item:last-child .item-content {
+    border: none;
+  }
+
+  .user {
+    font-size: 28rpx;
+    margin-bottom: 20rpx;
+    color: #333;
+
+    .contact {
+      color: #666;
+    }
+
+    .badge {
+      display: inline-block;
+      padding: 4rpx 10rpx 2rpx 14rpx;
+      margin: 2rpx 0 0 10rpx;
+      font-size: 26rpx;
+      color: #27ba9b;
+      border-radius: 6rpx;
+      border: 1rpx solid #27ba9b;
+    }
+  }
+
+  .locate {
+    line-height: 1.6;
+    font-size: 26rpx;
+    color: #333;
+  }
+}
+
+.blank {
+  margin-top: 300rpx;
+  text-align: center;
+  font-size: 32rpx;
+  color: #888;
+}
+
+.add-btn {
+  height: 80rpx;
+  text-align: center;
+  line-height: 80rpx;
+  margin: 30rpx 20rpx;
+  color: #fff;
+  border-radius: 80rpx;
+  font-size: 30rpx;
+  background-color: #27ba9b;
+}
+</style>

+ 330 - 0
src/pagesMember/profile/profile.vue

@@ -0,0 +1,330 @@
+<script setup lang="ts">
+import { getMemberProfileAPI, putMemberProfileAPI } from '@/services/profile'
+import { useMemberStore } from '@/stores'
+import type { Gender, ProfileDetail } from '@/types/member'
+import { formatDate } from '@/utils'
+import { onLoad } from '@dcloudio/uni-app'
+import { ref } from 'vue'
+
+// 获取屏幕边界到安全区域距离
+const { safeAreaInsets } = uni.getSystemInfoSync()
+
+// 获取个人信息,修改个人信息需提供初始值
+const profile = ref({} as ProfileDetail)
+const getMemberProfileData = async () => {
+  const res = await getMemberProfileAPI()
+  profile.value = res.result
+  // 同步 Store 的头像和昵称,用于我的页面展示
+  memberStore.profile!.avatar = res.result.avatar
+  memberStore.profile!.nickname = res.result.nickname
+}
+
+onLoad(() => {
+  getMemberProfileData()
+})
+
+const memberStore = useMemberStore()
+// 修改头像
+const onAvatarChange = () => {
+  // 调用拍照/选择图片
+  // 选择图片条件编译
+  // #ifdef H5 || APP-PLUS
+  // 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替
+  uni.chooseImage({
+    count: 1,
+    success: (res) => {
+      // 文件路径
+      const tempFilePaths = res.tempFilePaths
+      // 上传
+      uploadFile(tempFilePaths[0])
+    },
+  })
+  // #endif
+
+  // #ifdef MP-WEIXIN
+  // uni.chooseMedia 仅支持微信小程序端
+  uni.chooseMedia({
+    // 文件个数
+    count: 1,
+    // 文件类型
+    mediaType: ['image'],
+    success: (res) => {
+      // 本地路径
+      const { tempFilePath } = res.tempFiles[0]
+      // 上传
+      uploadFile(tempFilePath)
+    },
+  })
+  // #endif
+}
+
+// 文件上传-兼容小程序端、H5端、App端
+const uploadFile = (file: string) => {
+  // 文件上传
+  uni.uploadFile({
+    url: '/member/profile/avatar',
+    name: 'file',
+    filePath: file,
+    success: (res) => {
+      if (res.statusCode === 200) {
+        const avatar = JSON.parse(res.data).result.avatar
+        // 个人信息页数据更新
+        profile.value!.avatar = avatar
+        // Store头像更新
+        memberStore.profile!.avatar = avatar
+        uni.showToast({ icon: 'success', title: '更新成功' })
+      } else {
+        uni.showToast({ icon: 'error', title: '出现错误' })
+      }
+    },
+  })
+}
+
+// 修改性别
+const onGenderChange: UniHelper.RadioGroupOnChange = (ev) => {
+  profile.value.gender = ev.detail.value as Gender
+}
+
+// 修改生日
+const onBirthdayChange: UniHelper.DatePickerOnChange = (ev) => {
+  profile.value.birthday = ev.detail.value
+}
+
+// 修改城市
+let fullLocationCode: [string, string, string] = ['', '', '']
+const onFullLocationChange: UniHelper.RegionPickerOnChange = (ev) => {
+  // 修改前端界面
+  profile.value.fullLocation = ev.detail.value.join(' ')
+  // 提交后端更新
+  fullLocationCode = ev.detail.code!
+}
+
+// 点击保存提交表单
+const onSubmit = async () => {
+  const { nickname, gender, birthday } = profile.value
+  const res = await putMemberProfileAPI({
+    nickname,
+    gender,
+    birthday,
+    provinceCode: fullLocationCode[0],
+    cityCode: fullLocationCode[1],
+    countyCode: fullLocationCode[2],
+  })
+  // 更新Store昵称
+  memberStore.profile!.nickname = res.result.nickname
+  uni.showToast({ icon: 'success', title: '保存成功' })
+  setTimeout(() => {
+    uni.navigateBack()
+  }, 400)
+}
+</script>
+
+<template>
+  <view class="viewport">
+    <!-- 导航栏 -->
+    <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
+      <navigator open-type="navigateBack" class="back icon-left" hover-class="none"></navigator>
+      <view class="title">个人信息</view>
+    </view>
+    <!-- 头像 -->
+    <view class="avatar">
+      <view @tap="onAvatarChange" class="avatar-content">
+        <image class="image" :src="profile?.avatar" mode="aspectFill" />
+        <text class="text">点击修改头像</text>
+      </view>
+    </view>
+    <!-- 表单 -->
+    <view class="form">
+      <!-- 表单内容 -->
+      <view class="form-content">
+        <view class="form-item">
+          <text class="label">账号</text>
+          <text class="account placeholder">{{ profile?.account }}</text>
+        </view>
+        <view class="form-item">
+          <text class="label">昵称</text>
+          <input class="input" type="text" placeholder="请填写昵称" v-model="profile!.nickname" />
+        </view>
+        <view class="form-item">
+          <text class="label">性别</text>
+          <radio-group @change="onGenderChange">
+            <label class="radio">
+              <radio value="男" color="#27ba9b" :checked="profile?.gender === '男'" />
+              男
+            </label>
+            <label class="radio">
+              <radio value="女" color="#27ba9b" :checked="profile?.gender === '女'" />
+              女
+            </label>
+          </radio-group>
+        </view>
+        <view class="form-item">
+          <text class="label">生日</text>
+          <picker
+            @change="onBirthdayChange"
+            mode="date"
+            class="picker"
+            :value="profile?.birthday"
+            start="1900-01-01"
+            :end="formatDate(new Date())"
+          >
+            <view v-if="profile?.birthday">{{ profile?.birthday }}</view>
+            <view class="placeholder" v-else>请选择日期</view>
+          </picker>
+        </view>
+        <!-- 只有微信小程序端内置了省市区数据 -->
+        <!-- #ifdef MP-WEIXIN -->
+        <view class="form-item">
+          <text class="label">城市</text>
+          <picker
+            @change="onFullLocationChange"
+            mode="region"
+            class="picker"
+            :value="profile?.fullLocation?.split(' ')"
+          >
+            <view v-if="profile?.fullLocation">{{ profile.fullLocation }}</view>
+            <view class="placeholder" v-else>请选择城市</view>
+          </picker>
+        </view>
+        <!-- #endif -->
+        <view class="form-item">
+          <text class="label">职业</text>
+          <input class="input" type="text" placeholder="请填写职业" :value="profile?.profession" />
+        </view>
+      </view>
+      <!-- 提交按钮 -->
+      <button @tap="onSubmit" class="form-button">保 存</button>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+page {
+  background-color: #f4f4f4;
+}
+
+.viewport {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/order_bg.png);
+  background-size: auto 420rpx;
+  background-repeat: no-repeat;
+}
+
+// 导航栏
+.navbar {
+  position: relative;
+
+  .title {
+    height: 40px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    font-size: 16px;
+    font-weight: 500;
+    color: #fff;
+  }
+
+  .back {
+    position: absolute;
+    height: 40px;
+    width: 40px;
+    left: 0;
+    font-size: 20px;
+    color: #fff;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+}
+
+// 头像
+.avatar {
+  text-align: center;
+  width: 100%;
+  height: 260rpx;
+  display: flex;
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+
+  .image {
+    width: 160rpx;
+    height: 160rpx;
+    border-radius: 50%;
+    background-color: #eee;
+  }
+
+  .text {
+    display: block;
+    padding-top: 20rpx;
+    line-height: 1;
+    font-size: 26rpx;
+    color: #fff;
+  }
+}
+
+// 表单
+.form {
+  background-color: #f4f4f4;
+
+  &-content {
+    margin: 20rpx 20rpx 0;
+    padding: 0 20rpx;
+    border-radius: 10rpx;
+    background-color: #fff;
+  }
+
+  &-item {
+    display: flex;
+    height: 96rpx;
+    line-height: 46rpx;
+    padding: 25rpx 10rpx;
+    background-color: #fff;
+    font-size: 28rpx;
+    border-bottom: 1rpx solid #ddd;
+
+    &:last-child {
+      border: none;
+    }
+
+    .label {
+      width: 180rpx;
+      color: #333;
+    }
+
+    .account {
+      color: #666;
+    }
+
+    .input {
+      flex: 1;
+      display: block;
+      height: 46rpx;
+    }
+
+    .radio {
+      margin-right: 20rpx;
+    }
+
+    .picker {
+      flex: 1;
+    }
+    .placeholder {
+      color: #808080;
+    }
+  }
+
+  &-button {
+    height: 80rpx;
+    text-align: center;
+    line-height: 80rpx;
+    margin: 30rpx 20rpx;
+    color: #fff;
+    border-radius: 80rpx;
+    font-size: 30rpx;
+    background-color: #27ba9b;
+  }
+}
+</style>

+ 112 - 0
src/pagesMember/settings/settings.vue

@@ -0,0 +1,112 @@
+<script setup lang="ts">
+import { useMemberStore } from '@/stores'
+
+const memberStore = useMemberStore()
+// 退出登录
+const onLogout = () => {
+  // 模态弹窗
+  uni.showModal({
+    content: '是否退出登录?',
+    confirmColor: '#27BA9B',
+    success: (res) => {
+      if (res.confirm) {
+        // 清理用户信息
+        memberStore.clearProfile()
+        // 返回上一页
+        uni.navigateBack()
+      }
+    },
+  })
+}
+</script>
+
+<template>
+  <view class="viewport">
+    <!-- 列表1 -->
+    <view class="list" v-if="memberStore.profile">
+      <navigator url="/pagesMember/address/address" hover-class="none" class="item arrow">
+        我的收货地址
+      </navigator>
+    </view>
+    <!-- #ifdef MP-WEIXIN -->
+    <!-- 列表2 -->
+    <view class="list">
+      <button hover-class="none" class="item arrow" open-type="openSetting">授权管理</button>
+      <button hover-class="none" class="item arrow" open-type="feedback">问题反馈</button>
+      <button hover-class="none" class="item arrow" open-type="contact">联系我们</button>
+    </view>
+    <!-- #endif -->
+    <!-- 列表3 -->
+    <view class="list">
+      <button hover-class="none" class="item arrow">关于小兔鲜儿</button>
+    </view>
+    <!-- 操作按钮 -->
+    <view class="action" v-if="memberStore.profile">
+      <view @tap="onLogout" class="button">退出登录</view>
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+page {
+  background-color: #f4f4f4;
+}
+
+.viewport {
+  padding: 20rpx;
+}
+
+/* 列表 */
+.list {
+  padding: 0 20rpx;
+  background-color: #fff;
+  margin-bottom: 20rpx;
+  border-radius: 10rpx;
+  .item {
+    line-height: 90rpx;
+    padding-left: 10rpx;
+    font-size: 30rpx;
+    color: #333;
+    border-top: 1rpx solid #ddd;
+    position: relative;
+    text-align: left;
+    border-radius: 0;
+    background-color: #fff;
+    &::after {
+      width: auto;
+      height: auto;
+      left: auto;
+      border: none;
+    }
+    &:first-child {
+      border: none;
+    }
+    &::after {
+      right: 5rpx;
+    }
+  }
+  .arrow::after {
+    content: '\e6c2';
+    position: absolute;
+    top: 50%;
+    color: #ccc;
+    font-family: 'erabbit' !important;
+    font-size: 32rpx;
+    transform: translateY(-50%);
+  }
+}
+
+/* 操作按钮 */
+.action {
+  text-align: center;
+  line-height: 90rpx;
+  margin-top: 40rpx;
+  font-size: 32rpx;
+  color: #333;
+  .button {
+    background-color: #fff;
+    margin-bottom: 20rpx;
+    border-radius: 10rpx;
+  }
+}
+</style>

+ 392 - 0
src/pagesOrder/create/create.vue

@@ -0,0 +1,392 @@
+<script setup lang="ts">
+import {
+  getMemberOrderPreAPI,
+  getMemberOrderPreNowAPI,
+  getMemberOrderRepurchaseByIdAPI,
+  postMemberOrderAPI,
+} from '@/services/order'
+import { useAddressStore } from '@/stores/modules/address'
+import type { OrderPreResult } from '@/types/order'
+import { onLoad } from '@dcloudio/uni-app'
+import { computed, ref } from 'vue'
+
+// 获取屏幕边界到安全区域距离
+const { safeAreaInsets } = uni.getSystemInfoSync()
+// 订单备注
+const buyerMessage = ref('')
+// 配送时间
+const deliveryList = ref([
+  { type: 1, text: '时间不限 (周一至周日)' },
+  { type: 2, text: '工作日送 (周一至周五)' },
+  { type: 3, text: '周末配送 (周六至周日)' },
+])
+// 当前配送时间下标
+const activeIndex = ref(0)
+// 当前配送时间
+const activeDelivery = computed(() => deliveryList.value[activeIndex.value])
+// 修改配送时间
+const onChangeDelivery: UniHelper.SelectorPickerOnChange = (ev) => {
+  activeIndex.value = ev.detail.value
+}
+
+// 页面参数
+const query = defineProps<{
+  skuId?: string
+  count?: string
+  orderId?: string
+}>()
+
+// 获取订单信息
+const orderPre = ref<OrderPreResult>()
+const getMemberOrderPreData = async () => {
+  if (query.count && query.skuId) {
+    const res = await getMemberOrderPreNowAPI({
+      count: query.count,
+      skuId: query.skuId,
+    })
+    orderPre.value = res.result
+  } else if (query.orderId) {
+    // 再次购买
+    const res = await getMemberOrderRepurchaseByIdAPI(query.orderId)
+    orderPre.value = res.result
+  } else {
+    const res = await getMemberOrderPreAPI()
+    orderPre.value = res.result
+  }
+}
+
+onLoad(() => {
+  getMemberOrderPreData()
+})
+
+const addressStore = useAddressStore()
+// 收货地址
+const selecteAddress = computed(() => {
+  return addressStore.selectedAddress || orderPre.value?.userAddresses.find((v) => v.isDefault)
+})
+
+// 提交订单
+const onOrderSubmit = async () => {
+  // 没有收货地址提醒
+  if (!selecteAddress.value?.id) {
+    return uni.showToast({ icon: 'none', title: '请选择收货地址' })
+  }
+  // 发送请求
+  const res = await postMemberOrderAPI({
+    addressId: selecteAddress.value?.id,
+    buyerMessage: buyerMessage.value,
+    deliveryTimeType: activeDelivery.value.type,
+    goods: orderPre.value!.goods.map((v) => ({ count: v.count, skuId: v.skuId })),
+    payChannel: 2,
+    payType: 1,
+  })
+  // 关闭当前页面,跳转到订单详情,传递订单id
+  uni.redirectTo({ url: `/pagesOrder/detail/detail?id=${res.result.id}` })
+}
+</script>
+
+<template>
+  <scroll-view enable-back-to-top scroll-y class="viewport">
+    <!-- 收货地址 -->
+    <navigator
+      v-if="selecteAddress"
+      class="shipment"
+      hover-class="none"
+      url="/pagesMember/address/address?from=order"
+    >
+      <view class="user"> {{ selecteAddress.receiver }} {{ selecteAddress.contact }} </view>
+      <view class="address"> {{ selecteAddress.fullLocation }} {{ selecteAddress.address }} </view>
+      <text class="icon icon-right"></text>
+    </navigator>
+    <navigator
+      v-else
+      class="shipment"
+      hover-class="none"
+      url="/pagesMember/address/address?from=order"
+    >
+      <view class="address"> 请选择收货地址 </view>
+      <text class="icon icon-right"></text>
+    </navigator>
+
+    <!-- 商品信息 -->
+    <view class="goods">
+      <navigator
+        v-for="item in orderPre?.goods"
+        :key="item.skuId"
+        :url="`/pages/goods/goods?id=${item.id}`"
+        class="item"
+        hover-class="none"
+      >
+        <image class="picture" :src="item.picture" />
+        <view class="meta">
+          <view class="name ellipsis"> {{ item.name }} </view>
+          <view class="attrs">{{ item.attrsText }}</view>
+          <view class="prices">
+            <view class="pay-price symbol">{{ item.payPrice }}</view>
+            <view class="price symbol">{{ item.price }}</view>
+          </view>
+          <view class="count">x{{ item.count }}</view>
+        </view>
+      </navigator>
+    </view>
+
+    <!-- 配送及支付方式 -->
+    <view class="related">
+      <view class="item">
+        <text class="text">配送时间</text>
+        <picker :range="deliveryList" range-key="text" @change="onChangeDelivery">
+          <view class="icon-fonts picker">{{ activeDelivery.text }}</view>
+        </picker>
+      </view>
+      <view class="item">
+        <text class="text">订单备注</text>
+        <input
+          class="input"
+          :cursor-spacing="30"
+          placeholder="选题,建议留言前先与商家沟通确认"
+          v-model="buyerMessage"
+        />
+      </view>
+    </view>
+
+    <!-- 支付金额 -->
+    <view class="settlement">
+      <view class="item">
+        <text class="text">商品总价: </text>
+        <text class="number symbol">{{ orderPre?.summary.totalPrice.toFixed(2) }}</text>
+      </view>
+      <view class="item">
+        <text class="text">运费: </text>
+        <text class="number symbol">{{ orderPre?.summary.postFee.toFixed(2) }}</text>
+      </view>
+    </view>
+  </scroll-view>
+
+  <!-- 吸底工具栏 -->
+  <view class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
+    <view class="total-pay symbol">
+      <text class="number">{{ orderPre?.summary.totalPayPrice.toFixed(2) }}</text>
+    </view>
+    <view class="button" :class="{ disabled: !selecteAddress?.id }" @tap="onOrderSubmit">
+      提交订单
+    </view>
+  </view>
+</template>
+
+<style lang="scss">
+page {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  overflow: hidden;
+  background-color: #f4f4f4;
+}
+
+.symbol::before {
+  content: '¥';
+  font-size: 80%;
+  margin-right: 5rpx;
+}
+
+.shipment {
+  margin: 20rpx;
+  padding: 30rpx 30rpx 30rpx 84rpx;
+  font-size: 26rpx;
+  border-radius: 10rpx;
+  background: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/locate.png)
+    20rpx center / 50rpx no-repeat #fff;
+  position: relative;
+
+  .icon {
+    font-size: 36rpx;
+    color: #333;
+    transform: translateY(-50%);
+    position: absolute;
+    top: 50%;
+    right: 20rpx;
+  }
+
+  .user {
+    color: #333;
+    margin-bottom: 5rpx;
+  }
+
+  .address {
+    color: #666;
+  }
+}
+
+.goods {
+  margin: 20rpx;
+  padding: 0 20rpx;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .item {
+    display: flex;
+    padding: 30rpx 0;
+    border-top: 1rpx solid #eee;
+
+    &:first-child {
+      border-top: none;
+    }
+
+    .picture {
+      width: 170rpx;
+      height: 170rpx;
+      border-radius: 10rpx;
+      margin-right: 20rpx;
+    }
+
+    .meta {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      position: relative;
+    }
+
+    .name {
+      height: 80rpx;
+      font-size: 26rpx;
+      color: #444;
+    }
+
+    .attrs {
+      line-height: 1.8;
+      padding: 0 15rpx;
+      margin-top: 6rpx;
+      font-size: 24rpx;
+      align-self: flex-start;
+      border-radius: 4rpx;
+      color: #888;
+      background-color: #f7f7f8;
+    }
+
+    .prices {
+      display: flex;
+      align-items: baseline;
+      margin-top: 6rpx;
+      font-size: 28rpx;
+
+      .pay-price {
+        margin-right: 10rpx;
+        color: #cf4444;
+      }
+
+      .price {
+        font-size: 24rpx;
+        color: #999;
+        text-decoration: line-through;
+      }
+    }
+
+    .count {
+      position: absolute;
+      bottom: 0;
+      right: 0;
+      font-size: 26rpx;
+      color: #444;
+    }
+  }
+}
+
+.related {
+  margin: 20rpx;
+  padding: 0 20rpx;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .item {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    min-height: 80rpx;
+    font-size: 26rpx;
+    color: #333;
+  }
+
+  .input {
+    flex: 1;
+    text-align: right;
+    margin: 20rpx 0;
+    padding-right: 20rpx;
+    font-size: 26rpx;
+    color: #999;
+  }
+
+  .item .text {
+    width: 125rpx;
+  }
+
+  .picker {
+    color: #666;
+  }
+
+  .picker::after {
+    content: '\e6c2';
+  }
+}
+
+/* 结算清单 */
+.settlement {
+  margin: 20rpx;
+  padding: 0 20rpx;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .item {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    height: 80rpx;
+    font-size: 26rpx;
+    color: #333;
+  }
+
+  .danger {
+    color: #cf4444;
+  }
+}
+
+/* 吸底工具栏 */
+.toolbar {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: calc(var(--window-bottom));
+  z-index: 1;
+
+  background-color: #fff;
+  height: 100rpx;
+  padding: 0 20rpx;
+  border-top: 1rpx solid #eaeaea;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  box-sizing: content-box;
+
+  .total-pay {
+    font-size: 40rpx;
+    color: #cf4444;
+
+    .decimal {
+      font-size: 75%;
+    }
+  }
+
+  .button {
+    width: 220rpx;
+    text-align: center;
+    line-height: 72rpx;
+    font-size: 26rpx;
+    color: #fff;
+    border-radius: 72rpx;
+    background-color: #27ba9b;
+  }
+
+  .disabled {
+    opacity: 0.6;
+  }
+}
+</style>

+ 430 - 0
src/pagesOrder/detail/components/PageSkeleton.vue

@@ -0,0 +1,430 @@
+<template name="skeleton">
+  <view class="sk-container">
+    <scroll-view
+      :scroll-y="true"
+      class="viewport sk-transparent"
+      id="scroller"
+      :enable-back-to-top="true"
+    >
+      <view class="overview sk-image" style="padding-top: 64px">
+        <view class="status sk-transparent sk-text-0-0000-826 sk-text">待收货</view>
+      </view>
+      <view class="shipment">
+        <navigator class="logistics sk-image sk-pseudo sk-pseudo-circle" hover-class="none">
+          <view class="message sk-transparent sk-text-14-2857-512 sk-text"
+            >小兔兔到了小福家里,请签收</view
+          >
+          <view class="date sk-transparent sk-text-14-2857-990 sk-text">2023-04-15 23:23:04</view>
+        </navigator>
+        <view class="locate sk-image">
+          <view class="user sk-transparent sk-text-14-2857-630 sk-text">苏东坡 13633336666</view>
+          <view class="address sk-transparent sk-text-14-2857-606 sk-text"
+            >广东省 广州市 天河区吉山幼儿园</view
+          >
+        </view>
+      </view>
+      <view class="goods">
+        <view class="item">
+          <navigator class="navigator" hover-class="none">
+            <image class="cover sk-image"></image>
+            <view class="meta">
+              <view class="name ellipsis sk-transparent sk-text-14-2857-474 sk-text"
+                >厚厚一按就干爽,埃及进口长绒棉毛巾</view
+              >
+              <view class="type sk-transparent sk-text-22-2222-237 sk-text"
+                >超值4条装(灰蓝色+粉色+银灰+嫩黄)</view
+              >
+              <view class="price">
+                <view class="actual">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="sk-transparent sk-text-14-2857-102 sk-text">68</text>
+                </view>
+              </view>
+              <view class="quantity sk-transparent sk-opacity">x1</view>
+            </view>
+          </navigator>
+          <navigator class="navigator" hover-class="none">
+            <image class="cover sk-image"></image>
+            <view class="meta">
+              <view class="name ellipsis sk-transparent sk-text-14-2857-969 sk-text"
+                >KJE金属色系轻量电动车骑行盔男女通用</view
+              >
+              <view class="type sk-transparent sk-text-22-2222-510 sk-text">玫瑰金L</view>
+              <view class="price">
+                <view class="actual">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="sk-transparent sk-text-14-2857-431 sk-text">120</text>
+                </view>
+              </view>
+              <view class="quantity sk-transparent sk-opacity">x1</view>
+            </view>
+          </navigator>
+          <navigator class="navigator" hover-class="none">
+            <image class="cover sk-image"></image>
+            <view class="meta">
+              <view class="name ellipsis sk-transparent sk-text-14-2857-130 sk-text"
+                >源自澳洲进口羊毛,儿童奢暖羊毛被升级款</view
+              >
+              <view class="type sk-transparent sk-text-22-2222-110 sk-text"
+                >春秋款, 100%羊毛款:150x200cm,适合1.2米/1.35米床</view
+              >
+              <view class="price">
+                <view class="actual">
+                  <text class="symbol sk-transparent sk-opacity">¥</text>
+                  <text class="sk-transparent sk-text-14-2857-273 sk-text">289</text>
+                </view>
+              </view>
+              <view class="quantity sk-transparent sk-opacity">x1</view>
+            </view>
+          </navigator>
+        </view>
+        <view class="total">
+          <view class="row">
+            <view class="text sk-transparent sk-text-0-0000-302 sk-text">商品总价: </view>
+            <view
+              class="symbol sk-transparent sk-text-0-0000-998 sk-text sk-pseudo sk-pseudo-circle"
+              >477</view
+            >
+          </view>
+          <view class="row">
+            <view class="text sk-transparent sk-text-0-0000-912 sk-text">运费: </view>
+            <view
+              class="symbol sk-transparent sk-text-0-0000-208 sk-text sk-pseudo sk-pseudo-circle"
+              >2</view
+            >
+          </view>
+          <view class="row">
+            <view class="text sk-transparent sk-text-0-0000-538 sk-text">应付金额: </view>
+            <view
+              class="symbol primary sk-transparent sk-text-0-0000-858 sk-text sk-pseudo sk-pseudo-circle"
+              >479</view
+            >
+          </view>
+        </view>
+      </view>
+      <view class="detail">
+        <view class="title sk-transparent sk-text-0-0000-66 sk-text">订单信息</view>
+        <view class="row">
+          <view class="item sk-transparent">
+            订单编号: 1645809639951962113
+            <text class="copy sk-transparent sk-text-0-0000-522 sk-text">复制</text>
+          </view>
+          <view class="item sk-transparent sk-text-0-0000-353 sk-text"
+            >下单时间: 2023-04-11 23:22:50</view
+          >
+        </view>
+      </view>
+
+      <view class="toolbar" style="padding-bottom: 34px">
+        <view
+          class="button primary sk-transparent sk-text-31-9444-411 sk-text"
+          style="background-position-x: 50%"
+          >再次购买</view
+        >
+      </view>
+    </scroll-view>
+  </view>
+</template>
+
+<style>
+.sk-transparent {
+  color: transparent !important;
+}
+.sk-text-14-2857-107 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 44.8rpx;
+  position: relative !important;
+}
+.sk-text {
+  background-origin: content-box !important;
+  background-clip: content-box !important;
+  background-color: transparent !important;
+  color: transparent !important;
+  background-repeat: repeat-y !important;
+}
+.sk-text-0-0000-826 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-512 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-990 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-630 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-606 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-474 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-22-2222-237 {
+  background-image: linear-gradient(
+    transparent 22.2222%,
+    #eeeeee 0%,
+    #eeeeee 77.7778%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 43.2rpx;
+  position: relative !important;
+}
+.sk-opacity {
+  opacity: 0 !important;
+}
+.sk-text-14-2857-102 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-969 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-22-2222-510 {
+  background-image: linear-gradient(
+    transparent 22.2222%,
+    #eeeeee 0%,
+    #eeeeee 77.7778%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 43.2rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-431 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-130 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36.4rpx;
+  position: relative !important;
+}
+.sk-text-22-2222-110 {
+  background-image: linear-gradient(
+    transparent 22.2222%,
+    #eeeeee 0%,
+    #eeeeee 77.7778%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 43.2rpx;
+  position: relative !important;
+}
+.sk-text-14-2857-273 {
+  background-image: linear-gradient(
+    transparent 14.2857%,
+    #eeeeee 0%,
+    #eeeeee 85.7143%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 33.6rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-302 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 26rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-998 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 26rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-912 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 26rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-208 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 26rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-538 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 26rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-858 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 36rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-66 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 30rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-522 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 20rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-353 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 26rpx;
+  position: relative !important;
+}
+.sk-text-0-0000-375 {
+  background-image: linear-gradient(
+    transparent 0%,
+    #eeeeee 0%,
+    #eeeeee 100%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 32rpx;
+  position: relative !important;
+}
+.sk-text-31-9444-411 {
+  background-image: linear-gradient(
+    transparent 31.9444%,
+    #eeeeee 0%,
+    #eeeeee 68.0556%,
+    transparent 0%
+  ) !important;
+  background-size: 100% 72rpx;
+  position: relative !important;
+}
+.sk-image {
+  background: #efefef !important;
+}
+.sk-pseudo::before,
+.sk-pseudo::after {
+  background: #efefef !important;
+  background-image: none !important;
+  color: transparent !important;
+  border-color: transparent !important;
+}
+.sk-pseudo-rect::before,
+.sk-pseudo-rect::after {
+  border-radius: 0 !important;
+}
+.sk-pseudo-circle::before,
+.sk-pseudo-circle::after {
+  border-radius: 50% !important;
+}
+.sk-container {
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  background-color: transparent;
+}
+</style>

+ 839 - 0
src/pagesOrder/detail/detail.vue

@@ -0,0 +1,839 @@
+<script setup lang="ts">
+import { useGuessList } from '@/composables'
+import { OrderState, orderStateList } from '@/services/constants'
+import {
+  deleteMemberOrderAPI,
+  getMemberOrderByIdAPI,
+  getMemberOrderCancelByIdAPI,
+  getMemberOrderLogisticsByIdAPI,
+  getMemberOrderConsignmentByIdAPI,
+  putMemberOrderReceiptByIdAPI,
+} from '@/services/order'
+import type { LogisticItem, OrderResult } from '@/types/order'
+import { onLoad, onReady } from '@dcloudio/uni-app'
+import { ref } from 'vue'
+import PageSkeleton from './components/PageSkeleton.vue'
+import { getPayMockAPI, getPayWxPayMiniPayAPI } from '@/services/pay'
+
+// 获取屏幕边界到安全区域距离
+const { safeAreaInsets } = uni.getSystemInfoSync()
+// 猜你喜欢
+const { guessRef, onScrolltolower } = useGuessList()
+// 弹出层组件
+const popup = ref<UniHelper.UniPopupInstance>()
+// 取消原因列表
+const reasonList = ref([
+  '商品无货',
+  '不想要了',
+  '商品信息填错了',
+  '地址信息填写错误',
+  '商品降价',
+  '其它',
+])
+// 订单取消原因
+const reason = ref('')
+// 复制内容
+const onCopy = (id: string) => {
+  // 设置系统剪贴板的内容
+  uni.setClipboardData({ data: id })
+}
+// 获取页面参数
+const query = defineProps<{
+  id: string
+}>()
+
+// 获取页面栈
+const pages = getCurrentPages()
+
+// 基于小程序的 Page 类型扩展 uni-app 的 Page
+type PageInstance = Page.PageInstance & WechatMiniprogram.Page.InstanceMethods<any>
+
+// #ifdef MP-WEIXIN
+// 获取当前页面实例,数组最后一项
+const pageInstance = pages.at(-1) as PageInstance
+
+// 页面渲染完毕,绑定动画效果
+onReady(() => {
+  // 动画效果,导航栏背景色
+  pageInstance.animate(
+    '.navbar',
+    [{ backgroundColor: 'transparent' }, { backgroundColor: '#f8f8f8' }],
+    1000,
+    {
+      scrollSource: '#scroller',
+      timeRange: 1000,
+      startScrollOffset: 0,
+      endScrollOffset: 50,
+    },
+  )
+  // 动画效果,导航栏标题
+  pageInstance.animate('.navbar .title', [{ color: 'transparent' }, { color: '#000' }], 1000, {
+    scrollSource: '#scroller',
+    timeRange: 1000,
+    startScrollOffset: 0,
+    endScrollOffset: 50,
+  })
+  // 动画效果,导航栏返回按钮
+  pageInstance.animate('.navbar .back', [{ color: '#fff' }, { color: '#000' }], 1000, {
+    scrollSource: '#scroller',
+    timeRange: 1000,
+    startScrollOffset: 0,
+    endScrollOffset: 50,
+  })
+})
+// #endif
+
+// 获取订单详情
+const order = ref<OrderResult>()
+const getMemberOrderByIdData = async () => {
+  const res = await getMemberOrderByIdAPI(query.id)
+  order.value = res.result
+  if (
+    [OrderState.DaiShouHuo, OrderState.DaiPingJia, OrderState.YiWanCheng].includes(
+      order.value.orderState,
+    )
+  ) {
+    getMemberOrderLogisticsByIdData()
+  }
+}
+
+// 获取物流信息
+const logisticList = ref<LogisticItem[]>([])
+const getMemberOrderLogisticsByIdData = async () => {
+  const res = await getMemberOrderLogisticsByIdAPI(query.id)
+  logisticList.value = res.result.list
+}
+
+onLoad(() => {
+  getMemberOrderByIdData()
+})
+
+// 倒计时结束事件
+const onTimeup = () => {
+  // 修改订单状态为已取消
+  order.value!.orderState = OrderState.YiQuXiao
+}
+
+// 订单支付
+const onOrderPay = async () => {
+  if (import.meta.env.DEV) {
+    // 开发环境模拟支付
+    await getPayMockAPI({ orderId: query.id })
+  } else {
+    // #ifdef MP-WEIXIN
+    // 正式环境微信支付
+    const res = await getPayWxPayMiniPayAPI({ orderId: query.id })
+    await wx.requestPayment(res.result)
+    // #endif
+
+    // #ifdef H5 || APP-PLUS
+    // H5端 和 App 端未开通支付-模拟支付体验
+    await getPayMockAPI({ orderId: query.id })
+    // #endif
+  }
+  // 关闭当前页,再跳转支付结果页
+  uni.redirectTo({ url: `/pagesOrder/payment/payment?id=${query.id}` })
+}
+
+// 是否为开发环境
+const isDev = import.meta.env.DEV
+// 模拟发货
+const onOrderSend = async () => {
+  if (isDev) {
+    await getMemberOrderConsignmentByIdAPI(query.id)
+    uni.showToast({ icon: 'success', title: '模拟发货完成' })
+    // 主动更新订单状态
+    order.value!.orderState = OrderState.DaiShouHuo
+  }
+}
+// 确认收货
+const onOrderConfirm = () => {
+  // 二次确认弹窗
+  uni.showModal({
+    content: '为保障您的权益,请收到货并确认无误后,再确认收货',
+    confirmColor: '#27BA9B',
+    success: async (success) => {
+      if (success.confirm) {
+        const res = await putMemberOrderReceiptByIdAPI(query.id)
+        // 更新订单状态
+        order.value = res.result
+      }
+    },
+  })
+}
+// 删除订单
+const onOrderDelete = () => {
+  // 二次确认
+  uni.showModal({
+    content: '是否删除订单',
+    confirmColor: '#27BA9B',
+    success: async (success) => {
+      if (success.confirm) {
+        await deleteMemberOrderAPI({ ids: [query.id] })
+        uni.redirectTo({ url: '/pagesOrder/list/list' })
+      }
+    },
+  })
+}
+
+// 取消订单
+const onOrderCancel = async () => {
+  // 发送请求
+  const res = await getMemberOrderCancelByIdAPI(query.id, { cancelReason: reason.value })
+  // 更新订单信息
+  order.value = res.result
+  // 关闭弹窗
+  popup.value?.close!()
+  // 轻提示
+  uni.showToast({ icon: 'none', title: '订单取消成功' })
+}
+</script>
+
+<template>
+  <!-- 自定义导航栏: 默认透明不可见, scroll-view 滚动到 50 时展示 -->
+  <view class="navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
+    <view class="wrap">
+      <navigator
+        v-if="pages.length > 1"
+        open-type="navigateBack"
+        class="back icon-left"
+      ></navigator>
+      <navigator v-else url="/pages/index/index" open-type="switchTab" class="back icon-home">
+      </navigator>
+      <view class="title">订单详情</view>
+    </view>
+  </view>
+  <scroll-view
+    enable-back-to-top
+    scroll-y
+    class="viewport"
+    id="scroller"
+    @scrolltolower="onScrolltolower"
+  >
+    <template v-if="order">
+      <!-- 订单状态 -->
+      <view class="overview" :style="{ paddingTop: safeAreaInsets!.top + 20 + 'px' }">
+        <!-- 待付款状态:展示倒计时 -->
+        <template v-if="order.orderState === OrderState.DaiFuKuan">
+          <view class="status icon-clock">等待付款</view>
+          <view class="tips">
+            <text class="money">应付金额: ¥ {{ order.payMoney }}</text>
+            <text class="time">支付剩余</text>
+            <uni-countdown
+              :second="order.countdown"
+              color="#fff"
+              splitor-color="#fff"
+              :show-day="false"
+              :show-colon="false"
+              @timeup="onTimeup"
+            />
+          </view>
+          <view class="button" @tap="onOrderPay">去支付</view>
+        </template>
+        <!-- 其他订单状态:展示再次购买按钮 -->
+        <template v-else>
+          <!-- 订单状态文字 -->
+          <view class="status"> {{ orderStateList[order.orderState].text }} </view>
+          <view class="button-group">
+            <navigator
+              class="button"
+              :url="`/pagesOrder/create/create?orderId=${query.id}`"
+              hover-class="none"
+            >
+              再次购买
+            </navigator>
+            <!-- 待发货状态:模拟发货,开发期间使用,用于修改订单状态为已发货 -->
+            <view
+              v-if="isDev && order.orderState == OrderState.DaiFaHuo"
+              @tap="onOrderSend"
+              class="button"
+            >
+              模拟发货
+            </view>
+            <!-- 待收货状态: 展示确认收货按钮 -->
+            <view
+              v-if="order.orderState === OrderState.DaiShouHuo"
+              @tap="onOrderConfirm"
+              class="button"
+            >
+              确认收货
+            </view>
+          </view>
+        </template>
+      </view>
+      <!-- 配送状态 -->
+      <view class="shipment">
+        <!-- 订单物流信息 -->
+        <view v-for="item in logisticList" :key="item.id" class="item">
+          <view class="message">
+            {{ item.text }}
+          </view>
+          <view class="date"> {{ item.time }} </view>
+        </view>
+        <!-- 用户收货地址 -->
+        <view class="locate">
+          <view class="user"> {{ order.receiverContact }} {{ order.receiverMobile }} </view>
+          <view class="address"> {{ order.receiverAddress }} </view>
+        </view>
+      </view>
+
+      <!-- 商品信息 -->
+      <view class="goods">
+        <view class="item">
+          <navigator
+            class="navigator"
+            v-for="item in order.skus"
+            :key="item.id"
+            :url="`/pages/goods/goods?id=${item.spuId}`"
+            hover-class="none"
+          >
+            <image class="cover" :src="item.image"></image>
+            <view class="meta">
+              <view class="name ellipsis">{{ item.name }}</view>
+              <view class="type">{{ item.attrsText }}</view>
+              <view class="price">
+                <view class="actual">
+                  <text class="symbol">¥</text>
+                  <text>{{ item.curPrice }}</text>
+                </view>
+              </view>
+              <view class="quantity">x{{ item.quantity }}</view>
+            </view>
+          </navigator>
+          <!-- 待评价状态:展示按钮 -->
+          <view class="action" v-if="order.orderState === OrderState.DaiPingJia">
+            <view class="button primary">申请售后</view>
+            <navigator url="" class="button"> 去评价 </navigator>
+          </view>
+        </view>
+        <!-- 合计 -->
+        <view class="total">
+          <view class="row">
+            <view class="text">商品总价: </view>
+            <view class="symbol">{{ order.totalMoney }}</view>
+          </view>
+          <view class="row">
+            <view class="text">运费: </view>
+            <view class="symbol">{{ order.postFee }}</view>
+          </view>
+          <view class="row">
+            <view class="text">应付金额: </view>
+            <view class="symbol primary">{{ order.payMoney }}</view>
+          </view>
+        </view>
+      </view>
+
+      <!-- 订单信息 -->
+      <view class="detail">
+        <view class="title">订单信息</view>
+        <view class="row">
+          <view class="item">
+            订单编号: {{ query.id }} <text class="copy" @tap="onCopy(query.id)">复制</text>
+          </view>
+          <view class="item">下单时间: {{ order.createTime }}</view>
+        </view>
+      </view>
+
+      <!-- 猜你喜欢 -->
+      <XtxGuess ref="guessRef" />
+
+      <!-- 底部操作栏 -->
+      <view class="toolbar-height" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }"></view>
+      <view class="toolbar" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
+        <!-- 待付款状态:展示支付按钮 -->
+        <template v-if="order.orderState === OrderState.DaiFuKuan">
+          <view class="button primary" @tap="onOrderPay"> 去支付 </view>
+          <view class="button" @tap="popup?.open?.()"> 取消订单 </view>
+        </template>
+        <!-- 其他订单状态:按需展示按钮 -->
+        <template v-else>
+          <navigator
+            class="button secondary"
+            :url="`/pagesOrder/create/create?orderId=${query.id}`"
+            hover-class="none"
+          >
+            再次购买
+          </navigator>
+          <!-- 待收货状态: 展示确认收货 -->
+          <view
+            class="button primary"
+            v-if="order.orderState === OrderState.DaiShouHuo"
+            @tap="onOrderConfirm"
+          >
+            确认收货
+          </view>
+          <!-- 待评价状态: 展示去评价 -->
+          <view class="button" v-if="order.orderState === OrderState.DaiPingJia"> 去评价 </view>
+          <!-- 待评价/已完成/已取消 状态: 展示删除订单 -->
+          <view
+            class="button delete"
+            v-if="order.orderState >= OrderState.DaiPingJia"
+            @tap="onOrderDelete"
+          >
+            删除订单
+          </view>
+        </template>
+      </view>
+    </template>
+    <template v-else>
+      <!-- 骨架屏组件 -->
+      <PageSkeleton />
+    </template>
+  </scroll-view>
+  <!-- 取消订单弹窗 -->
+  <uni-popup ref="popup" type="bottom" background-color="#fff">
+    <view class="popup-root">
+      <view class="title">订单取消</view>
+      <view class="description">
+        <view class="tips">请选择取消订单的原因:</view>
+        <view class="cell" v-for="item in reasonList" :key="item" @tap="reason = item">
+          <text class="text">{{ item }}</text>
+          <text class="icon" :class="{ checked: item === reason }"></text>
+        </view>
+      </view>
+      <view class="footer">
+        <view class="button" @tap="popup?.close?.()">取消</view>
+        <view class="button primary" @tap="onOrderCancel">确认</view>
+      </view>
+    </view>
+  </uni-popup>
+</template>
+
+<style lang="scss">
+page {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  overflow: hidden;
+}
+
+.navbar {
+  width: 750rpx;
+  color: #000;
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 9;
+  /* background-color: #f8f8f8; */
+  background-color: transparent;
+
+  .wrap {
+    position: relative;
+
+    .title {
+      height: 44px;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      font-size: 32rpx;
+      /* color: #000; */
+      color: transparent;
+    }
+
+    .back {
+      position: absolute;
+      left: 0;
+      height: 44px;
+      width: 44px;
+      font-size: 44rpx;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      /* color: #000; */
+      color: #fff;
+    }
+  }
+}
+
+.viewport {
+  background-color: #f7f7f8;
+}
+
+.overview {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+
+  line-height: 1;
+  padding-bottom: 30rpx;
+  color: #fff;
+  background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/order_bg.png);
+  background-size: cover;
+
+  .status {
+    font-size: 36rpx;
+  }
+
+  .status::before {
+    margin-right: 6rpx;
+    font-weight: 500;
+  }
+
+  .tips {
+    margin: 30rpx 0;
+    display: flex;
+    font-size: 14px;
+    align-items: center;
+
+    .money {
+      margin-right: 30rpx;
+    }
+  }
+
+  .button-group {
+    margin-top: 30rpx;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+  }
+
+  .button {
+    width: 260rpx;
+    height: 64rpx;
+    margin: 0 10rpx;
+    text-align: center;
+    line-height: 64rpx;
+    font-size: 28rpx;
+    color: #27ba9b;
+    border-radius: 68rpx;
+    background-color: #fff;
+  }
+}
+
+.shipment {
+  line-height: 1.4;
+  padding: 0 20rpx;
+  margin: 20rpx 20rpx 0;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .locate,
+  .item {
+    min-height: 120rpx;
+    padding: 30rpx 30rpx 25rpx 75rpx;
+    background-size: 50rpx;
+    background-repeat: no-repeat;
+    background-position: 6rpx center;
+  }
+
+  .locate {
+    background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/locate.png);
+
+    .user {
+      font-size: 26rpx;
+      color: #444;
+    }
+
+    .address {
+      font-size: 24rpx;
+      color: #666;
+    }
+  }
+
+  .item {
+    background-image: url(https://pcapi-xiaotuxian-front-devtest.itheima.net/miniapp/images/car.png);
+    border-bottom: 1rpx solid #eee;
+    position: relative;
+
+    .message {
+      font-size: 26rpx;
+      color: #444;
+    }
+
+    .date {
+      font-size: 24rpx;
+      color: #666;
+    }
+  }
+}
+
+.goods {
+  margin: 20rpx 20rpx 0;
+  padding: 0 20rpx;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .item {
+    padding: 30rpx 0;
+    border-bottom: 1rpx solid #eee;
+
+    .navigator {
+      display: flex;
+      margin: 20rpx 0;
+    }
+
+    .cover {
+      width: 170rpx;
+      height: 170rpx;
+      border-radius: 10rpx;
+      margin-right: 20rpx;
+    }
+
+    .meta {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+      position: relative;
+    }
+
+    .name {
+      height: 80rpx;
+      font-size: 26rpx;
+      color: #444;
+    }
+
+    .type {
+      line-height: 1.8;
+      padding: 0 15rpx;
+      margin-top: 6rpx;
+      font-size: 24rpx;
+      align-self: flex-start;
+      border-radius: 4rpx;
+      color: #888;
+      background-color: #f7f7f8;
+    }
+
+    .price {
+      display: flex;
+      margin-top: 6rpx;
+      font-size: 24rpx;
+    }
+
+    .symbol {
+      font-size: 20rpx;
+    }
+
+    .original {
+      color: #999;
+      text-decoration: line-through;
+    }
+
+    .actual {
+      margin-left: 10rpx;
+      color: #444;
+    }
+
+    .text {
+      font-size: 22rpx;
+    }
+
+    .quantity {
+      position: absolute;
+      bottom: 0;
+      right: 0;
+      font-size: 24rpx;
+      color: #444;
+    }
+
+    .action {
+      display: flex;
+      flex-direction: row-reverse;
+      justify-content: flex-start;
+      padding: 30rpx 0 0;
+
+      .button {
+        width: 200rpx;
+        height: 60rpx;
+        text-align: center;
+        justify-content: center;
+        line-height: 60rpx;
+        margin-left: 20rpx;
+        border-radius: 60rpx;
+        border: 1rpx solid #ccc;
+        font-size: 26rpx;
+        color: #444;
+      }
+
+      .primary {
+        color: #27ba9b;
+        border-color: #27ba9b;
+      }
+    }
+  }
+
+  .total {
+    line-height: 1;
+    font-size: 26rpx;
+    padding: 20rpx 0;
+    color: #666;
+
+    .row {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      padding: 10rpx 0;
+    }
+
+    .symbol::before {
+      content: '¥';
+      font-size: 80%;
+      margin-right: 3rpx;
+    }
+
+    .primary {
+      color: #cf4444;
+      font-size: 36rpx;
+    }
+  }
+}
+
+.detail {
+  line-height: 1;
+  padding: 30rpx 20rpx 0;
+  margin: 20rpx 20rpx 0;
+  font-size: 26rpx;
+  color: #666;
+  border-radius: 10rpx;
+  background-color: #fff;
+
+  .title {
+    font-size: 30rpx;
+    color: #444;
+  }
+
+  .row {
+    padding: 20rpx 0;
+
+    .item {
+      padding: 10rpx 0;
+      display: flex;
+      align-items: center;
+    }
+
+    .copy {
+      border-radius: 20rpx;
+      font-size: 20rpx;
+      border: 1px solid #ccc;
+      padding: 5rpx 10rpx;
+      margin-left: 10rpx;
+    }
+  }
+}
+
+.toolbar-height {
+  height: 100rpx;
+  box-sizing: content-box;
+}
+
+.toolbar {
+  position: fixed;
+  left: 0;
+  right: 0;
+  bottom: calc(var(--window-bottom));
+  z-index: 1;
+
+  height: 100rpx;
+  padding: 0 20rpx;
+  display: flex;
+  align-items: center;
+  flex-direction: row-reverse;
+  border-top: 1rpx solid #ededed;
+  border-bottom: 1rpx solid #ededed;
+  background-color: #fff;
+  box-sizing: content-box;
+
+  .button {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    width: 200rpx;
+    height: 72rpx;
+    margin-left: 15rpx;
+    font-size: 26rpx;
+    border-radius: 72rpx;
+    border: 1rpx solid #ccc;
+    color: #444;
+  }
+
+  .delete {
+    order: 4;
+    color: #cf4444;
+  }
+
+  .button {
+    order: 3;
+  }
+
+  .secondary {
+    order: 2;
+    color: #27ba9b;
+    border-color: #27ba9b;
+  }
+
+  .primary {
+    order: 1;
+    color: #fff;
+    background-color: #27ba9b;
+  }
+}
+
+.popup-root {
+  padding: 30rpx 30rpx 0;
+  border-radius: 10rpx 10rpx 0 0;
+  overflow: hidden;
+
+  .title {
+    font-size: 30rpx;
+    text-align: center;
+    margin-bottom: 30rpx;
+  }
+
+  .description {
+    font-size: 28rpx;
+    padding: 0 20rpx;
+
+    .tips {
+      color: #444;
+      margin-bottom: 12rpx;
+    }
+
+    .cell {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding: 15rpx 0;
+      color: #666;
+    }
+
+    .icon::before {
+      content: '\e6cd';
+      font-family: 'erabbit' !important;
+      font-size: 38rpx;
+      color: #999;
+    }
+
+    .icon.checked::before {
+      content: '\e6cc';
+      font-size: 38rpx;
+      color: #27ba9b;
+    }
+  }
+
+  .footer {
+    display: flex;
+    justify-content: space-between;
+    padding: 30rpx 0 40rpx;
+    font-size: 28rpx;
+    color: #444;
+
+    .button {
+      flex: 1;
+      height: 72rpx;
+      text-align: center;
+      line-height: 72rpx;
+      margin: 0 20rpx;
+      color: #444;
+      border-radius: 72rpx;
+      border: 1rpx solid #ccc;
+    }
+
+    .primary {
+      color: #fff;
+      background-color: #27ba9b;
+      border: none;
+    }
+  }
+}
+</style>

+ 380 - 0
src/pagesOrder/list/components/OrderList.vue

@@ -0,0 +1,380 @@
+<script setup lang="ts">
+import { OrderState } from '@/services/constants'
+import { orderStateList } from '@/services/constants'
+import { putMemberOrderReceiptByIdAPI } from '@/services/order'
+import { deleteMemberOrderAPI } from '@/services/order'
+import { getMemberOrderAPI } from '@/services/order'
+import { getPayMockAPI, getPayWxPayMiniPayAPI } from '@/services/pay'
+import type { OrderItem } from '@/types/order'
+import type { OrderListParams } from '@/types/order'
+import { onMounted, ref } from 'vue'
+
+// 获取屏幕边界到安全区域距离
+const { safeAreaInsets } = uni.getSystemInfoSync()
+
+// 定义 porps
+const props = defineProps<{
+  orderState: number
+}>()
+
+// 请求参数
+const queryParams: Required<OrderListParams> = {
+  page: 1,
+  pageSize: 5,
+  orderState: props.orderState,
+}
+
+// 获取订单列表
+const orderList = ref<OrderItem[]>([])
+// 是否加载中标记,用于防止滚动触底触发多次请求
+const isLoading = ref(false)
+const getMemberOrderData = async () => {
+  // 如果数据出于加载中,退出函数
+  if (isLoading.value) return
+  // 退出分页判断
+  if (isFinish.value === true) {
+    return uni.showToast({ icon: 'none', title: '没有更多数据~' })
+  }
+  // 发送请求前,标记为加载中
+  isLoading.value = true
+  // 发送请求
+  const res = await getMemberOrderAPI(queryParams)
+  // 发送请求后,重置标记
+  isLoading.value = false
+  // 数组追加
+  orderList.value.push(...res.result.items)
+  // 分页条件
+  if (queryParams.page < res.result.pages) {
+    // 页码累加
+    queryParams.page++
+  } else {
+    // 分页已结束
+    isFinish.value = true
+  }
+}
+
+onMounted(() => {
+  getMemberOrderData()
+})
+
+// 订单支付
+const onOrderPay = async (id: string) => {
+  if (import.meta.env.DEV) {
+    // 开发环境模拟支付
+    await getPayMockAPI({ orderId: id })
+  } else {
+    // #ifdef MP-WEIXIN
+    // 正式环境微信支付
+    const res = await getPayWxPayMiniPayAPI({ orderId: id })
+    await wx.requestPayment(res.result)
+    // #endif
+
+    // #ifdef H5 || APP-PLUS
+    // H5端 和 App 端未开通支付-模拟支付体验
+    await getPayMockAPI({ orderId: id })
+    // #endif
+  }
+  // 成功提示
+  uni.showToast({ title: '支付成功' })
+  // 更新订单状态
+  const order = orderList.value.find((v) => v.id === id)
+  order!.orderState = OrderState.DaiFaHuo
+}
+
+// 确认收货
+const onOrderConfirm = (id: string) => {
+  uni.showModal({
+    content: '为保障您的权益,请收到货并确认无误后,再确认收货',
+    confirmColor: '#27BA9B',
+    success: async (res) => {
+      if (res.confirm) {
+        await putMemberOrderReceiptByIdAPI(id)
+        uni.showToast({ icon: 'success', title: '确认收货成功' })
+        // 确认成功,更新为待评价
+        const order = orderList.value.find((v) => v.id === id)
+        order!.orderState = OrderState.DaiPingJia
+      }
+    },
+  })
+}
+
+// 删除订单
+const onOrderDelete = (id: string) => {
+  uni.showModal({
+    content: '你确定要删除该订单?',
+    confirmColor: '#27BA9B',
+    success: async (res) => {
+      if (res.confirm) {
+        await deleteMemberOrderAPI({ ids: [id] })
+        // 删除成功,界面中删除订单
+        const index = orderList.value.findIndex((v) => v.id === id)
+        orderList.value.splice(index, 1)
+      }
+    },
+  })
+}
+
+// 是否分页结束
+const isFinish = ref(false)
+// 是否触发下拉刷新
+const isTriggered = ref(false)
+// 自定义下拉刷新被触发
+const onRefresherrefresh = async () => {
+  // 开始动画
+  isTriggered.value = true
+  // 重置数据
+  queryParams.page = 1
+  orderList.value = []
+  isFinish.value = false
+  // 加载数据
+  await getMemberOrderData()
+  // 关闭动画
+  isTriggered.value = false
+}
+</script>
+
+<template>
+  <scroll-view
+    enable-back-to-top
+    scroll-y
+    class="orders"
+    refresher-enabled
+    :refresher-triggered="isTriggered"
+    @refresherrefresh="onRefresherrefresh"
+    @scrolltolower="getMemberOrderData"
+  >
+    <view class="card" v-for="order in orderList" :key="order.id">
+      <!-- 订单信息 -->
+      <view class="status">
+        <text class="date">{{ order.createTime }}</text>
+        <!-- 订单状态文字 -->
+        <text>{{ orderStateList[order.orderState].text }}</text>
+        <!-- 待评价/已完成/已取消 状态: 展示删除订单 -->
+        <text
+          v-if="order.orderState >= OrderState.DaiPingJia"
+          class="icon-delete"
+          @tap="onOrderDelete(order.id)"
+        ></text>
+      </view>
+      <!-- 商品信息,点击商品跳转到订单详情,不是商品详情 -->
+      <navigator
+        v-for="item in order.skus"
+        :key="item.id"
+        class="goods"
+        :url="`/pagesOrder/detail/detail?id=${order.id}`"
+        hover-class="none"
+      >
+        <view class="cover">
+          <image class="image" mode="aspectFit" :src="item.image"></image>
+        </view>
+        <view class="meta">
+          <view class="name ellipsis">{{ item.name }}</view>
+          <view class="type">{{ item.attrsText }}</view>
+        </view>
+      </navigator>
+      <!-- 支付信息 -->
+      <view class="payment">
+        <text class="quantity">共{{ order.totalNum }}件商品</text>
+        <text>实付</text>
+        <text class="amount"> <text class="symbol">¥</text>{{ order.payMoney }}</text>
+      </view>
+      <!-- 订单操作按钮 -->
+      <view class="action">
+        <!-- 待付款状态:显示去支付按钮 -->
+        <template v-if="order.orderState === OrderState.DaiFuKuan">
+          <view class="button primary" @tap="onOrderPay(order.id)">去支付</view>
+        </template>
+        <template v-else>
+          <navigator
+            class="button secondary"
+            :url="`/pagesOrder/create/create?orderId=${order.id}`"
+            hover-class="none"
+          >
+            再次购买
+          </navigator>
+          <!-- 待收货状态: 展示确认收货 -->
+          <view
+            v-if="order.orderState === OrderState.DaiShouHuo"
+            class="button primary"
+            @tap="onOrderConfirm(order.id)"
+          >
+            确认收货
+          </view>
+        </template>
+      </view>
+    </view>
+    <!-- 底部提示文字 -->
+    <view class="loading-text" :style="{ paddingBottom: safeAreaInsets?.bottom + 'px' }">
+      {{ isFinish ? '没有更多数据~' : '正在加载...' }}
+    </view>
+  </scroll-view>
+</template>
+
+<style lang="scss">
+// 订单列表
+.orders {
+  .card {
+    min-height: 100rpx;
+    padding: 20rpx;
+    margin: 20rpx 20rpx 0;
+    border-radius: 10rpx;
+    background-color: #fff;
+
+    &:last-child {
+      padding-bottom: 40rpx;
+    }
+  }
+
+  .status {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    font-size: 28rpx;
+    color: #999;
+    margin-bottom: 15rpx;
+
+    .date {
+      color: #666;
+      flex: 1;
+    }
+
+    .primary {
+      color: #ff9240;
+    }
+
+    .icon-delete {
+      line-height: 1;
+      margin-left: 10rpx;
+      padding-left: 10rpx;
+      border-left: 1rpx solid #e3e3e3;
+    }
+  }
+
+  .goods {
+    display: flex;
+    margin-bottom: 20rpx;
+
+    .cover {
+      width: 170rpx;
+      height: 170rpx;
+      margin-right: 20rpx;
+      border-radius: 10rpx;
+      overflow: hidden;
+      position: relative;
+      .image {
+        width: 170rpx;
+        height: 170rpx;
+      }
+    }
+
+    .quantity {
+      position: absolute;
+      bottom: 0;
+      right: 0;
+      line-height: 1;
+      padding: 6rpx 4rpx 6rpx 8rpx;
+      font-size: 24rpx;
+      color: #fff;
+      border-radius: 10rpx 0 0 0;
+      background-color: rgba(0, 0, 0, 0.6);
+    }
+
+    .meta {
+      flex: 1;
+      display: flex;
+      flex-direction: column;
+      justify-content: center;
+    }
+
+    .name {
+      height: 80rpx;
+      font-size: 26rpx;
+      color: #444;
+    }
+
+    .type {
+      line-height: 1.8;
+      padding: 0 15rpx;
+      margin-top: 10rpx;
+      font-size: 24rpx;
+      align-self: flex-start;
+      border-radius: 4rpx;
+      color: #888;
+      background-color: #f7f7f8;
+    }
+
+    .more {
+      flex: 1;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+      font-size: 22rpx;
+      color: #333;
+    }
+  }
+
+  .payment {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    line-height: 1;
+    padding: 20rpx 0;
+    text-align: right;
+    color: #999;
+    font-size: 28rpx;
+    border-bottom: 1rpx solid #eee;
+
+    .quantity {
+      font-size: 24rpx;
+      margin-right: 16rpx;
+    }
+
+    .amount {
+      color: #444;
+      margin-left: 6rpx;
+    }
+
+    .symbol {
+      font-size: 20rpx;
+    }
+  }
+
+  .action {
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+    padding-top: 20rpx;
+
+    .button {
+      width: 180rpx;
+      height: 60rpx;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      margin-left: 20rpx;
+      border-radius: 60rpx;
+      border: 1rpx solid #ccc;
+      font-size: 26rpx;
+      color: #444;
+    }
+
+    .secondary {
+      color: #27ba9b;
+      border-color: #27ba9b;
+    }
+
+    .primary {
+      color: #fff;
+      background-color: #27ba9b;
+      border-color: #27ba9b;
+    }
+  }
+
+  .loading-text {
+    text-align: center;
+    font-size: 28rpx;
+    color: #666;
+    padding: 20rpx 0;
+  }
+}
+</style>

+ 105 - 0
src/pagesOrder/list/list.vue

@@ -0,0 +1,105 @@
+<script setup lang="ts">
+import { ref } from 'vue'
+import OrderList from './components/OrderList.vue'
+
+// 获取页面参数
+const query = defineProps<{
+  type: string
+}>()
+
+// tabs 数据
+const orderTabs = ref([
+  { orderState: 0, title: '全部', isRender: false },
+  { orderState: 1, title: '待付款', isRender: false },
+  { orderState: 2, title: '待发货', isRender: false },
+  { orderState: 3, title: '待收货', isRender: false },
+  { orderState: 4, title: '待评价', isRender: false },
+])
+
+// 高亮下标
+const activeIndex = ref(orderTabs.value.findIndex((v) => v.orderState === Number(query.type)))
+// 默认渲染容器
+orderTabs.value[activeIndex.value].isRender = true
+</script>
+
+<template>
+  <view class="viewport">
+    <!-- tabs -->
+    <view class="tabs">
+      <text
+        class="item"
+        v-for="(item, index) in orderTabs"
+        :key="item.title"
+        @tap="
+          () => {
+            activeIndex = index
+            item.isRender = true
+          }
+        "
+      >
+        {{ item.title }}
+      </text>
+      <!-- 游标 -->
+      <view class="cursor" :style="{ left: activeIndex * 20 + '%' }"></view>
+    </view>
+    <!-- 滑动容器 -->
+    <swiper class="swiper" :current="activeIndex" @change="activeIndex = $event.detail.current">
+      <!-- 滑动项 -->
+      <swiper-item v-for="item in orderTabs" :key="item.title">
+        <!-- 订单列表 -->
+        <OrderList v-if="item.isRender" :order-state="item.orderState" />
+      </swiper-item>
+    </swiper>
+  </view>
+</template>
+
+<style lang="scss">
+page {
+  height: 100%;
+  overflow: hidden;
+}
+
+.viewport {
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  background-color: #fff;
+}
+
+// tabs
+.tabs {
+  display: flex;
+  justify-content: space-around;
+  line-height: 60rpx;
+  margin: 0 10rpx;
+  background-color: #fff;
+  box-shadow: 0 4rpx 6rpx rgba(240, 240, 240, 0.6);
+  position: relative;
+  z-index: 9;
+
+  .item {
+    flex: 1;
+    text-align: center;
+    padding: 20rpx;
+    font-size: 28rpx;
+    color: #262626;
+  }
+
+  .cursor {
+    position: absolute;
+    left: 0;
+    bottom: 0;
+    width: 20%;
+    height: 6rpx;
+    padding: 0 50rpx;
+    background-color: #27ba9b;
+    /* 过渡效果 */
+    transition: all 0.4s;
+  }
+}
+
+// swiper
+.swiper {
+  background-color: #f7f7f8;
+}
+</style>

+ 95 - 0
src/pagesOrder/payment/payment.vue

@@ -0,0 +1,95 @@
+<script setup lang="ts">
+import { useGuessList } from '@/composables'
+
+// 获取页面参数
+const query = defineProps<{
+  id: string
+}>()
+
+// 猜你喜欢
+const { guessRef, onScrolltolower } = useGuessList()
+</script>
+
+<template>
+  <scroll-view enable-back-to-top class="viewport" scroll-y @scrolltolower="onScrolltolower">
+    <!-- 订单状态 -->
+    <view class="overview">
+      <view class="status icon-checked">支付成功</view>
+      <view class="buttons">
+        <navigator
+          hover-class="none"
+          class="button navigator"
+          url="/pages/index/index"
+          open-type="switchTab"
+        >
+          返回首页
+        </navigator>
+        <navigator
+          hover-class="none"
+          class="button navigator"
+          :url="`/pagesOrder/detail/detail?id=${query.id}`"
+          open-type="redirect"
+        >
+          查看订单
+        </navigator>
+      </view>
+    </view>
+
+    <!-- 猜你喜欢 -->
+    <XtxGuess ref="guessRef" />
+  </scroll-view>
+</template>
+
+<style lang="scss">
+page {
+  display: flex;
+  flex-direction: column;
+  height: 100%;
+  overflow: hidden;
+}
+
+.viewport {
+  background-color: #f7f7f8;
+}
+
+.overview {
+  line-height: 1;
+  padding: 50rpx 0;
+  color: #fff;
+  background-color: #27ba9b;
+
+  .status {
+    font-size: 36rpx;
+    font-weight: 500;
+    text-align: center;
+  }
+
+  .status::before {
+    display: block;
+    font-size: 110rpx;
+    margin-bottom: 20rpx;
+  }
+
+  .buttons {
+    height: 60rpx;
+    line-height: 60rpx;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin-top: 60rpx;
+  }
+
+  .button {
+    text-align: center;
+    margin: 0 10rpx;
+    font-size: 28rpx;
+    color: #fff;
+
+    &:first-child {
+      width: 200rpx;
+      border-radius: 64rpx;
+      border: 1rpx solid #fff;
+    }
+  }
+}
+</style>

+ 59 - 0
src/services/address.ts

@@ -0,0 +1,59 @@
+import type { AddressItem, AddressParams } from '@/types/address'
+import { http } from '@/utils/http'
+
+/**
+ * 添加收货地址
+ * @param data 请求参数
+ */
+export const postMemberAddressAPI = (data: AddressParams) => {
+  return http({
+    method: 'POST',
+    url: '/member/address',
+    data,
+  })
+}
+
+/**
+ * 获取收货地址列表
+ */
+export const getMemberAddressAPI = () => {
+  return http<AddressItem[]>({
+    method: 'GET',
+    url: '/member/address',
+  })
+}
+
+/**
+ * 获取收货地址详情
+ * @param id 地址id(路径参数)
+ */
+export const getMemberAddressByIdAPI = (id: string) => {
+  return http<AddressItem>({
+    method: 'GET',
+    url: `/member/address/${id}`,
+  })
+}
+
+/**
+ * 修改收货地址
+ * @param id 地址id(路径参数)
+ * @param data 表单数据(请求体参数)
+ */
+export const putMemberAddressByIdAPI = (id: string, data: AddressParams) => {
+  return http({
+    method: 'PUT',
+    url: `/member/address/${id}`,
+    data,
+  })
+}
+
+/**
+ * 删除收货地址
+ * @param id 地址id(路径参数)
+ */
+export const deleteMemberAddressByIdAPI = (id: string) => {
+  return http({
+    method: 'DELETE',
+    url: `/member/address/${id}`,
+  })
+}

+ 63 - 0
src/services/cart.ts

@@ -0,0 +1,63 @@
+import type { CartItem } from '@/types/cart'
+import { http } from '@/utils/http'
+/**
+ * 加入购物车
+ * @param data 请求体参数
+ */
+export const postMemberCartAPI = (data: { skuId: string; count: number }) => {
+  return http({
+    method: 'POST',
+    url: '/member/cart',
+    data,
+  })
+}
+
+/**
+ * 获取购物车列表
+ */
+export const getMemberCartAPI = () => {
+  return http<CartItem[]>({
+    method: 'GET',
+    url: '/member/cart',
+  })
+}
+
+/**
+ * 删除/清空购物车单品
+ * @param data 请求体参数 ids SKUID 集合
+ */
+export const deleteMemberCartAPI = (data: { ids: string[] }) => {
+  return http({
+    method: 'DELETE',
+    url: '/member/cart',
+    data,
+  })
+}
+
+/**
+ * 修改购物车单品
+ * @param skuId SKUID
+ * @param data selected 选中状态 count 商品数量
+ */
+export const putMemberCartBySkuIdAPI = (
+  skuId: string,
+  data: { selected?: boolean; count?: number },
+) => {
+  return http({
+    method: 'PUT',
+    url: `/member/cart/${skuId}`,
+    data,
+  })
+}
+
+/**
+ * 购物车全选/取消全选
+ * @param data selected 是否选中
+ */
+export const putMemberCartSelectedAPI = (data: { selected: boolean }) => {
+  return http({
+    method: 'PUT',
+    url: '/member/cart/selected',
+    data,
+  })
+}

+ 12 - 0
src/services/category.ts

@@ -0,0 +1,12 @@
+import type { CategoryTopItem } from '@/types/category'
+import { http } from '@/utils/http'
+
+/**
+ * 分类列表-小程序
+ */
+export const getCategoryTopAPI = () => {
+  return http<CategoryTopItem[]>({
+    method: 'GET',
+    url: '/category/top',
+  })
+}

+ 25 - 0
src/services/constants.ts

@@ -0,0 +1,25 @@
+/** 订单状态枚举 */
+export enum OrderState {
+  /** 待付款 */
+  DaiFuKuan = 1,
+  /** 待发货 */
+  DaiFaHuo = 2,
+  /** 待收货 */
+  DaiShouHuo = 3,
+  /** 待评价 */
+  DaiPingJia = 4,
+  /** 已完成 */
+  YiWanCheng = 5,
+  /** 已取消 */
+  YiQuXiao = 6,
+}
+/** 订单状态列表 */
+export const orderStateList = [
+  { id: 0, text: '' },
+  { id: 1, text: '待付款' },
+  { id: 2, text: '待发货' },
+  { id: 3, text: '待收货' },
+  { id: 4, text: '待评价' },
+  { id: 5, text: '已完成' },
+  { id: 6, text: '已取消' },
+]

+ 14 - 0
src/services/goods.ts

@@ -0,0 +1,14 @@
+import type { GoodsResult } from '@/types/goods'
+import { http } from '@/utils/http'
+
+/**
+ * 商品详情
+ * @param id 商品id
+ */
+export const getGoodsByIdAPI = (id: string) => {
+  return http<GoodsResult>({
+    method: 'GET',
+    url: '/goods',
+    data: { id },
+  })
+}

+ 48 - 0
src/services/home.ts

@@ -0,0 +1,48 @@
+import type { PageParams, PageResult } from '@/types/global'
+import type { BannerItem, CategoryItem, GuessItem, HotItem } from '@/types/home'
+import { http } from '@/utils/http'
+
+/**
+ * 首页-广告区域-小程序
+ * @param distributionSite 广告区域展示位置(投放位置 投放位置,1为首页,2为分类商品页) 默认是1
+ */
+export const getHomeBannerAPI = (distributionSite = 1) => {
+  return http<BannerItem[]>({
+    method: 'GET',
+    url: '/home/banner',
+    data: {
+      distributionSite,
+    },
+  })
+}
+
+/**
+ * 首页-前台分类-小程序
+ */
+export const getHomeCategoryAPI = () => {
+  return http<CategoryItem[]>({
+    method: 'GET',
+    url: '/home/category/mutli',
+  })
+}
+
+/**
+ * 首页-热门推荐-小程序
+ */
+export const getHomeHotAPI = () => {
+  return http<HotItem[]>({
+    method: 'GET',
+    url: '/home/hot/mutli',
+  })
+}
+
+/**
+ * 猜你喜欢-小程序
+ */
+export const getHomeGoodsGuessLikeAPI = (data?: PageParams) => {
+  return http<PageResult<GuessItem>>({
+    method: 'GET',
+    url: '/home/goods/guessLike',
+    data,
+  })
+}

+ 17 - 0
src/services/hot.ts

@@ -0,0 +1,17 @@
+import { http } from '@/utils/http'
+import type { PageParams } from '@/types/global'
+import type { HotResult } from '@/types/hot'
+
+type HotParams = PageParams & { subType?: string }
+/**
+ * 通用热门推荐类型
+ * @param url 请求地址
+ * @param data 请求参数
+ */
+export const getHotRecommendAPI = (url: string, data?: HotParams) => {
+  return http<HotResult>({
+    method: 'GET',
+    url,
+    data,
+  })
+}

+ 49 - 0
src/services/login.ts

@@ -0,0 +1,49 @@
+import type { LoginResult } from '@/types/member'
+import { http } from '@/utils/http'
+
+type LoginWxMinParams = {
+  code: string
+  encryptedData?: string
+  iv?: string
+}
+/**
+ * 小程序登录
+ * @param data 请求参数
+ */
+export const postLoginWxMinAPI = (data: LoginWxMinParams) => {
+  return http<LoginResult>({
+    method: 'POST',
+    url: '/login/wxMin',
+    data,
+  })
+}
+
+/**
+ * 小程序登录_内测版
+ * @param phoneNumber 模拟手机号码
+ */
+export const postLoginWxMinSimpleAPI = (phoneNumber: string) => {
+  return http<LoginResult>({
+    method: 'POST',
+    url: '/login/wxMin/simple',
+    data: {
+      phoneNumber,
+    },
+  })
+}
+
+type LoginParams = {
+  account: string
+  password: string
+}
+/**
+ * 传统登录-用户名+密码
+ * @param data 请求参数
+ */
+export const postLoginAPI = (data: LoginParams) => {
+  return http<LoginResult>({
+    method: 'POST',
+    url: '/login',
+    data,
+  })
+}

+ 142 - 0
src/services/order.ts

@@ -0,0 +1,142 @@
+import type { OrderListResult } from '@/types/order'
+import type {
+  OrderCreateParams,
+  OrderListParams,
+  OrderLogisticResult,
+  OrderPreResult,
+  OrderResult,
+} from '@/types/order'
+import { http } from '@/utils/http'
+/**
+ * 填写订单-获取预付订单
+ */
+export const getMemberOrderPreAPI = () => {
+  return http<OrderPreResult>({
+    method: 'GET',
+    url: '/member/order/pre',
+  })
+}
+
+/**
+ * 填写订单-获取立即购买订单
+ */
+export const getMemberOrderPreNowAPI = (data: {
+  skuId: string
+  count: string
+  addressId?: string
+}) => {
+  return http<OrderPreResult>({
+    method: 'GET',
+    url: '/member/order/pre/now',
+    data,
+  })
+}
+
+/**
+ * 填写订单-再次购买
+ * @param id 订单id
+ */
+export const getMemberOrderRepurchaseByIdAPI = (id: string) => {
+  return http<OrderPreResult>({
+    method: 'GET',
+    url: `/member/order/repurchase/${id}`,
+  })
+}
+
+/**
+ * 提交订单
+ * @param data 请求参数
+ */
+export const postMemberOrderAPI = (data: OrderCreateParams) => {
+  return http<{ id: string }>({
+    method: 'POST',
+    url: '/member/order',
+    data,
+  })
+}
+
+/**
+ * 获取订单详情
+ * @param id 订单id
+ */
+export const getMemberOrderByIdAPI = (id: string) => {
+  return http<OrderResult>({
+    method: 'GET',
+    url: `/member/order/${id}`,
+  })
+}
+
+/**
+ * 模拟发货-内测版
+ * @description 在DEV环境下使用,仅在订单状态为待发货时,可模拟发货,调用后订单状态修改为待收货,包含模拟物流。
+ * @param id 订单id
+ */
+export const getMemberOrderConsignmentByIdAPI = (id: string) => {
+  return http({
+    method: 'GET',
+    url: `/member/order/consignment/${id}`,
+  })
+}
+
+/**
+ * 确认收货
+ * @description 仅在订单状态为待收货时,可确认收货。
+ * @param id 订单id
+ */
+export const putMemberOrderReceiptByIdAPI = (id: string) => {
+  return http<OrderResult>({
+    method: 'PUT',
+    url: `/member/order/${id}/receipt`,
+  })
+}
+
+/**
+ * 获取订单物流
+ * @description 仅在订单状态为待收货,待评价,已完成时,可获取物流信息。
+ * @param id 订单id
+ */
+export const getMemberOrderLogisticsByIdAPI = (id: string) => {
+  return http<OrderLogisticResult>({
+    method: 'GET',
+    url: `/member/order/${id}/logistics`,
+  })
+}
+
+/**
+ * 删除订单
+ * @description 仅在订单状态为待评价,已完成,已取消时,可删除订单。
+ * @param data ids 订单集合
+ */
+export const deleteMemberOrderAPI = (data: { ids: string[] }) => {
+  return http({
+    method: 'DELETE',
+    url: `/member/order`,
+    data,
+  })
+}
+
+/**
+ * 取消订单
+ * @description 仅在订单状态为待付款时,可取消订单。
+ * @param id 订单id
+ * @param data cancelReason 取消理由
+ */
+export const getMemberOrderCancelByIdAPI = (id: string, data: { cancelReason: string }) => {
+  return http<OrderResult>({
+    method: 'PUT',
+    url: `/member/order/${id}/cancel`,
+    data,
+  })
+}
+
+/**
+ * 获取订单列表
+ * @param data orderState 订单状态
+ */
+export const getMemberOrderAPI = (data: OrderListParams) => {
+  return http<OrderListResult>({
+    method: 'GET',
+    url: `/member/order`,
+    data,
+  })
+}

+ 25 - 0
src/services/pay.ts

@@ -0,0 +1,25 @@
+import { http } from '@/utils/http'
+
+/**
+ * 获取微信支付参数
+ * @param data orderId 订单id
+ */
+export const getPayWxPayMiniPayAPI = (data: { orderId: string }) => {
+  return http<WechatMiniprogram.RequestPaymentOption>({
+    method: 'GET',
+    url: '/pay/wxPay/miniPay',
+    data,
+  })
+}
+
+/**
+ * 模拟支付-内测版
+ * @param data orderId 订单id
+ */
+export const getPayMockAPI = (data: { orderId: string }) => {
+  return http({
+    method: 'GET',
+    url: '/pay/mock',
+    data,
+  })
+}

+ 24 - 0
src/services/profile.ts

@@ -0,0 +1,24 @@
+import type { ProfileDetail, ProfileParams } from '@/types/member'
+import { http } from '@/utils/http'
+
+/**
+ * 获取个人信息
+ */
+export const getMemberProfileAPI = () => {
+  return http<ProfileDetail>({
+    method: 'GET',
+    url: '/member/profile',
+  })
+}
+
+/**
+ * 修改个人信息
+ * @param data 请求体参数
+ */
+export const putMemberProfileAPI = (data: ProfileParams) => {
+  return http<ProfileDetail>({
+    method: 'PUT',
+    url: '/member/profile',
+    data,
+  })
+}

BIN
src/static/images/blank.png


BIN
src/static/images/blank_cart.png


BIN
src/static/images/bubble.png


BIN
src/static/images/car.png


BIN
src/static/images/center_bg.png


BIN
src/static/images/locate.png


BIN
src/static/images/logo.png


BIN
src/static/images/logo_icon.png


BIN
src/static/images/navigator_bg.png


BIN
src/static/images/order_bg.png


BIN
src/static/images/rating_off.png


BIN
src/static/images/rating_on.png


BIN
src/static/images/remove.png


BIN
src/static/images/stars.png


BIN
src/static/tabs/cart_default.png


BIN
src/static/tabs/cart_selected.png


BIN
src/static/tabs/category_default.png


BIN
src/static/tabs/category_selected.png


Niektóre pliki nie zostały wyświetlone z powodu dużej ilości zmienionych plików