diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..5d126348471c348decba17143ce128130c9f4104 --- /dev/null +++ b/.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 diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 6b7235fccae983c3f2ea4a20730486b6ab3c84ff..2b45669829da08b85d071739b33000fb428130c4 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -3,6 +3,16 @@ require("@rushstack/eslint-patch/modern-module-resolution"); module.exports = { root: true, + // 定义全局变量 + globals: { + // QC 是QQ授权登录引入 js 文件提供的 + QC: true, + }, + // 规则 + rules: { + "vue/multi-word-component-names": "off", + "vue/no-setup-props-destructure": "off", + }, extends: [ "plugin:vue/vue3-essential", "eslint:recommended", diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000000000000000000000000000000000000..14b48971e849d9c18016b98591fa110f33a5a040 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text eol=lf +*.txt text eol=crlf + +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.tff binary +*.woff binary +*.woff2 binary \ No newline at end of file diff --git a/.gitignore b/.gitignore index 38adffa64e8300a31b749218081149e1fe3deaaa..8f16829e330224cfa4ff63a904e226fbe9614b5d 100644 --- a/.gitignore +++ b/.gitignore @@ -20,6 +20,7 @@ coverage # Editor directories and files .vscode/* !.vscode/extensions.json +!.vscode/settings.json .idea *.suo *.ntvs* diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 806eacda6197736736541ff3f0da725f96c08528..a15858a9a274108d314f77a6c79f09956f06c28f 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,10 @@ { - "recommendations": ["johnsoncodehk.volar", "johnsoncodehk.vscode-typescript-vue-plugin"] + "recommendations": [ + "johnsoncodehk.volar", + "johnsoncodehk.vscode-typescript-vue-plugin", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "editorconfig.editorconfig" + ], + "unwantedRecommendations": ["octref.vetur"] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000000000000000000000000000000000000..84bd34556a9ecefe345da465cafe129d22bc3dd2 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,33 @@ +{ + // 格式化 typescript 和 vue 使用 prettier-vscode + "[typescript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[vue]": { + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + // eslint 开启输入时检查错误,检查 js vue jsx tsx 文件 + "eslint.enable": true, + "eslint.run": "onType", + "eslint.options": { + "extensions": [ + ".js", + ".vue", + ".jsx", + ".tsx" + ] + }, + // 在保存时运行的代码自动修复操作 + "editor.codeActionsOnSave": { + "source.fixAll": true, + "source.fixAll.eslint": true + }, + // 操作时作为单词分隔符的字符 + "editor.wordSeparators": "`~!@#%^&*()=+[{]}\\|;:'\",.<>/?", + // 一个制表符等于的空格数 + "editor.tabSize": 2, + // 默认行尾字符 LF + "files.eol": "\n", + // 不已紧凑形式呈现文件夹 + "explorer.compactFolders": false +} diff --git a/README.md b/README.md index 8ad1dab8d8e26252bb7f8e0dac2ff3172dd2dc57..735ff4cf9ed28d0973d72ca755544aa782d90558 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,98 @@ -# rabbit-vue3-ts +# rabbit-vue3-ts-小兔鲜儿2022新版 -This template should help get you started developing with Vue 3 in Vite. +项目使用 `Vite` + `Vue3` + `TypeScript` + `Pinia` + `VueRouter@4` 进行开发。 -## Recommended IDE Setup +代码检查和格式化为:`ESlint` + `Prettier` -[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin). +## 推荐 IDE 设置 -## Type Support for `.vue` Imports in TS +[VSCode 编辑器](https://code.visualstudio.com/) + [插件-Volar](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.volar) (注意禁用 `Vetur` 插件) + [插件-TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin). -TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. +> 🚨注意: +> +> - Vue3 + TS 开发共安装 **2** 个 Volar 插件(拓展),其中一个为 TS 支持插件(拓展)。 +> - 需禁用 `Vetur` 插件(拓展)。 -If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: +## 在 TS 中支持 `.vue` 导入 -1. Disable the built-in TypeScript Extension - 1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette - 2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` -2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. +VsCode 内置的 TypeScript 插件(拓展)不能处理 `.vue` 导入的类型信息,所以我们用 `vue-tsc` 替换 `tsc` CLI 来进行类型检查。在编辑器中,我们需要通过 [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=johnsoncodehk.vscode-typescript-vue-plugin) 插件来识别 `.vue` 文件的 TypeScript 类型信息。 -## Customize configuration -See [Vite Configuration Reference](https://vitejs.dev/config/). -## Project Setup +如果你觉得独立的 TypeScript 插件(拓展)不够快,Volar 还实现了一个[接管模式](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669),它的性能更好。 + +可以通过以下步骤启用: + +1. 禁用内置的 TypeScript 插件(拓展) + 1) 在 `VSCode` 的拓展面板输入 `@builtin typescript` 搜索内置的 Typescript 插件(拓展)。 + 2) 找到 `TypeScript and JavaScript Language Features`,右键选择 “禁用” +2. 重启 VsCode 编辑器,在重启后生效。 + + + +## Vite 自定义配置 + +点击查看 [⚓Vite 配置参考](https://cn.vitejs.dev/config/). + +## 脚本命令 + +### package.json + +```json +"scripts": { + "dev": "vite", + "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", + "typecheck": "vue-tsc --noEmit", + "build": "vue-tsc --noEmit && vite build", + "preview": "vite preview --port 5050" +} +``` + +### 安装依赖 + +- 基于 `packjson.json` 文件安装项目所需依赖包。 ```sh yarn ``` -### Compile and Hot-Reload for Development +### 运行开发服务器 + +- 通过 `vite` 启动开发服务器,编译源码和热重载。 ```sh yarn dev ``` -### Type-Check, Compile and Minify for Production +### 运行 [ESLint](https://eslint.org/) 格式化 + +- ✨开发过程中,所有**黄色警告**类的格式问题,都可通过运行以下命令修复警告问题。 ```sh -yarn build +yarn lint ``` -### Lint with [ESLint](https://eslint.org/) +### 运行 TS 检查 + +- 调用 `vue-tsc` 检查 TypeScript 类型问题。(代码重构时十分好用) ```sh -yarn lint +yarn typecheck ``` + +### 运行打包命令 + +- 包含 TS 类型检查,编译 和 压缩。 + +```sh +yarn build +``` + +### 预览打包后的项目 + +- 运行 `yarn build` 打包后的项目源码。 + +``` +yarn preview +``` + diff --git a/deploy.sh b/deploy.sh new file mode 100644 index 0000000000000000000000000000000000000000..7901541cf019259d0833d0987f81fc05348caffa --- /dev/null +++ b/deploy.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env sh + +# 确保脚本抛出遇到的错误 +set -e + +# 生成静态文件 +npm run build + +# 进入生成的文件夹 +cd dist + +# 如果是发布到自定义域名 +# echo 'www.example.com' > CNAME + +git init +git add -A +git commit -m 'deploy' + +# 如果发布到 https://.github.io/ +# git push -f git@gitee.com:/.git master:gh-pages +git push -f git@gitee.com:Megasu/rabbit-vue3-ts-69.git master:gh-pages + +cd - \ No newline at end of file diff --git a/env.d.ts b/env.d.ts index 11f02fe2a0061d6e6e1f271b21da95423b448b32..e16f78191f0fa01f6095ef9dd04c9415cf9465a7 100644 --- a/env.d.ts +++ b/env.d.ts @@ -1 +1,19 @@ /// + +// QC 类型声明 - QQ 登录模块 +declare namespace QC { + // QC.api("get_user_info").success((res: unknown) => { + // console.log("获取QQ用户资料", res); + // }); + function api(s: string): { + success: (res: unknown) => void; + }; + const Login: { + // QC.Login.check() + check: () => boolean; + // QC.Login.getMe((openId) => { + // console.log("获取QQ用户openId", openId); + // }); + getMe: (callback: (openId: string) => void) => void; + }; +} diff --git a/hosts b/hosts new file mode 100644 index 0000000000000000000000000000000000000000..f69b3b49a509167507f5e8a3fd05ec550f06182d --- /dev/null +++ b/hosts @@ -0,0 +1,3 @@ +# 当前 hosts 文件需要复制到 C:\Windows\System32\drivers\etc 下 +# 访问 www.corho.com 其实就是优先访问本地服务器 +127.0.0.1 www.corho.com diff --git a/index.html b/index.html index 11603f878f12266e2f1f1db14989f381cb578594..69c748bf1fbc46863c5da73e094d80010810b195 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,17 @@ - + - Vite App + + 小兔鲜儿-Vue3+TS
+ +
+ diff --git a/package.json b/package.json index f2f3af95fa372e6f1311ac3f1b0c773819f93d10..39c84982d8509849542a1711c3a85f5fcafd9f65 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,13 @@ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore" }, "dependencies": { + "@vueuse/core": "^7.6.2", + "axios": "^0.26.0", + "dayjs": "^1.10.7", + "less": "^4.1.2", + "normalize.css": "^8.0.1", "pinia": "^2.0.11", + "pinia-plugin-persistedstate": "^1.2.2", "vue": "^3.2.29", "vue-router": "^4.0.12" }, @@ -26,6 +32,7 @@ "prettier": "^2.5.1", "typescript": "~4.5.5", "vite": "^2.7.13", + "vite-plugin-vue-setup-extend": "^0.4.0", "vue-tsc": "^0.31.1" } } diff --git a/src/App.vue b/src/App.vue index e911d934ff4de967c11de47012cc71b664c07ed9..460e84983409f2083f5a1025cc2145387ea8c2f3 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,125 +1,9 @@ - - diff --git a/src/assets/base.css b/src/assets/base.css deleted file mode 100644 index 71dc55a3cb5a72589496743a327c738ead3e1c83..0000000000000000000000000000000000000000 --- a/src/assets/base.css +++ /dev/null @@ -1,74 +0,0 @@ -/* color palette from */ -:root { - --vt-c-white: #ffffff; - --vt-c-white-soft: #f8f8f8; - --vt-c-white-mute: #f2f2f2; - - --vt-c-black: #181818; - --vt-c-black-soft: #222222; - --vt-c-black-mute: #282828; - - --vt-c-indigo: #2c3e50; - - --vt-c-divider-light-1: rgba(60, 60, 60, 0.29); - --vt-c-divider-light-2: rgba(60, 60, 60, 0.12); - --vt-c-divider-dark-1: rgba(84, 84, 84, 0.65); - --vt-c-divider-dark-2: rgba(84, 84, 84, 0.48); - - --vt-c-text-light-1: var(--vt-c-indigo); - --vt-c-text-light-2: rgba(60, 60, 60, 0.66); - --vt-c-text-dark-1: var(--vt-c-white); - --vt-c-text-dark-2: rgba(235, 235, 235, 0.64); -} - -/* semantic color variables for this project */ -:root { - --color-background: var(--vt-c-white); - --color-background-soft: var(--vt-c-white-soft); - --color-background-mute: var(--vt-c-white-mute); - - --color-border: var(--vt-c-divider-light-2); - --color-border-hover: var(--vt-c-divider-light-1); - - --color-heading: var(--vt-c-text-light-1); - --color-text: var(--vt-c-text-light-1); - - --section-gap: 160px; -} - -@media (prefers-color-scheme: dark) { - :root { - --color-background: var(--vt-c-black); - --color-background-soft: var(--vt-c-black-soft); - --color-background-mute: var(--vt-c-black-mute); - - --color-border: var(--vt-c-divider-dark-2); - --color-border-hover: var(--vt-c-divider-dark-1); - - --color-heading: var(--vt-c-text-dark-1); - --color-text: var(--vt-c-text-dark-2); - } -} - -*, -*::before, -*::after { - box-sizing: border-box; - margin: 0; - position: relative; - font-weight: normal; -} - -body { - min-height: 100vh; - color: var(--color-text); - background: var(--color-background); - transition: color 0.5s, background-color 0.5s; - line-height: 1.6; - font-family: Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, - Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; - font-size: 15px; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} diff --git a/src/assets/images/200.png b/src/assets/images/200.png new file mode 100644 index 0000000000000000000000000000000000000000..d84402882083ec1d77df2dccd759229c169b841d Binary files /dev/null and b/src/assets/images/200.png differ diff --git a/src/assets/images/center-bg.png b/src/assets/images/center-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..c89e65e27a1323c5cebc30bda1481ad9a49d893a Binary files /dev/null and b/src/assets/images/center-bg.png differ diff --git a/src/assets/images/load.gif b/src/assets/images/load.gif new file mode 100644 index 0000000000000000000000000000000000000000..938969472e958058dca81e849a2be975dddb98e7 Binary files /dev/null and b/src/assets/images/load.gif differ diff --git a/src/assets/images/loading.gif b/src/assets/images/loading.gif new file mode 100644 index 0000000000000000000000000000000000000000..c6a00ced8676cd0233bfe49b60e92d9dd2642849 Binary files /dev/null and b/src/assets/images/loading.gif differ diff --git a/src/assets/images/login-bg.png b/src/assets/images/login-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..602f92aac6581bd0aa7fef79a8b2d0701cf78cb9 Binary files /dev/null and b/src/assets/images/login-bg.png differ diff --git a/src/assets/images/logo.png b/src/assets/images/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..88b912a7e247a2acee35dbc5b507842aaf0bf477 Binary files /dev/null and b/src/assets/images/logo.png differ diff --git a/src/assets/images/none.png b/src/assets/images/none.png new file mode 100644 index 0000000000000000000000000000000000000000..e46c231917e7dd9d7a45729c9508ca72422c10ec Binary files /dev/null and b/src/assets/images/none.png differ diff --git a/src/assets/images/qrcode.jpg b/src/assets/images/qrcode.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1b0a62c564b50a57bf37f601940e2a2f16c500ec Binary files /dev/null and b/src/assets/images/qrcode.jpg differ diff --git a/src/assets/logo.svg b/src/assets/logo.svg deleted file mode 100644 index bc826fed80ad0c846e5ca25978776f555f4a2370..0000000000000000000000000000000000000000 --- a/src/assets/logo.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/assets/styles/common.less b/src/assets/styles/common.less new file mode 100644 index 0000000000000000000000000000000000000000..0bc8f87097d3415535a772d780c4434ebbd99483 --- /dev/null +++ b/src/assets/styles/common.less @@ -0,0 +1,247 @@ +// 按照网站自己的需求,提供公用的样式 +* { + box-sizing: border-box; +} + +html { + height: 100%; + font-size: 14px; +} + +body { + height: 100%; + color: #333; + min-width: 1240px; + font: 1em/1.4 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif; +} + +ul, +h1, +h3, +h4, +p, +dl, +dd { + padding: 0; + margin: 0; +} + +a { + text-decoration: none; + color: #333; + outline: none; +} + +i { + font-style: normal; +} + +input[type='text'], +input[type='search'], +input[type='password'], +input[type='checkbox'] { + padding: 0; + outline: none; + border: none; + -webkit-appearance: none; + &::placeholder { + color: #ccc; + } +} + +img { + max-width: 100%; + max-height: 100%; + vertical-align: middle; + // background: #ebebeb; +} + +ul { + list-style: none; +} + +#app { + background: #f5f5f5; + // 不能选中文字 + user-select: none; +} + +.container { + width: 1240px; + margin: 0 auto; + position: relative; +} + +// 一行省略 +.ellipsis { + white-space: nowrap; + text-overflow: ellipsis; + overflow: hidden; +} +// 二行省略 +.ellipsis-2 { + word-break: break-all; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +} + +.fl { + float: left; +} + +.fr { + float: right; +} + +.clearfix:after { + content: '.'; + display: block; + visibility: hidden; + height: 0; + line-height: 0; + clear: both; +} + +// 闪动画 +.shan { + &::after { + content: ''; + position: absolute; + animation: shan 1.5s ease 0s infinite; + top: 0; + width: 30%; + height: 100%; + background: linear-gradient(to left, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0.3) 50%, rgba(255, 255, 255, 0) 100%); + transform: skewX(-45deg); + } +} +@keyframes shan { + 0% { + left: -100%; + } + 100% { + left: 120%; + } +} + +// 离开淡出动画 +.fade { + &-leave { + &-active { + position: absolute; + width: 100%; + transition: opacity 0.5s 0.2s; + z-index: 1; + } + &-to { + opacity: 0; + } + } +} + +// 1. 离开,透明度 1---->0 位移 0---->30 +// 2. 进入,透明度 0---->1 位移 30---->0 +// 执行顺序,先离开再进入 +.pop { + &-leave { + &-from { + opacity: 1; + transform: none; + } + &-active { + transition: all 0.5s; + } + &-to { + opacity: 0; + transform: translateX(20px); + } + } + &-enter { + &-from { + opacity: 0; + transform: translateX(20px); + } + &-active { + transition: all 0.5s; + } + &-to { + opacity: 1; + transform: none; + } + } +} + +// 表单 +.xtx-form { + padding: 50px 0; + &-item { + display: flex; + align-items: center; + width: 700px; + margin: 0 auto; + padding-bottom: 25px; + .label { + width: 180px; + padding-right: 10px; + text-align: right; + color: #999; + ~ .field { + margin-left: 0; + } + } + .field { + width: 320px; + height: 50px; + position: relative; + margin-left: 190px; + .icon { + position: absolute; + left: 0; + top: 0; + width: 40px; + height: 50px; + text-align: center; + line-height: 50px; + color: #999; + ~ .input { + padding-left: 40px; + } + } + .input { + border: 1px solid #e4e4e4; + width: 320px; + height: 50px; + line-height: 50px; + padding: 0 10px; + &.err { + border-color: @priceColor; + } + &:focus, + &:active { + border-color: @xtxColor; + } + } + } + .error { + width: 180px; + padding-left: 10px; + color: @priceColor; + } + } + .submit { + width: 320px; + height: 50px; + border-radius: 4px; + background: @xtxColor; + height: 50px; + line-height: 50px; + text-align: center; + font-size: 16px; + color: #fff; + display: block; + margin: 0 auto; + } +} diff --git a/src/assets/styles/mixins.less b/src/assets/styles/mixins.less new file mode 100644 index 0000000000000000000000000000000000000000..47f7fff70de600ee5f55b69982eab43275241173 --- /dev/null +++ b/src/assets/styles/mixins.less @@ -0,0 +1,8 @@ +// 鼠标经过上移阴影动画 +.hoverShadow () { + transition: all 0.5s; + &:hover { + transform: translate3d(0, -3px, 0); + box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2); + } +} \ No newline at end of file diff --git a/src/assets/styles/variables.less b/src/assets/styles/variables.less new file mode 100644 index 0000000000000000000000000000000000000000..2bc16396f9e74dbaf9c1ac8af599c1c445e795af --- /dev/null +++ b/src/assets/styles/variables.less @@ -0,0 +1,10 @@ +// 主题 +@xtxColor: #27ba9b; +// 辅助 +@helpColor: #e26237; +// 成功 +@sucColor: #1dc779; +// 警告 +@warnColor: #ffb302; +// 价格 +@priceColor: #cf4444; \ No newline at end of file diff --git a/src/components.d.ts b/src/components.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..4090cd3e26934caa2aad9b8cd126728dc1f66392 --- /dev/null +++ b/src/components.d.ts @@ -0,0 +1,9 @@ +// components.d.ts +declare module "@vue/runtime-core" { + export interface GlobalComponents { + RouterLink: typeof import("vue-router")["RouterLink"]; + RouterView: typeof import("vue-router")["RouterView"]; + } +} + +export {}; diff --git a/src/components/HelloWorld.vue b/src/components/HelloWorld.vue deleted file mode 100644 index e67e9e6275baa47d21c78e1e354becb4568b8640..0000000000000000000000000000000000000000 --- a/src/components/HelloWorld.vue +++ /dev/null @@ -1,40 +0,0 @@ - - - - - diff --git a/src/components/TheWelcome.vue b/src/components/TheWelcome.vue deleted file mode 100644 index cdaf9421eccf8930a6124b27c1da893e1f147575..0000000000000000000000000000000000000000 --- a/src/components/TheWelcome.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - diff --git a/src/components/WelcomeItem.vue b/src/components/WelcomeItem.vue deleted file mode 100644 index 6cff1946564ea2d20322ded7fe5c0935f2422ac5..0000000000000000000000000000000000000000 --- a/src/components/WelcomeItem.vue +++ /dev/null @@ -1,86 +0,0 @@ - - - diff --git a/src/components/XtxUI/Bread/Item.vue b/src/components/XtxUI/Bread/Item.vue new file mode 100644 index 0000000000000000000000000000000000000000..568bf97223b9b9c0a5a10b95be026b6bc655836f --- /dev/null +++ b/src/components/XtxUI/Bread/Item.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/components/XtxUI/Bread/index.vue b/src/components/XtxUI/Bread/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..7ab2cd25e4062f78ca35d7c91854ea0e848fe9f6 --- /dev/null +++ b/src/components/XtxUI/Bread/index.vue @@ -0,0 +1,41 @@ + + + + + diff --git a/src/components/XtxUI/Button/index.vue b/src/components/XtxUI/Button/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..19aaf28564b7143cf0879ff89e72da59f57caffd --- /dev/null +++ b/src/components/XtxUI/Button/index.vue @@ -0,0 +1,80 @@ + + + + + diff --git a/src/components/XtxUI/CheckBox/index.vue b/src/components/XtxUI/CheckBox/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..5c7b05bc204858cb87276e36b8aa543a5aa67c09 --- /dev/null +++ b/src/components/XtxUI/CheckBox/index.vue @@ -0,0 +1,73 @@ + + + + + diff --git a/src/components/XtxUI/City/index.vue b/src/components/XtxUI/City/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..1ad370a78a4fc1226732cb850a0bb6bc18985483 --- /dev/null +++ b/src/components/XtxUI/City/index.vue @@ -0,0 +1,197 @@ + + + + + diff --git a/src/components/XtxUI/Count/index.vue b/src/components/XtxUI/Count/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..08102653e6e458c449695137435148fa4d6a973a --- /dev/null +++ b/src/components/XtxUI/Count/index.vue @@ -0,0 +1,119 @@ + + + + + diff --git a/src/components/XtxUI/Dialog/index.vue b/src/components/XtxUI/Dialog/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..38187512227a42d32c9c8d04d3456cd1844f052f --- /dev/null +++ b/src/components/XtxUI/Dialog/index.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/src/components/XtxUI/ImageView/index.vue b/src/components/XtxUI/ImageView/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..cda7547a9ae6ac440327764ee532a2b93b01e196 --- /dev/null +++ b/src/components/XtxUI/ImageView/index.vue @@ -0,0 +1,168 @@ + + + + + diff --git a/src/components/XtxUI/InfiniteLoad/index.vue b/src/components/XtxUI/InfiniteLoad/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..b64df1cdbcceb473733f401d68677d1735080efe --- /dev/null +++ b/src/components/XtxUI/InfiniteLoad/index.vue @@ -0,0 +1,84 @@ + + + + + diff --git a/src/components/XtxUI/Message/index.ts b/src/components/XtxUI/Message/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..ca96cdd2eaf617c1fd9785b1c2424233093a409a --- /dev/null +++ b/src/components/XtxUI/Message/index.ts @@ -0,0 +1,35 @@ +// 定义一个函数 +import Message from "./index.vue"; +import { createVNode, render } from "vue"; + +// 创建一个容器 +const div = document.createElement("div"); +document.body.appendChild(div); + +export type StyleType = "warn" | "error" | "success"; + +interface Props { + type?: StyleType; + text?: string; + time?: number; +} + +export function message({ type = "warn", text = "", time = 2000 }: Props) { + // 书写以函数形式调用组件的逻辑 + // 1. type text 将来会做为message的prop传入 + // createVNode(Message, {type, text}) -> VNode对象 + // 2. 最终需要把Message组件显示到真实dom中 + // render(VNode(Message), body下的直接子元素节点中) + // 3. 俩秒钟之后消失 + // setTimeout + // render(null,div) + + let timer = -1; + const VNode = createVNode(Message, { type, text }); + render(VNode, div); + clearTimeout(timer); + timer = window.setTimeout(() => { + // 销毁组件 + render(null, div); + }, time); +} diff --git a/src/components/XtxUI/Message/index.vue b/src/components/XtxUI/Message/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..fc08cd602ce1d5e6071c56293396522304cc8c67 --- /dev/null +++ b/src/components/XtxUI/Message/index.vue @@ -0,0 +1,112 @@ + + + + + diff --git a/src/components/XtxUI/More/index.vue b/src/components/XtxUI/More/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..05989611f03499b8d4f9f005e25a9700c06a1f02 --- /dev/null +++ b/src/components/XtxUI/More/index.vue @@ -0,0 +1,40 @@ + + + + + diff --git a/src/components/XtxUI/Skeleton/Skeleton.vue b/src/components/XtxUI/Skeleton/Skeleton.vue new file mode 100644 index 0000000000000000000000000000000000000000..d524f93c6e6f904cd005460e8166b776bb0fd87b --- /dev/null +++ b/src/components/XtxUI/Skeleton/Skeleton.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/components/XtxUI/Sku/goods.d.ts b/src/components/XtxUI/Sku/goods.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..00726f00d05070c3c7362dd99b0656077c0ec9f6 --- /dev/null +++ b/src/components/XtxUI/Sku/goods.d.ts @@ -0,0 +1,122 @@ +export interface Brand { + id: string; + name: string; + nameEn: string; + logo: string; + picture: string; +} + +interface Value { + name: string; + desc: string; + picture?: string; + selected?: boolean; + disabled?: boolean; +} + +export interface Spec { + name: string; + values: Value[]; +} + +export interface SkuSpec { + name: string; + valueName: string; +} + +export interface Sku { + id: string; + skuCode: string; + price: string; + oldPrice: string; + inventory: number; + specs: SkuSpec[]; +} + +export interface Parent { + id: string; + name: string; + layer: number; + parent?: Parent; +} + +export interface Category { + id: string; + name: string; + layer: number; + parent: Parent; +} + +export interface Property { + name: string; + value: string; +} + +export interface Detail { + pictures: string[]; + properties: Property[]; +} + +export interface SimilarProduct { + id: string; + name: string; + desc: string; + price: string; + picture: string; + orderNum: number; +} + +export interface HotByDay { + id: string; + name: string; + desc: string; + price: string; + picture: string; + orderNum: number; +} + +// /goods 商品详情类型定义 +export interface GoodsDetail { + id: string; + name: string; + spuCode: string; + desc: string; + price: string; + oldPrice: string; + discount: number; + inventory: number; + brand: Brand; + salesCount: number; + commentCount: number; + collectCount: number; + videoScale: number; + mainPictures: string[]; + specs: Spec[]; + skus: Sku[]; + categories: Category[]; + details: Detail; + isPreSale: boolean; + isCollect?: boolean; + userAddresses?: any; + similarProducts: SimilarProduct[]; + hotByDay: HotByDay[]; +} + +export interface IGoodsItem { + id: string; + name: string; + desc: string; + price: string; + picture: string; + discount?: number; + ordernum?: number; +} + +export interface Stock { + skuId?: string; + discount: number; + isEffective: boolean; + nowPrice: string; + oldPrice: string; + stock: number; +} diff --git a/src/components/XtxUI/Sku/index.vue b/src/components/XtxUI/Sku/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..97610cc1c052d9ae74f0ea1fbfc10edcfaea263f --- /dev/null +++ b/src/components/XtxUI/Sku/index.vue @@ -0,0 +1,237 @@ + + + + + diff --git a/src/components/XtxUI/Sku/power-set.ts b/src/components/XtxUI/Sku/power-set.ts new file mode 100644 index 0000000000000000000000000000000000000000..0a79017649aa174028482022cf710b031cbf6cf5 --- /dev/null +++ b/src/components/XtxUI/Sku/power-set.ts @@ -0,0 +1,42 @@ +/** + * Find power-set of a set using BITWISE approach. + * + * @param {*[]} originalSet + * @return {*[][]} + */ +export default function bwPowerSet(originalSet: any[]) { + const subSets = []; + + // We will have 2^n possible combinations (where n is a length of original set). + // It is because for every element of original set we will decide whether to include + // it or not (2 options for each set element). + const numberOfCombinations = 2 ** originalSet.length; + + // Each number in binary representation in a range from 0 to 2^n does exactly what we need: + // it shows by its bits (0 or 1) whether to include related element from the set or not. + // For example, for the set {1, 2, 3} the binary number of 0b010 would mean that we need to + // include only "2" to the current set. + for ( + let combinationIndex = 0; + combinationIndex < numberOfCombinations; + combinationIndex += 1 + ) { + const subSet = []; + + for ( + let setElementIndex = 0; + setElementIndex < originalSet.length; + setElementIndex += 1 + ) { + // Decide whether we need to include current element into the subset or not. + if (combinationIndex & (1 << setElementIndex)) { + subSet.push(originalSet[setElementIndex]); + } + } + + // Add current subset to the list of all subsets. + subSets.push(subSet); + } + + return subSets; +} diff --git a/src/components/XtxUI/Slider/index.vue b/src/components/XtxUI/Slider/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..1b3d8a3c3431bffc8113bf966cd089e3ae82919d --- /dev/null +++ b/src/components/XtxUI/Slider/index.vue @@ -0,0 +1,181 @@ + + + + + diff --git a/src/components/XtxUI/Tabs/index.vue b/src/components/XtxUI/Tabs/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..8e6d0a49d27ff2396a623d1e7416340effde3ae9 --- /dev/null +++ b/src/components/XtxUI/Tabs/index.vue @@ -0,0 +1,91 @@ + + + + + diff --git a/src/components/XtxUI/Tabs/pane.vue b/src/components/XtxUI/Tabs/pane.vue new file mode 100644 index 0000000000000000000000000000000000000000..9a8f7e7edfff211d0d6c545e329b29047d8af5af --- /dev/null +++ b/src/components/XtxUI/Tabs/pane.vue @@ -0,0 +1,20 @@ + + + diff --git a/src/components/XtxUI/global.d.ts b/src/components/XtxUI/global.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9171c163ad39ab83a3ca525f3a342c450f089ba9 --- /dev/null +++ b/src/components/XtxUI/global.d.ts @@ -0,0 +1,40 @@ +// 导入 .vue 源文件 +import Button from "./Button/index.vue"; +import Skeleton from "./Skeleton/Skeleton.vue"; +import Slider from "./Slider/index.vue"; +import Bread from "./Bread/index.vue"; +import BreadItem from "./Bread/Item.vue"; +import CheckBox from "./CheckBox/index.vue"; +import City from "./City/index.vue"; +import Count from "./Count/index.vue"; +import Dialog from "./Dialog/index.vue"; +import ImageView from "./ImageView/index.vue"; +import InfiniteLoad from "./InfiniteLoad/index.vue"; +import More from "./More/index.vue"; +import Sku from "./Sku/index.vue"; +import Tabs from "./Tabs/index.vue"; +import TabPane from "./Tabs/pane.vue"; + +// 全局组件类型声明文件 for Volar +declare module "vue" { + export interface GlobalComponents { + // typeof 表示基于 .vue 源文件 获取对应的 TS 类型 + XtxButton: typeof Button; + XtxSkeleton: typeof Skeleton; + XtxSlider: typeof Slider; + XtxMore: typeof More; + XtxBread: typeof Bread; + XtxBreadItem: typeof BreadItem; + XtxCity: typeof City; + XtxCount: typeof Count; + XtxCheckBox: typeof CheckBox; + XtxImageView: typeof ImageView; + XtxSku: typeof Sku; + XtxDialog: typeof Dialog; + XtxInfiniteLoad: typeof InfiniteLoad; + XtxTabs: typeof Tabs; + XtxTabPane: typeof TabPane; + } +} + +export {}; diff --git a/src/components/XtxUI/index.ts b/src/components/XtxUI/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a26a2677bafaf862c99c3ddcf04c636f9300d73 --- /dev/null +++ b/src/components/XtxUI/index.ts @@ -0,0 +1,64 @@ +import Skeleton from "./Skeleton/Skeleton.vue"; +import Slider from "./Slider/index.vue"; +import More from "./More/index.vue"; +import Bread from "./Bread/index.vue"; +import BreadItem from "./Bread/Item.vue"; +import City from "./City/index.vue"; +import Count from "./Count/index.vue"; +import Button from "./Button/index.vue"; +import CheckBox from "./CheckBox/index.vue"; +import ImageView from "./ImageView/index.vue"; +import Sku from "./Sku/index.vue"; +import Dialog from "./Dialog/index.vue"; +import InfiniteLoad from "./InfiniteLoad/index.vue"; +import Tabs from "./Tabs/index.vue"; +import TabPane from "./Tabs/pane.vue"; +import type { App, Plugin } from "vue"; + +// 统一出口和统一组件命名前缀,用户可以按需导入组件库的组件 +// PS:按需导入后,有 TS 类型提示 +export const XtxSkeleton = Skeleton; +export const XtxSlider = Slider; +export const XtxMore = More; +export const XtxBread = Bread; +export const XtxBreadItem = BreadItem; +export const XtxImageView = ImageView; +export const XtxCity = City; +export const XtxSku = Sku; +export const XtxCount = Count; +export const XtxButton = Button; +export const XtxCheckBox = CheckBox; +export const XtxDialog = Dialog; +export const XtxTabs = Tabs; +export const XtxTabPane = TabPane; +export const XtxInfiniteLoad = InfiniteLoad; + +// Vue3 插件写法 +const XtxUI: Plugin = { + // 安装组件库(插件) + install(app: App) { + // Vue3 注册全局组件:app.component('组件名', 组件) + // 把组件库所有组件注册成全局组件 + app.component(`XtxSkeleton`, Skeleton); + app.component(`XtxSlider`, Slider); + app.component(`XtxMore`, More); + app.component(`XtxBread`, Bread); + app.component(`XtxBreadItem`, BreadItem); + app.component(`XtxImageView`, ImageView); + app.component(`XtxCity`, City); + app.component(`XtxSku`, Sku); + app.component(`XtxCount`, Count); + app.component(`XtxButton`, Button); + app.component(`XtxCheckBox`, CheckBox); + app.component(`XtxDialog`, Dialog); + app.component(`XtxTabs`, Tabs); + app.component(`XtxTabPane`, TabPane); + app.component(`XtxInfiniteLoad`, InfiniteLoad); + }, +}; + +// 默认导出组件库插件 +export default XtxUI; + +// 做统一出口 +export * from "./Message/index"; diff --git "a/src/components/XtxUI/\345\205\254\345\205\261\347\273\204\344\273\266\350\257\264\346\230\216\346\226\207\346\241\243.md" "b/src/components/XtxUI/\345\205\254\345\205\261\347\273\204\344\273\266\350\257\264\346\230\216\346\226\207\346\241\243.md" new file mode 100644 index 0000000000000000000000000000000000000000..e9d01c4a3549df0a2c6d448d4052856baf3a838d --- /dev/null +++ "b/src/components/XtxUI/\345\205\254\345\205\261\347\273\204\344\273\266\350\257\264\346\230\216\346\226\207\346\241\243.md" @@ -0,0 +1,58 @@ +## 骨架屏 + +组件名称:`XtxSkeleton` + +props + +| 名称 | 类型 | 默认值 | +| ------ | ------ | ------ | +| width | Number | 100 | +| height | Number | 60 | +| bg | String | #ccc | + +## 轮播图 + +组件名称:`XtxSlider` + +props + +| 名称 | 类型 | 默认值 | +| -------- | ------- | ------ | +| sliders | Array | [] | +| autoPlay | Boolean | true | + +## 图片预览组件 + +组件名称:`XtxImageView` + +props + +| 名称 | 类型 | 默认值 | +| --------- | ----- | ------ | +| imageList | Array | [] | + +## 商品规格组件 + +组件名称:`XtxGoodSku` + +props + +| 名称 | 类型 | 默认值 | +| ----- | ------ | ----------------------- | +| goods | Object | { specs: [], skus: [] } | + +events + +| 名称 | 回调参数 | +| ------ | ---------------------------------------- | +| change | 完整的sku对象数据 (不完整时为空对象{}) | + +## 按钮组件 + +组件名称:`XtxButton` + +| 名称 | 类型 | 可选值 | 默认值 | +| ---- | ------ | -------------------------- | ------- | +| size | String | large/middle/small/mini | middle | +| type | String | default/primary/plain/gray | default | + diff --git a/src/components/icons/IconCommunity.vue b/src/components/icons/IconCommunity.vue deleted file mode 100644 index ea8ddefb973e9b149ec422f0048e9ffc758e7483..0000000000000000000000000000000000000000 --- a/src/components/icons/IconCommunity.vue +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/src/components/icons/IconDocumentation.vue b/src/components/icons/IconDocumentation.vue deleted file mode 100644 index 63a85340aae6f52dcdc0d83edcd08c8d3bb56421..0000000000000000000000000000000000000000 --- a/src/components/icons/IconDocumentation.vue +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/src/components/icons/IconEcosystem.vue b/src/components/icons/IconEcosystem.vue deleted file mode 100644 index 385a2029d7b1d73c9ca2507bc784c3dd09644962..0000000000000000000000000000000000000000 --- a/src/components/icons/IconEcosystem.vue +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/src/components/icons/IconSupport.vue b/src/components/icons/IconSupport.vue deleted file mode 100644 index 7db961e4d457094e582e8a5eb349a62222a5f79c..0000000000000000000000000000000000000000 --- a/src/components/icons/IconSupport.vue +++ /dev/null @@ -1,12 +0,0 @@ - diff --git a/src/components/icons/IconTooling.vue b/src/components/icons/IconTooling.vue deleted file mode 100644 index 660598d7c76644ffe126a1a1feb1606650bfb937..0000000000000000000000000000000000000000 --- a/src/components/icons/IconTooling.vue +++ /dev/null @@ -1,19 +0,0 @@ - - diff --git a/src/constants/index.ts b/src/constants/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..5e777fb92d64f0eb023abbd8f8b5e2b541291ea3 --- /dev/null +++ b/src/constants/index.ts @@ -0,0 +1,3 @@ +// 项目简单,入口文件直接定义所有常量即可。 +// 项目复杂,可以拆分成多个文件,再做统一导出 +export * from "./order"; diff --git a/src/constants/order.ts b/src/constants/order.ts new file mode 100644 index 0000000000000000000000000000000000000000..02223623589db7ef98eea5343ad3d52f4e4130ce --- /dev/null +++ b/src/constants/order.ts @@ -0,0 +1,10 @@ +// 订单状态 +export const orderStatus = [ + { name: "all", label: "全部订单" }, + { name: "unpay", label: "待付款" }, + { name: "deliver", label: "待发货" }, + { name: "receive", label: "待收货" }, + { name: "comment", label: "待评价" }, + { name: "complete", label: "已完成" }, + { name: "cancel", label: "已取消" }, +]; diff --git a/src/hooks/index.ts b/src/hooks/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..341273c11b53aa02d4db8b0229f7c6b787a3cd83 --- /dev/null +++ b/src/hooks/index.ts @@ -0,0 +1,68 @@ +// hooks 主要指应用在组件中的钩子函数 +// hooks 函数内部一般会涉及到响应式数据 ref 或 reactive +import { useIntersectionObserver, useIntervalFn } from "@vueuse/core"; +import { computed, ref } from "vue"; +import dayjs from "dayjs"; + +/** + * 请求按需加载 + * @param apiFn 发送请求函数 + * @returns 🚨 target 用于模板绑定 + */ +export const useObserver = (apiFn: () => void) => { + // 准备个 ref 用于绑定模板中的某个目标元素(DOM节点或组件) + const target = ref(null); + const { stop } = useIntersectionObserver(target, ([{ isIntersecting }]) => { + console.log("是否进入可视区域", isIntersecting); + if (isIntersecting) { + // 当目标元素进入可视区域时,才发送请求 + apiFn(); + // 请求已发送,主动停止检查 + stop(); + } + }); + // 🚨返回 ref 用于模板绑定,建议返回对象格式支持解构获取 + return { target }; +}; + +/** + * 倒计时封装 + * @param seconds 倒计时秒数 + */ +export const useCountDown = (seconds = 60) => { + // 倒计时时间 + const countTime = ref(0); + // 根据秒数格式化成 mm分ss秒 格式文本 + const countTimeText = computed(() => { + return dayjs.unix(countTime.value).format("mm分ss秒"); + }); + // pause 暂停 + // resume 启动 + const { pause, resume } = useIntervalFn( + // 等于以前写 setInterval 的回调函数 + () => { + // 倒计时减少 + countTime.value--; + // 是否到 0 + if (countTime.value <= 0) { + // 暂停定时器 + pause(); + } + }, + // 等于以前写 setInterval 的时间间隔 + 1000, + // 额外封装的 immediate 是否立刻执行 + { immediate: false } + ); + + // 初始化倒计时 + const start = (startTime?: number) => { + // 初始化倒计时时间,startTime 优先级更高 + countTime.value = startTime || seconds; + // 启动定时器 + resume(); + }; + + // return 给组件使用 + return { countTime, pause, resume, start, countTimeText }; +}; diff --git a/src/main.ts b/src/main.ts index 786af441a96f1cf0b51d2d379f61b32658998cef..fa4c93eb77a952e06b2c616b9a2cabd608bd7efd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,23 @@ import { createApp } from "vue"; -import { createPinia } from "pinia"; - import App from "./App.vue"; + +// 统一不同浏览器标签默认样式 +import "normalize.css"; +// 按照项目需求,提供自己的公用样式 +import "@/assets/styles/common.less"; import router from "./router"; +import { createPinia } from "pinia"; +import XtxUI from "./components/XtxUI"; +import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; +// 创建 pinia 实例 +const pinia = createPinia(); +pinia.use(piniaPluginPersistedstate); const app = createApp(App); - -app.use(createPinia()); +// 使用 vueRouter app.use(router); - +// 使用 pinia +app.use(pinia); +// 使用 XtxUI +app.use(XtxUI); app.mount("#app"); diff --git a/src/router/index.ts b/src/router/index.ts index 35612dc5636bbc297cd40e1526c08f7b6bef1edf..4c79177959a01acecd14f232437ab6e2abf60be4 100644 --- a/src/router/index.ts +++ b/src/router/index.ts @@ -1,23 +1,89 @@ -import { createRouter, createWebHistory } from "vue-router"; -import HomeView from "../views/HomeView.vue"; +import useStore from "@/store"; +import { + createRouter, + createWebHashHistory, + type RouteRecordRaw, +} from "vue-router"; +// 指定 RouteRecordRaw[] 类型后,书写的时候就有 TS 的类型提示和检查了 +const routes: RouteRecordRaw[] = [ + { + path: "/", + component: () => import("@/views/Layout/index.vue"), + children: [ + { path: "/", component: () => import("@/views/Home/index.vue") }, + { + path: "/category/:id", + component: () => import("@/views/Category/index.vue"), + }, + { + path: "/goods/:id", + component: () => import("@/views/Goods/index.vue"), + }, + { + path: "/cart", + component: () => import("@/views/Cart/index.vue"), + }, + { + path: "/checkout", + component: () => import("@/views/Checkout/index.vue"), + }, + { + path: "/pay", + component: () => import("@/views/Pay/index.vue"), + }, + { + path: "/pay/callback", + component: () => import("@/views/Pay/callback.vue"), + }, + { + path: "/member", + component: () => import("@/views/Member/Layout/index.vue"), + children: [ + { + path: "", + component: () => import("@/views/Member/Home/index.vue"), + }, + { + path: "order", + component: () => import("@/views/Member/Order/index.vue"), + }, + ], + }, + ], + }, + { path: "/login", component: () => import("@/views/Login/index.vue") }, + { + path: "/login/callback", + component: () => import("@/views/Login/callback.vue"), + }, + { path: "/test", component: () => import("@/views/Test/index.vue") }, +]; + +// 创建路由实例 const router = createRouter({ - history: createWebHistory(import.meta.env.BASE_URL), - routes: [ - { - path: "/", - name: "home", - component: HomeView, - }, - { - path: "/about", - name: "about", - // route level code-splitting - // this generates a separate chunk (About.[hash].js) for this route - // which is lazy-loaded when the route is visited. - component: () => import("../views/AboutView.vue"), - }, - ], + // 创建 hash 路由模式 + history: createWebHashHistory(), + // 路由规则 + routes, + // VueRouter@4官网 - 进阶 - 滚动行为 + scrollBehavior: () => { + // 始终滚动到顶部 + return { top: 0 }; + }, +}); + +// 📌需求:已登录的用户才允许访问个人中心。 +// (未登录怎么?跳转到登录页并携带回跳地址) +// 导航守卫 - 全局前置守卫 +// VueRouter4升级: return 取代了以前的 next() 放行函数 +// return true 或 undefined(没写return) 都默认放行 +router.beforeEach((to) => { + const { member } = useStore(); + // 进行判断:未登录情况但是访问 /member 开头的路径,需要跳转登录页 + if (!member.isLogin && to.path.startsWith("/member")) { + return `/login?target=${to.fullPath}`; + } }); export default router; diff --git a/src/store/index.ts b/src/store/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..cb0ade9b6a3d255a27153858cd6107e7bf4426bf --- /dev/null +++ b/src/store/index.ts @@ -0,0 +1,16 @@ +// 合并 Pinia 模块 +import useCartStore from "./modules/cart"; +import useHomeStore from "./modules/home"; +import useMemberStore from "./modules/member"; + +// 封装 useStore 合并管理所有模块 +const useStore = () => { + return { + home: useHomeStore(), + member: useMemberStore(), + cart: useCartStore(), + }; +}; + +// 默认导出 +export default useStore; diff --git a/src/store/modules/cart.ts b/src/store/modules/cart.ts new file mode 100644 index 0000000000000000000000000000000000000000..79d008d96f95f8aac7259af5e44f42aa6fabca0f --- /dev/null +++ b/src/store/modules/cart.ts @@ -0,0 +1,209 @@ +import { message } from "@/components/XtxUI"; +import type { CartItem, CartList } from "@/types"; +import { http } from "@/utils/request"; +import { defineStore } from "pinia"; +import useStore from ".."; + +const useCartStore = defineStore("cart", { + // 🔔购物车开启持久化 + persist: { + key: "rabbit-shop-cart", + }, + // 状态 + state: () => ({ + // 购物车列表 + list: [] as CartList, + }), + // 计算 + getters: { + // 登录状态 + isLogin(): boolean { + // 获取 member 模块 + const { member } = useStore(); + // 基于 member 模块的 isLogin 获取登录状态 + return member.isLogin; + }, + // 计算有效商品列表 isEffective = true filter + effectiveList(): CartList { + return this.list.filter((v) => v.stock > 0 && v.isEffective); + }, + // 有效商品总数量 把 effctiveList 中的每一项的 count 叠加起来 + effectiveListCounts(): number { + // 传统遍历写法 + // let sum = 0; + // this.effectiveList.forEach((item) => { + // sum += item.count; + // }); + // return sum; + // ✨进阶 reduce 写法 + return this.effectiveList.reduce((sum, item) => sum + item.count, 0); + }, + // 总钱数 = 所有单项的钱数累加 单项的钱数 = 数量 * 单价 + effectiveListPrice(): string { + return ( + this.effectiveList + // 叠加 单价 * 数量 + .reduce((sum, item) => sum + item.count * Number(item.nowPrice), 0) + // 转换成字符串并保留两位小数 + .toFixed(2) + ); + }, + // 计算全选状态 + isAllSelected(): boolean { + return ( + this.effectiveList.length !== 0 && + this.effectiveList.every((item) => item.selected) + ); + }, + // 计算选中商品列表 + selectedList(): CartList { + return this.effectiveList.filter((item) => item.selected); + }, + // 计算选中商品总件数 + selectedListCounts(): number { + return this.selectedList.reduce((sum, item) => sum + item.count, 0); + }, + // 计算选中商品总钱数 + selectedListPrice(): string { + return this.selectedList + .reduce((sum, item) => sum + item.count * Number(item.nowPrice), 0) + .toFixed(2); + }, + }, + // 方法 + actions: { + // 加入购物车 + async addCart(data: CartItem) { + if (this.isLogin) { + const { skuId, count } = data; + const res = await http("POST", "/member/cart", { skuId, count }); + console.log("POST", "/member/cart", res.data.result); + message({ type: "success", text: "添加成功~" }); + // 🐛获取最新购物车列表 + this.getCartList(); + } else { + // console.log("未登录-本地操作", data); + // 添加商品分两种情况: + const { skuId, count } = data; + const goodsItem = this.list.find((item) => item.skuId === skuId); + if (!goodsItem) { + // 情况1:新添加的商品,前添加到数组中 + this.list.unshift(data); + } else { + // 情况2:已添加过的的商品,累加数量即可 + goodsItem.count += count; + } + } + }, + // 获取购物车列表 + async getCartList() { + if (this.isLogin) { + const res = await http("GET", "/member/cart"); + // console.log("GET", "/member/cart", res.data.result); + // 保存购物车列表数据 + this.list = res.data.result; + } else { + // console.log("未登录-本地操作"); + // 本地存储的库存信息和价格**不是服务器最新的**,所以需**要主动获取**最新商品信息。 + this.list.forEach(async (cartItem) => { + const { skuId } = cartItem; + // 根据 skuId 获取最新商品信息 + const res = await http("GET", `/goods/stock/${skuId}`); + // console.log("GET", `/goods/stock/${skuId}`, res.data.result); + const lastCartInfo = res.data.result; + console.log(lastCartInfo); + // // 更新商品现价 + // cartItem.nowPrice = lastCartInfo.nowPrice; + // // 更新商品库存 + // cartItem.stock = lastCartInfo.stock; + // // 更新商品是否有效 + // cartItem.isEffective = lastCartInfo.isEffective; + // 📌TS中 遍历 可配合 Reflect 操作 + for (const key in lastCartInfo) { + // lastCartInfo[key] 🐛传统获取报错 + const value = Reflect.get(lastCartInfo, key); + // cartItem[key] = value 🐛传统设置报错 + if (value !== null) { + Reflect.set(cartItem, key, value); + } + } + }); + } + }, + // 删除/清空购物车商品 + async deleteCart(skuIds: string[]) { + if (this.isLogin) { + const res = await http("DELETE", "/member/cart", { ids: skuIds }); + // console.log("DELETE", "/member/cart", res.data.result); + if (res.data.result) { + message({ type: "success", text: "删除成功~" }); + // 获取购物车列表 + this.getCartList(); + } else { + message({ type: "warn", text: "删除失败" }); + } + } else { + // console.log("未登录-本地操作", skuIds); + this.list = this.list.filter((item) => !skuIds.includes(item.skuId)); + } + }, + // 修改购物车商品-修改选中-修改数量 + async updateCart( + skuId: string, + data: { selected?: boolean; count?: number } + ) { + if (this.isLogin) { + const res = await http("PUT", `/member/cart/${skuId}`, data); + console.log("PUT", `/member/cart/${skuId}`, res.data.result); + // 获取购物车列表 + this.getCartList(); + } else { + // console.log("未登录-本地操作"); + const { selected, count } = data; + // 根据 skuId 查找要进行修改的商品 + const cartItem = this.list.find((item) => item.skuId === skuId); + if (cartItem) { + // 如果有 count 才更新 count + if (count !== undefined) cartItem.count = count; + // 🐛 注意 false 也是假值,判断的时候要小心 + if (selected !== undefined) cartItem.selected = selected; + } + } + }, + // 购物车全选/取消全选 + async updateCartAllSelected(data: { selected: boolean; ids: string[] }) { + if (this.isLogin) { + const res = await http("PUT", "/member/cart/selected", data); + console.log("PUT", "/member/cart/selected", res.data.result); + // 获取购物车列表 + this.getCartList(); + } else { + // console.log("未登录-本地操作"); + const { selected } = data; + this.list.forEach((item) => { + item.selected = selected; + }); + } + }, + // 清空购物车 + clearCart() { + this.list = []; + }, + // 合并本地购物车 + async mergeLocalCart() { + // 准备后端所需的参数格式 + const data = this.list.map(({ skuId, selected, count }) => ({ + skuId, + selected, + count, + })); + // 合并本地购物车到服务器 + const res = await http("POST", "/member/cart/merge", data); + console.log("POST", "/member/cart/merge", res.data.result); + // 主动更新购物车列表 + this.getCartList(); + }, + }, +}); + +export default useCartStore; diff --git a/src/store/modules/home.ts b/src/store/modules/home.ts new file mode 100644 index 0000000000000000000000000000000000000000..be2fa46c75365942caff11c887c05d13f41c8f6b --- /dev/null +++ b/src/store/modules/home.ts @@ -0,0 +1,59 @@ +import { defineStore } from "pinia"; +import { http } from "@/utils/request"; +import type { BannerList, CategoryList, GoodsItem } from "@/types"; + +// 定义 Store 时建议遵循命名规范 useXxxStore +const useHomeStore = defineStore({ + // 唯一标识 + id: "home", + // 持久化插件 - 默认存所有模块数据 + // persist: true, + // 持久化插件 - 进阶用法 + persist: { + // 修改存储时的键名称 + key: "rabbit-shop-home", + // 按需存储分类数据 + paths: ["categoryList"], + }, + // 状态 + state: () => { + return { + money: 15000, + // 所有分类数据 + categoryList: [] as CategoryList, + // 轮播图数据 + bannerList: [] as BannerList, + // 新鲜好物数据 + // 如果 TS 项目某些变量改名重构了 + // 需通过命令 yarn typecheck 主动调用TS检查,提前发现错误 + newGoodsList: [] as GoodsItem[], + }; + }, + // 函数/方法 + actions: { + // 获取所有分类数据 + async getAllCategory() { + // const res = await request.get>("/home/category/head"); + const res = await http("GET", "/home/category/head"); + // 🎉恭喜已经有类型提醒了 + // console.log(res.data.result); + // 左右类型一致了 + this.categoryList = res.data.result; + }, + // 获取轮播图数据 + async getBannerList() { + const res = await http("GET", "/home/banner"); + // console.log(res.data.result); + this.bannerList = res.data.result; + }, + // 获取新鲜好物数据 + async getNewGoodsList() { + const res = await http("GET", "/home/new"); + // console.log("/home/new", res.data.result); + this.newGoodsList = res.data.result; + }, + }, +}); + +// 默认导出 +export default useHomeStore; diff --git a/src/store/modules/member.ts b/src/store/modules/member.ts new file mode 100644 index 0000000000000000000000000000000000000000..43e0637f44ac1e6ba2d903321bd4fa2af497ccff --- /dev/null +++ b/src/store/modules/member.ts @@ -0,0 +1,102 @@ +import { http } from "@/utils/request"; +import { defineStore } from "pinia"; +import type { Profile } from "@/types"; +import { message } from "@/components/XtxUI"; +// 📌在非 .vue 组件文件中,可通过导入 router 直接获取路由实例 +import router from "@/router"; +import { + clearStorageProfile, + getStorageProfile, + saveStorageProfile, +} from "@/utils/storage"; +import useStore from ".."; + +const useMemberStore = defineStore({ + id: "member", + // 🚨使用插件遇到小问题,自己手写本地存储 + // persist: true, + // 状态 + state: () => ({ + // 用户资料 + profile: getStorageProfile(), + }), + // 计算 + getters: { + // getters 封装用户是否登录,语义更强 + isLogin(): boolean { + return Boolean(this.profile.token); + }, + }, + // 方法 + actions: { + // 登录成功后的复用逻辑封装 + loginSuccess() { + // 存储到本地 + saveStorageProfile(this.profile); + // 📌主动合并本地购物车 + const { cart } = useStore(); + cart.mergeLocalCart(); + // 登录成功提示 + message({ type: "success", text: "登录成功" }); + // console.log(router); + // 🐛 在非 .vue 组件中 useRoute() 返回 undefined,没法使用 + // const route = useRoute() + // 📌 解决方案,通过 router 路由实例 currentRoute 获取 + const route = router.currentRoute.value; + // console.log(route.path); + if (route.query.target) { + // 跳转到指定地址 + router.push(decodeURIComponent(route.query.target as string)); + } else { + // 跳转到首页 + router.push("/"); + } + }, + // 用户名密码登录 + async login(data: { account: string; password: string }) { + // 发送请求 + const res = await http("POST", "/login", data); + // console.log("/login", res.data.result); + // 存储到 Pinia 中 + this.profile = res.data.result; + // 调用登录成功后的逻辑 + this.loginSuccess(); + }, + // 退出登录 + async logout() { + // 清空 Pinia 用户信息 + this.profile = {} as Profile; + // 清空 本地 用户信息 + clearStorageProfile(); + // 跳转到登录页 + router.push("/login"); + // 退出登录-主动清空购物车数据 + const { cart } = useStore(); + cart.clearCart(); + }, + // 获取QQ登录验证码 + async getCodeQQBind(mobile: string) { + // 🔔温馨提醒:验证码是发送到用户手机上的 + await http("GET", "/login/social/code", { mobile: mobile }); + // console.log("GET", "/login/social/code", res.data.result); + }, + // QQ登录_账号绑定 + async loginQQBind(data: { unionId: string; mobile: string; code: string }) { + const res = await http("POST", "/login/social/bind", data); + // console.log("POST", "/login/social/bind", res.data.result); + this.profile = res.data.result; + // 调用登录成功后的逻辑 + this.loginSuccess(); + }, + // QQ直接登录 + async loginQQUnionId(data: { unionId: string; source: number }) { + const res = await http("POST", "/login/social", data); + // console.log("POST", "/login/social", res.data.result); + this.profile = res.data.result; + // 调用登录成功后的逻辑 + this.loginSuccess(); + }, + }, +}); + +export default useMemberStore; diff --git a/src/stores/counter.ts b/src/stores/counter.ts deleted file mode 100644 index 252406e230245c5fd09aab57c7d0449d19510758..0000000000000000000000000000000000000000 --- a/src/stores/counter.ts +++ /dev/null @@ -1,16 +0,0 @@ -import { defineStore } from "pinia"; - -export const useCounterStore = defineStore({ - id: "counter", - state: () => ({ - counter: 0, - }), - getters: { - doubleCount: (state) => state.counter * 2, - }, - actions: { - increment() { - this.counter++; - }, - }, -}); diff --git a/src/types/api/cart.d.ts b/src/types/api/cart.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..8a9476cb66dec21c93ee7e40b33cc39a28771417 --- /dev/null +++ b/src/types/api/cart.d.ts @@ -0,0 +1,22 @@ +// 单个购物车商品 +export interface CartItem { + id: string; + skuId: string; + name: string; + attrsText: string; + // specs: any[]; + picture: string; + price: string; + nowPrice: string; + nowOriginalPrice: string; + selected: boolean; + stock: number; + count: number; + isEffective: boolean; + // discount?: any; + isCollect: boolean; + postFee: number; +} + +// 购物车列表 +export type CartList = CartItem[]; diff --git a/src/types/api/category.d.ts b/src/types/api/category.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc800eb1d2baba6a9a706f75c8e7e1ca871a9735 --- /dev/null +++ b/src/types/api/category.d.ts @@ -0,0 +1,16 @@ +import type { GoodsItem } from ".."; + +// 安装Vscode插件 json2ts +// 通过快捷键:ctrl+alt+V,可以把 json 快速生成 interface 格式的 TS 类型注解 +export interface TopCategoryChildren { + id: string; + name: string; + picture: string; + goods: GoodsItem[]; +} + +export interface TopCategory { + id: string; + name: string; + children: TopCategoryChildren[]; +} diff --git a/src/types/api/goods.d.ts b/src/types/api/goods.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..745efe720ec70e86db9763589a04acf765cede1b --- /dev/null +++ b/src/types/api/goods.d.ts @@ -0,0 +1,2 @@ +// 统一先使用 Sku 组件中定义的商品详情类型 +export * from "@/components/XtxUI/Sku/goods"; diff --git a/src/types/api/home.d.ts b/src/types/api/home.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9203a10edfc3599fd815b249c559b8aead8009c9 --- /dev/null +++ b/src/types/api/home.d.ts @@ -0,0 +1,44 @@ +export type ApiRes = { + code: string; + msg: string; + // 考虑到不同接口返回的 result 不同,用泛型占位 + result: T; +}; + +// 分类数据 +export type CategoryItem = { + id: string; + name: string; + picture: string; + children: CategoryChildren[]; + goods: CategoryGoods[]; +}; + +export type CategoryChildren = { + id: string; + name: string; + picture: string; +}; + +export type CategoryGoods = { + desc: string; + id: string; + name: string; + picture: string; + price: string; +}; + +// 存一个更有语义的类型名字 +export type GoodsItem = CategoryGoods; + +export type CategoryList = CategoryItem[]; + +// 轮播图类型 +export type BannerItem = { + id: string; + imgUrl: string; + hrefUrl: string; + type: string; +}; + +export type BannerList = BannerItem[]; diff --git a/src/types/api/member.d.ts b/src/types/api/member.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..6f5ab11755d6f136d74ec9ffaab83dd6b6f4cd26 --- /dev/null +++ b/src/types/api/member.d.ts @@ -0,0 +1,13 @@ +export interface Profile { + id: string; + account: string; + mobile: string; + token: string; + avatar: string; + nickname: string; + gender: string; + birthday?: string; + cityCode: string; + provinceCode: string; + profession: string; +} diff --git a/src/types/api/order.d.ts b/src/types/api/order.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e7ecf8ed9391116183c25eac5d1251908641fb7b --- /dev/null +++ b/src/types/api/order.d.ts @@ -0,0 +1,133 @@ +export interface UserAddresse { + id: string; + receiver: string; + contact: string; + provinceCode: string; + cityCode: string; + countyCode: string; + address: string; + isDefault: number; + fullLocation: string; + postalCode: string; + addressTags: string; +} + +export interface Good { + id: string; + name: string; + picture: string; + count: number; + skuId: string; + attrsText: string; + price: string; + payPrice: string; + totalPrice: string; + totalPayPrice: string; +} + +export interface Summary { + goodsCount: number; + totalPrice: number; + totalPayPrice: number; + postFee: number; + discountPrice: number; +} + +// 订单信息 +export interface CheckoutInfo { + userAddresses: UserAddresse[]; + goods: Good[]; + summary: Summary; +} + +// 下单后的返回值 +export interface OrderResponse { + id: string; + createTime: string; + payType: number; + orderState: number; + payLatestTime: string; + postFee: number; + payMoney: number; + totalMoney: number; + totalNum: number; + // skus?: any; + payChannel: number; + // countdown?: any; +} + +export interface Property { + propertyMainName: string; + propertyValueName: string; +} + +export interface Sku { + id: string; + spuId: string; + name: string; + quantity: number; + image: string; + realPay: number; + curPrice: number; + // totalMoney?: any; + properties: Property[]; + attrsText: string; +} + +// 支付订单信息 +export interface OrderPayInfo { + id: string; + createTime: string; + payType: number; + orderState: number; + payLatestTime: string; + countdown: number; + postFee: number; + payMoney: number; + payChannel: number; + payState: number; + totalMoney: number; + totalNum: number; + deliveryTimeType: number; + receiverContact: string; + receiverMobile: string; + provinceCode: string; + cityCode: string; + countyCode: string; + receiverAddress: string; + // payTime?: any; + // consignTime?: any; + // endTime?: any; + closeTime: string; + // evaluationTime?: any; + skus: Sku[]; + // arrivalEstimatedTime?: any; +} + +// 订单项 +export interface OrderItem { + id: string; + createTime: string; + payType: number; + orderState: number; + payLatestTime: string; + postFee: number; + payMoney: number; + totalMoney: number; + totalNum: number; + skus: Sku[]; + payChannel: number; + countdown: number; +} + +// 订单列表 +export type OrderItemList = OrderItem[]; + +// 订单列表总信息 +export interface OrderListInfo { + counts: number; + pageSize: number; + pages: number; + page: number; + items: OrderItemList; +} diff --git a/src/types/api/qq.d.ts b/src/types/api/qq.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..de3bed74506e6fbc23cc927da944eb630ea33bff --- /dev/null +++ b/src/types/api/qq.d.ts @@ -0,0 +1,34 @@ +export interface Data { + ret: number; + msg: string; + is_lost: number; + nickname: string; + gender: string; + gender_type: number; + province: string; + city: string; + year: string; + constellation: string; + figureurl: string; + figureurl_1: string; + figureurl_2: string; + figureurl_qq_1: string; + figureurl_qq_2: string; + figureurl_qq: string; + figureurl_type: string; + is_yellow_vip: string; + vip: string; + yellow_vip_level: string; + level: string; + is_yellow_year_vip: string; +} + +export interface QQUserInfo { + status: string; + fmt: string; + ret: number; + code: number; + data: Data; + seq: string; + dataText: string; +} diff --git a/src/types/index.d.ts b/src/types/index.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..f9530d31f0b73cbeb50da0165522a674f25f5923 --- /dev/null +++ b/src/types/index.d.ts @@ -0,0 +1,8 @@ +// 统一导出所有自定义的类型文件 +export * from "./api/home"; +export * from "./api/category"; +export * from "./api/goods"; +export * from "./api/member"; +export * from "./api/qq"; +export * from "./api/cart"; +export * from "./api/order"; diff --git a/src/utils/index.ts b/src/utils/index.ts new file mode 100644 index 0000000000000000000000000000000000000000..11372d982a22f4c4d42f7d6f367e55d014de96bc --- /dev/null +++ b/src/utils/index.ts @@ -0,0 +1,12 @@ +// 封装的通用方法 + +/** + * 隐藏手机号码部分信息 + * @param phone 手机号码 + */ +export const hidePhone = (phone: string) => { + // 正则写法 + // return phone.replace(/^(\d{3})(\d{4})(\d{4}$)/,'$1****$3') + // 字符串截取写法 + return phone.slice(0, 3) + "****" + phone.slice(-4); +}; diff --git a/src/utils/request.ts b/src/utils/request.ts new file mode 100644 index 0000000000000000000000000000000000000000..e2056df1690762e4cfc39cb11272db3d13b490f9 --- /dev/null +++ b/src/utils/request.ts @@ -0,0 +1,84 @@ +import { message } from "@/components/XtxUI"; +import useStore from "@/store"; +import axios, { type Method } from "axios"; + +// 🚨 支付宝支付需要跳转到 devtest 这台服务器 +export const baseURL = "http://pcapi-xiaotuxian-front-devtest.itheima.net/"; +// export const baseURL = "http://pcapi-xiaotuxian-front.itheima.net/"; + +// 创建 axios 实例 +const instance = axios.create({ + baseURL: baseURL, + timeout: 50000, +}); + +// 官方说明:https://pinia.vuejs.org/core-concepts/outside-component-usage.html +// ❌ 非组件中,常见错误写法 +// const { member } = useStore(); + +// 添加请求拦截器 +instance.interceptors.request.use( + function (config) { + // 在发送请求之前做些什么 + // ✅ 非组件中,在消费前获取 Store + const { member } = useStore(); + // 1. 获取token + const { token } = member.profile; + // 2. 如果有 token 同时 headers 非空 + if (token && config.headers) { + // 3. 请求头中携带 token 信息 + config.headers.Authorization = `Bearer ${token}`; + } + return config; + }, + function (error) { + // 对请求错误做些什么 + return Promise.reject(error); + } +); + +// 添加响应拦截器 +instance.interceptors.response.use( + function (response) { + // 如果请求成功成功 2xx 就直接返回 data 中的数据 + return response; + }, + function (error) { + // 对响应错误做点什么 + if (!error.response) { + // 网络错误,response 没有信息 + message({ type: "error", text: "网络错误,请换个网络环境" }); + } else { + // 对响应错误做点什么 400 401 404 500 ... + // 通用错误,通用提示 + message({ type: "error", text: error.response.data.message }); + } + return Promise.reject(error); + } +); + +// 后端返回的接口数据格式 +export interface ResponseData { + data: { + code: string; + msg: string; + result: T; + }; +} + +/** + * axios 二次封装 + * @param {String} url 请求地址 + * @param {String} method 请求类型 + * @param {Object} submitData 对象类型,提交数据 + */ +export const http = (method: Method, url: string, submitData?: unknown) => { + return instance.request>({ + url, + method, + // 🔔 自动设置合适的 params/data 键名称,如果 method 为 get 用 params 传请求参数,否则用 data + [method.toUpperCase() === "GET" ? "params" : "data"]: submitData, + }); +}; + +export default instance; diff --git a/src/utils/storage.ts b/src/utils/storage.ts new file mode 100644 index 0000000000000000000000000000000000000000..6788c5c51a8bd13b5eca1706d18e6b3186f81c3d --- /dev/null +++ b/src/utils/storage.ts @@ -0,0 +1,15 @@ +// 用户信息持久化存储 +import type { Profile } from "@/types"; +const PROFILE_KEY = "rabbit-shop-profile"; +// 存储用户信息 +export const saveStorageProfile = (profile: Profile) => { + localStorage.setItem(PROFILE_KEY, JSON.stringify(profile)); +}; +// 获取用户信息 +export const getStorageProfile = (): Profile => { + return JSON.parse(localStorage.getItem(PROFILE_KEY) || "{}") as Profile; +}; +// 清空用户信息 +export const clearStorageProfile = () => { + localStorage.removeItem(PROFILE_KEY); +}; diff --git a/src/vendor/power-set.ts b/src/vendor/power-set.ts new file mode 100644 index 0000000000000000000000000000000000000000..cf967ad124715bad0cb08a1a6dda4ff8b0ee41ab --- /dev/null +++ b/src/vendor/power-set.ts @@ -0,0 +1,42 @@ +/** + * Find power-set of a set using BITWISE approach. + * + * @param {*[]} originalSet + * @return {*[][]} + */ +export default function bwPowerSet(originalSet: unknown[]) { + const subSets = []; + + // We will have 2^n possible combinations (where n is a length of original set). + // It is because for every element of original set we will decide whether to include + // it or not (2 options for each set element). + const numberOfCombinations = 2 ** originalSet.length; + + // Each number in binary representation in a range from 0 to 2^n does exactly what we need: + // it shows by its bits (0 or 1) whether to include related element from the set or not. + // For example, for the set {1, 2, 3} the binary number of 0b010 would mean that we need to + // include only "2" to the current set. + for ( + let combinationIndex = 0; + combinationIndex < numberOfCombinations; + combinationIndex += 1 + ) { + const subSet = []; + + for ( + let setElementIndex = 0; + setElementIndex < originalSet.length; + setElementIndex += 1 + ) { + // Decide whether we need to include current element into the subset or not. + if (combinationIndex & (1 << setElementIndex)) { + subSet.push(originalSet[setElementIndex]); + } + } + + // Add current subset to the list of all subsets. + subSets.push(subSet); + } + + return subSets; +} diff --git a/src/views/AboutView.vue b/src/views/AboutView.vue deleted file mode 100644 index 756ad2a17909837834858538422308120cf09dab..0000000000000000000000000000000000000000 --- a/src/views/AboutView.vue +++ /dev/null @@ -1,15 +0,0 @@ - - - diff --git a/src/views/Cart/index.vue b/src/views/Cart/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..298c7c27f2e84099e11ab94528e39d7368c2e3c4 --- /dev/null +++ b/src/views/Cart/index.vue @@ -0,0 +1,244 @@ + + + + + diff --git a/src/views/Category/components/goods-item.vue b/src/views/Category/components/goods-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..dcdefb2a301d50dcb1e7c59faf41be0eb51942a7 --- /dev/null +++ b/src/views/Category/components/goods-item.vue @@ -0,0 +1,44 @@ + + + + + diff --git a/src/views/Category/index.vue b/src/views/Category/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..8dfd4e0945c50b9804a2571ac49add172dc7599d --- /dev/null +++ b/src/views/Category/index.vue @@ -0,0 +1,126 @@ + + + + + diff --git a/src/views/Checkout/index.vue b/src/views/Checkout/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..8e89d5ad5416057b67b70e11eb13abad9363570e --- /dev/null +++ b/src/views/Checkout/index.vue @@ -0,0 +1,426 @@ + + + + + diff --git a/src/views/Goods/components/goods-button.vue b/src/views/Goods/components/goods-button.vue new file mode 100644 index 0000000000000000000000000000000000000000..35bae6fa16b47c467504ef1bbee2168bd4c2e73d --- /dev/null +++ b/src/views/Goods/components/goods-button.vue @@ -0,0 +1,107 @@ + + + + + diff --git a/src/views/Goods/components/goods-count.vue b/src/views/Goods/components/goods-count.vue new file mode 100644 index 0000000000000000000000000000000000000000..faf2929cacf4877c69348203f6643197aaeb6714 --- /dev/null +++ b/src/views/Goods/components/goods-count.vue @@ -0,0 +1,108 @@ + + + + + diff --git a/src/views/Goods/components/goods-sku.vue b/src/views/Goods/components/goods-sku.vue new file mode 100644 index 0000000000000000000000000000000000000000..78c5a456328ea6539d9180f696b57cf113ae59a8 --- /dev/null +++ b/src/views/Goods/components/goods-sku.vue @@ -0,0 +1,233 @@ + + + + + diff --git a/src/views/Goods/index.vue b/src/views/Goods/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..66f16ca6a2f3c054c747af1292bba3b95f95047e --- /dev/null +++ b/src/views/Goods/index.vue @@ -0,0 +1,332 @@ + + + + + diff --git a/src/views/Home/components/home-banner.vue b/src/views/Home/components/home-banner.vue new file mode 100644 index 0000000000000000000000000000000000000000..736e3407cc8438b56214bbdb22e0e11f1bcd0510 --- /dev/null +++ b/src/views/Home/components/home-banner.vue @@ -0,0 +1,23 @@ + + + + + diff --git a/src/views/Home/components/home-category.vue b/src/views/Home/components/home-category.vue new file mode 100644 index 0000000000000000000000000000000000000000..bb873db2b6e4c21d80ecf2fd8189ef4a6d67ac3a --- /dev/null +++ b/src/views/Home/components/home-category.vue @@ -0,0 +1,166 @@ + + + + + diff --git a/src/views/Home/components/home-new.vue b/src/views/Home/components/home-new.vue new file mode 100644 index 0000000000000000000000000000000000000000..ff410f314d7be962c38360ac29d1b163503b6bdc --- /dev/null +++ b/src/views/Home/components/home-new.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/src/views/Home/components/home-panel.vue b/src/views/Home/components/home-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..1f4e4d5e78c778cbbbe3a873995c18c792528246 --- /dev/null +++ b/src/views/Home/components/home-panel.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/views/Home/index.vue b/src/views/Home/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..1190cdc33346cf15d3ac05d42d11f1d9a4504822 --- /dev/null +++ b/src/views/Home/index.vue @@ -0,0 +1,22 @@ + + + + + diff --git a/src/views/HomeView.vue b/src/views/HomeView.vue deleted file mode 100644 index 4fd9c4eee52352055e1e5c637f5d2348abaa5fc9..0000000000000000000000000000000000000000 --- a/src/views/HomeView.vue +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/src/views/Layout/components/app-footer.vue b/src/views/Layout/components/app-footer.vue new file mode 100644 index 0000000000000000000000000000000000000000..949d78c4a93b1c4377efea79b6c27fd4e192cbc2 --- /dev/null +++ b/src/views/Layout/components/app-footer.vue @@ -0,0 +1,207 @@ + + + + + diff --git a/src/views/Layout/components/app-header-cart.vue b/src/views/Layout/components/app-header-cart.vue new file mode 100644 index 0000000000000000000000000000000000000000..3dbb174b59ff073386f6dadc5d0a48a554ce67af --- /dev/null +++ b/src/views/Layout/components/app-header-cart.vue @@ -0,0 +1,207 @@ + + + + + diff --git a/src/views/Layout/components/app-header-nav.vue b/src/views/Layout/components/app-header-nav.vue new file mode 100644 index 0000000000000000000000000000000000000000..a4abc1c78bcc53b9036e0713857386af287d9cca --- /dev/null +++ b/src/views/Layout/components/app-header-nav.vue @@ -0,0 +1,46 @@ + + + + + diff --git a/src/views/Layout/components/app-header-sticky.vue b/src/views/Layout/components/app-header-sticky.vue new file mode 100644 index 0000000000000000000000000000000000000000..3a2a39f9fbbef493d908e535f862f4c1a87aa5cc --- /dev/null +++ b/src/views/Layout/components/app-header-sticky.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/views/Layout/components/app-header.vue b/src/views/Layout/components/app-header.vue new file mode 100644 index 0000000000000000000000000000000000000000..ca82ab90b49a10d0e94e5aa83277ad3ca27a5deb --- /dev/null +++ b/src/views/Layout/components/app-header.vue @@ -0,0 +1,110 @@ + + + + + diff --git a/src/views/Layout/components/app-topnav.vue b/src/views/Layout/components/app-topnav.vue new file mode 100644 index 0000000000000000000000000000000000000000..53e86f42992d90619213e75a4e76b10209ca7d4c --- /dev/null +++ b/src/views/Layout/components/app-topnav.vue @@ -0,0 +1,71 @@ + + + + + diff --git a/src/views/Layout/index.vue b/src/views/Layout/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..dcd62aacbf8a18b0826023cd68fb37dced12fcc4 --- /dev/null +++ b/src/views/Layout/index.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/src/views/Login/callback.vue b/src/views/Login/callback.vue new file mode 100644 index 0000000000000000000000000000000000000000..e69d9dd8614a606b4f90168e24495973b5d730e6 --- /dev/null +++ b/src/views/Login/callback.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/src/views/Login/components/callback-bind.vue b/src/views/Login/components/callback-bind.vue new file mode 100644 index 0000000000000000000000000000000000000000..fde8d89729060c22de6e5f5a53de3a88979cd6ad --- /dev/null +++ b/src/views/Login/components/callback-bind.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/src/views/Login/components/callback-register.vue b/src/views/Login/components/callback-register.vue new file mode 100644 index 0000000000000000000000000000000000000000..1af2665a927e06c41bc64e3b102f4e1db44d8865 --- /dev/null +++ b/src/views/Login/components/callback-register.vue @@ -0,0 +1,59 @@ + + + + + diff --git a/src/views/Login/components/login-footer.vue b/src/views/Login/components/login-footer.vue new file mode 100644 index 0000000000000000000000000000000000000000..bfaf4100cd9fb65124a48cee9950529ebbc16ee2 --- /dev/null +++ b/src/views/Login/components/login-footer.vue @@ -0,0 +1,39 @@ + + + diff --git a/src/views/Login/components/login-form.vue b/src/views/Login/components/login-form.vue new file mode 100644 index 0000000000000000000000000000000000000000..c64c02be3b17b5987cdc2f3b62e7ec984cbde9fb --- /dev/null +++ b/src/views/Login/components/login-form.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/src/views/Login/components/login-header.vue b/src/views/Login/components/login-header.vue new file mode 100644 index 0000000000000000000000000000000000000000..7caee929527995d3bf2a4c275db8cb2181a76a48 --- /dev/null +++ b/src/views/Login/components/login-header.vue @@ -0,0 +1,54 @@ + + + diff --git a/src/views/Login/index.vue b/src/views/Login/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..9e0de3a0b6f8bec55019d5c536409c239c932a06 --- /dev/null +++ b/src/views/Login/index.vue @@ -0,0 +1,117 @@ + + + + + diff --git a/src/views/Member/Home/components/home-overview.vue b/src/views/Member/Home/components/home-overview.vue new file mode 100644 index 0000000000000000000000000000000000000000..7713cdf65b912dce8d97b20b1e08b67855060208 --- /dev/null +++ b/src/views/Member/Home/components/home-overview.vue @@ -0,0 +1,94 @@ + + + + + diff --git a/src/views/Member/Home/components/home-panel.vue b/src/views/Member/Home/components/home-panel.vue new file mode 100644 index 0000000000000000000000000000000000000000..2e7eda639fe717581fa0be35b3e33bb2b5d577e3 --- /dev/null +++ b/src/views/Member/Home/components/home-panel.vue @@ -0,0 +1,38 @@ + + + + + diff --git a/src/views/Member/Home/index.vue b/src/views/Member/Home/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..42c7628feb2c50c0f7e87a89ab296316ff39df30 --- /dev/null +++ b/src/views/Member/Home/index.vue @@ -0,0 +1,12 @@ + + + diff --git a/src/views/Member/Layout/components/member-aside.vue b/src/views/Member/Layout/components/member-aside.vue new file mode 100644 index 0000000000000000000000000000000000000000..73a368255086071c6f554bcf593a075bc4efef34 --- /dev/null +++ b/src/views/Member/Layout/components/member-aside.vue @@ -0,0 +1,102 @@ + + + + + diff --git a/src/views/Member/Layout/index.vue b/src/views/Member/Layout/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..ec2794fb70d2cec1fe00973235163f2025476401 --- /dev/null +++ b/src/views/Member/Layout/index.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/src/views/Member/Order/components/order-item.vue b/src/views/Member/Order/components/order-item.vue new file mode 100644 index 0000000000000000000000000000000000000000..086a39fea65256c2e009d91bc34da75532156fe6 --- /dev/null +++ b/src/views/Member/Order/components/order-item.vue @@ -0,0 +1,184 @@ + + + + + diff --git a/src/views/Member/Order/index.vue b/src/views/Member/Order/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..52ea6822c056862d5d858c1c461203987e44610d --- /dev/null +++ b/src/views/Member/Order/index.vue @@ -0,0 +1,69 @@ + + + + + diff --git a/src/views/Pay/callback.vue b/src/views/Pay/callback.vue new file mode 100644 index 0000000000000000000000000000000000000000..7efe63f0d06ca536124c5ea641267cc4949b5330 --- /dev/null +++ b/src/views/Pay/callback.vue @@ -0,0 +1,95 @@ + + + + + diff --git a/src/views/Pay/index.vue b/src/views/Pay/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..fb591ebe9edaca1e1ba928ac634228501e21dedf --- /dev/null +++ b/src/views/Pay/index.vue @@ -0,0 +1,173 @@ + + + + + diff --git "a/src/views/Test/01-\347\273\204\344\273\266\345\272\223\346\265\213\350\257\225.vue" "b/src/views/Test/01-\347\273\204\344\273\266\345\272\223\346\265\213\350\257\225.vue" new file mode 100644 index 0000000000000000000000000000000000000000..fc19fdff9c2d2671dbec83f4a184ef1e2a4e183f --- /dev/null +++ "b/src/views/Test/01-\347\273\204\344\273\266\345\272\223\346\265\213\350\257\225.vue" @@ -0,0 +1,20 @@ + + + diff --git "a/src/views/Test/02-\346\243\200\346\265\213\345\205\203\347\264\240\350\277\233\345\205\245\345\217\257\350\247\206\345\214\272.vue" "b/src/views/Test/02-\346\243\200\346\265\213\345\205\203\347\264\240\350\277\233\345\205\245\345\217\257\350\247\206\345\214\272.vue" new file mode 100644 index 0000000000000000000000000000000000000000..26b56a1f198844f2dcd88aaf23664a97b88e472e --- /dev/null +++ "b/src/views/Test/02-\346\243\200\346\265\213\345\205\203\347\264\240\350\277\233\345\205\245\345\217\257\350\247\206\345\214\272.vue" @@ -0,0 +1,40 @@ + + + + + diff --git "a/src/views/Test/03-v-model\345\217\214\345\220\221\347\273\221\345\256\232.vue" "b/src/views/Test/03-v-model\345\217\214\345\220\221\347\273\221\345\256\232.vue" new file mode 100644 index 0000000000000000000000000000000000000000..562caf4398fe649f2374cfc969efee5f541217f0 --- /dev/null +++ "b/src/views/Test/03-v-model\345\217\214\345\220\221\347\273\221\345\256\232.vue" @@ -0,0 +1,17 @@ + + + + + diff --git "a/src/views/Test/04-\347\231\273\345\275\225\346\210\220\345\212\237\345\233\236\350\267\263\346\265\213\350\257\225.vue" "b/src/views/Test/04-\347\231\273\345\275\225\346\210\220\345\212\237\345\233\236\350\267\263\346\265\213\350\257\225.vue" new file mode 100644 index 0000000000000000000000000000000000000000..6c0d30ef86ba7dfb0374a08acaa61d20da533835 --- /dev/null +++ "b/src/views/Test/04-\347\231\273\345\275\225\346\210\220\345\212\237\345\233\236\350\267\263\346\265\213\350\257\225.vue" @@ -0,0 +1,11 @@ + + + + + diff --git "a/src/views/Test/05-\345\200\222\350\256\241\346\227\266\351\200\273\350\276\221\345\207\275\346\225\260\345\260\201\350\243\205.vue" "b/src/views/Test/05-\345\200\222\350\256\241\346\227\266\351\200\273\350\276\221\345\207\275\346\225\260\345\260\201\350\243\205.vue" new file mode 100644 index 0000000000000000000000000000000000000000..e57277f9780fd67c9950964e73f85bb6a62dc439 --- /dev/null +++ "b/src/views/Test/05-\345\200\222\350\256\241\346\227\266\351\200\273\350\276\221\345\207\275\346\225\260\345\260\201\350\243\205.vue" @@ -0,0 +1,47 @@ + + + diff --git "a/src/views/Test/06-\345\200\222\350\256\241\346\227\266hooks\345\260\201\350\243\205\350\260\203\347\224\250.vue" "b/src/views/Test/06-\345\200\222\350\256\241\346\227\266hooks\345\260\201\350\243\205\350\260\203\347\224\250.vue" new file mode 100644 index 0000000000000000000000000000000000000000..cb6c90c02b55a03e9c6adcb129219cdd30527a22 --- /dev/null +++ "b/src/views/Test/06-\345\200\222\350\256\241\346\227\266hooks\345\260\201\350\243\205\350\260\203\347\224\250.vue" @@ -0,0 +1,15 @@ + + + diff --git "a/src/views/Test/07-TS\351\201\215\345\216\206\345\222\214Reflect\345\257\271\350\261\241.vue" "b/src/views/Test/07-TS\351\201\215\345\216\206\345\222\214Reflect\345\257\271\350\261\241.vue" new file mode 100644 index 0000000000000000000000000000000000000000..f8bbfcd3aadb6e821d1cd5ed95cdbbe0db5216e3 --- /dev/null +++ "b/src/views/Test/07-TS\351\201\215\345\216\206\345\222\214Reflect\345\257\271\350\261\241.vue" @@ -0,0 +1,22 @@ + + + diff --git "a/src/views/Test/08-render\346\270\262\346\237\223\345\207\275\346\225\260+createVNode\345\210\233\345\273\272\350\231\232\346\213\237DOM.vue" "b/src/views/Test/08-render\346\270\262\346\237\223\345\207\275\346\225\260+createVNode\345\210\233\345\273\272\350\231\232\346\213\237DOM.vue" new file mode 100644 index 0000000000000000000000000000000000000000..5aed31df329c7715081e9dc4f620b74a77360376 --- /dev/null +++ "b/src/views/Test/08-render\346\270\262\346\237\223\345\207\275\346\225\260+createVNode\345\210\233\345\273\272\350\231\232\346\213\237DOM.vue" @@ -0,0 +1,15 @@ + diff --git "a/src/views/Test/09-vue\347\273\204\344\273\266\347\232\204\346\224\257\346\214\201TSX\346\210\226JSX.vue" "b/src/views/Test/09-vue\347\273\204\344\273\266\347\232\204\346\224\257\346\214\201TSX\346\210\226JSX.vue" new file mode 100644 index 0000000000000000000000000000000000000000..b3bf686648ea897f41a1b36efcb561d7e0cc77a1 --- /dev/null +++ "b/src/views/Test/09-vue\347\273\204\344\273\266\347\232\204\346\224\257\346\214\201TSX\346\210\226JSX.vue" @@ -0,0 +1,15 @@ + diff --git "a/src/views/Test/10-setup\344\270\255\344\275\277\347\224\250TSX.vue" "b/src/views/Test/10-setup\344\270\255\344\275\277\347\224\250TSX.vue" new file mode 100644 index 0000000000000000000000000000000000000000..5444267be941ff8733b267813baf0c9e7f12d4ed --- /dev/null +++ "b/src/views/Test/10-setup\344\270\255\344\275\277\347\224\250TSX.vue" @@ -0,0 +1,44 @@ + + + diff --git "a/src/views/Test/11-Tabs\347\273\204\344\273\266\345\260\201\350\243\205\346\265\213\350\257\225.vue" "b/src/views/Test/11-Tabs\347\273\204\344\273\266\345\260\201\350\243\205\346\265\213\350\257\225.vue" new file mode 100644 index 0000000000000000000000000000000000000000..d9835e36b7cc3d1158122cf9ed7aa5ac2bb51125 --- /dev/null +++ "b/src/views/Test/11-Tabs\347\273\204\344\273\266\345\260\201\350\243\205\346\265\213\350\257\225.vue" @@ -0,0 +1,39 @@ + + + diff --git "a/src/views/Test/12-\351\241\266\347\272\247await\347\273\204\344\273\266.vue" "b/src/views/Test/12-\351\241\266\347\272\247await\347\273\204\344\273\266.vue" new file mode 100644 index 0000000000000000000000000000000000000000..b70ac72960aab825be896a52fccd5dfae1b9f542 --- /dev/null +++ "b/src/views/Test/12-\351\241\266\347\272\247await\347\273\204\344\273\266.vue" @@ -0,0 +1,11 @@ + + + diff --git a/src/views/Test/components/Tabs/index.vue b/src/views/Test/components/Tabs/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..d642bb3d83dbb04d0a9f4d44b546ac021f44cc5f --- /dev/null +++ b/src/views/Test/components/Tabs/index.vue @@ -0,0 +1,75 @@ + + + diff --git a/src/views/Test/components/Tabs/pane.vue b/src/views/Test/components/Tabs/pane.vue new file mode 100644 index 0000000000000000000000000000000000000000..9fbc2b81a8e054a23b664400da7eeaa9f4afd5a3 --- /dev/null +++ b/src/views/Test/components/Tabs/pane.vue @@ -0,0 +1,26 @@ + + + + + diff --git a/src/views/Test/components/test-v-model.vue b/src/views/Test/components/test-v-model.vue new file mode 100644 index 0000000000000000000000000000000000000000..7840aa49237ccf28f547a670d80fd81625971bd1 --- /dev/null +++ b/src/views/Test/components/test-v-model.vue @@ -0,0 +1,30 @@ + + + + + diff --git a/src/views/Test/index.vue b/src/views/Test/index.vue new file mode 100644 index 0000000000000000000000000000000000000000..3710c15bbea70b244c5a449a396d88571ee96937 --- /dev/null +++ b/src/views/Test/index.vue @@ -0,0 +1,25 @@ + + + diff --git a/tsconfig.vite-config.json b/tsconfig.vite-config.json index d20d872603b7acf0755cd33e545cb8d10627b78f..93fe5846ea76dd7e5889b70e7236ae379b3704ef 100644 --- a/tsconfig.vite-config.json +++ b/tsconfig.vite-config.json @@ -3,6 +3,6 @@ "include": ["vite.config.*"], "compilerOptions": { "composite": true, - "types": ["node", "vitest"] + "types": ["node"] } } diff --git a/vite.config.ts b/vite.config.ts index f828a4c4d0656b3fe58ae1d527463a3677041216..ebf36140bbe6da9bf611f44c24ec211fa0239659 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -2,14 +2,49 @@ import { fileURLToPath, URL } from "url"; import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; +// 1. Jsx 插件,初始化项目的时候选择Jsx就会自动下载 import vueJsx from "@vitejs/plugin-vue-jsx"; +import vueSetupExtend from "vite-plugin-vue-setup-extend"; // https://vitejs.dev/config/ export default defineConfig({ - plugins: [vue(), vueJsx()], + // 配置开发服务器 + server: { + // QQ三方登录的回调uri为:http://www.corho.com:8080/#/login/callback + // vite 中配置: www.corho.com:8080 + // 🔔要实现QQ授权登录,需要做两个步骤: + // 1. 复制项目根目录的 hosts 文件到电脑中,位置为 C:\Windows\System32\drivers\etc 目录下 + // 2. 开启以下注释,让本地服务器支持通过 www.corho.com 访问当前 Vue项目。 + // host: "www.corho.com", + port: 8080, + // 其他常用配置项 + open: true, // 帮我们打开浏览器 + cors: true, // 允许开发时 ajax 跨域 + }, + base: "./", // 设置打包路径,部署到码云GiteePages需要 + plugins: [ + vue({ + // 响应性语法糖目前默认是关闭状态,需要你显式选择启用。此外,接下来的所有配置都需要 vue@^3.2.25 + reactivityTransform: true, + }), + // 2. 应用 vueJsx 插件 + vueJsx(), + vueSetupExtend(), + ], resolve: { alias: { "@": fileURLToPath(new URL("./src", import.meta.url)), }, }, + css: { + // https://cn.vitejs.dev/config/#css-preprocessoroptions + preprocessorOptions: { + less: { + additionalData: ` + @import "@/assets/styles/variables.less"; + @import "@/assets/styles/mixins.less"; + `, + }, + }, + }, }); diff --git a/yarn.lock b/yarn.lock index 1544ea8a434def51244319bbdfe2247edfd6aa51..405adea2d2bba5bcdbe2d2c44b39ff90746843e1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -586,7 +586,7 @@ "@vue/compiler-core" "3.2.31" "@vue/shared" "3.2.31" -"@vue/compiler-sfc@3.2.31": +"@vue/compiler-sfc@3.2.31", "@vue/compiler-sfc@^3.2.29": version "3.2.31" resolved "https://registry.npmmirror.com/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz#d02b29c3fe34d599a52c5ae1c6937b4d69f11c2f" integrity sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ== @@ -685,6 +685,21 @@ resolved "https://registry.npmmirror.com/@vue/tsconfig/-/tsconfig-0.1.3.tgz#4a61dbd29783d01ddab504276dcf0c2b6988654f" integrity sha512-kQVsh8yyWPvHpb8gIc9l/HIDiiVUy1amynLNpCy8p+FoCiZXCo6fQos5/097MmnNZc9AtseDsCrfkhqCrJ8Olg== +"@vueuse/core@^7.6.2": + version "7.6.2" + resolved "https://registry.npmmirror.com/@vueuse/core/-/core-7.6.2.tgz#1b9aa92048991189fac31577ff439296efa5fb4a" + integrity sha512-bjAbXJVJO6aElMaZtDz2B70C0L6jFk/jGVqJxWZS5huffxA6dW5DN6tQQJwzOnx9B9rDhePHJIFKsix0qZIH2Q== + dependencies: + "@vueuse/shared" "7.6.2" + vue-demi "*" + +"@vueuse/shared@7.6.2": + version "7.6.2" + resolved "https://registry.npmmirror.com/@vueuse/shared/-/shared-7.6.2.tgz#6d83bdb003cd8a56dc779fe501e73a4cb755b802" + integrity sha512-ThDld4Mx501tahRuHV6qJGkwCr17GknZrOzlD02Na9qJcH7Pq0quNTLx5cNDou7b1CKNvE3BXi2w/hz9KuPNTQ== + dependencies: + vue-demi "*" + acorn-jsx@^5.3.1: version "5.3.2" resolved "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" @@ -749,6 +764,13 @@ assert-never@^1.2.1: resolved "https://registry.npmmirror.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe" integrity sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw== +axios@^0.26.0: + version "0.26.0" + resolved "https://registry.npmmirror.com/axios/-/axios-0.26.0.tgz#9a318f1c69ec108f8cd5f3c3d390366635e13928" + integrity sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og== + dependencies: + follow-redirects "^1.14.8" + babel-walk@3.0.0-canary-5: version "3.0.0-canary-5" resolved "https://registry.npmmirror.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11" @@ -878,6 +900,13 @@ convert-source-map@^1.7.0: dependencies: safe-buffer "~5.1.1" +copy-anything@^2.0.1: + version "2.0.6" + resolved "https://registry.npmmirror.com/copy-anything/-/copy-anything-2.0.6.tgz#092454ea9584a7b7ad5573062b2a87f5900fc480" + integrity sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw== + dependencies: + is-what "^3.14.1" + cross-spawn@^7.0.2: version "7.0.3" resolved "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" @@ -892,6 +921,18 @@ csstype@^2.6.8: resolved "https://registry.npmmirror.com/csstype/-/csstype-2.6.19.tgz#feeb5aae89020bb389e1f63669a5ed490e391caa" integrity sha512-ZVxXaNy28/k3kJg0Fou5MiYpp88j7H9hLZp8PDC3jV0WFjfH5E9xHb56L0W59cPbKbcHXeP4qyT8PrHp8t6LcQ== +dayjs@^1.10.7: + version "1.10.7" + resolved "https://registry.npmmirror.com/dayjs/-/dayjs-1.10.7.tgz#2cf5f91add28116748440866a0a1d26f3a6ce468" + integrity sha512-P6twpd70BcPK34K26uJ1KT3wlhpuOAPoMwJzpsIWUxHZ7wpmbdZL/hQqBDfz7hGurYSa5PhzdhDHtt319hL3ig== + +debug@^3.2.6: + version "3.2.7" + resolved "https://registry.npmmirror.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" + integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== + dependencies: + ms "^2.1.1" + debug@^4.1.0, debug@^4.1.1, debug@^4.3.2: version "4.3.3" resolved "https://registry.npmmirror.com/debug/-/debug-4.3.3.tgz#04266e0b70a98d4462e6e288e38259213332b664" @@ -976,6 +1017,13 @@ entities@^3.0.1: resolved "https://registry.npmmirror.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== +errno@^0.1.1: + version "0.1.8" + resolved "https://registry.npmmirror.com/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f" + integrity sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A== + dependencies: + prr "~1.0.1" + esbuild-android-arm64@0.14.22: version "0.14.22" resolved "https://registry.npmmirror.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.22.tgz#fb051169a63307d958aec85ad596cfc7d7770303" @@ -1315,6 +1363,11 @@ flatted@^3.1.0: resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3" integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg== +follow-redirects@^1.14.8: + version "1.14.8" + resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc" + integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA== + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmmirror.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1399,6 +1452,11 @@ globby@^11.0.4: merge2 "^1.4.1" slash "^3.0.0" +graceful-fs@^4.1.2: + version "4.2.9" + resolved "https://registry.npmmirror.com/graceful-fs/-/graceful-fs-4.2.9.tgz#041b05df45755e587a24942279b9d113146e1c96" + integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.npmmirror.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -1448,6 +1506,13 @@ htmlparser2@^7.2.0: domutils "^2.8.0" entities "^3.0.1" +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.npmmirror.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + ignore@^4.0.6: version "4.0.6" resolved "https://registry.npmmirror.com/ignore/-/ignore-4.0.6.tgz#750e3db5862087b4737ebac8207ffd1ef27b25fc" @@ -1458,6 +1523,11 @@ ignore@^5.1.8, ignore@^5.2.0: resolved "https://registry.npmmirror.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a" integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ== +image-size@~0.5.0: + version "0.5.5" + resolved "https://registry.npmmirror.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" + integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== + import-fresh@^3.0.0, import-fresh@^3.2.1: version "3.3.0" resolved "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" @@ -1529,6 +1599,11 @@ is-regex@^1.0.3: call-bind "^1.0.2" has-tostringtag "^1.0.0" +is-what@^3.14.1: + version "3.14.1" + resolved "https://registry.npmmirror.com/is-what/-/is-what-3.14.1.tgz#e1222f46ddda85dead0fd1c9df131760e77755c1" + integrity sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA== + isexe@^2.0.0: version "2.0.0" resolved "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" @@ -1591,6 +1666,23 @@ jstransformer@1.0.0: is-promise "^2.0.0" promise "^7.0.1" +less@^4.1.2: + version "4.1.2" + resolved "https://registry.npmmirror.com/less/-/less-4.1.2.tgz#6099ee584999750c2624b65f80145f8674e4b4b0" + integrity sha512-EoQp/Et7OSOVu0aJknJOtlXZsnr8XE8KwuzTHOLeVSEx8pVWUICc8Q0VYRHgzyjX78nMEyC/oztWFbgyhtNfDA== + dependencies: + copy-anything "^2.0.1" + parse-node-version "^1.0.1" + tslib "^2.3.0" + optionalDependencies: + errno "^0.1.1" + graceful-fs "^4.1.2" + image-size "~0.5.0" + make-dir "^2.1.0" + mime "^1.4.1" + needle "^2.5.2" + source-map "~0.6.0" + levn@^0.4.1: version "0.4.1" resolved "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" @@ -1623,6 +1715,14 @@ magic-string@^0.25.7: dependencies: sourcemap-codec "^1.4.4" +make-dir@^2.1.0: + version "2.1.0" + resolved "https://registry.npmmirror.com/make-dir/-/make-dir-2.1.0.tgz#5f0310e18b8be898cc07009295a30ae41e91e6f5" + integrity sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA== + dependencies: + pify "^4.0.1" + semver "^5.6.0" + merge2@^1.3.0, merge2@^1.4.1: version "1.4.1" resolved "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" @@ -1636,6 +1736,11 @@ micromatch@^4.0.4: braces "^3.0.1" picomatch "^2.2.3" +mime@^1.4.1: + version "1.6.0" + resolved "https://registry.npmmirror.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" + integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== + minimatch@^3.0.4: version "3.1.2" resolved "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" @@ -1653,6 +1758,11 @@ ms@2.1.2: resolved "https://registry.npmmirror.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== +ms@^2.1.1: + version "2.1.3" + resolved "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + nanoid@^3.2.0: version "3.3.1" resolved "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" @@ -1663,11 +1773,25 @@ natural-compare@^1.4.0: resolved "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +needle@^2.5.2: + version "2.9.1" + resolved "https://registry.npmmirror.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" + integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + node-releases@^2.0.1: version "2.0.2" resolved "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01" integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg== +normalize.css@^8.0.1: + version "8.0.1" + resolved "https://registry.npmmirror.com/normalize.css/-/normalize.css-8.0.1.tgz#9b98a208738b9cc2634caacbc42d131c97487bf3" + integrity sha512-qizSNPO93t1YUuUhP22btGOo3chcvDFqFaj2TRybP0DMxkHOCTYwp3n34fel4a31ORXy4m1Xq0Gyqpb5m33qIg== + object-assign@^4.1.1: version "4.1.1" resolved "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" @@ -1699,6 +1823,11 @@ parent-module@^1.0.0: dependencies: callsites "^3.0.0" +parse-node-version@^1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" + integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== + path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.npmmirror.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" @@ -1729,6 +1858,16 @@ picomatch@^2.2.2, picomatch@^2.2.3: resolved "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== +pify@^4.0.1: + version "4.0.1" + resolved "https://registry.npmmirror.com/pify/-/pify-4.0.1.tgz#4b2cd25c50d598735c50292224fd8c6df41e3231" + integrity sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g== + +pinia-plugin-persistedstate@^1.2.2: + version "1.2.2" + resolved "https://registry.npmmirror.com/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-1.2.2.tgz#a9d36e18dcf903b32c013b6a8da865d6f9b00c21" + integrity sha512-qm/fYlkA0p7E3spL0bjSxxl2nm3szGNsIGKlWAbHddMvR48prWi7qYmtmVFIERCpbdCYVSmq5AvGIrGbWzkVzw== + pinia@^2.0.11: version "2.0.11" resolved "https://registry.npmmirror.com/pinia/-/pinia-2.0.11.tgz#ff03c714f5e5f16207280a4fc2eab01f3701ee2b" @@ -1770,6 +1909,11 @@ promise@^7.0.1: dependencies: asap "~2.0.3" +prr@~1.0.1: + version "1.0.1" + resolved "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz#d3fc114ba06995a45ec6893f484ceb1d78f5f476" + integrity sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw== + pug-attrs@^3.0.0: version "3.0.0" resolved "https://registry.npmmirror.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41" @@ -1933,6 +2077,21 @@ safe-buffer@~5.1.1: resolved "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== +"safer-buffer@>= 2.1.2 < 3": + version "2.1.2" + resolved "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +sax@^1.2.4: + version "1.2.4" + resolved "https://registry.npmmirror.com/sax/-/sax-1.2.4.tgz#2816234e2378bddc4e5354fab5caa895df7100d9" + integrity sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw== + +semver@^5.6.0: + version "5.7.1" + resolved "https://registry.npmmirror.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" + integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== + semver@^6.3.0: version "6.3.0" resolved "https://registry.npmmirror.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" @@ -1972,7 +2131,7 @@ source-map@^0.5.0: resolved "https://registry.npmmirror.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" integrity sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ== -source-map@^0.6.1: +source-map@^0.6.1, source-map@~0.6.0: version "0.6.1" resolved "https://registry.npmmirror.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== @@ -2045,6 +2204,11 @@ tslib@^1.8.1: resolved "https://registry.npmmirror.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.3.0: + version "2.3.1" + resolved "https://registry.npmmirror.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" + integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -2086,6 +2250,14 @@ v8-compile-cache@^2.0.3: resolved "https://registry.npmmirror.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee" integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA== +vite-plugin-vue-setup-extend@^0.4.0: + version "0.4.0" + resolved "https://registry.npmmirror.com/vite-plugin-vue-setup-extend/-/vite-plugin-vue-setup-extend-0.4.0.tgz#ebbbe265320039b8c6a3b9fcae3b8d152ecf4a13" + integrity sha512-WMbjPCui75fboFoUTHhdbXzu4Y/bJMv5N9QT9a7do3wNMNHHqrk+Tn2jrSJU0LS5fGl/EG+FEDBYVUeWIkDqXQ== + dependencies: + "@vue/compiler-sfc" "^3.2.29" + magic-string "^0.25.7" + vite@^2.7.13: version "2.8.3" resolved "https://registry.npmmirror.com/vite/-/vite-2.8.3.tgz#bb9b7f1f1446d2e538e81026f48d2fe9f1926963"