├── .editorconfig ├── .env ├── .eslintignore ├── .eslintrc.cjs ├── .gitignore ├── .husky └── commit-msg ├── .npmrc ├── .nvmrc ├── .prettierignore ├── .prettierrc.mjs ├── .stylelintignore ├── .stylelintrc.mjs ├── .vscode ├── extensions.json └── settings.json ├── LICENSE ├── README.md ├── manifest.config.ts ├── package.json ├── pages.config.ts ├── pnpm-lock.yaml ├── src ├── App.vue ├── apis │ ├── index.ts │ ├── interceptors │ │ ├── index.ts │ │ └── response.ts │ ├── modules │ │ ├── activity.ts │ │ ├── index.ts │ │ └── userInfo.ts │ └── request │ │ ├── index.ts │ │ └── log.ts ├── components │ ├── AvatarNickname.vue │ ├── Cell.vue │ ├── FooterActionsBar.vue │ ├── FormControl.vue │ ├── LabelValueBar.vue │ ├── List.vue │ ├── SwiperPro.vue │ ├── TabsPaneList.vue │ ├── TitleBar.vue │ ├── Uploader.vue │ ├── auth │ │ ├── AuthAvatarButton.vue │ │ ├── AuthAvatarNicknameDialog.vue │ │ ├── AuthPhoneNumberButton.vue │ │ ├── NickNameInput.vue │ │ └── index.ts │ ├── dialog │ │ ├── Dialog.vue │ │ ├── FormDialog.vue │ │ └── index.ts │ ├── index.ts │ ├── layout │ │ ├── Layout.vue │ │ ├── NavBar.vue │ │ ├── TabBar.vue │ │ └── index.ts │ ├── picker │ │ ├── DateTimePicker.vue │ │ ├── Picker.vue │ │ ├── index.ts │ │ └── type.ts │ ├── popup │ │ ├── Popup.vue │ │ └── index.ts │ ├── provideComponentOptions.ts │ └── u-charts │ │ ├── config-ucharts.js │ │ ├── u-charts.js │ │ └── u-charts.vue ├── constants │ ├── index.ts │ └── userInfo.ts ├── hooks │ ├── index.ts │ ├── pagination │ │ ├── index.ts │ │ ├── useListPagination.ts │ │ └── usePagination.ts │ ├── share │ │ └── index.ts │ └── timer │ │ └── index.ts ├── main.ts ├── manifest.json ├── pages.json ├── pages │ ├── home.vue │ ├── launch.vue │ ├── list.vue │ ├── login.vue │ ├── my.vue │ └── test.vue ├── routerInterceptor │ ├── index.ts │ └── login.ts ├── static │ └── image │ │ ├── List │ │ ├── back-top.png │ │ └── empty.png │ │ ├── layout │ │ └── TabBar │ │ │ ├── icon1-off.png │ │ │ ├── icon1-on.png │ │ │ ├── icon2-off.png │ │ │ └── icon2-on.png │ │ ├── mp_wx.svg │ │ ├── popup │ │ └── Popup │ │ │ └── close.png │ │ ├── share.jpg │ │ └── uniapp.ico ├── stores │ ├── activity.ts │ ├── index.ts │ ├── tabBar.ts │ └── userInfo.ts ├── styles │ ├── funs │ │ └── index.scss │ ├── index.scss │ ├── mixins │ │ └── index.scss │ └── variable │ │ ├── custom.scss │ │ └── uni.scss ├── subPackages │ └── webview │ │ └── pages │ │ └── webview.vue ├── types │ ├── authorize.ts │ ├── dts │ │ ├── api.d.ts │ │ ├── components.d.ts │ │ ├── index.d.ts │ │ └── pages.d.ts │ ├── index.ts │ └── userInfo.ts └── utils │ ├── data │ └── index.ts │ ├── dateTime │ └── index.ts │ ├── env │ └── index.ts │ ├── form │ ├── identityCard.ts │ └── index.ts │ ├── index.ts │ ├── location │ └── index.ts │ ├── media │ └── index.ts │ ├── miniProgram │ └── index.ts │ ├── pages │ ├── index.ts │ └── navigate.ts │ ├── tool │ └── index.ts │ ├── url │ └── index.ts │ └── userInfo │ └── index.ts ├── tsconfig.app.json ├── tsconfig.json ├── tsconfig.node.json ├── types ├── env.d.ts └── index.d.ts ├── vite.config.ts └── vite └── utils.ts /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 为了兼容不同的编辑器 2 | 3 | root = true 4 | 5 | [*] 6 | indent_style = space 7 | indent_size = 4 8 | end_of_line = lf 9 | charset = utf-8 10 | trim_trailing_whitespace = true 11 | insert_final_newline = true 12 | 13 | [*.md] 14 | trim_trailing_whitespace = false 15 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | # 组件目录 默认:components 2 | VITE_COMPONENT_DIR = components 3 | 4 | # 页面目录 默认:pages 5 | VITE_PAGE_DIR = pages 6 | 7 | # `src`下 分包目录 默认:subPackages 8 | VITE_SUB_PACKAGE_DIR = subPackages 9 | 10 | # `VITE_SUB_PACKAGE_DIR`下 分包子目录集合 如果涉及多个子分包,用逗号分隔 默认:webview 11 | VITE_SUB_PACKAGE_CHILD_DIRS = webview 12 | 13 | # 启动页路径 默认:pages/launch 14 | VITE_LAUNCH_PATH = ${VITE_PAGE_DIR}/launch 15 | 16 | # 是否使用启动页 默认:false 17 | # 可以在小程序运行时控制首次显示的页面,使用时注意: 18 | # 1.小程序首页变为`VITE_LAUNCH_PATH`,页面跳转逻辑将在 `launch.vue` 中的 `onLoad` 进行,可根据需求修改 19 | # 2.小程序码路径示例: 目标页面为`a`页面,则路径应该为 `/pages/launch?targetPath=pages/a&test=1`,其中`test=1`会传递给`a`页面 20 | VITE_USE_LAUNCH_PAGE = false 21 | 22 | # 登录页面的路径 默认:pages/login 23 | VITE_LOGIN_PATH = ${VITE_PAGE_DIR}/login 24 | 25 | # 首页路径 默认:pages/home 26 | VITE_HOME_PATH = ${VITE_PAGE_DIR}/home 27 | 28 | # TODO: wx小程序appid 29 | VITE_MP_WX_APPID = 30 | 31 | # 开发环境服务器网址(小程序开发版、体验版用到) 默认:http://xxx.com 32 | VITE_DEV_SERVER_URL = http://jsonplaceholder.typicode.com 33 | 34 | # 生产环境服务器网址(小程序体验版、线上版用到) 默认:http://xxx.com 35 | VITE_PROD_SERVER_URL = http://jsonplaceholder.typicode.com 36 | 37 | # 接口请求基础路径 默认:/api 38 | VITE_API_BASE_PATH = /posts 39 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | # 编辑目录和文件 2 | .idea 3 | .DS_Store 4 | .hbuilderx 5 | coverage 6 | *.suo 7 | *.ntvs* 8 | *.njsproj 9 | *.sln 10 | *.sw 11 | *.local 12 | 13 | # Logs文件 14 | logs 15 | *.log* 16 | 17 | # 项目文件 18 | dist 19 | node_modules 20 | src/components/u-charts 21 | src/static/ 22 | src/manifest.json 23 | src/pages.json 24 | .* 25 | *-lock.* 26 | 27 | # 不忽略的项目文件 28 | !.vscode 29 | !.prettierrc* 30 | !.stylelintrc* 31 | !.eslintrc* 32 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-07-30 22:17:34 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-07 21:11:19 6 | * @FilePath: /uniapp-mp-wx-template/.eslintrc.cjs 7 | * @Description: eslint配置文件 注意:每次配置文件的更改,建议重启一下vscode,否则可能不会生效 8 | */ 9 | 10 | module.exports = { 11 | extends: ["@dyb-dev/eslint-config"] 12 | } 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # 编辑目录和文件 2 | .idea 3 | .DS_Store 4 | coverage 5 | *.suo 6 | *.ntvs* 7 | *.njsproj 8 | *.sln 9 | *.sw? 10 | *.local 11 | 12 | # Logs 13 | logs 14 | *.log* 15 | 16 | # 忽略项目文件 17 | .history 18 | dist 19 | node_modules 20 | -------------------------------------------------------------------------------- /.husky/commit-msg: -------------------------------------------------------------------------------- 1 | # Git提交时自动效验提交信息 2 | project-cli commit-lint $1 3 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | # 默认走淘宝镜像 2 | registry = https://registry.npmmirror.com 3 | 4 | # 私服npm用到,携带@xxx前缀的包名会走http://xxx.com:8080 5 | # @dyb-dev:registry = https://registry.npmjs.org/ 6 | 7 | # 将所有的依赖提升到项目的根目录下,解决 @dcloudio/vite-plugin-uni 插件找不到依赖的问题 8 | shamefully-hoist = true 9 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/iron 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # 编辑目录和文件 2 | .idea 3 | .DS_Store 4 | coverage 5 | *.suo 6 | *.ntvs* 7 | *.njsproj 8 | *.sln 9 | *.sw? 10 | *.local 11 | 12 | # Logs 13 | logs 14 | *.log* 15 | 16 | # 忽略项目文件 17 | dist 18 | node_modules 19 | src/static/ 20 | src/manifest.json 21 | src/pages.json 22 | .* 23 | *-lock.* 24 | 25 | # 不忽略的项目文件 26 | !.vscode 27 | !.prettierrc* 28 | !.stylelintrc* 29 | !.eslintrc* 30 | -------------------------------------------------------------------------------- /.prettierrc.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-02-27 21:21:41 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-08-30 16:47:06 6 | * @FilePath: /vue_pinia_vite/.prettierrc.mjs 7 | * @Description: prettier配置文件 注意:每次配置文件的更改,建议重启一下vscode,否则可能不会生效 8 | */ 9 | 10 | import prettierConfig from "@dyb-dev/prettier-config" 11 | export default prettierConfig 12 | -------------------------------------------------------------------------------- /.stylelintignore: -------------------------------------------------------------------------------- 1 | # 编辑目录和文件 2 | .idea 3 | .DS_Store 4 | coverage 5 | *.suo 6 | *.ntvs* 7 | *.njsproj 8 | *.sln 9 | *.sw? 10 | *.local 11 | 12 | # Logs 13 | logs 14 | *.log* 15 | 16 | # 忽略的项目文件 17 | dist 18 | node_modules 19 | src/static/ 20 | types 21 | .* 22 | *-lock.* 23 | *.json 24 | *.yaml 25 | *.md 26 | *.ts 27 | *.d.ts 28 | *.*js 29 | -------------------------------------------------------------------------------- /.stylelintrc.mjs: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-03-20 16:17:30 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-10 20:13:41 6 | * @FilePath: /uniapp-mp-wx-template/.stylelintrc.mjs 7 | * @Description: stylelint配置文件 注意:每次配置文件的更改,建议重启一下vscode,否则可能不会生效 8 | */ 9 | 10 | export default { 11 | extends: ["@dyb-dev/stylelint-config"], 12 | rules: { 13 | // 忽略 `rpx` 单位的报错 14 | "unit-no-unknown": [true, { ignoreUnits: ["rpx"] }], 15 | // 忽略 `rpx` 单位的报错 16 | "declaration-property-value-no-unknown": null 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | // VS Code 会检查当前项目的根目录下是否存在 extensions.json 文件,并根据其中的推荐插件列表向你展示相应的推荐安装提示 2 | 3 | { 4 | "recommendations": [ 5 | // Chinese 插件 6 | "ms-ceintl.vscode-language-pack-zh-hans", 7 | // Code Spell Checker 插件 8 | "streetsidesoftware.code-spell-checker", 9 | // Color Info 插件 10 | "bierner.color-info", 11 | // comment tip 插件 12 | "yuechaoxu.vscode-comment-tip", 13 | // CSS Navigation 插件 14 | "pucelle.vscode-css-navigation", 15 | // CSS Peek 插件 16 | "pranaygp.vscode-css-peek", 17 | // dir tree generator 插件 18 | "openmynet.dir-tree-generator", 19 | // Document This 插件 20 | "oouo-diogo-perdigao.docthis", 21 | // EditorConfig for VS Code 插件 22 | "editorconfig.editorconfig", 23 | // Error Lens 插件 24 | "usernamehw.errorlens", 25 | // ESLint 插件 26 | "dbaeumer.vscode-eslint", 27 | // ESLint Chinese Rules 插件 28 | "maggie.eslint-rules-zh-plugin", 29 | // git-commit-plugin 插件 30 | "redjue.git-commit-plugin", 31 | // GitLens 插件 32 | "eamodio.gitlens", 33 | // Image Preview 插件 34 | "kisstkondoros.vscode-gutter-preview", 35 | // Import Cost 插件 36 | "wix.vscode-import-cost", 37 | // JavaScript (ES6) code snippets 插件 38 | "xabikos.javascriptsnippets", 39 | // koroFileHeader 插件 40 | "obkoro1.korofileheader", 41 | // Live Server 插件 42 | "ritwickdey.liveserver", 43 | // Local History 插件 44 | "xyz.local-history", 45 | // markdownlint 插件 46 | "davidanson.vscode-markdownlint", 47 | // Prettier ESLint 插件 48 | "rvest.vs-code-prettier-eslint", 49 | // SCSS IntelliSense 插件 50 | "mrmlnc.vscode-scss", 51 | // Stylelint 插件 52 | "stylelint.vscode-stylelint", 53 | // Template String Converter 插件 54 | "meganrogge.template-string-converter", 55 | // Todo Highlight 插件 56 | "wayou.vscode-todo-highlight", 57 | // Todo Tree 插件 58 | "gruntfuggly.todo-tree", 59 | // uni-app-schemas 插件 60 | "uni-helper.uni-app-schemas-vscode", 61 | // uni-app-snippets 插件 62 | "uni-helper.uni-app-snippets-vscode", 63 | // uni-create-view 插件 64 | "uni-helper.uni-highlight-vscode", 65 | // Var Conversion 插件 66 | "xiaoxintongxue.var-conv", 67 | // Vue - Official 插件 68 | "vue.volar", 69 | // Vue VSCode Snippets 插件 70 | "sdras.vue-vscode-snippets" 71 | ] 72 | } 73 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 dyb-dev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # UniApp-MP-WX-Template 2 | 3 | [![license](https://img.shields.io/badge/license-MIT-blue.svg)](https://zh.wikipedia.org/wiki/MIT%E8%A8%B1%E5%8F%AF%E8%AD%89) 4 | 5 | ## 项目简介 6 | 7 | UniApp-MP-WX-Template 提供了一个用于开发微信小程序的基本模板,该项目基于 [Uniapp](https://uniapp.dcloud.net.cn/) + [Vue3](https://cn.vuejs.org/) + [TS](https://www.typescriptlang.org/) 构建,该模板预配置了一些常用的开发工具和插件,帮助你快速开发微信小程序,为您带来极致的体验。 8 | 9 | ## 功能特点 10 | 11 | - **UniApp 工具链**: 支持自动生成 `pages.json` 和 `manifest.json` 配置文件,自动化处理页面和项目配置文件。 12 | - **TypeScript**: 项目使用 TypeScript 进行开发,集成 `vue-tsc` 插件自动进行 Vue 组件的类型检查,同时支持项目的模块化管理 13 | - **Vue 3 组件库**: 集成了 `nutui-uniapp` 组件库,支持 Vue 3 组件的按需自动导入,支持 TypeScript 类型提示。 14 | - **自定义布局组件**: 内置多个自定义布局组件(如顶部导航栏、底部导航栏),兼容 `pages.json` 配置。 15 | - **网络请求**: 提供 Promise 方式调用 UniApp API,处理网络请求和异步操作更加简洁高效。 16 | - **页面导航**: 内置多种跳转方法封装,支持跳转小程序内部页面、H5 页面和第三方小程序,提供统一的导航接口。 17 | - **登录拦截与路由守卫**: 集成登录拦截器和路由守卫功能,自动处理用户登录状态,确保未登录用户访问受限页面时跳转到登录页面,兼容 `pages.json` 配置。 18 | - **公用 WebView 页面**: 内置的公用 WebView 页面,用于展示 H5 页面或进行 H5 分享功能,兼容 `pages.json` 配置。 19 | - **业务开发**: 内置获取用户手机号、微信头像和昵称的组件,简化了微信授权和用户信息收集的流程。 20 | - **代码风格管理**: 预配置了 ESLint、Stylelint 及 Prettier 工具,集成自定义的 ESLint、Stylelint 规则集,帮助开发者统一代码风格。 21 | 22 | ## 安装与使用 23 | 24 | 你可以使用 npm、pnpm 或 yarn 等包管理器来安装项目依赖。推荐使用 pnpm 作为首选包管理器。在下面的示例中,我们默认使用 pnpm 进行演示: 25 | 26 | ### 环境要求 27 | 28 | - Node.js 版本 >= 18.0.0 29 | - 如果包管理器为 pnpm,版本需 >= 8.15.5 30 | 31 | ### 环境变量配置 32 | 33 | 该模板项目支持通过 `.env` 文件进行环境变量配置,你可以根据实际需要修改 `.env` 中的以下配置项: 34 | 35 | - `VITE_MP_WX_APPID`: 小程序的 AppID,用于微信小程序的配置和相关 API 调用。 36 | - `VITE_PAGE_DIR`: 页面目录,默认值为 `pages`,用于指定项目中主包页面所在的文件夹。 37 | - `VITE_SUB_PACKAGE_DIR`: 分包目录,默认值为 `subPackages`,用于指定 `src` 目录下的分包目录名称。 38 | - `VITE_SUB_PACKAGE_CHILD_DIRS`: 分包子目录集合,默认值为 `webview`,如果项目中存在多个子分包,可以用逗号分隔(例如:`webview,profile,shop`),用于配置分包子目录名称集合。 39 | - `VITE_LAUNCH_PATH`: 启动页路径,默认值为 `pages/launch`,用于指定小程序的启动页面路径。如果 `VITE_USE_LAUNCH_PAGE` 设为 `true`,小程序启动时将默认加载该页面。 40 | - `VITE_USE_LAUNCH_PAGE`: 是否使用启动页,默认值为 `false`。如果设为 `true`,启动页将作为小程序首次展示的页面,并且跳转逻辑将在 `App.vue` 中进行。启动页中可根据逻辑判断用户状态,再决定跳转到登录页或首页。使用时注意: 41 | 1. 设置 `VITE_USE_LAUNCH_PAGE=true` 后,小程序的首页将变为 `VITE_LAUNCH_PATH` 指定的路径。 42 | 2. 小程序码路径的格式为:目标页面为 `a` 页面,则路径应为 `/pages/launch?targetPath=pages/a¶m1=value1`,其中 `targetPath` 参数将指定跳转目标页面,而其他参数(如 `param1=value1`)会传递给目标页面作为查询参数使用。 43 | - `VITE_LOGIN_PATH`: 登录页面的路径,默认值为 `pages/login`,用于指定登录页面的位置。当用户未登录时,自动跳转到该页面进行登录。 44 | - `VITE_HOME_PATH`: 首页路径,默认值为 `pages/home`,用于指定应用程序的首页路径。用户登录成功后将跳转到该路径。 45 | - `VITE_DEV_SERVER_URL`: 开发环境服务器网址(小程序开发版、体验版用到),默认值为 `http://xxxx.com`,用于指定后端 API 的根路径。 46 | - `VITE_PROD_SERVER_URL`: 生产环境服务器网址(小程序体验版、线上版用到),默认值为 `http://xxxx.com`,用于指定后端 API 的根路径。 47 | - `VITE_API_BASE_PATH`: 接口请求基础路径,默认值为 `/test.aspx`,用于配置接口的基础路径,与 `VITE_DEV_SERVER_URL` 或者 `VITE_PROD_SERVER_URL` 结合使用,形成完整的 API 请求路径。 48 | 49 | ### 安装依赖 50 | 51 | ```bash 52 | pnpm install 53 | ``` 54 | 55 | ### 本地开发 56 | 57 | ```bash 58 | pnpm dev 59 | ``` 60 | 61 | ### 构建产物 62 | 63 | ```bash 64 | pnpm build 65 | ``` 66 | 67 | ## 许可证 68 | 69 | 本项目基于 `MIT 许可证` 开源。 70 | -------------------------------------------------------------------------------- /manifest.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 13:57:43 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-19 23:24:28 6 | * @FilePath: /uniapp-mp-wx-template/manifest.config.ts 7 | * @Description: 应用配置文件 8 | */ 9 | 10 | import { defineManifestConfig } from "@uni-helper/vite-plugin-uni-manifest" 11 | 12 | import pkg from "./package.json" 13 | import { VITE_ENV } from "./vite.config" 14 | 15 | /** CONST: 获取.env文件的环境变量 */ 16 | const { VITE_MP_WX_APPID } = VITE_ENV 17 | 18 | export default defineManifestConfig({ 19 | // 应用标题 20 | name: pkg.name, 21 | // 应用的唯一标识符 22 | appid: "", 23 | // 应用的描述 24 | description: pkg.description, 25 | // 应用版本名称 26 | versionName: pkg.version, 27 | // 应用版本号,主要用于应用的内部版本控制 28 | versionCode: "100", 29 | // 是否自动转换 px 单位 默认: true 30 | transformPx: false, 31 | // uni 统计配置项 32 | uniStatistics: { 33 | // 是否启用统计 默认: true 34 | enable: false 35 | }, 36 | 37 | /* 小程序特有相关 */ 38 | "mp-weixin": { 39 | // 微信小程序的应用 ID 40 | appid: VITE_MP_WX_APPID, 41 | // 是否使用自定义组件 42 | usingComponents: true, 43 | // 是否开启小程序按需注入特性 44 | lazyCodeLoading: "requiredComponents", 45 | // 合并组件虚拟节点外层属性(目前仅支持 style、class 属性) 46 | mergeVirtualHostAttributes: true, 47 | // 微信小程序的优化配置 48 | optimization: { 49 | // 是否开启分包优化 50 | subPackages: true 51 | }, 52 | // 微信小程序的相关设置 53 | setting: { 54 | // 是否启用 URL 校验 55 | urlCheck: false, 56 | // 是否ES6 转 ES5 57 | es6: true, 58 | // 上传代码时样式是否自动补全 59 | postcss: true, 60 | // 上传代码时是否自动压缩 61 | minified: true, 62 | // 预览及真机调试时包体积上限是否调整为4M,默认: true 63 | bigPackageSizeSupport: true 64 | } 65 | } 66 | }) 67 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uniapp-mp-wx-template", 3 | "version": "1.1.5", 4 | "description": "基于`uni-app + vue3 + ts`搭建的微信小程序模板", 5 | "author": "dyb-dev", 6 | "license": "MIT", 7 | "private": true, 8 | "main": "./src/main.ts", 9 | "scripts": { 10 | "prepare": "husky", 11 | "dev": "uni -p mp-weixin", 12 | "build": "npm run ts-check && npm run build-only", 13 | "build-only": "uni build -p mp-weixin", 14 | "ts-check": "vue-tsc --build --force", 15 | "release": "project-cli release" 16 | }, 17 | "engines": { 18 | "node": ">=18.0.0", 19 | "pnpm": ">=8.15.5" 20 | }, 21 | "repository": { 22 | "type": "git", 23 | "url": "https://github.com/dyb-dev/uniapp-mp-wx-template" 24 | }, 25 | "homepage": "https://github.com/dyb-dev/uniapp-mp-wx-template", 26 | "bugs": { 27 | "url": "https://github.com/dyb-dev/uniapp-mp-wx-template/issues", 28 | "directory": "https://github.com/dyb-dev/uniapp-mp-wx-template" 29 | }, 30 | "devDependencies": { 31 | "@dcloudio/types": "^3.4.14", 32 | "@dcloudio/vite-plugin-uni": "3.0.0-4040520250104002", 33 | "@dyb-dev/eslint-config": "^0.1.3", 34 | "@dyb-dev/prettier-config": "^0.0.5", 35 | "@dyb-dev/project-cli": "^0.0.9", 36 | "@dyb-dev/stylelint-config": "^0.0.5", 37 | "@dyb-dev/ts-config": "^0.0.7", 38 | "@uni-helper/uni-app-types": "1.0.0-alpha.4", 39 | "@uni-helper/vite-plugin-uni-components": "^0.1.0", 40 | "@uni-helper/vite-plugin-uni-manifest": "^0.2.7", 41 | "@uni-helper/vite-plugin-uni-pages": "^0.2.28", 42 | "eslint": "^8.57.1", 43 | "husky": "^9.1.6", 44 | "miniprogram-api-typings": "^4.0.1", 45 | "prettier": "^3.3.3", 46 | "prettier-eslint": "^16.3.0", 47 | "sass": "1.64.2", 48 | "stylelint": "^16.10.0", 49 | "typescript": "^5.6.3", 50 | "vite": "^5.2.8", 51 | "vue-tsc": "^2.1.6" 52 | }, 53 | "dependencies": { 54 | "@dcloudio/uni-app": "3.0.0-4040520250104002", 55 | "@dcloudio/uni-components": "3.0.0-4040520250104002", 56 | "@dcloudio/uni-mp-weixin": "3.0.0-4040520250104002", 57 | "@qiun/uni-ucharts": "2.5.0-20230101", 58 | "@uni-helper/uni-network": "^0.19.3", 59 | "@uni-helper/uni-promises": "^0.2.1", 60 | "@vueuse/core": "^9.13.0", 61 | "dayjs": "^1.11.13", 62 | "mp-html": "^2.5.0", 63 | "nutui-uniapp": "^1.8.1", 64 | "pinia": "^2.2.4", 65 | "pinia-plugin-persistedstate": "^4.1.1", 66 | "query-string": "^9.1.1", 67 | "vue": "^3.5.13" 68 | } 69 | } -------------------------------------------------------------------------------- /pages.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 13:57:47 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-11-30 13:12:20 6 | * @FilePath: /uniapp-mp-wx-template/pages.config.ts 7 | * @Description: 页面配置文件 8 | */ 9 | 10 | import { defineUniPages } from "@uni-helper/vite-plugin-uni-pages" 11 | 12 | import { VITE_ENV } from "./vite.config" 13 | 14 | /** CONST: 获取.env文件的环境变量 */ 15 | const { VITE_PAGE_DIR, VITE_LAUNCH_PATH, VITE_HOME_PATH, VITE_LOGIN_PATH, VITE_SUB_PACKAGE_DIR } = VITE_ENV 16 | 17 | export default defineUniPages({ 18 | // 全局配置 19 | globalStyle: { 20 | // 默认使用自定义导航栏 21 | navigationStyle: "custom", 22 | // 默认设置状态栏前景颜色为`black`,解决状态栏不可见的问题 23 | navigationBarTextStyle: "black", 24 | // 默认设置导航栏背景颜色为白色 解决在 `webview` 中不支持自定义导航栏的问题,因此设置保持与自定义导航栏一致的背景颜色 25 | navigationBarBackgroundColor: "#ffffff", 26 | // 默认页面(page元素)为白色背景,解决系统暗黑主题下页面背景与导航栏背景不一致的问题 27 | backgroundColor: "#ffffff", 28 | // 默认禁用页面滑动,解决ios与安卓默认行为不一致的问题 29 | disableScroll: true 30 | }, 31 | // 页面配置 32 | pages: [ 33 | { 34 | path: VITE_LAUNCH_PATH 35 | }, 36 | { 37 | path: VITE_LOGIN_PATH, 38 | style: { 39 | navigationBarTitleText: "登录" 40 | } 41 | }, 42 | { 43 | path: VITE_HOME_PATH, 44 | style: { 45 | navigationBarTitleText: "首页" 46 | } 47 | }, 48 | { 49 | path: `${VITE_PAGE_DIR}/list`, 50 | style: { 51 | navigationBarTitleText: "列表" 52 | } 53 | }, 54 | { 55 | path: `${VITE_PAGE_DIR}/my`, 56 | style: { 57 | navigationBarTitleText: "我的" 58 | }, 59 | // 标识该页面是需要先登录 60 | needLogin: true 61 | }, 62 | { 63 | path: `${VITE_PAGE_DIR}/test`, 64 | style: { 65 | navigationBarTitleText: "测试", 66 | // 允许滑动 67 | disableScroll: false, 68 | // 开启下拉刷新 69 | enablePullDownRefresh: true 70 | }, 71 | // 标识该页面是需要先登录 72 | needLogin: true 73 | } 74 | ], 75 | // wx开发工具页面测试配置 76 | condition: { 77 | current: 0, 78 | list: [ 79 | { 80 | name: "启动页", 81 | path: VITE_LAUNCH_PATH, 82 | query: `targetPath=/${VITE_PAGE_DIR}/test&test=测试启动参数` 83 | }, 84 | { 85 | name: "首页", 86 | path: VITE_HOME_PATH, 87 | query: "test=首页" 88 | }, 89 | { 90 | name: "我的", 91 | path: `${VITE_PAGE_DIR}/my` 92 | }, 93 | { 94 | name: "测试", 95 | path: `${VITE_PAGE_DIR}/test` 96 | } 97 | ] 98 | }, 99 | // TabBar页面配置 100 | tabBar: { 101 | // 默认使用自定义TabBar 102 | custom: true, 103 | // 默认颜色 104 | color: "#7d7e80", 105 | // 选中颜色 106 | selectedColor: "#29d446", 107 | // tabBar 项列表 108 | list: [ 109 | { 110 | pagePath: VITE_HOME_PATH, 111 | text: "首页", 112 | iconfont: { 113 | text: "home", 114 | selectedText: "home" 115 | } 116 | }, 117 | { 118 | pagePath: `${VITE_PAGE_DIR}/list`, 119 | text: "列表", 120 | iconfont: { 121 | text: "dongdong", 122 | selectedText: "dongdong" 123 | } 124 | }, 125 | { 126 | pagePath: `${VITE_PAGE_DIR}/my`, 127 | text: "我的", 128 | iconfont: { 129 | text: "my", 130 | selectedText: "my" 131 | } 132 | } 133 | ] 134 | }, 135 | 136 | // 分包配置 137 | subPackages: [ 138 | { 139 | root: `${VITE_SUB_PACKAGE_DIR}/webview`, 140 | pages: [ 141 | { 142 | path: `${VITE_SUB_PACKAGE_DIR}/webview/${VITE_PAGE_DIR}/webview` 143 | } 144 | ] 145 | } 146 | ], 147 | // 分包预加载配置 148 | preloadRule: { 149 | [VITE_HOME_PATH]: { 150 | packages: [`${VITE_SUB_PACKAGE_DIR}/webview`] 151 | } 152 | } 153 | }) 154 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 51 | 52 | 56 | -------------------------------------------------------------------------------- /src/apis/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-08 20:34:09 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 23:29:52 6 | * @FilePath: /uniapp-mp-wx-template/src/apis/index.ts 7 | * @Description: 接口模块 8 | */ 9 | 10 | /** 导出接口模块 */ 11 | export * from "./modules" 12 | 13 | import { un } from "@uni-helper/uni-network" 14 | 15 | import { getCurrentServerUrl, isDevEnv } from "@/utils" 16 | 17 | import { setupApiInterceptor } from "./interceptors" 18 | import { setGlobalTestRequestConfig } from "./request" 19 | 20 | /** 21 | * FUN: 设置接口配置 22 | * 23 | * @author dyb-dev 24 | * @date 08/10/2024/ 20:47:43 25 | */ 26 | const setupApi = () => { 27 | 28 | // 设置请求基础路径 29 | un.defaults.baseUrl = getCurrentServerUrl() + __PROJECT_INFO__.env.VITE_API_BASE_PATH 30 | 31 | // 设置接口拦截器 32 | setupApiInterceptor() 33 | 34 | // 设置全局测试请求配置 35 | setGlobalTestRequestConfig({ 36 | test: isDevEnv() && true, 37 | testDelay: 500 38 | }) 39 | 40 | } 41 | 42 | export { setupApi } 43 | -------------------------------------------------------------------------------- /src/apis/interceptors/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-17 16:16:56 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:55:54 6 | * @FilePath: /uniapp-mp-wx-template/src/apis/interceptors/index.ts 7 | * @Description: Api 拦截器模块 8 | */ 9 | 10 | import { setupResponseInterceptor } from "./response" 11 | 12 | /** 13 | * FUN: 设置接口拦截器 14 | * 15 | * @author dyb-dev 16 | * @date 17/10/2024/ 16:17:31 17 | */ 18 | const setupApiInterceptor = () => { 19 | 20 | setupResponseInterceptor() 21 | 22 | } 23 | 24 | export { setupApiInterceptor } 25 | -------------------------------------------------------------------------------- /src/apis/interceptors/response.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:54:48 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:55:25 6 | * @FilePath: /uniapp-mp-wx-template/src/apis/interceptors/response.ts 7 | * @Description: 响应拦截器模块 8 | */ 9 | 10 | import { un } from "@uni-helper/uni-network" 11 | 12 | /** 13 | * FUN: 设置响应拦截器 14 | * 15 | * @author dyb-dev 16 | * @date 21/02/2025/ 21:55:38 17 | */ 18 | const setupResponseInterceptor = () => { 19 | 20 | // 设置响应拦截器 21 | un.interceptors.response.use( 22 | response => { 23 | 24 | response.success = response.errMsg?.includes("request:fail") ? false : true 25 | response.message = response.errMsg || (response.success ? "请求成功" : "请求失败") 26 | return response 27 | 28 | }, 29 | undefined, 30 | { synchronous: true } 31 | ) 32 | 33 | } 34 | 35 | export { setupResponseInterceptor } 36 | -------------------------------------------------------------------------------- /src/apis/modules/activity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-08 21:22:48 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 22:48:25 6 | * @FilePath: /uniapp-mp-wx-template/src/apis/modules/activity.ts 7 | * @Description: 本次活动接口模块 8 | */ 9 | 10 | import un from "@uni-helper/uni-network" 11 | 12 | import { sendRequest } from "../request" 13 | 14 | import type { UnResponse } from "@uni-helper/uni-network" 15 | 16 | /** 获取用户信息的结果数据 */ 17 | interface IGetUserInfoApiResultData { 18 | /** 内容 */ 19 | body: string 20 | /** id */ 21 | id: number 22 | /** 标题 */ 23 | title: string 24 | /** 用户唯一标识 */ 25 | userId: number 26 | } 27 | 28 | /** 29 | * FUN: 获取用户信息 30 | * 31 | * @author dyb-dev 32 | * @date 21/02/2025/ 22:46:30 33 | * @param {TModifyProperties, "test">} [testRequestConfig] 测试请求配置 34 | * @returns {*} {Promise>} 结果数据 35 | */ 36 | const getUserInfoApi = async( 37 | testRequestConfig?: TModifyProperties, "test"> 38 | ): Promise> => { 39 | 40 | return sendRequest({ 41 | url: "/1", 42 | requestFn: un.get, 43 | testRequestConfig 44 | }) 45 | 46 | } 47 | 48 | /** 获取id的参数 */ 49 | interface IGetIdApiParams { 50 | /** 用户唯一标识 */ 51 | userId: number 52 | } 53 | 54 | /** 获取id的结果数据 */ 55 | interface IGetIdApiResultData { 56 | /** id */ 57 | id: number 58 | } 59 | 60 | /** 61 | * FUN: 获取id 62 | * 63 | * @author dyb-dev 64 | * @date 21/02/2025/ 22:48:09 65 | * @param {IGetIdApiParams} params 参数 66 | * @param {TModifyProperties, "test">} [testRequestConfig] 测试请求配置 67 | * @returns {*} {Promise>} 结果数据 68 | */ 69 | const getIdApi = async( 70 | params: IGetIdApiParams, 71 | testRequestConfig?: TModifyProperties, "test"> 72 | ): Promise> => { 73 | 74 | return sendRequest({ 75 | url: "", 76 | params, 77 | testRequestConfig 78 | }) 79 | 80 | } 81 | 82 | export type { IGetUserInfoApiResultData, IGetIdApiParams, IGetIdApiResultData } 83 | 84 | export { getUserInfoApi, getIdApi } 85 | -------------------------------------------------------------------------------- /src/apis/modules/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:56:51 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:56:56 6 | * @FilePath: /uniapp-mp-wx-template/src/apis/modules/index.ts 7 | * @Description: 接口模块 8 | */ 9 | 10 | /** 导出本次活动相关接口 */ 11 | export * from "./activity" 12 | /** 导出用户信息相关接口 */ 13 | export * from "./userInfo" 14 | -------------------------------------------------------------------------------- /src/apis/modules/userInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-09 15:23:42 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 23:06:02 6 | * @FilePath: /uniapp-mp-wx-template/src/apis/modules/userInfo.ts 7 | * @Description: 用户信息相关接口 8 | */ 9 | 10 | import { sendRequest } from "../request" 11 | 12 | import type { UnResponse } from "@uni-helper/uni-network" 13 | 14 | /** 登录的参数 */ 15 | interface ILoginApiParams { 16 | /** 17 | * 用户登录凭证(有效期五分钟)。开发者需要在开发者服务器后台调用 [code2Session](https://developers.weixin.qq.com/miniprogram/dev/OpenApiDoc/user-login/code2Session.html),使用 code 换取 openid、unionid、session_key 等信息 18 | */ 19 | code: string 20 | } 21 | 22 | /** 登录的结果数据 */ 23 | interface ILoginApiResultData { 24 | /** 用户在我们系统中的加密用户代码 */ 25 | userId: string 26 | /** 由微信生成的用户在开放平台的唯一标识符 */ 27 | unionId: string 28 | /** 由微信生成的用户在本小程序唯一标识 */ 29 | openId: string 30 | /** 昵称 */ 31 | nickName: string 32 | /** 头像 */ 33 | avatarUrl: string 34 | /** 绑定手机号 */ 35 | phoneNumber: string 36 | } 37 | 38 | /** 39 | * FUN: 登录 40 | * 41 | * @author dyb-dev 42 | * @date 21/02/2025/ 22:49:58 43 | * @param {ILoginApiParams} params 参数 44 | * @param {TModifyProperties, "test">} [testRequestConfig] 测试请求配置 45 | * @returns {*} {Promise>} 结果数据 46 | */ 47 | const loginApi = async( 48 | params: ILoginApiParams, 49 | testRequestConfig?: TModifyProperties, "test"> 50 | ): Promise> => { 51 | 52 | return sendRequest({ 53 | url: "", 54 | params, 55 | testRequestConfig 56 | }) 57 | 58 | } 59 | 60 | /** 获取手机号的参数 */ 61 | interface IGetPhoneNumberApiParams { 62 | /** 授权手机号code */ 63 | code: string 64 | } 65 | 66 | /** 获取手机号的结果数据 */ 67 | interface IGetPhoneNumberApiResultData { 68 | /** 手机号 */ 69 | phoneNumber: string 70 | } 71 | 72 | /** 73 | * FUN: 获取手机号 74 | * 75 | * @author dyb-dev 76 | * @date 21/02/2025/ 22:54:45 77 | * @param {IGetPhoneNumberApiParams} params 参数 78 | * @param {TModifyProperties, "test">} [testRequestConfig] 测试请求配置 79 | * @returns {*} {Promise>} 结果数据 80 | */ 81 | const getPhoneNumberApi = async( 82 | params: IGetPhoneNumberApiParams, 83 | testRequestConfig?: TModifyProperties, "test"> 84 | ): Promise> => { 85 | 86 | return sendRequest({ 87 | url: "", 88 | params, 89 | testRequestConfig 90 | }) 91 | 92 | } 93 | 94 | /** 上传头像的参数 */ 95 | interface IUploadAvatarApiParams { 96 | /** 头像url */ 97 | avatarUrl: string 98 | } 99 | 100 | /** 101 | * FUN: 上传头像 102 | * 103 | * @author dyb-dev 104 | * @date 21/02/2025/ 23:03:54 105 | * @param {IUploadAvatarApiParams} params 参数 106 | * @param {TModifyProperties} [testRequestConfig] 测试请求配置 107 | * @returns {*} {Promise} 上传头像结果 108 | */ 109 | const uploadAvatarApi = async( 110 | params: IUploadAvatarApiParams, 111 | testRequestConfig?: TModifyProperties 112 | ): Promise => { 113 | 114 | return sendRequest({ 115 | url: "", 116 | params, 117 | testRequestConfig 118 | }) 119 | 120 | } 121 | 122 | /** 上传用户信息的参数 */ 123 | interface IUploadUserInfoApiParams { 124 | /** 头像url */ 125 | avatarUrl: string 126 | /** 昵称 */ 127 | nickName: string 128 | } 129 | 130 | /** 131 | * FUN: 上传用户信息 132 | * 133 | * @author dyb-dev 134 | * @date 21/02/2025/ 23:05:52 135 | * @param {IUploadUserInfoApiParams} params 参数 136 | * @param {TModifyProperties} [testRequestConfig] 测试请求配置 137 | * @returns {*} {Promise} 上传用户信息结果 138 | */ 139 | const uploadUserInfoApi = async( 140 | params: IUploadUserInfoApiParams, 141 | testRequestConfig?: TModifyProperties 142 | ): Promise => { 143 | 144 | return sendRequest({ 145 | url: "", 146 | params, 147 | testRequestConfig 148 | }) 149 | 150 | } 151 | 152 | export type { 153 | IGetPhoneNumberApiParams, 154 | IGetPhoneNumberApiResultData, 155 | IUploadAvatarApiParams, 156 | IUploadUserInfoApiParams, 157 | ILoginApiParams, 158 | ILoginApiResultData 159 | } 160 | 161 | export { getPhoneNumberApi, uploadAvatarApi, uploadUserInfoApi, loginApi } 162 | -------------------------------------------------------------------------------- /src/apis/request/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:58:00 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 22:37:49 6 | * @FilePath: /uniapp-mp-wx-template/src/apis/request/index.ts 7 | * @Description: 封装请求模块 8 | */ 9 | 10 | import un from "@uni-helper/uni-network" 11 | 12 | import { delay, isDevEnv } from "@/utils" 13 | 14 | import { ERequestLogType, requestLog } from "./log" 15 | 16 | import type { UnResponse } from "@uni-helper/uni-network" 17 | 18 | /** LET: 全局测试请求配置 */ 19 | let _globalTestRequestConfig: Omit = { 20 | test: isDevEnv() && true, 21 | testDelay: 500 22 | } 23 | 24 | /** 25 | * FUN: 设置全局测试请求配置 26 | * 27 | * @author dyb-dev 28 | * @date 21/02/2025/ 19:50:27 29 | * @param {Omit} config 测试请求配置 30 | */ 31 | const setGlobalTestRequestConfig = (config: Omit) => { 32 | 33 | _globalTestRequestConfig = config 34 | 35 | } 36 | 37 | /** LET: 全局请求id,用于进行日志输出 */ 38 | let _globalRequestId = 0 39 | 40 | /** 发送请求选项 */ 41 | interface ISendRequestOptions, P extends Record = Record> { 42 | /** 请求地址 */ 43 | url: string 44 | /** 请求参数 */ 45 | params?: P 46 | /** 发送 http 请求函数 默认 axios.post 方法 */ 47 | requestFn?: (url: string, params?: P) => Promise> 48 | /** 测试请求配置 */ 49 | testRequestConfig?: TModifyProperties, "test"> 50 | } 51 | 52 | /** 53 | * FUN: 发送请求 54 | * 55 | * @author dyb-dev 56 | * @date 21/02/2025/ 14:18:09 57 | * @template T 58 | * @template P 59 | * @param {ISendRequestOptions} options 选项 60 | * @returns {*} {Promise>} 请求结果 61 | */ 62 | const sendRequest = async , P extends Record = Record>( 63 | options: ISendRequestOptions 64 | ): Promise> => { 65 | 66 | _globalRequestId++ 67 | // 当前请求id 68 | const _currentRequestId = _globalRequestId 69 | 70 | const { url, params = {}, requestFn = un.post, testRequestConfig } = options 71 | 72 | // 是否使用测试模式 73 | if (testRequestConfig?.test ?? _globalTestRequestConfig.test) { 74 | 75 | // 输出测试请求参数日志 76 | requestLog({ 77 | type: ERequestLogType.TEST_REQUEST_PARAMS, 78 | url, 79 | requestId: _currentRequestId, 80 | data: params 81 | }) 82 | 83 | // 测试请求配置 84 | // eslint-disable-next-line prefer-const 85 | let { testResult, testDelay = _globalTestRequestConfig.testDelay ?? 0 } = testRequestConfig ?? {} 86 | 87 | // 如果测试数据未提供,则返回测试失败 88 | if (!testResult) { 89 | 90 | testResult = { 91 | success: false, 92 | message: "测试数据未提供" 93 | } 94 | 95 | // 输出测试请求结果日志 96 | requestLog({ 97 | type: ERequestLogType.TEST_REQUEST_RESULT_FAIL, 98 | url, 99 | requestId: _currentRequestId, 100 | data: testResult 101 | }) 102 | 103 | return testResult 104 | 105 | } 106 | 107 | // 模拟请求延迟 108 | await delay(testDelay) 109 | 110 | // 确定日志类型 111 | const _requestLogType = testResult.success 112 | ? ERequestLogType.TEST_REQUEST_RESULT_SUCCESS 113 | : ERequestLogType.TEST_REQUEST_RESULT_FAIL 114 | 115 | // 输出测试请求结果日志 116 | requestLog({ 117 | type: _requestLogType, 118 | url, 119 | requestId: _currentRequestId, 120 | data: testResult 121 | }) 122 | 123 | return testResult 124 | 125 | } 126 | 127 | try { 128 | 129 | // 输出真实请求参数日志 130 | requestLog({ 131 | type: ERequestLogType.REQUEST_PARAMS, 132 | url, 133 | requestId: _currentRequestId, 134 | data: params 135 | }) 136 | 137 | // 发送请求 138 | const _result = await requestFn(url, params) 139 | 140 | // 确定日志类型 141 | const _requestLogType = _result.success ? ERequestLogType.REQUEST_RESULT_SUCCESS : ERequestLogType.REQUEST_RESULT_FAIL 142 | 143 | requestLog({ 144 | type: _requestLogType, 145 | url, 146 | requestId: _currentRequestId, 147 | data: _result 148 | }) 149 | 150 | return _result 151 | 152 | } 153 | catch (error) { 154 | 155 | const _result = { 156 | success: false, 157 | message: "请求失败" 158 | } 159 | 160 | requestLog({ 161 | type: ERequestLogType.REQUEST_RESULT_FAIL, 162 | url, 163 | requestId: _currentRequestId, 164 | data: _result 165 | }) 166 | 167 | return _result 168 | 169 | } 170 | 171 | } 172 | 173 | export type { ISendRequestOptions } 174 | 175 | export { setGlobalTestRequestConfig, sendRequest } 176 | -------------------------------------------------------------------------------- /src/apis/request/log.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:57:45 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:57:50 6 | * @FilePath: /uniapp-mp-wx-template/src/apis/request/log.ts 7 | * @Description: 请求日志模块 8 | */ 9 | 10 | import { isAbsoluteUrl, isDevEnv, isEnableDebug, trimUrlSlashes } from "@/utils" 11 | 12 | /** 请求日志类型枚举 */ 13 | enum ERequestLogType { 14 | /** 测试请求参数 */ 15 | TEST_REQUEST_PARAMS, 16 | /** 测试请求结果, 测试成功 */ 17 | TEST_REQUEST_RESULT_SUCCESS, 18 | /** 测试请求结果, 测试失败 or 没有测试数据 */ 19 | TEST_REQUEST_RESULT_FAIL, 20 | /** 真实请求参数 */ 21 | REQUEST_PARAMS, 22 | /** 真实请求结果, 接口调用成功 */ 23 | REQUEST_RESULT_SUCCESS, 24 | /** 真实请求结果, 接口调用失败 */ 25 | REQUEST_RESULT_FAIL 26 | } 27 | 28 | /** 请求日志配置的类型 */ 29 | interface IRequestLogConfig { 30 | /** 样式字符串 */ 31 | style: string 32 | /** 描述 */ 33 | description: string 34 | } 35 | 36 | /** CONST: 请求日志配置列表 */ 37 | const REQUEST_LOG_CONFIG_LIST: Record = { 38 | [ERequestLogType.TEST_REQUEST_PARAMS]: { 39 | style: "color: #fff; background-color: #747474; padding: 5px 10px;", 40 | description: "测试请求参数" 41 | }, 42 | [ERequestLogType.TEST_REQUEST_RESULT_SUCCESS]: { 43 | style: "color: #fff; background-color: #9d9d9d; padding: 5px 10px;", 44 | description: "测试请求成功" 45 | }, 46 | [ERequestLogType.TEST_REQUEST_RESULT_FAIL]: { 47 | style: "color: #fff; background-color: #ff7781; padding: 5px 10px;", 48 | description: "测试请求失败" 49 | }, 50 | [ERequestLogType.REQUEST_PARAMS]: { 51 | style: "color: #fff; background-color: #0078fe; padding: 5px 10px;", 52 | description: "请求参数" 53 | }, 54 | [ERequestLogType.REQUEST_RESULT_SUCCESS]: { 55 | style: "color: #fff; background-color: #2da000; padding: 5px 10px;", 56 | description: "请求成功" 57 | }, 58 | [ERequestLogType.REQUEST_RESULT_FAIL]: { 59 | style: "color: #fff; background-color: #fe003c; padding: 5px 10px;", 60 | description: "请求失败" 61 | } 62 | } 63 | 64 | /** 输出请求日志选项 */ 65 | interface IRequestLogOptions { 66 | /** 请求日志类型 */ 67 | type: ERequestLogType 68 | /** 请求地址 */ 69 | url: string 70 | /** 请求id */ 71 | requestId: number 72 | /** 请求日志数据 */ 73 | data: Record 74 | } 75 | 76 | /** 77 | * FUN: 请求日志 78 | * 79 | * @author dyb-dev 80 | * @date 21/02/2025/ 18:32:32 81 | * @param {IRequestLogOptions} option 请求日志选项 82 | */ 83 | const requestLog = (option: IRequestLogOptions) => { 84 | 85 | // 如果不是开发环境且不启用调试模式,则不输出请求日志 86 | if (!isDevEnv() && !isEnableDebug()) { 87 | 88 | return 89 | 90 | } 91 | 92 | let url = option.url 93 | 94 | // 如果不是绝对路径,则拼接基础 API 路径 95 | if (!isAbsoluteUrl(url)) { 96 | 97 | url = trimUrlSlashes(url) 98 | url = `${__PROJECT_INFO__.env.VITE_API_BASE_PATH}${url && `/${url}`}` 99 | 100 | } 101 | 102 | // 输出请求日志选项 103 | const { type, requestId, data } = option 104 | // 获取请求日志配置 105 | const { description, style } = REQUEST_LOG_CONFIG_LIST[type] 106 | 107 | console.log(`\n %c${requestId} ${description} :>>`, style, url, "\n", data) 108 | 109 | } 110 | 111 | export { ERequestLogType, requestLog } 112 | -------------------------------------------------------------------------------- /src/components/AvatarNickname.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 79 | 80 | 90 | 91 | 118 | 119 | 149 | -------------------------------------------------------------------------------- /src/components/Cell.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 138 | 139 | 151 | 152 | 190 | 211 | -------------------------------------------------------------------------------- /src/components/FooterActionsBar.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 152 | 153 | 163 | 164 | 213 | 214 | 247 | -------------------------------------------------------------------------------- /src/components/LabelValueBar.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 90 | 91 | 103 | 104 | 136 | 137 | 156 | -------------------------------------------------------------------------------- /src/components/TabsPaneList.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 178 | 179 | 191 | 192 | 220 | 221 | 260 | -------------------------------------------------------------------------------- /src/components/TitleBar.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 54 | 55 | 66 | 67 | 92 | 93 | 116 | -------------------------------------------------------------------------------- /src/components/auth/AuthAvatarButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 135 | 136 | 147 | 148 | 156 | 157 | 179 | -------------------------------------------------------------------------------- /src/components/auth/AuthPhoneNumberButton.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 85 | 86 | 97 | 98 | 101 | 102 | 124 | -------------------------------------------------------------------------------- /src/components/auth/NickNameInput.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 167 | 168 | 180 | 181 | 195 | -------------------------------------------------------------------------------- /src/components/auth/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-31 01:55:52 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-12-07 20:18:17 6 | * @FilePath: /uniapp-mp-wx-template/src/components/auth/index.ts 7 | * @Description: 用户信息组件相关工具函数 8 | */ 9 | 10 | /** 导出授权头像按钮组件类型 */ 11 | export type * from "./AuthAvatarButton.vue" 12 | /** 导出授权头像昵称对话框类型 */ 13 | export type * from "./AuthAvatarNicknameDialog.vue" 14 | /** 导出授权手机号按钮组件类型 */ 15 | export type * from "./AuthPhoneNumberButton.vue" 16 | /** 导出昵称输入框组件类型 */ 17 | export type * from "./NickNameInput.vue" 18 | 19 | import { providerComponentOptions } from "@/components" 20 | 21 | import type { 22 | IAuthAvatarNicknameDialogOptions, 23 | TAuthAvatarNicknameDialogUnmountParam, 24 | TAuthAvatarNicknameDialogCustomKey 25 | } from "./AuthAvatarNicknameDialog.vue" 26 | 27 | /** 显示授权头像昵称对话框的结果 */ 28 | type TShowAuthAvatarNicknameDialogResult = TAuthAvatarNicknameDialogUnmountParam 29 | 30 | /** 31 | * 使用授权头像昵称对话框 32 | * 33 | * @author dyb-dev 34 | * @date 29/10/2024/ 22:10:25 35 | * @param {TAuthAvatarNicknameDialogCustomKey} [customKey='__AUTH_AVATAR_NICKNAME_DIALOG__'] - 授权头像昵称对话框唯一标识key 默认: `__AUTH_AVATAR_NICKNAME_DIALOG__` 36 | * @returns {*} {TUseAuthAvatarNicknameDialog} - 授权头像昵称对话框相关函数 37 | */ 38 | const useAuthAvatarNicknameDialog = (customKey: string = "") => { 39 | 40 | const _customKey: TAuthAvatarNicknameDialogCustomKey = `__AUTH_AVATAR_NICKNAME_DIALOG__${customKey}` 41 | const _options = providerComponentOptions(_customKey) 42 | 43 | /** 44 | * 显示授权头像昵称对话框 45 | * 46 | * @author dyb-dev 47 | * @date 30/10/2024/ 23:00:06 48 | * @returns {*} {Promise} - 显示授权头像昵称对话框的结果 49 | */ 50 | const showAuthAvatarNicknameDialog = (): Promise => { 51 | 52 | return new Promise(resolve => { 53 | 54 | _options.value = { 55 | show: true, 56 | unmount: (...args: TShowAuthAvatarNicknameDialogResult) => resolve(args) 57 | } 58 | 59 | }) 60 | 61 | } 62 | return { 63 | showAuthAvatarNicknameDialog 64 | } 65 | 66 | } 67 | 68 | export type { TShowAuthAvatarNicknameDialogResult } 69 | 70 | export { useAuthAvatarNicknameDialog } 71 | -------------------------------------------------------------------------------- /src/components/dialog/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-11-26 15:44:43 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-12-07 19:36:39 6 | * @FilePath: /uniapp-mp-wx-template/src/components/dialog/index.ts 7 | * @Description: 对话框组件相关工具函数 8 | */ 9 | 10 | /** 导出对话框类型 */ 11 | export type * from "./Dialog.vue" 12 | /** 导出表单对话框类型 */ 13 | export type * from "./FormDialog.vue" 14 | 15 | import { providerComponentOptions } from "@/components" 16 | 17 | import type { IDialogOptions, TDialogCustomKey, TDialogUnmountParam } from "./Dialog.vue" 18 | import type { IFormDialogOptions, TFormDialogCustomKey, TFormDialogUnmountParam } from "./FormDialog.vue" 19 | import type { TFilteredDefaultOptions } from "@/components" 20 | 21 | /** 显示对话框的选项 */ 22 | type TShowDialogOptions = TFilteredDefaultOptions 23 | 24 | /** 显示对话框的结果 */ 25 | type TShowDialogResult = TDialogUnmountParam 26 | 27 | /** 28 | * 使用对话框 29 | * 30 | * @author dyb-dev 31 | * @date 29/10/2024/ 22:10:25 32 | * @param {TDialogCustomKey} [customKey='__DIALOG__'] - 对话框唯一标识key 默认: `__DIALOG__` 33 | * @returns {*} {TUseDialog} - 对话框相关函数 34 | */ 35 | const useDialog = (customKey: string = "") => { 36 | 37 | const _customKey: TDialogCustomKey = `__DIALOG__${customKey}` 38 | const _options = providerComponentOptions(_customKey) 39 | 40 | /** 41 | * 显示对话框 42 | * 43 | * @author dyb-dev 44 | * @date 29/10/2024/ 22:05:36 45 | * @param {TShowDialogOptions} options - 对话框选项 46 | * @returns {*} {Promise} - 显示对话框的结果 47 | */ 48 | const showDialog = (options: TShowDialogOptions): Promise => { 49 | 50 | return new Promise(resolve => { 51 | 52 | _options.value = { 53 | ...options, 54 | show: true, 55 | unmount: (...args: TShowDialogResult) => resolve(args) 56 | } 57 | 58 | }) 59 | 60 | } 61 | return { 62 | showDialog 63 | } 64 | 65 | } 66 | 67 | /** 显示表单对话框的选项 */ 68 | type TShowFormDialogOptions> = TFilteredDefaultOptions> 69 | 70 | /** 显示表单对话框的结果 */ 71 | type TShowFormDialogResult> = TFormDialogUnmountParam 72 | 73 | /** 74 | * 使用表单对话框 75 | * 76 | * @author dyb-dev 77 | * @date 29/10/2024/ 22:10:25 78 | * @param {TFormDialogCustomKey} [customKey='__FORM_DIALOG__'] - 表单对话框唯一标识key 默认: `__FORM_DIALOG__` 79 | * @returns {*} {TUseFormDialog} - 表单对话框相关函数 80 | */ 81 | const useFormDialog = (customKey: string = "") => { 82 | 83 | const _customKey: TFormDialogCustomKey = `__FORM_DIALOG__${customKey}` 84 | const _options = providerComponentOptions>>(_customKey) 85 | 86 | /** 87 | * 显示表单对话框 88 | * 89 | * @author dyb-dev 90 | * @date 29/10/2024/ 22:05:36 91 | * @param {TShowFormDialogOptions} options - 表单对话框选项 92 | * @returns {*} {Promise>} - 显示表单对话框的结果 93 | */ 94 | const showFormDialog = >( 95 | options: TShowFormDialogOptions 96 | ): Promise> => { 97 | 98 | return new Promise(resolve => { 99 | 100 | _options.value = { 101 | ...options, 102 | show: true, 103 | unmount: (...args: TShowFormDialogResult) => resolve(args) 104 | } as IFormDialogOptions> 105 | 106 | }) 107 | 108 | } 109 | return { 110 | showFormDialog 111 | } 112 | 113 | } 114 | 115 | export type { TShowDialogOptions, TShowDialogResult, TShowFormDialogOptions } 116 | 117 | export { useDialog, useFormDialog } 118 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-01 22:46:34 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-12-07 21:25:21 6 | * @FilePath: /uniapp-mp-wx-template/src/components/index.ts 7 | * @Description: 组件模块 8 | */ 9 | 10 | /** 导出选择器组件相关工具函数 */ 11 | export * from "./picker" 12 | /** 导出弹窗组件相关工具函数 */ 13 | export * from "./popup" 14 | /** 导出对话框组件相关工具函数 */ 15 | export * from "./dialog" 16 | /** 导出用户信息组件相关工具函数 */ 17 | export * from "./auth" 18 | /** 导出提供组件选项相关工具函数 */ 19 | export * from "./provideComponentOptions" 20 | /** 导出标签&值栏组件 */ 21 | export * from "./LabelValueBar.vue" 22 | /** 导出列表组件 */ 23 | export * from "./List.vue" 24 | /** 导出表单控件组件 */ 25 | export * from "./FormControl.vue" 26 | /** 导出头像昵称组件 */ 27 | export * from "./AvatarNickname.vue" 28 | /** 导出单元格组件 */ 29 | export * from "./Cell.vue" 30 | /** 导出标签&值栏组件 */ 31 | export * from "./LabelValueBar.vue" 32 | /** 导出选项卡面板列表组件 */ 33 | export * from "./TabsPaneList.vue" 34 | /** 导出上传文件组件 */ 35 | export * from "./Uploader.vue" 36 | /** 导出轮播组件 */ 37 | export * from "./SwiperPro.vue" 38 | /** 导出底部操作栏组件 */ 39 | export * from "./FooterActionsBar.vue" 40 | -------------------------------------------------------------------------------- /src/components/layout/Layout.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 152 | 153 | 164 | 165 | 207 | 208 | 234 | -------------------------------------------------------------------------------- /src/components/layout/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-12-07 19:36:59 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-12-07 19:37:08 6 | * @FilePath: /uniapp-mp-wx-template/src/components/layout/index.ts 7 | * @Description: 导入相关组件类型 8 | */ 9 | 10 | /** 导出页面布局容器类型 */ 11 | export type * from "./Layout.vue" 12 | /** 导出顶部导航栏类型 */ 13 | export type * from "./NavBar.vue" 14 | /** 导出底部导航栏类型 */ 15 | export type * from "./TabBar.vue" 16 | -------------------------------------------------------------------------------- /src/components/picker/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-11-04 19:51:37 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-12-07 19:38:42 6 | * @FilePath: /uniapp-mp-wx-template/src/components/picker/index.ts 7 | * @Description: 选择器组件模块 8 | */ 9 | 10 | /** 导出选择器组件公共类型 */ 11 | export type * from "./type" 12 | /** 导出自定义选择器类型 */ 13 | export type * from "./Picker.vue" 14 | /** 导出日期时间选择器类型 */ 15 | export type * from "./DateTimePicker.vue" 16 | 17 | import { providerComponentOptions } from "@/components" 18 | 19 | import type { IDateTimePickerOptions, TDateTimePickerCustomKey, TShowDateTimePickerResult } from "./DateTimePicker.vue" 20 | import type { IPickerOptions, TPickerCustomKey } from "./Picker.vue" 21 | import type { TShowPickerBaseResult } from "./type" 22 | import type { TFilteredDefaultOptions } from "@/components" 23 | 24 | /** 显示选择器的选项 */ 25 | type TShowPickerOptions = TFilteredDefaultOptions 26 | 27 | /** 28 | * 使用选择器 29 | * 30 | * @author dyb-dev 31 | * @date 04/11/2024/ 21:12:53 32 | * @param {string} [customKey=""] - 弹窗唯一标识key 默认: `__PICKER__` 33 | * @returns {*} {TUsePicker} - 选择器相关函数 34 | */ 35 | const usePicker = (customKey: string = "") => { 36 | 37 | const _customKey: TPickerCustomKey = `__PICKER__${customKey}` 38 | const _options = providerComponentOptions(_customKey) 39 | 40 | /** 41 | * 显示选择器 42 | * 43 | * @author dyb-dev 44 | * @date 04/11/2024/ 21:13:25 45 | * @param {TShowPickerOptions} options - 选择器选项 46 | * @returns {*} {Promise} - 显示选择器的结果 47 | */ 48 | const showPicker = (options: TShowPickerOptions): Promise => { 49 | 50 | return new Promise(resolve => { 51 | 52 | _options.value = { 53 | ...options, 54 | show: true, 55 | unmount: (...args: TShowPickerBaseResult) => resolve(args) 56 | } 57 | 58 | }) 59 | 60 | } 61 | return { 62 | showPicker 63 | } 64 | 65 | } 66 | 67 | /** 显示日期时间选择器的选项 */ 68 | type TShowDateTimePickerOptions = TFilteredDefaultOptions 69 | 70 | /** 71 | * 使用日期时间选择器 72 | * 73 | * @author dyb-dev 74 | * @date 04/11/2024/ 21:12:53 75 | * @param {string} [customKey=""] - 弹窗唯一标识key 默认: `__DATE_TIME_PICKER__` 76 | * @returns {*} {TUseDateTimePicker} - 日期时间选择器相关函数 77 | */ 78 | const useDateTimePicker = (customKey: string = "") => { 79 | 80 | const _customKey: TDateTimePickerCustomKey = `__DATE_TIME_PICKER__${customKey}` 81 | const _options = providerComponentOptions(_customKey) 82 | 83 | /** 84 | * 显示日期时间选择器 85 | * 86 | * @author dyb-dev 87 | * @date 04/11/2024/ 21:13:25 88 | * @param {TShowDateTimePickerOptions} options - 日期时间选择器选项 89 | * @returns {*} {Promise} - 显示日期时间选择器的结果 90 | */ 91 | const showDateTimePicker = (options?: TShowDateTimePickerOptions): Promise => { 92 | 93 | return new Promise(resolve => { 94 | 95 | _options.value = { 96 | ...options, 97 | show: true, 98 | unmount: (...args: TShowDateTimePickerResult) => resolve(args) 99 | } 100 | 101 | }) 102 | 103 | } 104 | return { 105 | showDateTimePicker 106 | } 107 | 108 | } 109 | 110 | export type { TShowPickerOptions, TShowDateTimePickerOptions } 111 | 112 | export { usePicker, useDateTimePicker } 113 | -------------------------------------------------------------------------------- /src/components/picker/type.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-11-04 20:02:48 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-11-04 20:06:35 6 | * @FilePath: /uniapp-mp-wx-template/src/components/picker/type.ts 7 | * @Description: 选择器组件公共类型 8 | */ 9 | 10 | import type { PickerBaseEvent } from "nutui-uniapp" 11 | 12 | /** 选择器组件 基础动作类型 */ 13 | type TPickerBaseActionType = "click-cancel-button" | "click-confirm-button" 14 | 15 | /** 选择器组件 基础选择结果类型 */ 16 | type TPickerBaseSelectedResult = PickerBaseEvent & { 17 | /** 动作类型 */ 18 | actionType: TPickerBaseActionType 19 | } 20 | 21 | /** 选择器组件 函数式调用时组件卸载回调参数 基础类型 */ 22 | type TPickerBaseUnmountParam = [TPickerBaseSelectedResult] 23 | 24 | /** 选择器组件 函数式调用时基础结果类型 */ 25 | type TShowPickerBaseResult = TPickerBaseUnmountParam 26 | 27 | export type { TPickerBaseActionType, TPickerBaseSelectedResult, TPickerBaseUnmountParam, TShowPickerBaseResult } 28 | -------------------------------------------------------------------------------- /src/components/popup/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-30 20:56:29 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-12-07 19:39:52 6 | * @FilePath: /uniapp-mp-wx-template/src/components/popup/index.ts 7 | * @Description: 弹窗组件相关工具函数 8 | */ 9 | 10 | /** 导出基础弹窗类型 */ 11 | export type * from "./Popup.vue" 12 | 13 | import { providerComponentOptions } from "@/components" 14 | 15 | import type { IPopupOptions, TPopupUnmountParam, TPopupCustomKey } from "./Popup.vue" 16 | import type { TFilteredDefaultOptions } from "@/components" 17 | 18 | /** 显示弹窗的选项 */ 19 | type TShowPopupOptions = TFilteredDefaultOptions 20 | 21 | /** 显示弹窗的结果 */ 22 | type TShowPopupResult = TPopupUnmountParam 23 | 24 | /** 25 | * 使用弹窗 26 | * 27 | * @author dyb-dev 28 | * @date 29/10/2024/ 22:10:25 29 | * @param {TPopupCustomKey} [customKey='__POPUP__'] - 弹窗唯一标识key 默认: `__POPUP__` 30 | * @returns {*} {TUsePopup} - 弹窗相关函数 31 | */ 32 | const usePopup = (customKey: string = "") => { 33 | 34 | const _customKey: TPopupCustomKey = `__POPUP__${customKey}` 35 | const _options = providerComponentOptions(_customKey) 36 | 37 | /** 38 | * 显示弹窗 39 | * 40 | * @author dyb-dev 41 | * @date 29/10/2024/ 22:05:36 42 | * @param {TShowPopupOptions} options - 弹窗选项 43 | * @returns {*} {Promise} - 显示弹窗的结果 44 | */ 45 | const showPopup = (options: TShowPopupOptions): Promise => { 46 | 47 | return new Promise(resolve => { 48 | 49 | _options.value = { 50 | ...options, 51 | show: true, 52 | unmount: (...args: TShowPopupResult) => resolve(args) 53 | } 54 | 55 | }) 56 | 57 | } 58 | return { 59 | showPopup 60 | } 61 | 62 | } 63 | 64 | export type { TShowPopupOptions, TShowPopupResult } 65 | 66 | export { usePopup } 67 | -------------------------------------------------------------------------------- /src/components/provideComponentOptions.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-30 00:22:30 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-30 21:38:41 6 | * @FilePath: /uniapp-mp-wx-template/src/components/provideComponentOptions.ts 7 | * @Description: 提供组件选项相关工具函数 8 | */ 9 | 10 | import { provide, ref } from "vue" 11 | 12 | import type { Ref } from "vue" 13 | 14 | /** 15 | * 默认选项接口 16 | */ 17 | interface IDefaultOptions { 18 | /** 是否显示 */ 19 | show: boolean 20 | /** 组件唯一标识key */ 21 | customKey: string 22 | /** 卸载组件回调 */ 23 | unmount: (...args: any[]) => void 24 | } 25 | 26 | /** 27 | * 用于过滤掉默认选项的类型工具 28 | * 29 | * @template Target - 目标类型 30 | */ 31 | type TFilteredDefaultOptions = Omit> 32 | 33 | /** 34 | * 提供组件选项 35 | * 36 | * @author dyb-dev 37 | * @date 29/10/2024/ 21:55:37 38 | * @param {Key} customKey - 组件唯一标识key 39 | * @returns {*} {Ref} - 组件选项 40 | */ 41 | const providerComponentOptions = = Record>( 42 | customKey: Key 43 | ): Ref => { 44 | 45 | const _options = ref({}) as Ref 46 | 47 | provide(customKey, _options as any) 48 | 49 | return _options 50 | 51 | } 52 | 53 | export type { IDefaultOptions, TFilteredDefaultOptions } 54 | 55 | export { providerComponentOptions } 56 | -------------------------------------------------------------------------------- /src/constants/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-06 23:32:48 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:01:42 6 | * @FilePath: /uniapp-mp-wx-template/src/constants/index.ts 7 | * @Description: 常量模块 8 | */ 9 | 10 | /** 导出用户信息常量模块 */ 11 | export * from "./userInfo" 12 | -------------------------------------------------------------------------------- /src/constants/userInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:01:57 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:02:04 6 | * @FilePath: /uniapp-mp-wx-template/src/constants/userInfo.ts 7 | * @Description: 用户信息常量模块 8 | */ 9 | 10 | import { ECertificatesType, EGenderType } from "@/types" 11 | 12 | /** CONST: 性别配置列表 */ 13 | const GENDER_CONFIG_LIST = [ 14 | { type: EGenderType.Man, desc: "男" }, 15 | { type: EGenderType.Woman, desc: "女" } 16 | ] 17 | 18 | /** CONST: 证件配置列表 */ 19 | const CERTIFICATE_CONFIG_LIST = [ 20 | { type: ECertificatesType.IdCard, desc: "身份证" }, 21 | { type: ECertificatesType.MilitaryCard, desc: "军人证" }, 22 | { type: ECertificatesType.Passport, desc: "护照" }, 23 | { type: ECertificatesType.BirthCertificate, desc: "出生证" }, 24 | { type: ECertificatesType.HkMoTwPass, desc: "港澳台通行证" }, 25 | { type: ECertificatesType.SoldierCard, desc: "士兵证" }, 26 | { type: ECertificatesType.PoliceCard, desc: "警官证" }, 27 | { type: ECertificatesType.HkMoTwResidentPermit, desc: "港澳台居民居住证" }, 28 | { type: ECertificatesType.ForeignerPermanentResidentIdCard, desc: "外国人永久居留身份证" }, 29 | { type: ECertificatesType.ResidentAccountBook, desc: "居民户口薄" } 30 | ] 31 | 32 | export { GENDER_CONFIG_LIST, CERTIFICATE_CONFIG_LIST } 33 | -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-08 12:02:08 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-05-28 23:47:42 6 | * @FilePath: /uniapp-mp-wx-template/src/hooks/index.ts 7 | * @Description: hook模块 8 | */ 9 | 10 | /** 导出分页相关hook */ 11 | export * from "./pagination" 12 | /** 导出分享相关hook */ 13 | export * from "./share" 14 | /** 导出时间控制相关hooks函数 */ 15 | export * from "./timer" 16 | -------------------------------------------------------------------------------- /src/hooks/pagination/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-11-16 02:11:23 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-11-16 02:11:32 6 | * @FilePath: /uniapp-mp-wx-template/src/hooks/pagination/index.ts 7 | * @Description: 分页hooks模块 8 | */ 9 | 10 | // 导出分页器 11 | export * from "./usePagination" 12 | // 导出列表分页器 13 | export * from "./useListPagination" 14 | -------------------------------------------------------------------------------- /src/hooks/pagination/useListPagination.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-11-16 02:11:23 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-11-19 15:19:20 6 | * @FilePath: /uniapp-mp-wx-template/src/hooks/pagination/useListPagination.ts 7 | * @Description: 列表分页器 8 | */ 9 | 10 | import { usePagination } from "./usePagination" 11 | 12 | import type { IUsePaginationOptions, IUsePaginationReturn, TPaginationDataItem } from "./usePagination" 13 | 14 | /** 列表分页配置,继承自 IUsePaginationOptions */ 15 | interface IUseListPaginationOptions 16 | extends Omit, "usePreviousDataOnFail"> {} 17 | 18 | /** 列表分页返回结果,继承自 IUsePaginationReturn */ 19 | interface IUseListPaginationReturn extends Omit, "refresh" | "prev"> { 20 | /** 清空所有数据并刷新首页 */ 21 | clearRefresh: () => Promise 22 | /** 清空所有数据并初始化分页器 */ 23 | clearInitialize: () => Promise 24 | } 25 | 26 | /** 27 | * 列表分页器 28 | * 29 | * @author dyb-dev 30 | * @date 05/09/2024/ 14:19:07 31 | * @template T 列表分页数据类型 32 | * @param {IUseListPaginationOptions} options 列表分页配置 33 | * @returns {*} {IUseListPaginationReturn} 列表分页返回结果 34 | */ 35 | const useListPagination = (options: IUseListPaginationOptions): IUseListPaginationReturn => { 36 | 37 | // 调用 usePagination 并传入参数 38 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 39 | const { refresh, prev, ..._pagination } = usePagination(options) 40 | 41 | /** 42 | * 清空重置分页数据和状态 43 | * 44 | * @author dyb-dev 45 | * @date 16/11/2024/ 00:53:21 46 | */ 47 | const _clear = () => { 48 | 49 | _pagination.currentTotalDataMap.value.clear() 50 | _pagination.totalSize.value = 0 51 | _pagination.initialized.value = false 52 | 53 | } 54 | 55 | /** 56 | * 清空所有数据并初始化分页器 57 | * 58 | * @author dyb-dev 59 | * @date 16/11/2024/ 00:53:54 60 | */ 61 | const clearInitialize = async() => { 62 | 63 | try { 64 | 65 | _clear() 66 | await _pagination.load(1) 67 | 68 | } 69 | catch (error) { 70 | 71 | throw `clearInitialize() ${error}` 72 | 73 | } 74 | 75 | } 76 | 77 | /** 78 | * 清空所有数据并刷新首页 79 | * 80 | * @author dyb-dev 81 | * @date 06/09/2024/ 22:24:56 82 | */ 83 | const clearRefresh = async() => { 84 | 85 | try { 86 | 87 | _clear() 88 | await refresh(1) 89 | 90 | } 91 | catch (error) { 92 | 93 | throw `clearRefresh() ${error}` 94 | 95 | } 96 | 97 | } 98 | 99 | /** 100 | * 列表分页的下一页逻辑 101 | * 102 | * @author dyb 103 | * @date 05/09/2024/ 13:16:57 104 | */ 105 | const next = async() => { 106 | 107 | try { 108 | 109 | const { initialized, currentLoadStatus, currentPage, finished } = _pagination 110 | 111 | if (!initialized.value) { 112 | 113 | console.warn("next() 初始化未完成取消执行下一页,开始执行初始化操作") 114 | await clearInitialize() 115 | return 116 | 117 | } 118 | if (finished.value) { 119 | 120 | console.warn(`next() 已经加载完所有数据,无法执行下一页 currentPage: ${currentPage.value}`) 121 | return 122 | 123 | } 124 | if (currentLoadStatus.value === "loading") { 125 | 126 | console.warn( 127 | `next() 当前页码数据正在加载中,取消执行下一页 currentPage: ${currentPage.value} currentLoadStatus: ${currentLoadStatus.value}` 128 | ) 129 | return 130 | 131 | } 132 | if (currentLoadStatus.value === "fail") { 133 | 134 | console.warn( 135 | `next() 当前页码数据加载失败,取消执行下一页,开始重新加载当前页数据 currentPage: ${currentPage.value} currentLoadStatus: ${currentLoadStatus.value}` 136 | ) 137 | 138 | // 加载失败,重新加载当前页码数据 139 | await _pagination.load() 140 | return 141 | 142 | } 143 | await _pagination.next() 144 | 145 | } 146 | catch (error) { 147 | 148 | throw `next() ${error}` 149 | 150 | } 151 | 152 | } 153 | 154 | return { 155 | ..._pagination, 156 | clearInitialize, 157 | clearRefresh, 158 | next 159 | } 160 | 161 | } 162 | 163 | export type { IUseListPaginationOptions, IUseListPaginationReturn } 164 | 165 | export { useListPagination } 166 | -------------------------------------------------------------------------------- /src/hooks/share/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-30 12:28:29 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-30 12:28:45 6 | * @FilePath: /uniapp-mp-wx-template/src/hooks/share/index.ts 7 | * @Description: 分享相关hook 8 | */ 9 | 10 | import { useUserInfoStoreWithOut } from "@/stores" 11 | import { setUrlQueryValue } from "@/utils" 12 | 13 | /** 分享配置 */ 14 | interface IShareConfig extends Page.CustomShareContent { 15 | /** 16 | * 转发路径,必须是以 / 开头的完整路径。默认值:当前页面 path 17 | */ 18 | path?: NavigateToOptions["url"] 19 | /** 20 | * 邀请用户ID 21 | */ 22 | inviteUserId?: string 23 | /** 24 | * 来源 25 | */ 26 | source?: string 27 | } 28 | 29 | /** CONST: 默认分享配置 */ 30 | const DEFAULT_SHARE_CONFIG: IShareConfig = { 31 | title: "祝您前程似锦,未来可期!", 32 | imageUrl: "/static/image/share.jpg" 33 | } 34 | 35 | // HOOKS: 使用用户信息Store 36 | const { userInfoStoreState } = useUserInfoStoreWithOut() 37 | 38 | /** 39 | * FUN: 使用分享 40 | * 41 | * @author dyb-dev 42 | * @date 30/10/2024/ 12:29:17 43 | * @param {IShareConfig} [shareConfig] 分享配置 44 | * @returns {*} {Page.CustomShareContent} 分享配置 45 | */ 46 | const useShare = (shareConfig?: IShareConfig): Page.CustomShareContent => { 47 | 48 | /** 当前分享配置 */ 49 | const _shareConfig = { ...DEFAULT_SHARE_CONFIG, ...shareConfig } 50 | 51 | let _path = _shareConfig.path || "/" + __PROJECT_INFO__.env.VITE_HOME_PATH 52 | 53 | /** 邀请用户ID */ 54 | const _inviteUserId = _shareConfig.inviteUserId ?? userInfoStoreState.userId 55 | if (_inviteUserId) { 56 | 57 | _path = setUrlQueryValue(_path, "inviteUserId", _inviteUserId) 58 | delete _shareConfig.inviteUserId 59 | 60 | } 61 | 62 | // 来源 63 | const _source = _shareConfig.source ?? "share" 64 | if (_source) { 65 | 66 | _path = setUrlQueryValue(_path, "source", _source) 67 | delete _shareConfig.source 68 | 69 | } 70 | 71 | _shareConfig.path = _path as NavigateToOptions["url"] 72 | 73 | return _shareConfig 74 | 75 | } 76 | 77 | export { useShare } 78 | -------------------------------------------------------------------------------- /src/hooks/timer/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-05-28 23:47:05 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-05-28 23:47:15 6 | * @FilePath: /uniapp-mp-wx-template/src/hooks/timer/index.ts 7 | * @Description: 时间控制相关hooks函数 8 | */ 9 | 10 | /** 使用轮询器返回值 */ 11 | export interface IUsePollingReturn { 12 | /** 开始轮询 */ 13 | start: () => void 14 | /** 停止轮询 */ 15 | stop: () => void 16 | } 17 | 18 | /** 19 | * FUN: 使用轮询器 20 | * 21 | * @author dyb-dev 22 | * @date 28/05/2025/ 23:01:57 23 | * @param {(() => boolean | void | Promise)} callback 回调函数 24 | * @param {number} [interval=1000] 轮询间隔,默认为 1000ms 25 | * @returns {*} {IUsePollingReturn} 返回值 26 | */ 27 | export const usePolling = ( 28 | callback: () => boolean | void | Promise, 29 | interval: number = 1000 30 | ): IUsePollingReturn => { 31 | 32 | /** 轮询器 */ 33 | let _timer: TTimeoutId 34 | /** 是否轮询中 */ 35 | let _polling: boolean = false 36 | 37 | /** 启动轮询 */ 38 | const start = () => { 39 | 40 | // 如果已经在轮询中,不重复启动 41 | if (_polling) { 42 | 43 | return 44 | 45 | } 46 | 47 | _polling = true 48 | 49 | const _poll = async() => { 50 | 51 | try { 52 | 53 | // 执行回调函数,判断是否需要继续轮询 54 | const _isStop = await callback() 55 | 56 | if (!_isStop) { 57 | 58 | // 如果不需要停止,继续轮询 59 | _timer = setTimeout(() => { 60 | 61 | _poll() 62 | 63 | }, interval) 64 | return 65 | 66 | } 67 | 68 | stop() 69 | 70 | } 71 | catch (error) { 72 | 73 | stop() 74 | console.error("usePolling()", error) 75 | 76 | } 77 | 78 | } 79 | 80 | _poll() 81 | 82 | } 83 | 84 | /** 停止轮询 */ 85 | const stop = () => { 86 | 87 | clearTimeout(_timer) 88 | _polling = false 89 | 90 | } 91 | 92 | return { start, stop } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 20:10:08 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:43:32 6 | * @FilePath: /uniapp-mp-wx-template/src/main.ts 7 | * @Description: 程序入口文件 8 | */ 9 | 10 | import { createSSRApp } from "vue" 11 | 12 | import App from "@/App.vue" 13 | import { setupRouterInterceptor } from "@/routerInterceptor" 14 | import { store } from "@/stores" 15 | 16 | import { setupApi } from "./apis" 17 | 18 | export function createApp() { 19 | 20 | const app = createSSRApp(App) 21 | // 使用商店 22 | app.use(store) 23 | // 初始化接口配置 24 | setupApi() 25 | // 初始化路由拦截器 26 | setupRouterInterceptor() 27 | 28 | return { 29 | app 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uniapp-mp-wx-template", 3 | "appid": "", 4 | "description": "基于`uni-app + vue3 + ts`搭建的微信小程序模板", 5 | "versionName": "1.1.5", 6 | "versionCode": "100", 7 | "transformPx": false, 8 | "app-plus": { 9 | "usingComponents": true, 10 | "nvueStyleCompiler": "uni-app", 11 | "compilerVersion": 3, 12 | "splashscreen": { 13 | "alwaysShowBeforeRender": true, 14 | "waiting": true, 15 | "autoclose": true, 16 | "delay": 0 17 | }, 18 | "modules": {}, 19 | "distribute": { 20 | "android": { 21 | "permissions": [] 22 | }, 23 | "ios": {}, 24 | "sdkConfigs": {} 25 | } 26 | }, 27 | "quickapp": {}, 28 | "mp-weixin": { 29 | "appid": "", 30 | "setting": { 31 | "urlCheck": false, 32 | "es6": true, 33 | "postcss": true, 34 | "minified": true, 35 | "bigPackageSizeSupport": true 36 | }, 37 | "usingComponents": true, 38 | "lazyCodeLoading": "requiredComponents", 39 | "mergeVirtualHostAttributes": true, 40 | "optimization": { 41 | "subPackages": true 42 | } 43 | }, 44 | "mp-alipay": { 45 | "usingComponents": true 46 | }, 47 | "mp-baidu": { 48 | "usingComponents": true 49 | }, 50 | "mp-toutiao": { 51 | "usingComponents": true 52 | }, 53 | "uniStatistics": { 54 | "enable": false 55 | }, 56 | "vueVersion": "3" 57 | } -------------------------------------------------------------------------------- /src/pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "globalStyle": { 3 | "navigationStyle": "custom", 4 | "navigationBarTextStyle": "black", 5 | "navigationBarBackgroundColor": "#ffffff", 6 | "backgroundColor": "#ffffff", 7 | "disableScroll": true 8 | }, 9 | "pages": [ 10 | { 11 | "path": "pages/home", 12 | "type": "home", 13 | "style": { 14 | "navigationBarTitleText": "首页" 15 | } 16 | }, 17 | { 18 | "path": "pages/launch", 19 | "type": "page", 20 | "style": {} 21 | }, 22 | { 23 | "path": "pages/list", 24 | "type": "page", 25 | "style": { 26 | "navigationBarTitleText": "列表" 27 | } 28 | }, 29 | { 30 | "path": "pages/login", 31 | "type": "page", 32 | "style": { 33 | "navigationBarTitleText": "登录" 34 | } 35 | }, 36 | { 37 | "path": "pages/my", 38 | "type": "page", 39 | "style": { 40 | "navigationBarTitleText": "我的" 41 | }, 42 | "needLogin": true 43 | }, 44 | { 45 | "path": "pages/test", 46 | "type": "page", 47 | "style": { 48 | "navigationBarTitleText": "测试", 49 | "disableScroll": false, 50 | "enablePullDownRefresh": true 51 | }, 52 | "needLogin": true 53 | } 54 | ], 55 | "condition": { 56 | "current": 0, 57 | "list": [ 58 | { 59 | "name": "启动页", 60 | "path": "pages/launch", 61 | "query": "targetPath=/pages/test&test=测试启动参数" 62 | }, 63 | { 64 | "name": "首页", 65 | "path": "pages/home", 66 | "query": "test=首页" 67 | }, 68 | { 69 | "name": "我的", 70 | "path": "pages/my" 71 | }, 72 | { 73 | "name": "测试", 74 | "path": "pages/test" 75 | } 76 | ] 77 | }, 78 | "tabBar": { 79 | "custom": true, 80 | "color": "#7d7e80", 81 | "selectedColor": "#29d446", 82 | "list": [ 83 | { 84 | "pagePath": "pages/home", 85 | "text": "首页", 86 | "iconfont": { 87 | "text": "home", 88 | "selectedText": "home" 89 | } 90 | }, 91 | { 92 | "pagePath": "pages/list", 93 | "text": "列表", 94 | "iconfont": { 95 | "text": "dongdong", 96 | "selectedText": "dongdong" 97 | } 98 | }, 99 | { 100 | "pagePath": "pages/my", 101 | "text": "我的", 102 | "iconfont": { 103 | "text": "my", 104 | "selectedText": "my" 105 | } 106 | } 107 | ] 108 | }, 109 | "subPackages": [ 110 | { 111 | "root": "subPackages/webview", 112 | "pages": [ 113 | { 114 | "path": "pages/webview", 115 | "type": "page", 116 | "style": {} 117 | } 118 | ] 119 | } 120 | ], 121 | "preloadRule": { 122 | "pages/home": { 123 | "packages": [ 124 | "subPackages/webview" 125 | ] 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/pages/home.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 49 | 50 | 70 | 71 | 93 | -------------------------------------------------------------------------------- /src/pages/launch.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 81 | 82 | 88 | 89 | 107 | -------------------------------------------------------------------------------- /src/pages/list.vue: -------------------------------------------------------------------------------- 1 | 9 | 66 | 67 | 83 | -------------------------------------------------------------------------------- /src/pages/login.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 130 | 131 | 178 | 179 | 204 | -------------------------------------------------------------------------------- /src/pages/my.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 21 | 22 | 40 | 41 | 52 | -------------------------------------------------------------------------------- /src/pages/test.vue: -------------------------------------------------------------------------------- 1 | 93 | 94 | 111 | 112 | 126 | -------------------------------------------------------------------------------- /src/routerInterceptor/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 14:01:47 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:41:43 6 | * @FilePath: /uniapp-mp-wx-template/src/routerInterceptor/index.ts 7 | * @Description: 路由拦截器模块 8 | */ 9 | 10 | import queryString from "query-string" 11 | 12 | import { setupLoginPreInterceptor } from "./login" 13 | 14 | /** 15 | * 路由Invoke参数 16 | */ 17 | interface IRouterInvokeParams { 18 | /** url 调用除了`uni.navigateBack`方法时才有此参数 */ 19 | url?: string 20 | /** 返回层级 调用`uni.navigateBack`方法时才有此参数 */ 21 | delta?: number 22 | } 23 | 24 | /** 25 | * 路由前置拦截器参数 26 | */ 27 | interface IRouterPreInterceptorParams { 28 | /** 目标路径 */ 29 | path: string 30 | /** 目标路由参数 */ 31 | query: Record 32 | /** 返回层级 */ 33 | delta: number 34 | } 35 | 36 | /** 路由前置拦截器函数 */ 37 | type TRouterPreInterceptor = (params: IRouterPreInterceptorParams) => boolean 38 | 39 | /** 40 | * FUN: 设置路由拦截器 41 | * 42 | * @author dyb-dev 43 | * @date 02/10/2024/ 15:03:56 44 | */ 45 | const setupRouterInterceptor = () => { 46 | 47 | const _options: UniNamespace.InterceptorOptions = { 48 | // 前置拦截器 49 | invoke({ url = "", delta = 0 }: IRouterInvokeParams) { 50 | 51 | /** url解析结果 */ 52 | const _parseResult = queryString.parseUrl(url) 53 | /** 路由前置拦截器参数 */ 54 | const _params: IRouterPreInterceptorParams = { 55 | path: _parseResult.url, 56 | query: _parseResult.query, 57 | delta 58 | } 59 | 60 | /** 是否继续执行 */ 61 | let isNext = true 62 | 63 | isNext = setupLoginPreInterceptor(_params) 64 | 65 | return isNext 66 | 67 | } 68 | // 后置拦截器(一般用不到) 69 | // returnValue() { 70 | 71 | // } 72 | } 73 | 74 | uni.addInterceptor("navigateTo", _options) 75 | uni.addInterceptor("redirectTo", _options) 76 | uni.addInterceptor("reLaunch", _options) 77 | uni.addInterceptor("switchTab", _options) 78 | uni.addInterceptor("navigateBack", _options) 79 | 80 | } 81 | 82 | export { setupRouterInterceptor } 83 | 84 | export type { IRouterPreInterceptorParams, TRouterPreInterceptor } 85 | -------------------------------------------------------------------------------- /src/routerInterceptor/login.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-02 14:51:37 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-11 17:10:45 6 | * @FilePath: /uniapp-mp-wx-template/src/interceptors/router/login.ts 7 | * @Description: 登录拦截器模块 8 | */ 9 | 10 | import { useUserInfoStoreWithOut } from "@/stores" 11 | import { getPageConfig, navigateToLogin } from "@/utils" 12 | 13 | import type { TRouterPreInterceptor } from "." 14 | 15 | /** CONST: 用户信息Store */ 16 | const { userInfoStoreState } = useUserInfoStoreWithOut() 17 | 18 | /** 19 | * FUN: 设置登录前置拦截器 20 | * 21 | * @author dyb-dev 22 | * @date 02/10/2024/ 14:54:31 23 | * @param {string} url - 路由路径 24 | * @returns {*} 是否继续执行 25 | */ 26 | const setupLoginPreInterceptor: TRouterPreInterceptor = ({ path, query }) => { 27 | 28 | // 如果是没有登录且需要登录的页面,跳转到登录页 29 | if (!userInfoStoreState.isLogin && getPageConfig(path)?.needLogin) { 30 | 31 | navigateToLogin({ 32 | redirectPath: path as NavigateToOptions["url"], 33 | query, 34 | findBackDelta: false 35 | }) 36 | return false 37 | 38 | } 39 | 40 | return true 41 | 42 | } 43 | 44 | export { setupLoginPreInterceptor } 45 | -------------------------------------------------------------------------------- /src/static/image/List/back-top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/List/back-top.png -------------------------------------------------------------------------------- /src/static/image/List/empty.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/List/empty.png -------------------------------------------------------------------------------- /src/static/image/layout/TabBar/icon1-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/layout/TabBar/icon1-off.png -------------------------------------------------------------------------------- /src/static/image/layout/TabBar/icon1-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/layout/TabBar/icon1-on.png -------------------------------------------------------------------------------- /src/static/image/layout/TabBar/icon2-off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/layout/TabBar/icon2-off.png -------------------------------------------------------------------------------- /src/static/image/layout/TabBar/icon2-on.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/layout/TabBar/icon2-on.png -------------------------------------------------------------------------------- /src/static/image/mp_wx.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /src/static/image/popup/Popup/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/popup/Popup/close.png -------------------------------------------------------------------------------- /src/static/image/share.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/share.jpg -------------------------------------------------------------------------------- /src/static/image/uniapp.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dyb-dev/uniapp-mp-wx-template/2aa2f2cb1ef032b8013e9e830b9f5f6777647877/src/static/image/uniapp.ico -------------------------------------------------------------------------------- /src/stores/activity.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 21:40:38 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-13 21:24:10 6 | * @FilePath: /uniapp-mp-wx-template/src/stores/activity.ts 7 | * @Description: 活动状态管理 8 | */ 9 | 10 | import { defineStore } from "pinia" 11 | import { reactive } from "vue" 12 | 13 | import { store } from "." 14 | 15 | /** Store 状态类型 */ 16 | interface IActivityStoreState { 17 | count: number 18 | } 19 | 20 | /** Store 实例 */ 21 | const useActivityStore = defineStore( 22 | "ActivityStore", 23 | () => { 24 | 25 | /** Store 状态 */ 26 | const activityStoreState = reactive({ 27 | count: 0 28 | }) 29 | 30 | return { activityStoreState } 31 | 32 | } 33 | // { 34 | // // 配置持久化状态,如果不需要可以删除掉 或 设置为false 35 | // persist: { 36 | // // 指定需要持久化的状态 37 | // pick: ['state.count'] 38 | // } 39 | // } 40 | ) 41 | 42 | /** 43 | * FUN: 使用状态管理 44 | * - 在没有Vue组件上下文的情况下使用 45 | * 46 | * @author dyb-dev 47 | * @date 15/09/2024/ 23:53:35 48 | * @returns store实例 49 | */ 50 | const useActivityStoreWithOut = () => { 51 | 52 | return useActivityStore(store) 53 | 54 | } 55 | 56 | export { useActivityStore, useActivityStoreWithOut } 57 | -------------------------------------------------------------------------------- /src/stores/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 21:26:18 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-15 13:51:26 6 | * @FilePath: /uniapp-mp-wx-template/src/stores/index.ts 7 | * @Description: store模块 8 | */ 9 | 10 | export * from "./activity" 11 | export * from "./tabBar" 12 | export * from "./userInfo" 13 | 14 | import { createPinia } from "pinia" 15 | import { createPersistedState } from "pinia-plugin-persistedstate" 16 | 17 | /** store 实例 */ 18 | const store = createPinia() 19 | 20 | // 使用全局持久化状态插件 21 | store.use( 22 | createPersistedState({ 23 | // 设置全局存储键名 默认: store名称 24 | key: id => `persisted_${id}`, 25 | // 设置全局存储方式 默认: localStorage 26 | storage: { 27 | getItem: uni.getStorageSync, 28 | setItem: uni.setStorageSync 29 | } 30 | }) 31 | ) 32 | 33 | export { store } 34 | -------------------------------------------------------------------------------- /src/stores/tabBar.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 21:49:17 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-13 21:24:32 6 | * @FilePath: /uniapp-mp-wx-template/src/stores/tabBar.ts 7 | * @Description: TabBar状态管理 8 | */ 9 | 10 | import { defineStore } from "pinia" 11 | import { reactive } from "vue" 12 | 13 | import pagesJson from "@/pages.json" 14 | import { getCurrentPagePath } from "@/utils" 15 | 16 | import type { TTabBarItem } from "@/components/layout/TabBar.vue" 17 | import type { TabBarItem } from "@uni-helper/vite-plugin-uni-pages" 18 | 19 | import { store } from "." 20 | 21 | /** Store 状态类型 */ 22 | type TTabBarStoreState = { 23 | /** 当前TabBar索引 */ 24 | currentIndex: number 25 | /** TabBar 列表 */ 26 | list: TTabBarItem[] 27 | } 28 | 29 | /** Store 实例 */ 30 | const useTabBarStore = defineStore("TabBarStore", () => { 31 | 32 | /** Store State */ 33 | const tabBarStoreState = reactive({ 34 | currentIndex: 0, 35 | list: 36 | // @ts-ignore 37 | pagesJson?.tabBar?.list.map((item: TabBarItem) => { 38 | 39 | return { 40 | ...item, 41 | // 优先级: 图标 > 图片路径 42 | // @ts-ignore 43 | icon: item?.iconfont?.text || item?.iconPath || "", 44 | // @ts-ignore 45 | selectedIcon: item?.iconfont?.selectedText || item?.selectedIconPath || "", 46 | classPrefix: "nut-icon", 47 | fontClassName: "nutui-iconfont", 48 | showDot: false, 49 | dotValue: 0 50 | } as TTabBarItem 51 | 52 | }) || [] 53 | }) 54 | 55 | /** 56 | * FUN: 获取指定 TabBarItem 57 | * 58 | * @author dyb-dev 59 | * @date 25/09/2024/ 17:41:06 60 | * @param {string} pagePath 页面路径 61 | * @returns {*} TabBarItem配置 62 | */ 63 | const getTabBarItem = (pagePath: string) => tabBarStoreState.list.find(item => item.pagePath === pagePath) 64 | 65 | /** 66 | * FUN: 获取当前 TabBarItem 67 | * 68 | * @author dyb-dev 69 | * @date 25/09/2024/ 17:41:55 70 | * @returns {*} 当前TabBarItem配置 71 | */ 72 | const getCurrentTabBarItem = () => getTabBarItem(getCurrentPagePath()) 73 | 74 | /** 75 | * FUN: 获取 TabBar 索引 76 | * 77 | * @author dyb-dev 78 | * @date 09/10/2024/ 12:29:36 79 | * @param {string} pagePath 页面路径 80 | * @returns {*} TabBar 索引 81 | */ 82 | const getTabBarIndex = (pagePath: string) => tabBarStoreState.list.findIndex(item => item.pagePath === pagePath) 83 | 84 | /** 85 | * FUN: 获取当前 TabBar 索引 86 | * 87 | * @author dyb-dev 88 | * @date 25/09/2024/ 17:42:12 89 | * @returns {*} 当前 TabBar 索引 90 | */ 91 | const getCurrentTabBarIndex = () => getTabBarIndex(getCurrentPagePath()) 92 | 93 | /** 94 | * FUN: 更新当前 TabBar 索引 95 | * 96 | * @author dyb-dev 97 | * @date 09/10/2024/ 12:34:31 98 | */ 99 | const updateCurrentTabBarIndex = () => { 100 | 101 | /** 获取当前TabBar索引 */ 102 | const _currentTabBarIndex = getCurrentTabBarIndex() 103 | // 只有在当前TabBar索引大于等于0时才会设置 104 | if (_currentTabBarIndex >= 0) { 105 | 106 | tabBarStoreState.currentIndex = _currentTabBarIndex 107 | 108 | } 109 | 110 | } 111 | 112 | return { 113 | tabBarStoreState, 114 | getTabBarItem, 115 | getCurrentTabBarItem, 116 | getTabBarIndex, 117 | getCurrentTabBarIndex, 118 | updateCurrentTabBarIndex 119 | } 120 | 121 | }) 122 | 123 | /** 124 | * 使用状态管理 125 | * - 在没有Vue组件上下文的情况下使用 126 | * 127 | * @author dyb-dev 128 | * @date 15/09/2024/ 23:53:35 129 | * @returns store实例 130 | */ 131 | const useTabBarStoreWithOut = () => { 132 | 133 | return useTabBarStore(store) 134 | 135 | } 136 | 137 | export { useTabBarStore, useTabBarStoreWithOut } 138 | -------------------------------------------------------------------------------- /src/stores/userInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-06 14:42:41 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 23:06:25 6 | * @FilePath: /uniapp-mp-wx-template/src/stores/userInfo.ts 7 | * @Description: 用户信息状态管理 8 | */ 9 | 10 | import { defineStore } from "pinia" 11 | import { reactive } from "vue" 12 | 13 | import { getPhoneNumberApi, loginApi, uploadAvatarApi, uploadUserInfoApi } from "@/apis" 14 | 15 | import type { IGetPhoneNumberApiParams, IUploadAvatarApiParams, IUploadUserInfoApiParams } from "@/apis" 16 | 17 | import { store } from "." 18 | 19 | /** Store 状态类型 */ 20 | interface IUserInfoStoreState { 21 | /** 22 | * 是否登录成功 23 | */ 24 | isLogin: boolean 25 | /** 26 | * 用户在我们系统中的加密用户代码 27 | */ 28 | userId: string 29 | /** 30 | * 由微信生成的用户在开放平台的唯一标识符 31 | */ 32 | unionId: string 33 | /** 34 | * 由微信生成的用户在本小程序唯一标识 35 | */ 36 | openId: string 37 | /** 38 | * 用户的微信昵称 39 | */ 40 | nickName: string 41 | /** 42 | * 用户的微信头像 43 | */ 44 | avatarUrl: string 45 | /** 46 | * 用户的微信绑定手机号(不带区号的手机号) 47 | */ 48 | phoneNumber: string 49 | } 50 | 51 | /** Store 实例 */ 52 | const useUserInfoStore = defineStore("UserInfoStore", () => { 53 | 54 | /** Store 状态 */ 55 | const userInfoStoreState = reactive({ 56 | isLogin: false, 57 | 58 | userId: "", 59 | unionId: "", 60 | openId: "", 61 | 62 | nickName: "", 63 | avatarUrl: "", 64 | phoneNumber: "" 65 | }) 66 | 67 | /** 68 | * FUN: 微信登录 69 | * 70 | * @author dyb-dev 71 | * @date 09/10/2024/ 17:02:42 72 | * @returns {*} 微信登录结果 73 | */ 74 | const wxLogin = (): Promise => { 75 | 76 | return new Promise(resolve => { 77 | 78 | wx.login({ 79 | success: res => resolve(res), 80 | fail: err => resolve(err) 81 | }) 82 | 83 | }) 84 | 85 | } 86 | 87 | /** 88 | * FUN: 登录 89 | * 90 | * @author dyb-dev 91 | * @date 09/10/2024/ 17:02:57 92 | */ 93 | const login = async() => { 94 | 95 | try { 96 | 97 | uni.showLoading({ title: "正在登录...", mask: true }) 98 | 99 | const _wxLoginResult = await wxLogin() 100 | 101 | // @ts-ignore 102 | const _code = _wxLoginResult?.code || "" 103 | 104 | // 如果没有code 105 | if (!_code) { 106 | 107 | throw _wxLoginResult.errMsg 108 | 109 | } 110 | 111 | const _loginApiResult = await loginApi( 112 | { code: _code }, 113 | { 114 | testResult: { 115 | success: true, 116 | message: "登录成功", 117 | data: { 118 | userId: "userId", 119 | unionId: "unionId", 120 | openId: "openId", 121 | nickName: "xxx", 122 | avatarUrl: 123 | "https://img12.360buyimg.com/imagetools/jfs/t1/196430/38/8105/14329/60c806a4Ed506298a/e6de9fb7b8490f38.png", 124 | phoneNumber: "111111111" 125 | } 126 | } 127 | } 128 | ) 129 | 130 | uni.hideLoading() 131 | 132 | // 如果登录失败 133 | if (!_loginApiResult.success || !_loginApiResult.data) { 134 | 135 | throw _loginApiResult.message 136 | 137 | } 138 | 139 | const { 140 | data: { userId = "", unionId = "", openId = "", nickName = "", avatarUrl = "", phoneNumber = "" } 141 | } = _loginApiResult 142 | 143 | userInfoStoreState.userId = userId 144 | userInfoStoreState.unionId = unionId 145 | userInfoStoreState.openId = openId 146 | userInfoStoreState.nickName = nickName 147 | userInfoStoreState.avatarUrl = avatarUrl 148 | userInfoStoreState.phoneNumber = phoneNumber 149 | 150 | userInfoStoreState.isLogin = true 151 | 152 | } 153 | catch (error) { 154 | 155 | uni.hideLoading() 156 | console.error("login()", error) 157 | 158 | uni.showModal({ 159 | title: "提示", 160 | content: "登录失败,请稍后重试!", 161 | showCancel: false 162 | }) 163 | 164 | } 165 | 166 | } 167 | 168 | /** 169 | * FUN: 获取手机号 170 | * 171 | * @author dyb-dev 172 | * @date 09/10/2024/ 20:22:11 173 | * @param {IGetPhoneNumberApiParams} params 参数 174 | * @returns {*} 获取手机号结果 175 | */ 176 | const getPhoneNumber = async(params: IGetPhoneNumberApiParams) => { 177 | 178 | const _result = await getPhoneNumberApi(params, { 179 | testResult: { 180 | success: true, 181 | message: "获取手机号成功", 182 | data: { 183 | phoneNumber: "111111111" 184 | } 185 | } 186 | }) 187 | 188 | // 如果获取成功 189 | if (_result.success && _result.data?.phoneNumber) { 190 | 191 | userInfoStoreState.phoneNumber = _result.data.phoneNumber 192 | 193 | } 194 | 195 | return _result 196 | 197 | } 198 | 199 | /** 200 | * FUN: 上传头像 201 | * 202 | * @author dyb-dev 203 | * @date 09/10/2024/ 20:33:52 204 | * @param {IUploadAvatarApiParams} params 参数 205 | * @returns {*} 上传头像结果 206 | */ 207 | const uploadAvatar = async(params: IUploadAvatarApiParams) => { 208 | 209 | const _result = await uploadAvatarApi(params, { 210 | testResult: { 211 | success: true, 212 | message: "上传头像成功" 213 | } 214 | }) 215 | 216 | // 如果上传头像成功 217 | if (_result.success) { 218 | 219 | userInfoStoreState.avatarUrl = params.avatarUrl 220 | 221 | } 222 | 223 | return _result 224 | 225 | } 226 | 227 | /** 228 | * FUN: 上传用户信息 229 | * 230 | * @author dyb-dev 231 | * @date 09/10/2024/ 20:39:29 232 | * @param {IUploadUserInfoApiParams} params 参数 233 | * @returns {*} 上传用户信息结果 234 | */ 235 | const uploadUserInfo = async(params: IUploadUserInfoApiParams) => { 236 | 237 | const _result = await uploadUserInfoApi(params, { 238 | testResult: { 239 | success: true, 240 | message: "上传用户信息成功" 241 | } 242 | }) 243 | 244 | // 如果上传用户信息成功 245 | if (_result.success) { 246 | 247 | userInfoStoreState.avatarUrl = params.avatarUrl 248 | userInfoStoreState.nickName = params.nickName 249 | 250 | } 251 | 252 | return _result 253 | 254 | } 255 | 256 | return { userInfoStoreState, login, getPhoneNumber, uploadAvatar, uploadUserInfo } 257 | 258 | }) 259 | 260 | /** 261 | * FUN: 使用状态管理 262 | * - 在没有Vue组件上下文的情况下使用 263 | * 264 | * @author dyb-dev 265 | * @date 15/09/2024/ 23:53:35 266 | * @returns store实例 267 | */ 268 | const useUserInfoStoreWithOut = () => { 269 | 270 | return useUserInfoStore(store) 271 | 272 | } 273 | 274 | export { useUserInfoStore, useUserInfoStoreWithOut } 275 | -------------------------------------------------------------------------------- /src/styles/funs/index.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-09-14 17:54:48 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-24 13:31:24 6 | * @FilePath: /uniapp-mp-wx-template/src/styles/funs/index.scss 7 | * @Description: scss全局函数文件 8 | */ 9 | 10 | /** 必须写在顶部 */ 11 | @use "sass:list"; 12 | @use "sass:color"; 13 | 14 | /** 15 | FUN: 创建不同方向的文本阴影函数 16 | $color - 阴影颜色 17 | $long - 阴影长度 可传 正负 数字控制阴影方向 18 | $direction - 阴影方向 "horizontal" | "vertical" | "all" 19 | $fade-step - 颜色透明度递减步长 20 | */ 21 | @function create-directional-text-shadow-fun($color: #0ebeff, $long: 50, $direction: "all", $fade-step: 0.02) { 22 | $current-color: $color; 23 | $val: 0 0 $current-color; 24 | $shadow-values: (#{$val}); 25 | 26 | @for $i from 1 through $long { 27 | $current-color: fade-out($current-color, $fade-step); 28 | 29 | @if $direction == "horizontal" { 30 | $val: #{$i}px 0 #{$current-color}; 31 | } @else if $direction == "vertical" { 32 | $val: 0 #{$i}px #{$current-color}; 33 | } @else { 34 | $val: #{$i}px #{$i}px #{$current-color}; 35 | } 36 | 37 | /* stylelint-disable-next-line order/order */ 38 | $shadow-values: list.append($shadow-values, $val, comma); 39 | } 40 | 41 | @return $shadow-values; 42 | } 43 | 44 | /** 45 | FUN: 创建具有递增模糊半径的文本阴影函数 46 | $color - 阴影颜色 47 | $iterations - 迭代次数 48 | $increment - 模糊半径递增值 49 | */ 50 | @function create-text-shadow-fun($color: #0ebeff, $iterations: 5, $increment: 5px) { 51 | $shadow-values: (); 52 | $current-radius: "0px"; 53 | 54 | @for $i from 1 through $iterations { 55 | $current-radius: $increment * $i; 56 | $shadow-values: list.append($shadow-values, 0 0 $current-radius $color, comma); 57 | } 58 | 59 | @return $shadow-values; 60 | } 61 | -------------------------------------------------------------------------------- /src/styles/index.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-09-14 17:54:09 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-08 20:38:11 6 | * @FilePath: /uniapp-mp-wx-template/src/styles/index.scss 7 | * @Description: scss模块 8 | */ 9 | -------------------------------------------------------------------------------- /src/styles/mixins/index.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-09-14 17:54:48 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-03-25 01:24:26 6 | * @FilePath: /uniapp-mp-wx-template/src/styles/mixins/index.scss 7 | * @Description: scss全局混合文件 8 | */ 9 | 10 | /** 必须写在顶部 */ 11 | @use "sass:list"; 12 | 13 | /** 14 | 开启滚动 15 | $direction - 滚动方向 "x" | "y" 16 | */ 17 | @mixin open-scroll-mixin($direction: "y") { 18 | @if $direction == "x" { 19 | overflow-x: auto; 20 | -webkit-overflow-scrolling: touch; 21 | } @else if $direction == "y" { 22 | overflow-y: auto; 23 | -webkit-overflow-scrolling: touch; 24 | } 25 | } 26 | 27 | /** 隐藏滚动条 */ 28 | @mixin hide-scroll-bar-mixin { 29 | &::-webkit-scrollbar { 30 | display: none; 31 | width: 0; 32 | height: 0; 33 | color: transparent; 34 | } 35 | } 36 | 37 | /** 文本支持 \n 换行 */ 38 | @mixin rich-text-mixin() { 39 | white-space: pre-wrap; 40 | word-break: break-word; 41 | } 42 | 43 | /** 44 | * 文本溢出省略号效果 45 | * 根据行数设置单行或多行文本的溢出显示省略号效果。 46 | * 47 | * $rows - 行数 48 | * - 当 $rows > 1 时,显示多行文本,并在超过指定行数时截断显示省略号。 49 | * - 当 $rows <= 1 时,显示单行文本,并在超出宽度时显示省略号。 50 | */ 51 | @mixin text-ellipsis-mixin($rows: 1) { 52 | text-overflow: ellipsis; 53 | @if $rows > 1 { 54 | display: -webkit-box; 55 | -webkit-box-orient: vertical; 56 | -webkit-line-clamp: $rows; 57 | overflow: hidden; 58 | } @else { 59 | overflow: hidden; 60 | white-space: nowrap; 61 | } 62 | } 63 | 64 | /** 65 | 高亮闪烁动画文本效果 66 | $sub-class-name - 子类名 67 | $sub-class-count - 子类数量 68 | $font-highlight-color - 高亮颜色 69 | $shadow-color - 阴影颜色 70 | $animation-duration - 动画持续时间 71 | $delay-step - 延迟步长 72 | */ 73 | @mixin text-flicker-animation-mixin( 74 | $sub-class-name, 75 | $sub-class-count, 76 | $font-highlight-color: #fff, 77 | $shadow-color: #42fff6, 78 | $animation-duration: 1s, 79 | $delay-step: 0.2s 80 | ) { 81 | $shadow-intensity: (5px, 10px, 20px, 50px); 82 | 83 | @for $i from 1 through $sub-class-count { 84 | .#{$sub-class-name}-#{$i} { 85 | animation-delay: $delay-step * $i; 86 | } 87 | } 88 | 89 | @keyframes flicker { 90 | 0% { 91 | color: inherit; 92 | } 93 | 94 | 5%, 95 | 15%, 96 | 25%, 97 | 30%, 98 | 100% { 99 | color: $font-highlight-color; 100 | text-shadow: 101 | 0 0 list.nth($shadow-intensity, 1) $shadow-color, 102 | 0 0 list.nth($shadow-intensity, 2) $shadow-color, 103 | 0 0 list.nth($shadow-intensity, 3) $shadow-color, 104 | 0 0 list.nth($shadow-intensity, 4) $shadow-color; 105 | } 106 | 107 | 10%, 108 | 20% { 109 | color: inherit; 110 | text-shadow: none; 111 | } 112 | } 113 | 114 | .#{$sub-class-name} { 115 | animation: flicker $animation-duration linear forwards; 116 | } 117 | } 118 | 119 | /** 文本遮罩 */ 120 | @mixin text-clip-mixin { 121 | color: transparent; 122 | background-clip: text; 123 | } 124 | 125 | /** 126 | 渐变背景 + 色相旋转动画的文本效果 127 | $animation-duration - 动画持续时间 128 | */ 129 | @mixin text-gradient-effect-mixin($animation-duration: 5s) { 130 | background-image: linear-gradient(45deg, #009688, yellowgreen, pink, #03a9f4, #9c27b0, #8bc34a); 131 | animation: hue-rotate $animation-duration infinite; 132 | @include text-clip-mixin; 133 | 134 | @keyframes hue-rotate { 135 | 100% { 136 | filter: hue-rotate(360deg); 137 | } 138 | } 139 | } 140 | 141 | /** 142 | 动态线性渐变背景滑动文本效果 143 | $highlight-color - 高亮颜色 144 | $animation-duration - 动画持续时间 145 | $background-color - 背景颜色 (注意: 实际展现效果其实是文字颜色) 146 | */ 147 | @mixin text-shine-mixin($highlight-color: white, $animation-duration: 5s, $background-color: transparent) { 148 | color: transparent; 149 | background-color: $background-color; 150 | background-image: linear-gradient( 151 | 125deg, 152 | transparent 0%, 153 | transparent 10%, 154 | $highlight-color 20%, 155 | transparent 30%, 156 | transparent 100% 157 | ); 158 | background-repeat: no-repeat; 159 | background-position: 100% 0; 160 | 161 | /** 注意: 当background-size设置的百分比大于或者小于100%时,有多余的空间移动了,background-position才会有效 */ 162 | background-size: 150% 100%; 163 | background-clip: text; 164 | animation: shine $animation-duration infinite linear; 165 | 166 | @keyframes shine { 167 | 0% { 168 | background-position: 100% 0; 169 | } 170 | 171 | 100% { 172 | background-position: -200% 0; 173 | } 174 | } 175 | } 176 | 177 | /** 178 | 创建具有遮罩效果的背景 179 | @param {Length} $width - 容器的宽度 180 | @param {Length} $height - 容器的高度 181 | @param {String} $bg-image1 - 背景图片1的路径 182 | @param {String} $bg-image2 - 背景图片2的路径 183 | */ 184 | @mixin background-mask-mixin($width, $height, $bg-image1, $bg-image2) { 185 | position: relative; 186 | width: $width; 187 | height: $height; 188 | background: url(#{$bg-image1}) no-repeat center/cover; 189 | 190 | &::before { 191 | position: absolute; 192 | inset: 0; 193 | background: url(#{$bg-image2}) no-repeat center/cover; 194 | content: ""; 195 | mask: linear-gradient(45deg, #000 40%, transparent 60%); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/styles/variable/custom.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-09-14 17:54:18 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-09-19 16:34:08 6 | * @FilePath: /uni-app/src/styles/variable/custom.scss 7 | * @Description: scss全局变量文件 8 | */ 9 | -------------------------------------------------------------------------------- /src/styles/variable/uni.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里是uni-app内置的常用样式变量 3 | * 4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App 6 | * 7 | */ 8 | 9 | /** 10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 11 | * 12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 13 | */ 14 | 15 | /* 颜色变量 */ 16 | 17 | /* 行为相关颜色 */ 18 | $uni-color-primary: #007aff; 19 | $uni-color-success: #4cd964; 20 | $uni-color-warning: #f0ad4e; 21 | $uni-color-error: #dd524d; 22 | 23 | /* 文字基本颜色 */ 24 | $uni-text-color: #333; /* 基本色 */ 25 | $uni-text-color-inverse: #fff; /* 反色 */ 26 | $uni-text-color-grey: #999; /* 辅助灰色,如加载更多的提示信息 */ 27 | $uni-text-color-placeholder: #808080; 28 | $uni-text-color-disable: #c0c0c0; 29 | 30 | /* 背景颜色 */ 31 | $uni-bg-color: #fff; 32 | $uni-bg-color-grey: #f8f8f8; 33 | $uni-bg-color-hover: #f1f1f1; /* 点击状态颜色 */ 34 | $uni-bg-color-mask: rgba(0, 0, 0, 0.4); /* 遮罩颜色 */ 35 | 36 | /* 边框颜色 */ 37 | $uni-border-color: #c8c7cc; 38 | 39 | /* 尺寸变量 */ 40 | 41 | /* 文字尺寸 */ 42 | $uni-font-size-sm: 12px; 43 | $uni-font-size-base: 14px; 44 | $uni-font-size-lg: 16px; 45 | 46 | /* 图片尺寸 */ 47 | $uni-img-size-sm: 20px; 48 | $uni-img-size-base: 26px; 49 | $uni-img-size-lg: 40px; 50 | 51 | /* Border Radius */ 52 | $uni-border-radius-sm: 2px; 53 | $uni-border-radius-base: 3px; 54 | $uni-border-radius-lg: 6px; 55 | $uni-border-radius-circle: 50%; 56 | 57 | /* 水平间距 */ 58 | $uni-spacing-row-sm: 5px; 59 | $uni-spacing-row-base: 10px; 60 | $uni-spacing-row-lg: 15px; 61 | 62 | /* 垂直间距 */ 63 | $uni-spacing-col-sm: 4px; 64 | $uni-spacing-col-base: 8px; 65 | $uni-spacing-col-lg: 12px; 66 | 67 | /* 透明度 */ 68 | $uni-opacity-disabled: 0.3; /* 组件禁用态的透明度 */ 69 | 70 | /* 文章场景相关 */ 71 | $uni-color-title: #2c405a; /* 文章标题颜色 */ 72 | $uni-font-size-title: 20px; 73 | $uni-color-subtitle: #555; /* 二级标题颜色 */ 74 | $uni-font-size-subtitle: 18px; 75 | $uni-color-paragraph: #3f536e; /* 文章段落颜色 */ 76 | $uni-font-size-paragraph: 15px; 77 | -------------------------------------------------------------------------------- /src/subPackages/webview/pages/webview.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 147 | 148 | 151 | -------------------------------------------------------------------------------- /src/types/authorize.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-08 12:04:14 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-08 12:04:22 6 | * @FilePath: /uniapp-mp-wx-template/src/types/authorize.ts 7 | * @Description: 授权类型模块 8 | */ 9 | 10 | /** 授权错误码 */ 11 | enum EAuthErrorCode { 12 | /** 拒绝授权 */ 13 | DENIED = 1, 14 | /** 未登录 */ 15 | NOT_LOGGED_IN, 16 | /** 接口调用失败 */ 17 | API_FAILED 18 | } 19 | 20 | /** 授权错误回调参数 */ 21 | interface IAuthErrorOptions { 22 | /** 授权错误码 */ 23 | code: EAuthErrorCode 24 | /** 授权错误信息 */ 25 | message: string 26 | } 27 | 28 | export type { IAuthErrorOptions } 29 | 30 | export { EAuthErrorCode } 31 | -------------------------------------------------------------------------------- /src/types/dts/api.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-17 16:21:11 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 22:09:54 6 | * @FilePath: /uniapp-mp-wx-template/src/types/dts/api.d.ts 7 | * @Description: api 类型声明补充文件 8 | */ 9 | 10 | import type { UnResponse } from "@uni-helper/uni-network" 11 | 12 | declare module "@uni-helper/uni-network" { 13 | interface UnResponse { 14 | /** 请求是否成功 */ 15 | success: boolean 16 | /** 结果描述信息 */ 17 | message: string 18 | } 19 | } 20 | 21 | declare global { 22 | /** 测试请求配置 */ 23 | interface ITestRequestConfig = Record> { 24 | /** 是否启用测试模式 */ 25 | test: boolean 26 | /** 测试模式下请求延迟时间 单位: 毫秒 */ 27 | testDelay?: number 28 | /** 测试模式下请求结果 */ 29 | testResult: TModifyProperties, "success" | "message" | "data">, "data"> 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/types/dts/components.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by vite-plugin-uni-components 5 | // Read more: https://github.com/vuejs/core/pull/3399 6 | export {} 7 | 8 | declare module 'vue' { 9 | export interface GlobalComponents { 10 | AuthAvatarButton: typeof import('./../../components/auth/AuthAvatarButton.vue')['default'] 11 | AuthAvatarNicknameDialog: typeof import('./../../components/auth/AuthAvatarNicknameDialog.vue')['default'] 12 | AuthPhoneNumberButton: typeof import('./../../components/auth/AuthPhoneNumberButton.vue')['default'] 13 | AvatarNickname: typeof import('./../../components/AvatarNickname.vue')['default'] 14 | Cell: typeof import('./../../components/Cell.vue')['default'] 15 | DateTimePicker: typeof import('./../../components/picker/DateTimePicker.vue')['default'] 16 | Dialog: typeof import('./../../components/dialog/Dialog.vue')['default'] 17 | FooterActionsBar: typeof import('./../../components/FooterActionsBar.vue')['default'] 18 | FormControl: typeof import('./../../components/FormControl.vue')['default'] 19 | FormDialog: typeof import('./../../components/dialog/FormDialog.vue')['default'] 20 | LabelValueBar: typeof import('./../../components/LabelValueBar.vue')['default'] 21 | Layout: typeof import('./../../components/layout/Layout.vue')['default'] 22 | List: typeof import('./../../components/List.vue')['default'] 23 | NavBar: typeof import('./../../components/layout/NavBar.vue')['default'] 24 | NickNameInput: typeof import('./../../components/auth/NickNameInput.vue')['default'] 25 | NutAnimate: typeof import('nutui-uniapp/components/animate/animate.vue')['default'] 26 | NutAvatar: typeof import('nutui-uniapp/components/avatar/avatar.vue')['default'] 27 | NutBadge: typeof import('nutui-uniapp/components/badge/badge.vue')['default'] 28 | NutButton: typeof import('nutui-uniapp/components/button/button.vue')['default'] 29 | NutConfigProvider: typeof import('nutui-uniapp/components/configprovider/configprovider.vue')['default'] 30 | NutIcon: typeof import('nutui-uniapp/components/icon/icon.vue')['default'] 31 | NutInput: typeof import('nutui-uniapp/components/input/input.vue')['default'] 32 | NutPopup: typeof import('nutui-uniapp/components/popup/popup.vue')['default'] 33 | NutSwiper: typeof import('nutui-uniapp/components/swiper/swiper.vue')['default'] 34 | NutSwiperItem: typeof import('nutui-uniapp/components/swiperitem/swiperitem.vue')['default'] 35 | NutTabPane: typeof import('nutui-uniapp/components/tabpane/tabpane.vue')['default'] 36 | NutTabs: typeof import('nutui-uniapp/components/tabs/tabs.vue')['default'] 37 | NutTextarea: typeof import('nutui-uniapp/components/textarea/textarea.vue')['default'] 38 | NutUploader: typeof import('nutui-uniapp/components/uploader/uploader.vue')['default'] 39 | Picker: typeof import('./../../components/picker/Picker.vue')['default'] 40 | Popup: typeof import('./../../components/popup/Popup.vue')['default'] 41 | SwiperPro: typeof import('./../../components/SwiperPro.vue')['default'] 42 | TabBar: typeof import('./../../components/layout/TabBar.vue')['default'] 43 | TabsPaneList: typeof import('./../../components/TabsPaneList.vue')['default'] 44 | TitleBar: typeof import('./../../components/TitleBar.vue')['default'] 45 | UCharts: typeof import('./../../components/u-charts/u-charts.vue')['default'] 46 | Uploader: typeof import('./../../components/Uploader.vue')['default'] 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/types/dts/index.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 14:03:54 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-05 20:37:42 6 | * @FilePath: /uniapp-mp-wx-template/src/types/dts/index.d.ts 7 | * @Description: 全局类型声明补充文件 8 | */ 9 | -------------------------------------------------------------------------------- /src/types/dts/pages.d.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable */ 2 | /* prettier-ignore */ 3 | // @ts-nocheck 4 | // Generated by vite-plugin-uni-pages 5 | 6 | interface NavigateToOptions { 7 | url: "/pages/home" | 8 | "/pages/launch" | 9 | "/pages/list" | 10 | "/pages/login" | 11 | "/pages/my" | 12 | "/pages/test" | 13 | "/subPackages/webview/pages/webview"; 14 | } 15 | interface RedirectToOptions extends NavigateToOptions {} 16 | 17 | interface SwitchTabOptions { 18 | url: "/pages/home" | "/pages/list" | "/pages/my" 19 | } 20 | 21 | type ReLaunchOptions = NavigateToOptions | SwitchTabOptions; 22 | 23 | declare interface Uni { 24 | navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void; 25 | redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void; 26 | switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void; 27 | reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void; 28 | } 29 | -------------------------------------------------------------------------------- /src/types/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-08 11:50:30 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:02:57 6 | * @FilePath: /uniapp-mp-wx-template/src/types/index.ts 7 | * @Description: 类型模块 8 | */ 9 | 10 | /** 导出授权类型模块 */ 11 | export * from "./authorize" 12 | /** 导出用户信息类型模块 */ 13 | export * from "./userInfo" 14 | -------------------------------------------------------------------------------- /src/types/userInfo.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:02:41 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:02:46 6 | * @FilePath: /uniapp-mp-wx-template/src/types/userInfo.ts 7 | * @Description: 用户信息类型模块 8 | */ 9 | 10 | /** 性别类型枚举 */ 11 | enum EGenderType { 12 | /** 男 */ 13 | Man = 1, 14 | /** 女 */ 15 | Woman = 2 16 | } 17 | 18 | /** 证件类型枚举 */ 19 | enum ECertificatesType { 20 | /** 身份证 */ 21 | IdCard = 1, 22 | /** 军人证 */ 23 | MilitaryCard, 24 | /** 护照 */ 25 | Passport, 26 | /** 出生证 */ 27 | BirthCertificate, 28 | /** 港澳台通行证 */ 29 | HkMoTwPass, 30 | /** 士兵证 */ 31 | SoldierCard, 32 | /** 警官证 */ 33 | PoliceCard, 34 | /** 港澳台居民居住证 */ 35 | HkMoTwResidentPermit, 36 | /** 外国人永久居留身份证 */ 37 | ForeignerPermanentResidentIdCard, 38 | /** 居民户口薄 */ 39 | ResidentAccountBook 40 | } 41 | 42 | export { EGenderType, ECertificatesType } 43 | -------------------------------------------------------------------------------- /src/utils/dateTime/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:11:50 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:12:00 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/dateTime/index.ts 7 | * @Description: 日期时间相关工具函数 8 | */ 9 | 10 | /** 11 | * FUN: 延迟函数 12 | * 13 | * @author dyb-dev 14 | * @date 19/02/2025/ 15:55:58 15 | * @param {number} ms - 延迟时间(毫秒) 16 | * @returns {*} {Promise} - 返回一个 Promise 对象 17 | */ 18 | const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms)) 19 | 20 | export { delay } 21 | -------------------------------------------------------------------------------- /src/utils/env/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-09 22:15:17 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:23:01 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/env/index.ts 7 | * @Description: 环境相关工具函数 8 | */ 9 | 10 | import { getEnvVersion } from "@/utils" 11 | 12 | /** CONST: 设备信息 */ 13 | let systemInfo: UniApp.GetSystemInfoResult 14 | 15 | /** 16 | * FUN: 获取系统信息 17 | * 18 | * @author dyb-dev 19 | * @date 06/10/2024/ 23:13:50 20 | * @param {boolean} [isForceRefresh=false] 是否强制刷新 21 | * @returns {*} {UniApp.GetSystemInfoResult} 系统信息 22 | */ 23 | const getSystemInfo = (isForceRefresh = false) => { 24 | 25 | // 如果已经获取过系统信息,且不是强制刷新,则直接返回 26 | if (systemInfo && !isForceRefresh) { 27 | 28 | return systemInfo 29 | 30 | } 31 | 32 | try { 33 | 34 | systemInfo = uni.getSystemInfoSync() 35 | 36 | } 37 | catch (error) { 38 | 39 | console.error("getSystemInfo()", error) 40 | 41 | } 42 | 43 | return systemInfo 44 | 45 | } 46 | 47 | /** 48 | * FUN: 是否为开发者工具 49 | * 50 | * @author dyb-dev 51 | * @date 06/10/2024/ 23:15:36 52 | * @returns {*} {boolean} 是否为开发者工具 53 | */ 54 | const isDevTool = (): boolean => getSystemInfo()?.platform === "devtools" 55 | 56 | /** 57 | * FUN: 是否启用调试 58 | * 59 | * @author dyb-dev 60 | * @date 09/10/2024/ 22:02:43 61 | * @returns {*} {boolean} 是否启用调试 62 | */ 63 | const isEnableDebug = (): boolean => isDevTool() ? true : getSystemInfo()?.enableDebug ?? false 64 | 65 | /** 66 | * FUN: 是否为开发环境 67 | * 68 | * @author dyb-dev 69 | * @date 09/10/2024/ 17:34:47 70 | * @returns {*} {boolean} 是否为开发环境 71 | */ 72 | const isDevEnv = (): boolean => __PROJECT_INFO__.env.VITE_USER_NODE_ENV === "development" 73 | 74 | /** 75 | * FUN: 获取当前服务器网址 76 | * - 小程序开发版和体验版 默认: `VITE_DEV_SERVER_URL` 77 | * - 小程序正式版 默认: `VITE_PROD_SERVER_URL` 78 | * 79 | * @author dyb-dev 80 | * @date 01/11/2024/ 20:34:09 81 | * @returns {*} {string} 当前服务器网址 82 | */ 83 | const getCurrentServerUrl = (): string => { 84 | 85 | const { VITE_DEV_SERVER_URL, VITE_PROD_SERVER_URL } = __PROJECT_INFO__.env 86 | return getEnvVersion() === "release" ? VITE_PROD_SERVER_URL : VITE_DEV_SERVER_URL 87 | 88 | } 89 | 90 | export { getSystemInfo, isDevTool, isEnableDebug, isDevEnv, getCurrentServerUrl } 91 | -------------------------------------------------------------------------------- /src/utils/form/identityCard.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:13:29 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:13:44 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/form/identityCard.ts 7 | * @Description: 身份证相关工具函数 8 | */ 9 | 10 | /** 11 | * FUN: 是否为有效的省、直辖市代码 12 | * 13 | * @author dyb-dev 14 | * @date 15/10/2024/ 16:34:48 15 | * @param {string} identityCardNumber - 身份证号码 16 | * @returns {*} {boolean} 是否为有效的省、直辖市代码 17 | */ 18 | const _isProvinceCode = (identityCardNumber: string): boolean => { 19 | 20 | /** CONST: 省、直辖市代码表映射 */ 21 | const provinceCodeMap: Record = { 22 | 11: "北京", 23 | 12: "天津", 24 | 13: "河北", 25 | 14: "山西", 26 | 15: "内蒙古", 27 | 21: "辽宁", 28 | 22: "吉林", 29 | 23: "黑龙江", 30 | 31: "上海", 31 | 32: "江苏", 32 | 33: "浙江", 33 | 34: "安徽", 34 | 35: "福建", 35 | 36: "江西", 36 | 37: "山东", 37 | 41: "河南", 38 | 42: "湖北", 39 | 43: "湖南", 40 | 44: "广东", 41 | 45: "广西", 42 | 46: "海南", 43 | 50: "重庆", 44 | 51: "四川", 45 | 52: "贵州", 46 | 53: "云南", 47 | 54: "西藏", 48 | 61: "陕西", 49 | 62: "甘肃", 50 | 63: "青海", 51 | 64: "宁夏", 52 | 65: "新疆", 53 | 71: "台湾", 54 | 81: "香港", 55 | 82: "澳门", 56 | 91: "国外" 57 | } 58 | 59 | const _addressCode = identityCardNumber.substring(0, 6) 60 | const _check = /^[1-9]\d{5}$/.test(_addressCode) 61 | if (!_check) { 62 | 63 | return false 64 | 65 | } 66 | 67 | return !!provinceCodeMap[parseInt(_addressCode.substring(0, 2))] 68 | 69 | } 70 | 71 | /** 72 | * FUN: 是否为有效的生日 73 | * 74 | * @author dyb-dev 75 | * @date 15/10/2024/ 16:41:44 76 | * @param {string} identityCardNumber - 身份证号码 77 | * @returns {*} {boolean} 是否为有效的生日 78 | */ 79 | const _isBirthday = (identityCardNumber: string): boolean => { 80 | 81 | // 生日代码 82 | const _birDay = identityCardNumber.substring(6, 14) 83 | const _check = /^[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))$/.test(_birDay) 84 | if (!_check) { 85 | 86 | return false 87 | 88 | } 89 | const _year = parseInt(_birDay.substring(0, 4)) 90 | const _month = parseInt(_birDay.substring(4, 6)) 91 | const _day = parseInt(_birDay.substring(6)) 92 | const _date = new Date(_year, _month - 1, _day) 93 | 94 | // 生日大于当前日期时 95 | if (_date > new Date()) { 96 | 97 | return false 98 | 99 | } 100 | return _date.getFullYear() === _year && _date.getMonth() === _month - 1 && _date.getDate() === _day 101 | 102 | } 103 | 104 | /** 105 | * FUN: 是否为有效的校验位 106 | * 107 | * @author dyb-dev 108 | * @date 15/10/2024/ 16:57:06 109 | * @param {string} identityCardNumber - 身份证号码 110 | * @returns {*} {boolean} 是否为有效的校验位 111 | */ 112 | const _isParityBit = (identityCardNumber: string): boolean => { 113 | 114 | /** CONST: 每位加权因子 */ 115 | const powers = ["7", "9", "10", "5", "8", "4", "2", "1", "6", "3", "7", "9", "10", "5", "8", "4", "2"] 116 | /** CONST: 第18位校检码 */ 117 | const parityBit = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"] 118 | 119 | // 18位身份证需要验证最后一位校验位 120 | const _parityBit = identityCardNumber.charAt(17).toUpperCase() 121 | 122 | // 加权因子 123 | let _power = 0 124 | for (let i = 0; i < 17; i++) { 125 | 126 | _power += parseInt(identityCardNumber.charAt(i)) * parseInt(powers[i]) 127 | 128 | } 129 | 130 | return _parityBit === parityBit[_power % 11] 131 | 132 | } 133 | 134 | /** 135 | * FUN: 是否为有效的身份证号码 136 | * 137 | * @author dyb-dev 138 | * @date 15/10/2024/ 16:59:56 139 | * @param {string} identityCardNumber - 身份证号码 140 | * @returns {*} {boolean} 是否为有效的身份证号码 141 | */ 142 | const isIdentityCard = (identityCardNumber: string): boolean => { 143 | 144 | const _check = /^[1-9]\d{5}[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}(\d|x|X)$/.test( 145 | identityCardNumber 146 | ) 147 | return _check && _isProvinceCode(identityCardNumber) && _isBirthday(identityCardNumber) && _isParityBit(identityCardNumber) 148 | 149 | } 150 | 151 | export { isIdentityCard } 152 | -------------------------------------------------------------------------------- /src/utils/form/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:13:29 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:13:37 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/form/index.ts 7 | * @Description: 表单相关工具函数 8 | */ 9 | 10 | /** 导出身份证相关工具函数 */ 11 | export * from "./identityCard" 12 | 13 | /** 14 | * FUN: 是否为手机号 15 | * 16 | * @author dyb-dev 17 | * @date 14/10/2024/ 15:34:16 18 | * @param {string} phone - 手机号 19 | * @returns {*} {boolean} - 是否为手机号 20 | */ 21 | const isPhoneNumber = (phone: string): boolean => /^1[3456789]\d{9}$/.test(phone) 22 | 23 | /** 24 | * FUN: 是否为邮箱 25 | * 26 | * @author dyb-dev 27 | * @date 14/10/2024/ 15:35:02 28 | * @param {string} email - 邮箱 29 | * @returns {*} {boolean} - 是否为邮箱 30 | */ 31 | const isEmail = (email: string): boolean => /^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\.[a-zA-Z0-9_-]+)+$/.test(email) 32 | 33 | export { isPhoneNumber, isEmail } 34 | -------------------------------------------------------------------------------- /src/utils/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 20:37:15 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:25:53 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/index.ts 7 | * @Description: 工具函数模块 8 | */ 9 | 10 | /** 导出数据处理相关工具函数 */ 11 | export * from "./data" 12 | /** 导出日期时间相关工具函数 */ 13 | export * from "./dateTime" 14 | /** 导出环境相关工具函数 */ 15 | export * from "./env" 16 | /** 导出表单相关工具函数 */ 17 | export * from "./form" 18 | /** 导出媒体相关工具函数 */ 19 | export * from "./media" 20 | /** 导出位置相关工具函数 */ 21 | export * from "./location" 22 | /** 导出小程序相关工具函数 */ 23 | export * from "./miniProgram" 24 | /** 导出页面相关工具函数 */ 25 | export * from "./pages" 26 | /** 导出工具相关工具函数 */ 27 | export * from "./tool" 28 | /** 导出url相关工具函数 */ 29 | export * from "./url" 30 | /** 导出用户信息相关工具函数 */ 31 | export * from "./userInfo" 32 | -------------------------------------------------------------------------------- /src/utils/location/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:14:28 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:14:35 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/location/index.ts 7 | * @Description: 位置相关工具函数 8 | */ 9 | 10 | /** 位置 */ 11 | interface ILocation { 12 | /** 纬度 */ 13 | latitude: number 14 | /** 经度 */ 15 | longitude: number 16 | } 17 | 18 | /** 19 | * FUN: 计算两个位置之间的距离 20 | * 21 | * @author dyb-dev 22 | * @date 15/10/2024/ 14:59:41 23 | * @param {ILocation} startLocation 起始位置 24 | * @param {ILocation} endLocation 结束位置 25 | * @returns {*} {number} 两个坐标之间的距离 (单位: 米) 26 | */ 27 | const calculateDistanceBetweenLocations = (startLocation: ILocation, endLocation: ILocation): number => { 28 | 29 | // 解构起始位置的纬度和经度 30 | const { latitude: startLatitude, longitude: startLongitude } = startLocation 31 | // 解构结束位置的纬度和经度 32 | const { latitude: endLatitude, longitude: endLongitude } = endLocation 33 | 34 | /** 地球半径, 单位: 米 */ 35 | const _earthRadius = 6378136.49 36 | 37 | // 角度转换为弧度的函数 38 | const _rad = (d: number) => d * Math.PI / 180.0 39 | 40 | // 将起始和结束的纬度转换为弧度 41 | const _startRadLat = _rad(startLatitude) 42 | const _endRadLat = _rad(endLatitude) 43 | 44 | // 计算纬度之差的弧度 45 | const _latitudeDifference = _startRadLat - _endRadLat 46 | // 计算经度之差的弧度 47 | const _longitudeDifference = _rad(startLongitude) - _rad(endLongitude) 48 | 49 | // 计算 sin(纬度差/2) 的平方 50 | const _sinLatDiffSquared = Math.pow(Math.sin(_latitudeDifference / 2), 2) 51 | 52 | // 计算 cos(起始纬度) * cos(结束纬度) 53 | const _cosLatProduct = Math.cos(_startRadLat) * Math.cos(_endRadLat) 54 | 55 | // 计算 sin(经度差/2) 的平方 56 | const _sinLongDiffSquared = Math.pow(Math.sin(_longitudeDifference / 2), 2) 57 | 58 | // 计算 Haversine 公式的核心值,即 (sin(纬度差/2))^2 + cos(起始纬度) * cos(结束纬度) * (sin(经度差/2))^2 59 | const _haversineCore = _sinLatDiffSquared + _cosLatProduct * _sinLongDiffSquared 60 | 61 | // 计算两个点之间的弧度距离 62 | const _angularDistance = 2 * Math.asin(Math.sqrt(_haversineCore)) 63 | 64 | // 将弧度距离转换为米 65 | let _distance = _angularDistance * _earthRadius 66 | 67 | // 对结果进行四舍五入到最接近的整数 68 | _distance = Math.round(_distance * 10000) / 10000 69 | 70 | // 返回以米为单位的距离,结果为整数 71 | return parseFloat(_distance.toFixed(0)) 72 | 73 | } 74 | 75 | export type { ILocation } 76 | 77 | export { calculateDistanceBetweenLocations } 78 | -------------------------------------------------------------------------------- /src/utils/media/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-06 15:04:04 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:25:40 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/media/index.ts 7 | * @Description: 媒体相关工具函数 8 | */ 9 | 10 | /** CONST: 常见的图片扩展名 */ 11 | const IMAGE_EXTENSIONS = ["jpg", "jpeg", "png", "gif", "bmp", "webp", "svg"] 12 | 13 | /** 14 | * FUN: 是否为有效的图片路径 15 | * - 支持网络在线地址、绝对路径、相对路径或根目录文件,并忽略查询参数 16 | * 17 | * @author dyb-dev 18 | * @date 23/09/2024/ 11:31:25 19 | * @param {string} str 图片路径 20 | * @returns {*} {boolean} 是否为图片路径 21 | */ 22 | const isImagePath = (str: string): boolean => { 23 | 24 | // 动态生成正则表达式来匹配图片扩展名 25 | const _pattern = new RegExp(`^(https?:\\/\\/|\\/|[^\\s]*\\/)?[^\\s]+\\.(${IMAGE_EXTENSIONS.join("|")})(\\?.*)?$`, "i") 26 | 27 | // 首先验证是否符合路径规则 28 | if (!_pattern.test(str)) { 29 | 30 | return false 31 | 32 | } 33 | 34 | // 提取文件扩展名并验证是否为图片扩展名 35 | const _extension = str.split(".").pop()?.split("?")[0].toLowerCase() || "" 36 | 37 | // 检查提取到的扩展名是否在 IMAGE_EXTENSIONS 中 38 | return IMAGE_EXTENSIONS.includes(_extension) 39 | 40 | } 41 | 42 | export { isImagePath } 43 | -------------------------------------------------------------------------------- /src/utils/miniProgram/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-09 22:19:06 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-12-02 20:20:29 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/miniProgram/index.ts 7 | * @Description: 小程序相关工具函数 8 | */ 9 | 10 | import pagesJson from "@/pages.json" 11 | 12 | /** CONST: 设备信息 */ 13 | let accountInfo: UniApp.AccountInfo 14 | 15 | /** 16 | * FUN: 获取账号信息 17 | * 18 | * @author dyb-dev 19 | * @date 09/10/2024/ 22:23:47 20 | * @param {boolean} [isForceRefresh=false] 是否强制刷新 21 | * @returns {*} 账号信息 22 | */ 23 | const getAccountInfo = (isForceRefresh: boolean = false): UniApp.AccountInfo => { 24 | 25 | // 如果已经获取过系统信息,且不是强制刷新,则直接返回 26 | if (accountInfo && !isForceRefresh) { 27 | 28 | return accountInfo 29 | 30 | } 31 | 32 | try { 33 | 34 | accountInfo = uni.getAccountInfoSync() 35 | 36 | } 37 | catch (error) { 38 | 39 | console.error("getAccountInfo()", error) 40 | 41 | } 42 | 43 | return accountInfo 44 | 45 | } 46 | 47 | /** 48 | * FUN: 获取小程序环境版本 49 | * - 开发版: develop 50 | * - 体验版: trial 51 | * - 正式版: release 52 | * 53 | * @author dyb-dev 54 | * @date 01/11/2024/ 20:34:23 55 | * @returns {*} {string} 小程序环境版本 56 | */ 57 | const getEnvVersion = () => getAccountInfo()?.miniProgram.envVersion 58 | 59 | /** 60 | * FUN: 获取小程序线上版本 61 | * - 线上小程序版本号(仅在正式版小程序上支持) 62 | * 63 | * @author dyb-dev 64 | * @date 09/10/2024/ 22:23:41 65 | * @returns {*} {string} 小程序线上版本 66 | */ 67 | const getOnlineVersion = (): string => getAccountInfo()?.miniProgram.version 68 | 69 | /** LET: 胶囊按钮区域 */ 70 | let capsuleBoundingClientRect: UniApp.GetMenuButtonBoundingClientRectRes | null = null 71 | 72 | /** 73 | * FUN: 获取胶囊按钮区域 74 | * 75 | * @author dyb-dev 76 | * @date 02/12/2024/ 20:21:12 77 | * @param {boolean} [isForceRefresh=false] 是否强制刷新 78 | * @returns {*} {(UniApp.GetMenuButtonBoundingClientRectRes | null)} 胶囊按钮区域 79 | */ 80 | const getCapsuleBoundingClientRect = (isForceRefresh = false): UniApp.GetMenuButtonBoundingClientRectRes | null => { 81 | 82 | // @ts-ignore 是否开启 自定义导航栏 83 | const _isCustomNavigationBar = pagesJson?.globalStyle?.navigationStyle === "custom" 84 | 85 | // 如果没有开启自定义导航栏, 进行警告提示 86 | !_isCustomNavigationBar && console.warn("getCapsuleBoundingClientRect() 未开启自定义导航栏, 不需要获取菜单按钮区域") 87 | 88 | // 如果已经获取过系统信息,且不是强制刷新,则直接返回 89 | if (capsuleBoundingClientRect && !isForceRefresh) { 90 | 91 | return capsuleBoundingClientRect 92 | 93 | } 94 | 95 | try { 96 | 97 | capsuleBoundingClientRect = uni.getMenuButtonBoundingClientRect() 98 | 99 | } 100 | catch (error) { 101 | 102 | console.error("getCapsuleBoundingClientRect()", error) 103 | 104 | } 105 | 106 | return capsuleBoundingClientRect 107 | 108 | } 109 | 110 | export { getAccountInfo, getEnvVersion, getOnlineVersion, getCapsuleBoundingClientRect } 111 | -------------------------------------------------------------------------------- /src/utils/pages/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 20:49:07 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-10 11:29:40 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/pages/index.ts 7 | * @Description: 页面相关工具函数 8 | */ 9 | 10 | /** 导出页面跳转相关工具函数 */ 11 | export * from "./navigate" 12 | 13 | import queryString from "query-string" 14 | 15 | import pagesJson from "@/pages.json" 16 | 17 | import { trimUrlSlashes } from "../url" 18 | 19 | import type { PageMetaDatum } from "@uni-helper/vite-plugin-uni-pages" 20 | 21 | /** 22 | * FUN: 获取当前页面实例 23 | * - 确保在页面 onLoad 之后调用 24 | * - 不要在 App.onLaunch 或者 App.onShow 的时候调用 getCurrentPages(),此时 page 还没有生成 25 | * 26 | * @author dyb-dev 27 | * @date 05/10/2024/ 20:54:47 28 | * @export 29 | * @returns {*} 当前页面实例 30 | */ 31 | const getCurrentPageInstance = () => { 32 | 33 | const _pages = getCurrentPages() as WechatMiniprogram.Page.Instance< 34 | WechatMiniprogram.IAnyObject, 35 | WechatMiniprogram.IAnyObject 36 | >[] 37 | return _pages[_pages.length - 1] 38 | 39 | } 40 | 41 | /** 42 | * FUN: 获取当前页面的路径 43 | * - 确保在页面 onLoad 之后调用 44 | * - 不要在 App.onLaunch 或者 App.onShow 的时候调用 getCurrentPages(),此时 page 还没有生成 45 | * - H5 平台不支持这种方式获取 46 | * 47 | * @author dyb-dev 48 | * @date 05/10/2024/ 21:09:23 49 | * @returns {*} 当前页面路径 50 | */ 51 | const getCurrentPagePath = () => getCurrentPageInstance().route 52 | 53 | /** 54 | * FUN: 获取当前页面的完整路径 55 | * - 确保在页面 onLoad 之后调用 56 | * - 不要在 App.onLaunch 或者 App.onShow 的时候调用 getCurrentPages(),此时 page 还没有生成 57 | * - H5 平台不支持这种方式获取 58 | * 59 | * @author dyb-dev 60 | * @date 05/10/2024/ 21:11:26 61 | * @returns {*} 当前页面完整路径 62 | */ 63 | const getCurrentPageFullPath = () => `${getCurrentPagePath()}?${getCurrentPageSearch()}` 64 | 65 | /** 66 | * FUN: 获取当前页面的 query 参数 67 | * - 确保在页面 onLoad 之后调用 68 | * - 不要在 App.onLaunch 或者 App.onShow 的时候调用 getCurrentPages(),此时 page 还没有生成 69 | * - H5 平台不支持这种方式获取 70 | * 71 | * @author dyb-dev 72 | * @date 05/10/2024/ 21:03:56 73 | * @returns {*} 当前页面的 query 参数 74 | */ 75 | const getCurrentPageQuery = () => getCurrentPageInstance()?.options || {} 76 | 77 | /** 78 | * FUN: 获取当前页面的 search 参数 79 | * - 确保在页面 onLoad 之后调用 80 | * - 不要在 App.onLaunch 或者 App.onShow 的时候调用 getCurrentPages(),此时 page 还没有生成 81 | * - H5 平台不支持这种方式获取 82 | * 83 | * @author dyb-dev 84 | * @date 05/10/2024/ 21:07:28 85 | * @returns {*} 当前页面 search 参数 86 | */ 87 | const getCurrentPageSearch = () => queryString.stringify(getCurrentPageQuery(), { encode: false }) 88 | 89 | const { VITE_SUB_PACKAGE_DIR, VITE_PAGE_DIR } = __PROJECT_INFO__.env 90 | 91 | /** CONST: 分包页面路径正则表达式 */ 92 | const SUB_PACKAGE_PAGE_PATH_REGEX = new RegExp(`^(${VITE_SUB_PACKAGE_DIR}(?:\\/[^\\/]+)*)\\/((${VITE_PAGE_DIR}\\/.*))`) 93 | 94 | /** 95 | * FUN: 获取页面配置 96 | * - `pagePath`示例: "pages/home" 或 "subPackages/webview/pages/webview" 97 | * 98 | * @author dyb-dev 99 | * @date 06/10/2024/ 15:00:21 100 | * @param {string} pagePath 页面路径 101 | * @returns {*} 页面配置 102 | */ 103 | const getPageConfig = (pagePath: string): PageMetaDatum | void => { 104 | 105 | // 如果路径开头携带斜杠,则去除开头的斜杠 106 | pagePath = trimUrlSlashes(pagePath, { trimStart: true }) 107 | 108 | // 匹配分包页面并提取结果 109 | const _result = pagePath.match(SUB_PACKAGE_PAGE_PATH_REGEX) 110 | 111 | // 如果匹配分包页面结果为空,则尝试匹配主包页面 112 | if (!_result) { 113 | 114 | return pagesJson?.pages.find((item: PageMetaDatum) => item.path === pagePath) 115 | 116 | } 117 | 118 | /** 分包根路径 */ 119 | const _subPackagesRootPath = _result[1] || "" 120 | /** 分包页面路径 */ 121 | const _subPackagesPagePath = _result[2] || "" 122 | 123 | /** 分包配置 */ 124 | const _subPackageConfig = pagesJson?.subPackages.find(item => item.root === _subPackagesRootPath) 125 | 126 | return _subPackageConfig?.pages.find((item: PageMetaDatum) => item.path === _subPackagesPagePath) 127 | 128 | } 129 | 130 | /** 131 | * FUN: 获取当前页面配置 132 | * 133 | * @author dyb-dev 134 | * @date 06/10/2024/ 15:01:07 135 | * @returns {*} 当前页面配置 136 | */ 137 | const getCurrentPageConfig = (): PageMetaDatum | void => getPageConfig(getCurrentPagePath()) 138 | 139 | export { 140 | getCurrentPageInstance, 141 | getCurrentPagePath, 142 | getCurrentPageFullPath, 143 | getCurrentPageQuery, 144 | getCurrentPageSearch, 145 | getPageConfig, 146 | getCurrentPageConfig 147 | } 148 | -------------------------------------------------------------------------------- /src/utils/tool/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-11-16 02:16:21 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-11-16 02:16:23 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/tool/index.ts 7 | * @Description: 基础相关工具函数 8 | */ 9 | 10 | /** 任意函数类型声明 */ 11 | type TFuncType = (...args: any[]) => void 12 | 13 | /** 14 | * 创建防抖函数 15 | * 16 | * @author dyb-dev 17 | * @date 18/08/2024/ 17:04:51 18 | * @param {TFuncType} fn - 需要防抖的函数 19 | * @param {number} wait - 防抖时间 20 | * @param {boolean} [immediate=false] - 是否立即执行 21 | * @returns {*} {TFuncType} - 防抖函数 22 | */ 23 | const debounce = (fn: TFuncType, wait: number, immediate = false): TFuncType => { 24 | 25 | let _timeout: ReturnType | undefined 26 | 27 | return (...args: any[]) => { 28 | 29 | const _this = this 30 | 31 | // 延迟执行函数 32 | const later = function() { 33 | 34 | _timeout = undefined 35 | if (!immediate) { 36 | 37 | fn.apply(_this, args) 38 | 39 | } 40 | 41 | } 42 | 43 | if (immediate) { 44 | 45 | fn.apply(_this, args) 46 | 47 | } 48 | else { 49 | 50 | clearTimeout(_timeout) 51 | _timeout = setTimeout(later, wait) 52 | 53 | } 54 | 55 | } 56 | 57 | } 58 | 59 | export { debounce } 60 | -------------------------------------------------------------------------------- /src/utils/url/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 21:14:00 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 15:51:02 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/url/index.ts 7 | * @Description: url相关工具函数 8 | */ 9 | 10 | import queryString from "query-string" 11 | 12 | import { getCurrentServerUrl } from "@/utils" 13 | 14 | /** 15 | * FUN: 获取基础 URL(去除查询参数) 16 | * 17 | * @author dyb-dev 18 | * @date 10/10/2024/ 11:36:50 19 | * @param {string} url - 完整的 URL 字符串 20 | * @returns {*} {string} - 去除查询参数后的基础 URL 21 | */ 22 | const getBaseUrl = (url: string): string => queryString.parseUrl(url).url 23 | 24 | /** 25 | * FUN: 获取 url 的 查询参数对象 26 | * 27 | * @author dyb-dev 28 | * @date 14/07/2023/ 15:16:25 29 | * @param {string} url 需要解析的 URL 30 | * @returns {queryString.ParsedQuery} query对象 31 | */ 32 | const getUrlQuery = (url: string): queryString.ParsedQuery => queryString.parseUrl(url).query 33 | 34 | /** 35 | * FUN: 根据 key 从 url 的 查询参数对象 中获取单个参数值 36 | * 37 | * @author dyb-dev 38 | * @date 14/07/2023/ 15:28:49 39 | * @param {string} [url] 需要解析的 URL 40 | * @param {string} key query 的 key 41 | * @returns {string} query 的 value 42 | */ 43 | const getUrlQueryValue = (url: string, key: string): string => (getUrlQuery(url)[key] as string) || "" 44 | 45 | /** 46 | * FUN: 设置或更新 从 url 的 查询参数对象 中的指定参数,并返回更新后的 URL 字符串 47 | * 48 | * @author dyb-dev 49 | * @date 14/07/2023/ 16:06:14 50 | * @param {string} [url] 需要解析的 URL 51 | * @param {string} key 需要 设置或更新 参数 的 key 52 | * @param {string} value 需要 设置或更新 参数 的 value 53 | * @param {queryString.StringifyOptions} [options] stringifyUrl 的 options 54 | * @returns {string} 设置或更新后的 url 55 | */ 56 | const setUrlQueryValue = (url: string, key: string, value: string, options?: queryString.StringifyOptions): string => { 57 | 58 | const _query = getUrlQuery(url) 59 | 60 | _query[key] = value 61 | 62 | return queryString.stringifyUrl({ url: url, query: _query }, options) 63 | 64 | } 65 | 66 | /** 67 | * FUN: 合并 URL 的查询参数,并返回更新后的 URL 字符串 68 | * 69 | * @author dyb-dev 70 | * @date 14/07/2023/ 16:10:39 71 | * @param {string} [url] 完整的 URL 字符串 72 | * @param {queryString.ParsedQuery} obj 需要合并到 URL 中的查询参数对象 73 | * @param {queryString.StringifyOptions} [options] stringifyUrl 的 options 74 | * @returns {string} 更新后的 URL 字符串 75 | */ 76 | const mergeUrlQuery = (url: string, obj: queryString.ParsedQuery, options?: queryString.StringifyOptions): string => { 77 | 78 | const _query = getUrlQuery(url) 79 | 80 | Object.assign(_query, obj) 81 | 82 | return queryString.stringifyUrl({ url: url, query: _query }, options) 83 | 84 | } 85 | 86 | /** 87 | * FUN: 判断路径是否为绝对路径 88 | * - 匹配以 `协议` | `域名` | `端口号` 开头的路径 89 | * 90 | * @author dyb-dev 91 | * @date 15/10/2024/ 22:26:21 92 | * @param {string} path - 路径 93 | * @returns {*} {boolean} - 是否为绝对路径 94 | */ 95 | const isAbsoluteUrl = (path: string): boolean => /^(https?:\/\/|:\/\/|[a-zA-Z0-9.-]+:\d+|:\d+)/.test(path) 96 | 97 | /** 相对路径转换为绝对路径的选项 */ 98 | interface IToAbsoluteUrlOptions { 99 | /** 相对 URL 路径 */ 100 | relativePath: string 101 | /** 102 | * 网址的协议、域名、端口号组成的字符串 默认: `getCurrentServerUrl()` 103 | */ 104 | urlOrigin?: string 105 | /** 基础路径 默认: '' */ 106 | basePath?: string 107 | /** 版本号 默认: `__PROJECT_INFO__.version` */ 108 | version?: string 109 | } 110 | 111 | /** 112 | * FUN: 将相对 Url 路径转换为绝对 Url 路径 113 | * 114 | * @author dyb-dev 115 | * @date 15/10/2024/ 11:43:52 116 | * @param {IToAbsoluteUrlOptions} options - 选项 117 | * @returns {*} {string} - 绝对路径 118 | */ 119 | const toAbsoluteUrl = (options: IToAbsoluteUrlOptions): string => { 120 | 121 | const { relativePath, urlOrigin = getCurrentServerUrl(), basePath = "", version = __PROJECT_INFO__.version } = options 122 | 123 | if (!relativePath || typeof relativePath !== "string" || isAbsoluteUrl(relativePath)) { 124 | 125 | console.error("toAbsoluteUrl() relativePath:", relativePath) 126 | return relativePath 127 | 128 | } 129 | 130 | const _urlOrigin = trimUrlSlashes(urlOrigin, { trimStart: false }) 131 | const _basePath = trimUrlSlashes(basePath) 132 | const _relativePath = trimUrlSlashes(relativePath, { trimEnd: false }) 133 | 134 | const _tempList = [] 135 | _urlOrigin && _tempList.push(_urlOrigin) 136 | _basePath && _tempList.push(_basePath) 137 | _relativePath && _tempList.push(_relativePath) 138 | 139 | const _url = _tempList.join("/") 140 | 141 | if (!version) { 142 | 143 | return _url 144 | 145 | } 146 | 147 | return setUrlQueryValue(_url, "version", version, {}) 148 | 149 | } 150 | 151 | /** 152 | * FUN: 根据选项移除 URL 的首尾斜杠 153 | * 154 | * @author dyb-dev 155 | * @date 23/07/2024/ 20:28:05 156 | * @param {string} url - 需要处理的 URL 157 | * @param {object} [options={}] - 配置项 158 | * @param {boolean} [options.trimStart=true] - 是否移除开头的斜杠 159 | * @param {boolean} [options.trimEnd=true] - 是否移除结尾的斜杠 160 | * @returns {string} - 处理后的url 161 | */ 162 | const trimUrlSlashes = (url: string, options: { trimStart?: boolean; trimEnd?: boolean } = {}): string => { 163 | 164 | const { trimStart = true, trimEnd = true } = options 165 | 166 | let _url = url 167 | if (trimStart) { 168 | 169 | _url = _url.replace(/^\//, "") 170 | 171 | } 172 | if (trimEnd) { 173 | 174 | _url = _url.replace(/\/$/, "") 175 | 176 | } 177 | return _url 178 | 179 | } 180 | 181 | export { 182 | trimUrlSlashes, 183 | getBaseUrl, 184 | getUrlQuery, 185 | getUrlQueryValue, 186 | setUrlQueryValue, 187 | mergeUrlQuery, 188 | isAbsoluteUrl, 189 | toAbsoluteUrl 190 | } 191 | -------------------------------------------------------------------------------- /src/utils/userInfo/index.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2025-02-21 21:15:36 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-21 21:15:51 6 | * @FilePath: /uniapp-mp-wx-template/src/utils/userInfo/index.ts 7 | * @Description: 用户信息相关工具函数 8 | */ 9 | 10 | import { CERTIFICATE_CONFIG_LIST, GENDER_CONFIG_LIST } from "@/constants" 11 | import { isIdentityCard } from "@/utils" 12 | 13 | import { ECertificatesType, EGenderType } from "@/types" 14 | 15 | /** 16 | * FUN: 获取性别描述 17 | * 18 | * @author dyb-dev 19 | * @date 15/10/2024/ 15:17:06 20 | * @param {EGenderType} genderType - 性别类型 21 | * @returns {*} {string} - 性别字符串 如果未知性别,返回空字符串 22 | */ 23 | const getGenderDesc = (genderType: EGenderType): string => GENDER_CONFIG_LIST.find(item => item.type === genderType)?.desc || "" 24 | 25 | /** 26 | * FUN: 获取证件描述 27 | * 28 | * @author dyb-dev 29 | * @date 16/10/2024/ 22:44:22 30 | * @param {ECertificatesType} certificateType - 证件类型 31 | * @returns {*} {string} - 证件描述字符串,如果未知证件,返回空字符串 32 | */ 33 | const getCertificateDesc = (certificateType: ECertificatesType): string => 34 | CERTIFICATE_CONFIG_LIST.find(item => item.type === certificateType)?.desc || "" 35 | 36 | /** 37 | * FUN: 通过身份证号码获取性别 38 | * 39 | * @author dyb-dev 40 | * @date 15/10/2024/ 21:37:45 41 | * @param {string} identityCardNumber - 身份证号码 42 | * @returns {*} {string} - 返回性别字符串,如果身份证无效,返回空字符串 43 | */ 44 | const getGenderFromIdentityCard = (identityCardNumber: string): string => { 45 | 46 | // 检查身份证号码是否合法 47 | if (!isIdentityCard(identityCardNumber)) { 48 | 49 | console.error("getGenderFromIdentityCard() 身份证号码无效 identityCardNumber:", identityCardNumber) 50 | return "" 51 | 52 | } 53 | 54 | // 提取性别信息,身份证号码的倒数第二位表示性别,奇数为男,偶数为女 55 | const _genderCode = identityCardNumber.charAt(16) 56 | 57 | const _gender = parseInt(_genderCode, 10) % 2 === 1 ? EGenderType.Man : EGenderType.Woman 58 | 59 | return getGenderDesc(_gender) 60 | 61 | } 62 | 63 | /** 64 | * FUN: 通过身份证号码获取生日 65 | * 66 | * @author dyb-dev 67 | * @date 15/10/2024/ 21:38:14 68 | * @param {string} identityCardNumber - 身份证号码 69 | * @returns {*} {string} - 返回生日字符串,格式为 YYYY/MM/DD,如果身份证无效,返回空字符串 70 | */ 71 | const getBirthdayFromIdentityCard = (identityCardNumber: string): string => { 72 | 73 | // 检查身份证号码是否合法 74 | if (!isIdentityCard(identityCardNumber)) { 75 | 76 | console.error("getBirthdayFromIdentityCard() 身份证号码无效 identityCardNumber:", identityCardNumber) 77 | return "" 78 | 79 | } 80 | 81 | // 提取生日信息,身份证号码的第7位到第14位是生日 82 | const _birthYear = identityCardNumber.substring(6, 10) 83 | const _birthMonth = identityCardNumber.substring(10, 12) 84 | const _birthDay = identityCardNumber.substring(12, 14) 85 | 86 | return `${_birthYear}/${_birthMonth}/${_birthDay}` 87 | 88 | } 89 | 90 | export { getGenderDesc, getCertificateDesc, getGenderFromIdentityCard, getBirthdayFromIdentityCard } 91 | -------------------------------------------------------------------------------- /tsconfig.app.json: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 13:56:46 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2025-02-10 20:16:33 6 | * @FilePath: /uniapp-mp-wx-template/tsconfig.app.json 7 | * @Description: app环境ts配置 8 | */ 9 | 10 | { 11 | "extends": "@dyb-dev/ts-config/vue", 12 | "include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", "types/**/*.ts", "types/**/*.d.ts"], 13 | "exclude": ["node_modules", "**/node_modules", ".history"], 14 | "compilerOptions": { 15 | // 指定要包含的库 16 | "lib": ["ESNext", "DOM", "DOM.Iterable"], 17 | // 指定要包含的类型定义文件 18 | "types": ["@dyb-dev/ts-config/types", "@uni-helper/uni-app-types", "miniprogram-api-typings"], 19 | // 指定用于存储 TypeScript 编译器在增量编译模式下生成的编译信息的文件路径,以便下次编译时可以使用 20 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", 21 | // 设置基础路径,解决模块导入路径问题 22 | "baseUrl": "./", 23 | // 配置路径映射,简化模块导入路径 24 | "paths": { 25 | "@/*": ["./src/*"], 26 | "nutui-uniapp/components/*.vue": [""] 27 | } 28 | }, 29 | // Volar(Vue 3 的 TypeScript 支持插件)相关的配置项 30 | "vueCompilerOptions": { 31 | // 扩展模板组件的类型检查 例如: view、text组件 32 | "plugins": ["@uni-helper/uni-app-types/volar-plugin"] 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 13:56:46 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-05 20:18:25 6 | * @FilePath: /uniapp-mp-wx-template/tsconfig.json 7 | * @Description: ts配置文件 8 | */ 9 | 10 | { 11 | // 主项目本身不包含任何要编译的文件,文件编译由 `references` 引用的子项目控制 12 | "files": [], 13 | // 引用其他 TypeScript 项目,支持项目间的模块化开发和增量编译 14 | "references": [ 15 | { 16 | "path": "./tsconfig.app.json" 17 | }, 18 | { 19 | "path": "./tsconfig.node.json" 20 | } 21 | ] 22 | } 23 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 13:56:46 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-06 13:58:45 6 | * @FilePath: /uniapp-mp-wx-template/tsconfig.node.json 7 | * @Description: node环境ts配置 8 | */ 9 | 10 | { 11 | "extends": "@dyb-dev/ts-config/node", 12 | "include": [ 13 | "manifest.config.ts", 14 | "pages.config.ts", 15 | "vite.config.ts", 16 | "vite/**/*.ts", 17 | "vite/**/*.d.ts", 18 | "types/**/*.ts", 19 | "types/**/*.d.ts" 20 | ], 21 | "exclude": ["node_modules", "**/node_modules", ".history"], 22 | "compilerOptions": { 23 | // 指定要包含的类型定义文件 24 | "types": ["vite/client", "@dyb-dev/ts-config/types"], 25 | // 指定用于存储 TypeScript 编译器在增量编译模式下生成的编译信息的文件路径,以便下次编译时可以使用 26 | "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", 27 | // 指定生成的模块代码 28 | "module": "ESNext", 29 | // 指定模块解析策略,支持现代打包工具 30 | "moduleResolution": "Bundler" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /types/env.d.ts: -------------------------------------------------------------------------------- 1 | /** vite环境变量 */ 2 | interface ImportMetaEnv { 3 | // CONST: 共用 4 | /** 组件目录 默认:components */ 5 | readonly VITE_COMPONENT_DIR: string 6 | /** 页面目录 默认:pages */ 7 | readonly VITE_PAGE_DIR: string 8 | /** `src`下 分包目录 默认:subPackages */ 9 | readonly VITE_SUB_PACKAGE_DIR: string 10 | /** `VITE_SUB_PACKAGE_DIR`下 分包子目录集合 如果涉及多个子分包,用逗号分隔 默认:webview */ 11 | readonly VITE_SUB_PACKAGE_CHILD_DIRS: string 12 | /** 启动页路径 默认:pages/launch */ 13 | readonly VITE_LAUNCH_PATH: string 14 | /** 15 | * 是否使用启动页 默认:false 16 | * - 可以在小程序运行时控制首次显示的页面,使用时注意: 17 | * - 小程序首页变为`VITE_LAUNCH_PATH`,页面跳转逻辑将在`App.vue`中进行,可根据需求修改 18 | * - 小程序码路径示例: 目标页面为`a`页面,则路径应该为 `/pages/launch?targetPath=pages/a&test=1`,其中`test=1`会传递给`a`页面 19 | */ 20 | readonly VITE_USE_LAUNCH_PAGE: string 21 | /** 登录页面的路径 默认:pages/login */ 22 | readonly VITE_LOGIN_PATH: string 23 | /** 首页路径 默认:pages/home */ 24 | readonly VITE_HOME_PATH: string 25 | /** 开发环境服务器网址(小程序开发版、体验版用到) 默认:http://xxx.com */ 26 | readonly VITE_DEV_SERVER_URL: string 27 | /** 生产环境服务器网址(小程序体验版、线上版用到) 默认:http://xxx.com */ 28 | readonly VITE_PROD_SERVER_URL: string 29 | /** 接口请求基础路径 默认:/api */ 30 | readonly VITE_API_BASE_PATH: string 31 | /** 32 | * 用户 node 环境 33 | * - development: 开发环境 34 | * - production: 生产环境 35 | */ 36 | readonly VITE_USER_NODE_ENV: "development" | "production" 37 | 38 | // CONST: wx小程序相关 39 | /** wx小程序appid */ 40 | readonly VITE_MP_WX_APPID: string 41 | } 42 | 43 | /** 扩展 ImportMeta 接口 */ 44 | interface ImportMeta { 45 | readonly env: ImportMetaEnv 46 | } 47 | -------------------------------------------------------------------------------- /types/index.d.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-08-02 22:58:16 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-09-30 14:50:05 6 | * @FilePath: /vue_pinia_vite/types/index.d.ts 7 | * @Description: app 和 node 共有的环境类型定义模块 8 | */ 9 | 10 | /** 项目信息(全局) */ 11 | declare interface IProjectInfo { 12 | /** 项目版本 */ 13 | version: string 14 | /** 项目最后构建时间 */ 15 | lastBuildTime: string 16 | /** 环境变量信息 */ 17 | env: ImportMetaEnv 18 | /** `package.json`信息 */ 19 | pkg: { 20 | /** 包名 */ 21 | name: string 22 | /** 包版本 */ 23 | version: string 24 | /** 生产依赖 */ 25 | dependencies: Record 26 | } 27 | } 28 | 29 | /** 项目信息(全局) */ 30 | declare const __PROJECT_INFO__: IProjectInfo 31 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-05 13:56:39 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-12-05 17:44:23 6 | * @FilePath: /uniapp-mp-wx-template/vite.config.ts 7 | * @Description: vite配置文件 8 | */ 9 | 10 | import { resolve } from "node:path" 11 | 12 | import Uni from "@dcloudio/vite-plugin-uni" 13 | import UniHelperComponents from "@uni-helper/vite-plugin-uni-components" 14 | import UniHelperManifest from "@uni-helper/vite-plugin-uni-manifest" 15 | import UniHelperPages from "@uni-helper/vite-plugin-uni-pages" 16 | import { NutResolver } from "nutui-uniapp" 17 | import { defineConfig, loadEnv } from "vite" 18 | 19 | import { generateProjectInfo } from "./vite/utils" 20 | 21 | /** CONST: 项目根目录 */ 22 | const projectRootDir = process.cwd() 23 | 24 | /** CONST: 获取.env文件的环境变量 */ 25 | export const VITE_ENV = loadEnv(process.env.NODE_ENV as string, projectRootDir) as ImportMetaEnv 26 | 27 | /** CONST: 项目信息 */ 28 | const __PROJECT_INFO__ = generateProjectInfo(VITE_ENV) 29 | 30 | const { 31 | VITE_COMPONENT_DIR, 32 | VITE_PAGE_DIR, 33 | VITE_LAUNCH_PATH, 34 | VITE_USE_LAUNCH_PAGE, 35 | VITE_HOME_PATH, 36 | VITE_SUB_PACKAGE_DIR, 37 | VITE_SUB_PACKAGE_CHILD_DIRS 38 | } = VITE_ENV 39 | 40 | /** CONST: 分包子目录路径列表 */ 41 | const subPackageChildDirPathList = VITE_SUB_PACKAGE_CHILD_DIRS.split(",").map(item => `src/${VITE_SUB_PACKAGE_DIR}/${item}`) 42 | 43 | export default defineConfig(async() => { 44 | 45 | return { 46 | plugins: [ 47 | // 使用 `pages.config.ts` 文件来编写生成 `pages.json` 文件,注意: 生成的 `pages.json` 文件不要更改 48 | UniHelperPages({ 49 | // .d.ts文件输出路径 默认: 根目录 50 | dts: `src/types/dts/${VITE_PAGE_DIR}.d.ts`, 51 | // 扫描的页面目录 默认: src/pages 52 | dir: `src/${VITE_PAGE_DIR}`, 53 | // 首页路径 默认: pages/index 54 | homePage: VITE_USE_LAUNCH_PAGE === "true" ? VITE_LAUNCH_PATH : VITE_HOME_PATH, 55 | // subPackages 扫描的目录 默认: src/pages-sub 56 | subPackages: subPackageChildDirPathList, 57 | // 排除的文件,相对于 dir 和 subPackages 58 | exclude: [`**/${VITE_COMPONENT_DIR}/**/*.*`] 59 | }), 60 | 61 | // 使用 `manifest.config.ts` 来编写生成 `manifest.json` 文件,注意: 生成的 `manifest.json` 文件不要更改 62 | UniHelperManifest(), 63 | 64 | // 组件自动导入插件 65 | UniHelperComponents({ 66 | // 组件 扫描的目录 默认: src/components 67 | dirs: [`src/${VITE_COMPONENT_DIR}`, ...subPackageChildDirPathList.map(item => `${item}/${VITE_COMPONENT_DIR}`)], 68 | // .d.ts文件输出路径 69 | dts: resolve(projectRootDir, `./src/types/dts/${VITE_COMPONENT_DIR}.d.ts`), 70 | // 自定义自动导入逻辑 71 | resolvers: [NutResolver()] 72 | }), 73 | 74 | // 核心插件,能够在 `uni-app` 中使用 `vite` 来构建项目 75 | // 注意: 部分插件需要在 `Uni()` 的前面,这是因为其他插件代码最终会被 `Uni()` 做处理 76 | Uni() 77 | ], 78 | 79 | build: { 80 | // js兼容目标 默认:modules 81 | target: "es6", 82 | // css兼容目标 默认:与 build.target 一致 83 | cssTarget: ["chrome61"] 84 | }, 85 | 86 | resolve: { 87 | // 设置路径别名 88 | alias: { 89 | "@": resolve(projectRootDir, "./src") 90 | }, 91 | // 导入时想要省略的扩展名集合 92 | extensions: [".js", ".ts", ".jsx", ".tsx", ".json", ".mjs", ".mts", ".cjs", ".cts"] 93 | }, 94 | 95 | // 定义变量,编译时会将使用的地方替换为硬编码的形式 96 | define: { 97 | __PROJECT_INFO__: JSON.stringify(__PROJECT_INFO__) 98 | }, 99 | 100 | css: { 101 | preprocessorOptions: { 102 | // scss全局文件 103 | scss: { 104 | additionalData: ` 105 | @use "${projectRootDir}/src/styles/variable/uni.scss" as *; 106 | @use "${projectRootDir}/src/styles/variable/custom.scss" as *; 107 | @use "${projectRootDir}/src/styles/mixins/index.scss" as *; 108 | @use "${projectRootDir}/src/styles/funs/index.scss" as *; 109 | @import "nutui-uniapp/styles/variables.scss"; 110 | ` 111 | } 112 | } 113 | }, 114 | 115 | json: { 116 | // 是否支持从 .json 文件中进行按名导入,示例:import { name } from './package.json'; 117 | namedExports: false, 118 | // 开启则会禁用按名导入,导入的 JSON 会被转换为 export default JSON.parse("...") 会比转译成对象字面量性能更好, 119 | stringify: true 120 | } 121 | } 122 | 123 | }) 124 | -------------------------------------------------------------------------------- /vite/utils.ts: -------------------------------------------------------------------------------- 1 | /* 2 | * @Author: dyb-dev 3 | * @Date: 2024-10-06 13:52:21 4 | * @LastEditors: dyb-dev 5 | * @LastEditTime: 2024-10-06 23:39:46 6 | * @FilePath: /uniapp-mp-wx-template/vite/utils.ts 7 | * @Description: vite配置工具函数 8 | */ 9 | 10 | import dayjs from "dayjs" 11 | 12 | import pkg from "../package.json" 13 | 14 | /** 15 | * FUN: 生成项目信息 16 | * 17 | * @author dyb-dev 18 | * @date 30/09/2024/ 15:00:27 19 | * @param {ImportMetaEnv} env - 环境变量 20 | * @returns {*} {IProjectInfo} 21 | */ 22 | const generateProjectInfo = (env: ImportMetaEnv): IProjectInfo => { 23 | 24 | const { name, version, dependencies } = pkg 25 | const _dayObj = dayjs() 26 | const _projectVersion = _dayObj.format("YYYYMMDDHHmmss") 27 | const _lastBuildTime = _dayObj.format("YYYY-MM-DD HH:mm:ss") 28 | 29 | const _projectInfo: IProjectInfo = { 30 | version: _projectVersion, 31 | lastBuildTime: _lastBuildTime, 32 | env, 33 | pkg: { name, version, dependencies } 34 | } 35 | 36 | return _projectInfo 37 | 38 | } 39 | 40 | export { generateProjectInfo } 41 | --------------------------------------------------------------------------------