Compare commits

...

326 Commits

Author SHA1 Message Date
feige996
0dd0cab15d chore: 更新 package.json 中的版本号至 3.1.0 2025-06-22 16:58:18 +08:00
feige996
02d8fb1268 feat(utils): 添加查询字符串序列化工具函数替代qs库
实现一个轻量级的查询字符串序列化工具,支持基本类型和数组,用于替代第三方qs库以减少打包体积
2025-06-22 16:55:57 +08:00
feige996
ee8fc19a47 feat(env): 添加 VITE_APP_PROXY 和 UNI_PLATFORM 类型声明 2025-06-22 14:22:08 +08:00
feige996
dd11e45d5f refactor: 将类型定义文件移动到src/types目录下并添加登录相关类型
将types目录移动到src下以保持项目结构一致性
新增登录相关的类型定义文件,包含用户信息、登录返回数据等接口
2025-06-22 14:04:49 +08:00
feige996
8455f09e8c chore: 调整 vscode 文件嵌套配置 2025-06-22 13:12:42 +08:00
feige996
edefc99702 refactor(api): 将登录相关类型定义移动到types目录下 2025-06-22 13:02:10 +08:00
feige996
946ad7c976 feat(首页): 添加作者和官网信息并调整顶部间距
在首页添加作者和官网信息展示,同时将顶部图片间距从mt-12调整为mt-10以优化布局
2025-06-22 12:06:39 +08:00
feige996
cfe5634c91 ci(workflow): 添加 base-uv-ui 和 base-uview-plus 的自动合并工作流
添加两个新的工作流用于将 main 分支自动合并到 base-uv-ui 和 base-uview-plus 分支
2025-06-22 11:59:30 +08:00
feige996
2000eda414 chore: 禁用prettier并启用eslint作为默认格式化工具 2025-06-22 10:07:22 +08:00
feige996
69b7429909 style: 修改页面配置和代码片段格式
- 将首页图标类型从 'wot' 改为 'uiLib'
- 调整 Vue3 代码片段中 template 和 script 的顺序
- 格式化代码片段的缩进和换行
2025-06-22 10:04:44 +08:00
feige996
926d700b60 docs: tabbarList 2025-06-21 22:51:51 +08:00
feige996
f0cd8ee943 docs: tabbar.md 2025-06-21 22:46:42 +08:00
feige996
67f76415e1 build: auto merge to base-sard-ui 2025-06-21 19:44:24 +08:00
feige996
e67da2203c feat: uiLib 匹配 2025-06-21 19:36:04 +08:00
feige996
cb033104ee chore: // @ts-expect-error 预知的判断 2025-06-21 19:35:26 +08:00
feige996
28e59cd599 chore: 更新项目版本号至3.0.0,分支合并和eslint等重大变更 2025-06-21 17:40:14 +08:00
feige996
1af7213e96 chore: 移除oxlint及相关配置
清理不再使用的oxlint依赖及其配置文件
更新vscode设置以反映lint工具变更
2025-06-21 17:32:13 +08:00
feige996
d5c14ac3ae build: 将 ESLint 配置从 @antfu 迁移到 @uni-helper
统一使用 uni-app 生态的 ESLint 配置,保持工具链一致性
2025-06-21 17:29:42 +08:00
feige996
27d4441ca6 chore: 更新版本号至2.16.0, 使用 antfu/eslint-config 2025-06-21 17:23:51 +08:00
feige996
ea50a639ed Merge branch 'eslint' 2025-06-21 17:22:34 +08:00
feige996
e171c29d28 chore: 将 lint 工具从 oxlint 切换为 eslint 并启用多语言支持
更新 package.json 中的 lint 脚本,使用 eslint 替代 oxlint
在 .vscode/settings.json 中启用 eslint 对所有支持语言的校验
2025-06-21 17:22:06 +08:00
feige996
ae515cd85e chore: 将eslint替换为oxlint并更新vscode配置
更新package.json中的lint脚本,从eslint改为oxlint
调整.vscode/settings.json中的格式化配置,启用prettier并优化保存时的自动修复行为
2025-06-21 17:18:35 +08:00
feige996
e1654359fd style: 总算处理好所有的 eslint 报错 2025-06-21 17:11:17 +08:00
feige996
32cf872405 refactor: 在多个文件中添加process导入并调整tsconfig顺序
统一添加node:process导入以提高代码一致性
调整tsconfig.json中配置项的顺序以提升可读性
2025-06-21 17:03:45 +08:00
feige996
c355f6e8f1 chore(eslint): 更新eslint配置,禁用部分规则并添加忽略目录
添加markdown支持为false,并配置忽略目录src/uni_modules/和dist
禁用no-console等规则以提升开发体验
2025-06-21 16:59:01 +08:00
feige996
227f19a93c style: 统一代码格式和类型定义,测试eslint --fix, 还是有报错 2025-06-21 16:56:24 +08:00
feige996
9cf0c212bf build: 将 lint 工具从 oxlint 替换为 eslint
统一使用 eslint 作为代码检查工具,简化 lint-staged 配置
2025-06-21 16:53:10 +08:00
feige996
79c333e647 feat: 添加 ESLint 配置并集成到 VSCode
pnpm dlx @antfu/eslint-config@latest
2025-06-21 16:50:31 +08:00
feige996
9c074652e1 chore: 简化 commitlint 配置 2025-06-21 16:28:17 +08:00
feige996
6402f8825f docs: 修正描述中Vite的版本号从6改为5 2025-06-21 15:56:51 +08:00
feige996
4fc50a6bb5 Revert "chore: 更新.gitignore并删除未使用的配置文件"
This reverts commit 28d8000ad82b20e749f50dc1848f1e24ed588220.
2025-06-21 15:53:24 +08:00
feige996
28d8000ad8 chore: 更新.gitignore并删除未使用的配置文件
移除src/pages.json和src/manifest.json配置文件
更新.gitignore以忽略src/types目录和删除的配置文件
2025-06-21 15:50:13 +08:00
feige996
144dec8881 chore: 更新版本号至2.15.0 2025-06-21 15:33:19 +08:00
feige996
e2c319079a refactor(about): 移除未使用的导航代码并简化页面结构 2025-06-21 15:30:53 +08:00
feige996
1efb68cb9b docs: 将模板分支从tabbar更新为base 2025-06-21 15:29:36 +08:00
feige996
ba7252c332 ci(workflow): 移除不再需要的自动合并工作流
删除针对 tabbar 和 spa 分支的自动合并工作流配置,仅保留 i18n 分支的配置
2025-06-21 15:11:43 +08:00
feige996
548b0515f6 docs(pages.config): 添加关于 tabbar 配置位置的注释 2025-06-21 15:04:54 +08:00
feige996
b03f7ee310 docs: 更新README中微信平台的命令名称
将微信平台开发和生产环境的命令从`dev:mp-weixin`和`build:mp-weixin`统一简化为`dev:mp`和`build:mp`,保持命令一致性
2025-06-21 14:59:00 +08:00
feige996
e2eda79403 docs(fg-tabbar): 完善 tabbar 策略的注释说明
添加更详细的注释说明 tabbar 策略选项及其使用注意事项
提醒代码更改后需要重新运行以更新 pages.json
2025-06-21 14:55:06 +08:00
feige996
2a97bf0aaa chore(fg-tabbar): 将selectedTabbarStrategy默认值改为0 2025-06-21 14:46:50 +08:00
feige996
f8641457ad refactor(fg-tabbar): 更新tabbar策略并添加safelist配置
将selectedTabbarStrategy从0改为1以切换tabbar策略
在uno.config.ts中添加空的safelist配置
移除注释掉的i-carbon-code导入
2025-06-21 14:36:30 +08:00
feige996
407bc849ec Revert "chore: pnpm-lock.yaml"
This reverts commit 21234cdbe2bd40104cbb4abf1da2d62b44c0227e.
2025-06-21 14:31:00 +08:00
feige996
38328280c4 chore: 回退esbuild版本至0.20.2 解决ios模拟器报错 2025-06-21 14:24:30 +08:00
feige996
d2f7b5e1ec docs(layouts): 添加tabbar组件使用说明文档 2025-06-21 14:17:23 +08:00
feige996
7b3e757301 refactor(utils): 移除未使用的 tabBar 相关代码并简化导入
清理不再使用的 tabBar 相关函数和变量,简化 pages.json 的导入方式
2025-06-21 13:37:17 +08:00
feige996
2a71d02ab8 refactor(tabbar): 将 tabbar 组件从 components 移动到 layouts 目录
重构 tabbar 组件结构,将其从 components 目录迁移至 layouts 目录以更符合项目结构规范
移除未使用的 midButton 配置
2025-06-21 13:35:29 +08:00
feige996
4b77ca6c13 chore: midButton 2025-06-21 13:30:41 +08:00
feige996
21234cdbe2 chore: pnpm-lock.yaml 2025-06-21 13:30:26 +08:00
feige996
5250fac0a1 refactor(tabbar): 重构自定义tabbar逻辑并添加图标路径
移除冗余注释并整理tabbar相关配置
添加自定义tabbar的图标路径配置
优化tabbar显示逻辑,根据CUSTOM_TABBAR_ENABLE决定显示方式
2025-06-21 13:09:14 +08:00
feige996
91faa0f301 refactor(tabbar): 重构底部导航栏配置及逻辑
将底部导航栏配置从 pages.config.ts 移动到单独的文件中
添加 CUSTOM_TABBAR_ENABLE 开关控制自定义导航栏行为
优化导航栏切换逻辑,根据开关选择不同跳转方式
2025-06-21 12:54:52 +08:00
GitHub Actions
13ebc5aacc Auto merge main into tabbar 2025-06-21 02:53:22 +00:00
feige996
d86bfa719d ci: 将自动合并工作流中的基础分支从base改为main 2025-06-21 10:52:02 +08:00
GitHub Actions
72c261ef67 Auto merge base into tabbar 2025-06-21 02:38:08 +00:00
feige996
a7671ce4a9 docs(pages.config.ts): 更新tabBar配置的注释说明 2025-06-21 10:36:22 +08:00
GitHub Actions
de32f4515f Auto merge base into tabbar 2025-06-21 02:35:28 +00:00
feige996
74e52f000a docs(useUpload): 更新注释以明确不同平台的返回值差异
补充说明H5和App平台返回的File对象字段差异,并调整注释顺序使其更清晰
2025-06-21 10:33:22 +08:00
feige996
7bd338e07c fix(useUpload): 修复文件路径和大小获取逻辑错误
调整条件编译逻辑,确保在非微信小程序环境下也能正确获取文件路径和大小
2025-06-21 10:29:55 +08:00
feige996
119edac05a refactor(页面布局): 移除自定义导航栏相关代码并简化样式
移除about页面的自定义导航栏代码及相关配置,统一使用默认导航栏样式
优化index页面布局,移除多余的overflow-hidden样式
修复upload组件图片显示条件判断
2025-06-21 10:27:59 +08:00
GitHub Actions
f93ed08cb2 Auto merge base into tabbar 2025-06-21 02:14:34 +00:00
feige996
393c37bd56 chore: 更新版本号至2.14.0 2025-06-21 10:14:20 +08:00
GitHub Actions
7a1ae5b0f3 Auto merge base into tabbar 2025-06-21 02:12:02 +00:00
feige996
c47564e51a build: 添加对@dcloudio/uni-h5的补丁支持
添加pnpm-workspace.yaml配置文件并创建补丁文件,修复uni-h5组件中tabBar高度可能为undefined的问题
2025-06-21 10:11:39 +08:00
feige996
7a2e870641 chore: 在.prettierignore中添加node_modules目录 2025-06-21 10:08:35 +08:00
GitHub Actions
3b7688a20b Auto merge base into tabbar 2025-06-21 01:26:18 +00:00
feige996
e2c0ac4721 refactor(layout): 移除隐私弹窗组件 2025-06-21 09:20:04 +08:00
GitHub Actions
11c6f158d0 Auto merge base into tabbar 2025-06-21 01:19:12 +00:00
feige996
9ef3b7d7d4 build: 将 uno.css 替换为 virtual:uno.css 2025-06-20 23:16:20 +08:00
feige996
4f67168494 feat(tabbar): 添加中间按钮并移除自定义tabbar配置
添加发布按钮作为tabbar中间按钮,同时移除不再需要的custom配置
2025-06-20 21:40:52 +08:00
feige996
bf61c3d1f0 chore: 在页面中添加调试日志
添加console.log语句用于调试index和about页面
2025-06-20 19:35:31 +08:00
feige996
9a34868fb7 refactor(tabbar): 将tabbar配置抽离为单独模块以提高可维护性
将pages.config.ts中的tabbar配置抽离到单独的tabbarList.ts文件
移除pages.json中无用的__esModule字段
2025-06-20 19:32:34 +08:00
GitHub Actions
fe751745e5 Auto merge base into tabbar 2025-06-20 02:45:43 +00:00
feige996
29f526242d ci: 移除不再需要的自动合并到main的工作流 2025-06-20 10:45:22 +08:00
GitHub Actions
ba8c0a2e7f Auto merge base into tabbar 2025-06-20 02:43:41 +00:00
feige996
33638c5a9e build: 添加 prettier 依赖并升级至 3.5.3 版本 2025-06-20 10:43:18 +08:00
GitHub Actions
6a7ece9b3f Auto merge base into tabbar 2025-06-17 14:57:59 +00:00
feige996
47d8401c47 chore: 更新 package.json 版本号至 2.13.1 2025-06-17 22:57:37 +08:00
feige996
11b7880810 refactor: 移除未使用的"我的"页面及相关路由配置 2025-06-17 22:39:31 +08:00
feige996
c1b12eab84 Merge branch 'base' into tabbar 2025-06-17 22:39:11 +08:00
feige996
136fb5d507 chore: 更新版本号至2.13.0 2025-06-17 22:36:31 +08:00
feige996
4445e18b02 refactor: 移除用户中心相关页面和隐私弹窗组件
删除与用户中心相关的所有页面,包括个人资料、修改密码、关于我们等
移除不再使用的隐私协议弹窗组件及相关样式文件
清理路由配置中不再需要的页面路径
2025-06-17 22:35:29 +08:00
feige996
7dd210e427 docs(package.json): 修复bugs.url字段的命名错误 2025-06-17 18:17:37 +08:00
feige996
2f2a14cd06 docs: 更新废弃仓库字段名从repository-deprecated到repository-old 2025-06-17 18:16:32 +08:00
GitHub Actions
3eb06627c2 Auto merge base into tabbar 2025-06-17 10:15:28 +00:00
feige996
c6d4a46968 fix(vite-plugins): 仅在生产环境构建时更新package.json 2025-06-17 18:15:08 +08:00
GitHub Actions
09e048ba85 Auto merge base into tabbar 2025-06-17 10:14:14 +00:00
feige996
968a090876 refactor(vite): 将 updatePackageJson 插件从 scripts 移动到 vite-plugins 目录 2025-06-17 18:11:41 +08:00
feige996
39f4ba793d chore: 更新 package.json 中的 update-time 字段 2025-06-17 18:11:15 +08:00
GitHub Actions
ea2f3a6990 Auto merge base into tabbar 2025-06-17 10:10:22 +00:00
feige996
f6208a191d feat(build): 添加自动更新package.json时间戳的vite插件
添加updatePackageJson插件,在构建时自动更新package.json中的时间戳字段
2025-06-17 18:09:51 +08:00
GitHub Actions
23cd9a522e Auto merge base into tabbar 2025-06-16 01:53:25 +00:00
feige996
f2119f3f73 fix: await getUserInfo need await 2025-06-16 09:53:03 +08:00
GitHub Actions
61a2fd8a27 Auto merge base into tabbar 2025-06-15 14:28:46 +00:00
菲鸽
3808f515cf !16 让http工具方法支持传递更多参数
Merge pull request !16 from 浑宣凯/N/A
2025-06-15 20:51:37 +08:00
GitHub Actions
61bb8363cd Auto merge base into tabbar 2025-06-15 09:04:45 +00:00
feige996
bd64215f86 chore: 更新版本至 2.12.2 2025-06-15 16:46:57 +08:00
GitHub Actions
a4d366c01e Auto merge base into tabbar 2025-06-15 08:46:51 +00:00
feige996
8f711caada chore: 更新 z-paging 依赖至 2.8.7 版本 2025-06-15 16:46:28 +08:00
GitHub Actions
3a2b4febd2 Auto merge base into tabbar 2025-06-14 04:36:21 +00:00
feige996
da79ac04e6 chore: 移除.eslintignore并将忽略规则移至.oxlintrc.json
将原本在.eslintignore中配置的忽略文件移动到.oxlintrc.json的ignorePatterns中,简化配置文件管理
2025-06-14 12:36:00 +08:00
GitHub Actions
95dbb93256 Auto merge base into tabbar 2025-06-14 04:00:41 +00:00
feige996
6996037cd4 Revert "chore: 更新 unocss 依赖至 66.2.0 版本"
This reverts commit c1200eabe875019b4ff2e1aff53dff49d0ef9bcf.
2025-06-14 12:00:17 +08:00
GitHub Actions
cc886bd326 Auto merge base into tabbar 2025-06-14 03:13:50 +00:00
feige996
c1200eabe8 chore: 更新 unocss 依赖至 66.2.0 版本 2025-06-14 10:55:57 +08:00
feige996
0864bc9e3f chore: 降级依赖版本至vue@3.4.21和vite@5.2.8
更新package.json和pnpm-lock.yaml文件,将vue从3.5.15降级至3.4.21,vite从6.3.5降级至5.2.8版本
2025-06-14 10:54:05 +08:00
GitHub Actions
26d49ac5bc Auto merge base into tabbar 2025-06-14 02:46:12 +00:00
feige996
907c20d9d8 chore: 更新 @uni-helper 相关依赖版本 2025-06-14 10:45:08 +08:00
GitHub Actions
25fb9705cc Auto merge base into tabbar 2025-06-13 10:34:52 +00:00
feige996
fa18cfa406 chore: 更新版本号至2.12.1 2025-06-13 18:34:29 +08:00
GitHub Actions
4efb14cbff Auto merge base into tabbar 2025-06-13 10:33:46 +00:00
feige996
af1e9d727f refactor(index.vue): 修复ts报错 2025-06-13 18:33:25 +08:00
feige996
a79e59cdf3 chore: 更新 lint 配置和忽略文件
- 添加 .eslintignore 文件并配置忽略规则
- 更新 .oxlintrc.json 的 ignorePatterns 配置
- 简化 package.json 中的 lint 命令
- 调整 .vscode/settings.json 的文件关联
2025-06-13 18:28:58 +08:00
feige996
24622137c8 chore(oxlint): 添加忽略模式到oxlint配置 2025-06-13 17:56:51 +08:00
feige996
fc7d30c510 chore: 更新 oxlint 配置并添加自动修复命令
扩展 oxlint 配置,添加更多文件类型支持并引入插件
添加 lint:oxlint-fix 命令以支持自动修复
2025-06-13 17:52:04 +08:00
feige996
305ac6dccd ci: 添加oxlint配置并更新prettier规则
添加.oxlintrc.json配置文件以启用oxlint检查,并更新.prettierrc.cjs以支持jsonc文件格式。同时在useUpload.ts中恢复accept参数的默认值。
2025-06-13 17:28:51 +08:00
GitHub Actions
0723fb178b Auto merge base into tabbar 2025-06-13 09:01:21 +00:00
feige996
2fb1e6d71c chore: 更新版本号至2.12.0 2025-06-13 17:00:59 +08:00
GitHub Actions
6866c17184 Auto merge base into tabbar 2025-06-13 09:00:53 +00:00
feige996
3893aff68f chore: 更新oxlint配置及依赖版本
- 添加oxlint到VSCode设置
- 在useUpload.ts中移除未使用的import和注释掉accept参数
- 在utils/index.ts中简化getAllPages函数
- 更新package.json中的oxlint版本和lint配置
- 更新pnpm-lock.yaml中的oxlint相关依赖
2025-06-13 17:00:31 +08:00
GitHub Actions
1558430435 Auto merge base into tabbar 2025-06-13 08:47:40 +00:00
feige996
02b78976bd chore(依赖): 更新oxlint版本至1.1.0 2025-06-13 16:42:02 +08:00
GitHub Actions
6da7d46913 Auto merge base into tabbar 2025-06-13 08:38:56 +00:00
feige996
2e03861c55 fix(配置): 更新文件嵌套模式,添加README.md和pages.config.ts的关联文件 2025-06-13 16:38:31 +08:00
GitHub Actions
5f97a3ff8a Auto merge base into tabbar 2025-06-11 15:39:50 +00:00
feige996
860e2c5e07 chore: 更新版本号至2.11.1 2025-06-11 23:36:46 +08:00
GitHub Actions
12b9384df6 Auto merge base into tabbar 2025-06-10 09:14:21 +00:00
feige996
a82f5d4624 docs(unocss): 添加注释说明并配置presetUni的attributify选项
添加关于@uni-helper/unocss-preset-uni的npm链接注释
配置presetUni的attributify选项,设置prefixedOnly为true
2025-06-09 17:19:30 +08:00
GitHub Actions
1f3749d08d Auto merge base into tabbar 2025-06-06 15:46:16 +00:00
feige996
218d618e0e fix(上传): 修复头像上传成功回调处理逻辑
修改上传成功回调的参数类型为对象,并正确处理响应数据
统一处理H5和微信端的头像上传成功日志
2025-06-06 23:45:39 +08:00
feige996
4bed7f2d13 fix: 修复上传组件图片显示和数据处理问题
修正上传组件中图片显示路径错误的问题,并正确处理上传接口返回的JSON数据
2025-06-06 23:37:44 +08:00
GitHub Actions
2c566253f3 Auto merge base into tabbar 2025-06-06 14:59:38 +00:00
feige996
a18880675b refactor(用户信息): 使用storeToRefs解构userInfo并添加调试日志
重构用户信息相关代码,使用storeToRefs解构userInfo以简化模板中的访问
在setUserAvatar方法中添加调试日志以便跟踪头像设置过程
2025-06-06 22:54:53 +08:00
GitHub Actions
28fad4a480 Auto merge base into tabbar 2025-06-06 14:42:50 +00:00
feige996
008a1cc58a fix(upload): 更新头像上传逻辑,使用环境变量作为上传地址,增强成功回调处理 2025-06-06 22:38:57 +08:00
feige996
b5a71b7788 fix(useUpload): 优化文件选择逻辑,支持H5和小程序环境,增强错误处理 2025-06-06 22:31:10 +08:00
feige996
4e7dd994b4 fix(index.vue): 修改toast提示方法,统一使用toast.show 2025-06-06 21:59:06 +08:00
GitHub Actions
8233556683 Auto merge base into tabbar 2025-06-06 04:45:21 +00:00
feige996
6362129e34 fix(useUpload): 修复上传文件响应数据处理问题
移除对上传成功响应的JSON解析,直接使用原始数据
2025-06-06 12:44:59 +08:00
GitHub Actions
8464a040ab Auto merge base into tabbar 2025-06-06 04:41:25 +00:00
feige996
c99a11a9fa fix(useUpload): 条件编译不配对 2025-06-06 12:41:06 +08:00
feige996
41bd05fd0f fix(manifest): 更新微信小程序配置,启用es6和代码压缩 2025-06-06 12:37:11 +08:00
GitHub Actions
0d42825314 Auto merge base into tabbar 2025-06-05 14:56:49 +00:00
feige996
e5db0193a3 Merge remote-tracking branch 'gitee/base' into base 2025-06-05 22:56:21 +08:00
菲鸽
2b0d381af4
!15 feat(hook): 上传文件
Merge pull request !15 from 爱吃蒜薹的小白/laowu-pr
2025-06-05 14:54:29 +00:00
GitHub Actions
8a434fa761 Auto merge base into tabbar 2025-06-04 14:44:02 +00:00
feige996
aca9b3337d Revert "chore: 都不生效"
This reverts commit c820ebf2d01036567ca1d2ace1f2cfac639c7cd3.
2025-06-04 22:42:51 +08:00
feige996
c820ebf2d0 chore: 都不生效 2025-06-04 22:42:43 +08:00
GitHub Actions
b286d07623 Auto merge base into tabbar 2025-06-04 09:41:11 +00:00
feige996
6eccf0a294 feat: 添加 ES6 转 ES5 和压缩选项到配置 2025-06-04 17:40:49 +08:00
吴宇杰
da5a2b1afe feat(hook): 上传文件 2025-06-04 16:21:35 +08:00
GitHub Actions
9dcea8189e Auto merge base into tabbar 2025-06-04 07:20:27 +00:00
菲鸽
b8c6beabb5
Merge pull request #129 from icjs-cc/base
feat: 添加登录页面URL配置及页面权限控制
2025-06-04 15:20:15 +08:00
陈剑术
7e81d97742 feat: 优化页面权限控制逻辑
- 添加isLogined函数以简化登录状态检查
- 更新usePageAuth函数,使用isLogined来判断用户是否已登录
2025-06-04 12:45:33 +08:00
陈剑术
dcae738e69 feat: 添加登录页面URL配置及页面权限控制
- 在.env文件中新增VITE_LOGIN_URL配置
- 在App.vue中引入并调用usePageAuth以实现页面权限控制
- 更新route.ts以使用环境变量中的登录页面URL
2025-06-04 12:25:13 +08:00
GitHub Actions
d5a3772b3a Auto merge base into tabbar 2025-06-04 01:03:41 +00:00
feige996
f23d8128f9 test: 测试 lint-staged 2025-06-04 09:03:13 +08:00
GitHub Actions
3bf7fcb844 Auto merge base into tabbar 2025-06-04 01:03:12 +00:00
feige996
0b4147a15e style: 更新lint-staged配置以支持Vue文件 2025-06-04 09:02:49 +08:00
GitHub Actions
7b61242611 Auto merge base into tabbar 2025-06-04 01:00:58 +00:00
feige996
13c166139d Revert "style: 移除无用样式并修复样式检查警告"
This reverts commit a5a359528a4f2183bd3110dc288c140485345bbe.
2025-06-04 09:00:34 +08:00
GitHub Actions
51d399f38b Auto merge base into tabbar 2025-06-03 12:04:11 +00:00
feige996
0f9431cf1e chore(husky): 在pre-commit钩子中添加--allow-empty参数
允许在git commit时即使没有变更也能执行lint-staged
2025-06-03 20:03:45 +08:00
GitHub Actions
e79ae9a17f Auto merge base into tabbar 2025-06-03 11:51:51 +00:00
feige996
6bc115cd36 chore: 在prepare脚本中添加git初始化步骤
在prepare脚本中增加git init命令,确保husky安装前git仓库已初始化
2025-06-03 19:51:28 +08:00
GitHub Actions
3f08e45e06 Auto merge base into tabbar 2025-06-03 11:50:30 +00:00
feige996
ad522ee570 chore: 更新版本号至2.11.0
这是一个简单的版本号更新提交,属于杂项任务(chore),没有涉及功能修改或修复,因此不需要详细说明原因。根据提交消息指南,我们保持了简洁的描述,使用小写字母开头且没有句号。
2025-06-03 19:50:11 +08:00
GitHub Actions
a0a2106fe7 Auto merge base into tabbar 2025-06-03 11:43:14 +00:00
feige996
a5a359528a style: 移除无用样式并修复样式检查警告
移除被禁用的 stylelint 规则注释及无用的 button::after 样式
保持 scroll-view 和 swiper 的 flex 属性不变
2025-06-03 19:42:35 +08:00
feige996
901c677946 chore: 更新 lint-staged 配置以优化性能
- 为 prettier 和 oxlint 添加 --cache 标志以提高执行速度
- 调整文件匹配模式,将 css 和 scss 文件移至 prettier 处理
- 添加排除 node_modules 和 dist 目录的规则
2025-06-03 19:42:11 +08:00
feige996
0fbe59fe92 build: 添加commitlint及相关配置
添加commitlint工具用于规范提交信息格式,包括husky钩子、commitlint配置文件和依赖包
2025-06-03 18:57:52 +08:00
feige996
c5a1b1cd9c chore: 添加 husky 并配置 pre-commit 钩子
- 添加 husky 依赖用于 Git 钩子管理
- 配置 pre-commit 钩子自动执行 eslint 检查
- 在 package.json 中添加 prepare 脚本自动安装 husky
2025-06-03 18:44:23 +08:00
GitHub Actions
291be04925 Auto merge base into tabbar 2025-06-03 10:12:13 +00:00
feige996
604dab5f38 Merge remote-tracking branch 'gitee/base' into base 2025-06-03 18:10:17 +08:00
菲鸽
d6a883c23f
!14 feat: 增加图片导入路径别名和路径提示,同步修改vscode中推荐安装的插件
Merge pull request !14 from mengxiaofei/base
2025-06-03 10:09:35 +00:00
GitHub Actions
81a1e8e42d Auto merge base into tabbar 2025-06-03 09:48:13 +00:00
feige996
91ee84cd49 style(页面配置): 更新页面路由配置和注释代码
- 在分包页面中添加 navigationStyle 配置
- 注释掉 about 页面中未使用的分包跳转按钮代码
```

这个提交消息:
1. 使用了 `style` 类型,因为修改主要是样式配置和代码注释
2. 添加了 `页面配置` 作为可选范围
3. 描述简洁说明了两个主要修改点
4. 符合中文提交规范,使用简体中文且简明扼要
5. 没有使用句号结尾
6. 保持在50个字符以内
2025-06-03 17:46:29 +08:00
GitHub Actions
3e604e3c7f Auto merge base into tabbar 2025-06-03 09:26:52 +00:00
feige996
9ad46fe088 fix(安全区域): 修复微信小程序获取安全区域距离的问题
微信小程序需要使用 wx.getWindowInfo() API 获取安全区域距离,其他平台继续使用 uni.getSystemInfoSync()
2025-06-03 17:26:32 +08:00
GitHub Actions
bb0a57bd1f Auto merge base into tabbar 2025-06-03 06:49:14 +00:00
feige996
35bdf827d2 build: 禁用sourcemap以优化构建性能
默认情况下禁用sourcemap生成,减少构建输出体积并提高构建速度。移除原有通过环境变量控制的逻辑,因为大多数情况下不需要生成sourcemap
2025-06-03 14:48:50 +08:00
feige996
720c7c7e35 fix: 更新应用ID并禁用app平台的copyNativeRes插件
更新manifest.json和.env文件中的应用ID为'__UNI__D1E5001'以匹配新配置
注释掉vite.config.ts中app平台的copyNativeRes插件调用以优化构建流程
2025-06-03 14:39:03 +08:00
GitHub Actions
22ce0f481d Auto merge base into tabbar 2025-06-03 04:55:35 +00:00
feige996
de55a88d0e docs: 更新注释中关于spa模板的推荐说明
将注释中的建议从"请使用"改为更推荐的"推荐使用",使表述更加友好
2025-06-03 12:45:13 +08:00
feige996
d76a5baad9 Merge branch 'tabbar' of github.com:feige996/unibest into tabbar 2025-06-03 12:42:18 +08:00
GitHub Actions
667e0647c4 Auto merge base into tabbar 2025-06-03 04:40:02 +00:00
feige996
a5121bfef6 feat(utils): 添加 tabBarList 导出以获取 tabbar 列表
添加 tabBarList 导出项,方便其他模块直接获取 tabbar 配置列表
2025-06-03 12:39:32 +08:00
feige996
0042afe215 refactor(fg-tabbar): 使用工具函数中的tabBarList替代直接导入
从直接导入pages.json中的tabBar改为使用utils中封装的_tabBarList,提高代码复用性和维护性
2025-06-03 12:38:13 +08:00
GitHub Actions
b857669c0c Auto merge base into tabbar 2025-06-03 02:20:33 +00:00
feige996
f4fa5127d7 chore: 更新版本号至2.10.3 2025-06-03 10:20:12 +08:00
GitHub Actions
b454473591 Auto merge base into tabbar 2025-06-03 02:14:09 +00:00
feige996
834e2b4ce5 Reapply "chore: 将 unocss 依赖版本降级至 65.4.2"
This reverts commit ef81357272462beb6f4e2a91dd9160c84edbbafc.
2025-06-03 10:13:52 +08:00
GitHub Actions
91d66fda51 Auto merge base into tabbar 2025-06-03 01:28:54 +00:00
feige996
ef81357272 Revert "chore: 将 unocss 依赖版本降级至 65.4.2"
This reverts commit 99ca3d430a6f00dea82dc550bf9b537505919220.
2025-06-03 09:28:38 +08:00
GitHub Actions
6777d20d41 Auto merge base into tabbar 2025-06-03 01:17:55 +00:00
feige996
99ca3d430a chore: 将 unocss 依赖版本降级至 65.4.2
```

解释:
1. 类型选择 `chore`,因为这是依赖版本的调整,属于维护性变更
2. 没有使用 scope,因为这是简单的依赖版本变更
3. 描述简洁说明了"将 unocss 依赖版本降级至 65.4.2",符合50字符限制
4. 使用中文描述,符合要求
5. 不需要 body,因为变更简单明了
2025-06-03 09:17:17 +08:00
GitHub Actions
1905a3f8bb Auto merge base into tabbar 2025-05-30 03:17:55 +00:00
laifeipeng
b5a74cf371 chore: 更新 .npmrc 中的注册表配置 2025-05-30 11:17:29 +08:00
GitHub Actions
54105b9968 Auto merge base into tabbar 2025-05-28 08:18:01 +00:00
feige996
7e108dc5db chore: 更新版本号至2.10.2 2025-05-28 16:17:42 +08:00
GitHub Actions
d980dbb97a Auto merge base into tabbar 2025-05-28 07:21:17 +00:00
feige996
9f832d6bb8 chore: 在 package.json 中添加 update-time 字段
添加 update-time 字段以记录项目最后更新时间,便于维护和版本追踪
2025-05-28 15:18:12 +08:00
GitHub Actions
c594222abd Auto merge base into tabbar 2025-05-28 07:15:45 +00:00
feige996
cd182d3e20 chore: 更新 @esbuild/darwin 依赖至 0.25.5 版本
升级 @esbuild/darwin-arm64 和 @esbuild/darwin-x64 依赖版本以获取最新功能和修复
2025-05-28 15:15:24 +08:00
GitHub Actions
53d68c10a0 Auto merge base into tabbar 2025-05-28 03:40:44 +00:00
feige996
8762df2392 ci: 重构工作流配置并添加自动发布日志功能
- 重命名 auto-merge.yml 中的任务名称并优化结构
- 新增 release-log.yml 工作流用于自动生成发布日志
- 删除旧的 release.yml 工作流文件
```

这个提交消息:
1. 使用了 `ci` 类型,因为这些变更都是针对 CI/CD 工作流配置的修改
2. 简洁地描述了三个主要变更:
   - 重构了 auto-merge 工作流
   - 新增了发布日志功能
   - 删除了旧的工作流文件
3. 符合中文语言习惯和提交消息规范
4. 在50字符限制内完成了描述
5. 使用动词开头并保持简洁
2025-05-28 11:40:26 +08:00
feige996
b5cd1319c0 Merge branch 'base' into tabbar 2025-05-28 11:35:54 +08:00
feige996
ac32d6a97b docs: 添加微信小程序运行命令到快速开始指南
在快速开始部分添加了运行微信小程序的命令 `pnpm dev:mp`,以完善文档并帮助用户快速启动小程序开发
2025-05-28 11:28:44 +08:00
feige996
b6fbf54c1c Merge branch 'base' into tabbar 2025-05-28 00:59:32 +08:00
feige996
d35fabd893 feat(tabbar): 调整 2025-05-28 00:51:49 +08:00
feige996
6673c2e269 Merge branch 'base' into tabbar 2025-05-28 00:46:30 +08:00
GitHub Actions
a57d4c08b3 Auto merge base into tabbar 2025-05-27 08:55:54 +00:00
GitHub Actions
cb33ed7a00 Auto merge base into tabbar 2025-05-26 16:32:23 +00:00
feige996
efe570c8c9 Merge branch 'base' into tabbar 2025-05-26 23:30:37 +08:00
lucas
fc062e6480 feat: 增加图片导入路径别名和路径提示,同步修改vscode中推荐安装的插件 2025-05-23 10:56:00 +08:00
GitHub Actions
3c398ab101 Auto merge base into tabbar 2025-05-22 12:02:38 +00:00
GitHub Actions
aed4b934ed Auto merge base into tabbar 2025-05-21 02:17:02 +00:00
GitHub Actions
56bfdfaf70 Auto merge base into tabbar 2025-05-19 07:12:12 +00:00
feige996
2959974f86 Merge branch 'base' into tabbar 2025-05-19 14:54:48 +08:00
GitHub Actions
085a1d73a2 Auto merge base into tabbar 2025-04-22 06:47:50 +00:00
GitHub Actions
7f80ded4c2 Auto merge base into tabbar 2025-04-12 06:09:19 +00:00
GitHub Actions
639c2a0e75 Auto merge base into tabbar 2025-03-07 11:43:14 +00:00
GitHub Actions
3c7701d21b Auto merge base into tabbar 2025-02-25 03:51:55 +00:00
GitHub Actions
5952515ac7 Auto merge base into tabbar 2025-01-23 15:50:34 +00:00
GitHub Actions
13c3e78cc6 Auto merge base into tabbar 2025-01-14 13:59:27 +00:00
GitHub Actions
e5103ddb7a Auto merge base into tabbar 2025-01-14 11:30:02 +00:00
Burt
ac2128bb76 Merge branch 'base' into tabbar 2025-01-14 19:26:40 +08:00
Burt
5b55e01089 Merge branch 'base' into tabbar 2025-01-06 23:27:20 +08:00
Burt
e57853fda0 Merge branch 'base' into tabbar 2025-01-06 14:00:47 +08:00
Burt
16c27345ed Merge branch 'base' into tabbar 2025-01-05 12:38:34 +08:00
Burt
c214a15bb7 Merge branch 'base' into tabbar 2025-01-01 18:09:03 +08:00
Burt
24b43088a9 Merge branch 'base' into tabbar 2024-12-31 12:21:03 +08:00
Burt
1ee3d06ed8 Merge branch 'base' into tabbar 2024-12-26 12:33:51 +08:00
Burt
5144ceabd1 Merge branch 'base' into tabbar 2024-12-10 21:45:05 +08:00
Burt
7eb4c23116 Merge branch 'base' into tabbar 2024-12-06 14:49:33 +08:00
Burt
53ebadf0a2 Merge branch 'base' into tabbar 2024-12-04 09:42:36 +08:00
Burt
8c1dda51ed Merge branch 'base' into tabbar 2024-12-02 21:02:12 +08:00
Burt
637ca983cf build: 降级uni版本,确保样式正确 2024-12-02 20:57:33 +08:00
Burt
1b61a262da Merge branch 'base' into tabbar 2024-12-02 20:54:08 +08:00
Burt
6fe2b888e4 Merge branch 'base' into tabbar 2024-12-02 16:25:40 +08:00
Burt
003e8685a4 build: update version 2024-12-02 16:20:50 +08:00
菲鸽
7eab089118
Merge pull request #20 from gladtoeatu/base
修复支付宝小程序无法build、onReachBottom支持组件直接调用
2024-11-27 22:17:47 +08:00
Burt
cd130de1cf Merge branch 'base' into tabbar 2024-11-15 16:47:22 +08:00
Burt
988cf6aa53 Merge branch 'base' into tabbar 2024-11-10 22:59:55 +08:00
Burt
7afa74d381 Merge branch base into tabbar 2024-11-10 22:52:08 +08:00
Burt
1bec786aaa Merge branch 'base' into tabbar 2024-11-10 22:48:37 +08:00
Burt
46ae55c73c Merge branch 'base' into tabbar 2024-09-11 20:50:58 +08:00
Burt
0865a33ad4 Merge branch 'base' into tabbar 2024-09-05 20:57:43 +08:00
Burt
f01b98693e Merge branch 'base' 2024-09-05 20:51:47 +08:00
菲鸽
8b357ab8c5 Merge branch 'base' into tabbar 2024-07-16 21:09:37 +08:00
菲鸽
7f41078fa0 Merge branch 'base' into tabbar 2024-06-28 13:08:08 +08:00
菲鸽
e4571097c8 Merge branch 'base' into tabbar 2024-06-25 21:59:57 +08:00
菲鸽
dec635a59d Merge branch 'base' into tabbar 2024-06-25 17:48:07 +08:00
菲鸽
5823f89e5f Merge branch 'base' into tabbar 2024-06-19 14:11:45 +08:00
菲鸽
d20f359769 Merge branch 'base' into tabbar 2024-06-17 17:24:21 +08:00
菲鸽
18d52a8e48 Merge branch 'base' into tabbar 2024-06-17 17:23:20 +08:00
菲鸽
3b2395da26 Merge branch 'base' into tabbar 2024-06-17 16:25:52 +08:00
菲鸽
12aae659fe Merge branch 'base' into tabbar 2024-06-16 16:47:37 +08:00
菲鸽
45744d412e Merge branch 'base' into tabbar 2024-06-16 16:39:14 +08:00
菲鸽
4b7a28fe97 Merge branch 'base' into tabbar 2024-06-07 10:48:09 +08:00
菲鸽
c1ed654ff3 Merge branch 'base' into tabbar 2024-06-04 18:03:36 +08:00
菲鸽
bb30afe1b6 Merge branch 'base' into tabbar 2024-06-03 12:33:53 +08:00
菲鸽
4ebc32dd3b Merge branch 'base' into tabbar 2024-06-01 21:53:56 +08:00
菲鸽
b1bfd0d336 Merge branch 'base' into tabbar 2024-06-01 21:48:43 +08:00
菲鸽
208f2395a6 build: pnpm-lock.yaml 2024-06-01 21:46:39 +08:00
菲鸽
a616867641 Merge branch 'base' into tabbar 2024-06-01 21:45:48 +08:00
菲鸽
bde0332183 refine: my 2024-05-28 11:36:23 +08:00
菲鸽
588c3a446f chore: 注意tabbar路由需要使用 layout:tabbar 布局 2024-05-28 11:34:30 +08:00
菲鸽
a9d3eccad5 feat: 优化tabbar,精简tabbar 2024-05-28 11:33:15 +08:00
菲鸽
01679809b8 fix: tabBar 我的 icon 不见了 2024-05-28 10:15:50 +08:00
菲鸽
a4fd81d2a8 Merge branch 'base' into tabbar 2024-05-26 09:52:31 +08:00
菲鸽
7ab753ee8d Merge branch 'base' into tabbar 2024-05-25 19:28:18 +08:00
菲鸽
e20b84c8d5 Merge branch 'base' into tabbar 2024-05-19 18:48:46 +08:00
菲鸽
77020a13a0 Merge branch 'base' into tabbar 2024-05-18 18:01:12 +08:00
菲鸽
79bbc09850 feat: 优化btn宽度 2024-05-18 17:58:02 +08:00
菲鸽
556f9052d1 Merge branch 'base' into tabbar 2024-05-18 17:46:50 +08:00
菲鸽
3e2cc7be81 chore: 运行 2024-05-18 17:43:34 +08:00
菲鸽
5871cb5fd8 Merge branch 'base' into tabbar 2024-05-12 19:53:51 +08:00
菲鸽
3a088be68b build: pnpm upgrade wot-design-uni 2024-05-12 17:46:37 +08:00
菲鸽
eab3de02fd build(manifest): ignoreVersion: true 2024-05-12 17:43:25 +08:00
菲鸽
7e26a71db8 chore: json 2024-05-12 17:24:47 +08:00
菲鸽
8bf7145752 Merge branch 'base' into tabbar 2024-05-12 17:24:11 +08:00
菲鸽
fd1102b425 Merge branch 'base' into tabbar 2024-05-12 17:19:28 +08:00
菲鸽
005d2406ab Merge branch 'base' into tabbar 2024-05-12 17:06:16 +08:00
菲鸽
fbd6343b2b Merge branch 'base' into tabbar 2024-05-12 16:47:28 +08:00
菲鸽
b11b17ec40 feat: tabbar 支持4种图标 2024-05-12 16:37:25 +08:00
菲鸽
9736931bfd Merge branch 'base' into tabbar 2024-05-12 16:25:53 +08:00
菲鸽
85c34bc922 build: uni-pages.d.ts 2024-05-12 11:54:50 +08:00
菲鸽
70801959ea Merge branch 'base' into tabbar 2024-05-12 11:54:22 +08:00
菲鸽
aa758845f6 build: 运行出自己的pages.json 2024-05-12 10:28:53 +08:00
菲鸽
c6eccbfa19 Merge branch 'base' into tabbar 2024-05-12 10:28:19 +08:00
菲鸽
3ae09fc804 refine: unocss icon 生效提示 2024-05-11 21:27:46 +08:00
菲鸽
695486e41f feat: 三种方式的图标,tabbar都支持 2024-05-11 21:26:56 +08:00
菲鸽
7cf16d608c feat: 续上,H5 也需要隐藏 2024-05-11 20:11:54 +08:00
菲鸽
0d948a0691 feat: app 里面才需要 hideTabBar 2024-05-11 20:10:26 +08:00
菲鸽
50cb3419c2 refine: store key 2024-05-11 11:36:44 +08:00
菲鸽
7271f3c618 fix: tabbar 2个BUG 2024-05-11 11:32:34 +08:00
菲鸽
d6c6022a18 fix: app 未隐藏原生tabbar 2024-05-11 11:26:04 +08:00
菲鸽
84d4607b2a fix: isUnocssIcon 没有了 2024-05-11 09:43:58 +08:00
菲鸽
4f6d271c28 feat: 使用 tabbar layout + wot icon 2024-05-11 09:41:33 +08:00
菲鸽
ee52ae132d refactor: 自定义tabbar 2024-05-11 09:36:26 +08:00
菲鸽
de8df540c2 Merge branch 'base' into tabbar 2024-05-09 08:49:40 +08:00
菲鸽
6d4bb21f52 Merge branch 'base' into tabbar 2024-05-08 21:02:02 +08:00
菲鸽
ffa5c68e66 Merge branch 'base' into tabbar 2024-05-08 20:27:06 +08:00
菲鸽
5eadda62de Merge branch 'base' into tabbar 2024-05-03 20:01:03 +08:00
菲鸽
041403f5d8 Merge branch 'base' into tabbar 2024-05-02 16:55:26 +08:00
菲鸽
137fa5846b Merge branch 'base' into tabbar 2024-04-30 15:00:40 +08:00
菲鸽
ffc37d9030 build: 写成 easycom 规范可以不用显式custom 规则 2024-04-20 11:08:54 +08:00
菲鸽
7e3ab3ca19 fix: tabbar 引入 2024-04-20 11:02:20 +08:00
菲鸽
cdae57adce Merge branch 'base' into tabbar 2024-04-20 10:53:07 +08:00
菲鸽
39007ec045 Merge branch 'base' into tabbar 2024-04-12 15:12:47 +08:00
菲鸽
d4935d1186 Merge branch 'base' into tabbar 2024-04-11 18:40:25 +08:00
菲鸽
12acce4575 Merge branch 'base' into tabbar 2024-04-11 09:25:03 +08:00
菲鸽
3f15da98de Merge branch 'base' into tabbar 2024-04-09 14:27:21 +08:00
菲鸽
fb4a6e7bec Merge branch 'base' into tabbar 2024-04-07 17:23:25 +08:00
菲鸽
6cdb56bb12 Merge branch 'base' into tabbar 2024-04-07 09:02:55 +08:00
菲鸽
e7d0a685d4 Merge branch 'base' into tabbar 2024-04-06 22:22:06 +08:00
菲鸽
c3debbb636 fix: code-snippets 2024-04-05 15:00:03 +08:00
菲鸽
a7c3dbaea2 Merge branch 'base' into tabbar 2024-04-05 14:20:06 +08:00
菲鸽
c032fe5a1e build(tsconfig.json): add components.d.ts ,自定义组件就有提示了 2024-04-04 16:55:26 +08:00
菲鸽
6a018d89be fix: 自动导入的函数,在模板报错 2024-04-04 16:17:29 +08:00
菲鸽
cc716a1e0c build: remove @uni-helper/uni-app-types 才有提示 2024-04-03 16:34:16 +08:00
菲鸽
be35efd530 Merge branch 'base' into tabbar 2024-04-02 21:32:38 +08:00
菲鸽
37b52d1243 refine: 显示模板分支 2024-04-02 21:05:29 +08:00
菲鸽
4770817fdc fix: ts 报错 2024-04-02 20:51:53 +08:00
菲鸽
3a12eb35eb refine: about 页面 2024-04-02 20:46:44 +08:00
菲鸽
01916970e8
!7 自定义tabbar
Merge pull request !7 from summer/custom-tabbar
2024-04-02 12:37:42 +00:00
summer
ee43df4fa6 feat: 新增自定义tabbar分支 2024-04-02 17:31:39 +08:00
72 changed files with 4178 additions and 2824 deletions

3
.commitlintrc.cjs Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
extends: ['@commitlint/config-conventional'],
}

60
.github/release.yml vendored
View File

@ -1,31 +1,31 @@
categories:
- title: '🚀 新功能'
labels: ['feat', 'feature']
- title: '🛠️ 修复'
labels: ['fix', 'bugfix']
- title: '💅 样式'
labels: ['style']
- title: '📄 文档'
labels: ['docs']
- title: '⚡️ 性能'
labels: ['perf']
- title: '🧪 测试'
labels: ['test']
- title: '♻️ 重构'
labels: ['refactor']
- title: '📦 构建'
labels: ['build']
- title: '🚨 补丁'
labels: ['patch', 'hotfix']
- title: '🌐 发布'
labels: ['release', 'publish']
- title: '🔧 流程'
labels: ['ci', 'cd', 'workflow']
- title: '⚙️ 配置'
labels: ['config', 'chore']
- title: '📁 文件'
labels: ['file']
- title: '🎨 格式化'
labels: ['format']
- title: '🔀 其他'
labels: ['other', 'misc']
- title: 🚀 新功能
labels: [feat, feature]
- title: 🛠️ 修复
labels: [fix, bugfix]
- title: 💅 样式
labels: [style]
- title: 📄 文档
labels: [docs]
- title: ⚡️ 性能
labels: [perf]
- title: 🧪 测试
labels: [test]
- title: ♻️ 重构
labels: [refactor]
- title: 📦 构建
labels: [build]
- title: 🚨 补丁
labels: [patch, hotfix]
- title: 🌐 发布
labels: [release, publish]
- title: 🔧 流程
labels: [ci, cd, workflow]
- title: ⚙️ 配置
labels: [config, chore]
- title: 📁 文件
labels: [file]
- title: 🎨 格式化
labels: [format]
- title: 🔀 其他
labels: [other, misc]

View File

@ -1,13 +1,14 @@
name: Auto Merge Base to Other Branches
name: Auto Merge Main to Other Branches
on:
push:
branches:
- base
- main
workflow_dispatch: # 手动触发
jobs:
auto-merge:
merge-to-i18n:
name: Merge main into i18n
runs-on: ubuntu-latest
steps:
- name: Checkout repository
@ -16,34 +17,64 @@ jobs:
fetch-depth: 0
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
- name: Merge base into main
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git checkout main
git merge base --no-ff -m "Auto merge base into main"
git push origin main
- name: Merge base into i18n
- name: Merge main into i18n
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git checkout i18n
git merge base --no-ff -m "Auto merge base into i18n"
git merge main --no-ff -m "Auto merge main into i18n"
git push origin i18n
- name: Merge base into tabbar
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git checkout tabbar
git merge base --no-ff -m "Auto merge base into tabbar"
git push origin tabbar
merge-to-base-sard-ui:
name: Merge main into base-sard-ui
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
- name: Merge base into spa
- name: Merge main into base-sard-ui
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git checkout spa
git merge base --no-ff -m "Auto merge base into spa"
git push origin spa
git checkout base-sard-ui
git merge main --no-ff -m "Auto merge main into base-sard-ui"
git push origin base-sard-ui
merge-to-base-uv-ui:
name: Merge main into base-uv-ui
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
- name: Merge main into base-uv-ui
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git checkout base-uv-ui
git merge main --no-ff -m "Auto merge main into base-uv-ui"
git push origin base-uv-ui
merge-to-base-uview-plus:
name: Merge main into base-uview-plus
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
- name: Merge main into base-uview-plus
run: |
git config user.name "GitHub Actions"
git config user.email "actions@github.com"
git checkout base-uview-plus
git merge main --no-ff -m "Auto merge main into base-uview-plus"
git push origin base-uview-plus

2
.gitignore vendored
View File

@ -27,7 +27,7 @@ dist
docs/.vitepress/dist
docs/.vitepress/cache
types
src/types
# lock 文件还是不要了,我主要的版本写死就好了
# pnpm-lock.yaml

1
.husky/commit-msg Normal file
View File

@ -0,0 +1 @@
npx --no-install commitlint --edit "$1"

1
.husky/pre-commit Normal file
View File

@ -0,0 +1 @@
npx lint-staged --allow-empty

4
.npmrc
View File

@ -1,5 +1,5 @@
registry = https://registry.npmjs.org
# registry = https://registry.npmmirror.com
# registry = https://registry.npmjs.org
registry = https://registry.npmmirror.com
strict-peer-dependencies=false
auto-install-peers=true

View File

@ -1,3 +1,5 @@
node_modules
# unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
auto-import.d.ts

View File

@ -10,7 +10,7 @@ module.exports = {
htmlWhitespaceSensitivity: 'ignore',
overrides: [
{
files: '*.json',
files: '*.{json,jsonc}',
options: {
trailingComma: 'none',
},

View File

@ -13,6 +13,8 @@
"uni-helper.uni-ui-snippets-vscode",
"uni-helper.uni-app-snippets-vscode",
"mrmlnc.vscode-json5",
"streetsidesoftware.code-spell-checker"
"streetsidesoftware.code-spell-checker",
"foxundermoon.shell-format",
"christian-kohler.path-intellisense"
]
}

79
.vscode/settings.json vendored
View File

@ -1,14 +1,7 @@
{
// prettier
"editor.defaultFormatter": "esbenp.prettier-vscode",
//
"editor.formatOnSave": true,
//
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
},
// stylelint
"stylelint.validate": ["css", "scss", "vue", "html"], // package.jsonscripts
"stylelint.enable": true,
@ -41,12 +34,14 @@
"commitlint",
"dcloudio",
"iconfont",
"oxlint",
"qrcode",
"refresherrefresh",
"scrolltolower",
"tabbar",
"Toutiao",
"unibest",
"uview",
"uvui",
"Wechat",
"WechatMiniprogram",
@ -56,7 +51,69 @@
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,.npmrc,.browserslistrc",
".eslintrc.cjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,.stylelintrc.*,.eslintrc-auto-import.json,.editorconfig,.commitlint.cjs"
}
"README.md": "index.html,favicon.ico,robots.txt,CHANGELOG.md",
"pages.config.ts": "manifest.config.ts,openapi-ts-request.config.ts",
"package.json": "tsconfig.json,pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,.npmrc,.browserslistrc",
"eslint.config.mjs": ".commitlintrc.*,.prettier*,.editorconfig,.commitlint.cjs,.eslint*"
},
// //
// "prettier.enable": true,
// "editor.formatOnSave": true,
// //
// "editor.codeActionsOnSave": {
// "source.fixAll": "explicit",
// "source.fixAll.eslint": "explicit",
// "source.fixAll.stylelint": "explicit"
// },
// Disable the default formatter, use eslint instead
"prettier.enable": false,
"editor.formatOnSave": false,
// Auto fix
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit",
"source.organizeImports": "never"
},
// Silent the stylistic rules in you IDE, but still auto fix them
"eslint.rules.customizations": [
{ "rule": "style/*", "severity": "off", "fixable": true },
{ "rule": "format/*", "severity": "off", "fixable": true },
{ "rule": "*-indent", "severity": "off", "fixable": true },
{ "rule": "*-spacing", "severity": "off", "fixable": true },
{ "rule": "*-spaces", "severity": "off", "fixable": true },
{ "rule": "*-order", "severity": "off", "fixable": true },
{ "rule": "*-dangle", "severity": "off", "fixable": true },
{ "rule": "*-newline", "severity": "off", "fixable": true },
{ "rule": "*quotes", "severity": "off", "fixable": true },
{ "rule": "*semi", "severity": "off", "fixable": true }
],
// Enable eslint for all supported languages
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact",
"vue",
"html",
"markdown",
"json",
"json5",
"jsonc",
"yaml",
"toml",
"xml",
"gql",
"graphql",
"astro",
"svelte",
"css",
"less",
"scss",
"pcss",
"postcss"
]
}

View File

@ -27,12 +27,12 @@
" },",
"}",
"</route>\n",
"<template>",
" <view class=\"\">$2</view>",
"</template>\n",
"<script lang=\"ts\" setup>",
"//$3",
"</script>\n",
"<template>",
" <view class=\"\">$2</view>",
"</template>\n",
"<style lang=\"scss\" scoped>",
"//$4",
"</style>\n",
@ -41,16 +41,28 @@
"Print unibest style": {
"scope": "vue",
"prefix": "st",
"body": ["<style lang=\"scss\" scoped>", "//", "</style>\n"],
"body": [
"<style lang=\"scss\" scoped>",
"//",
"</style>\n"
],
},
"Print unibest script": {
"scope": "vue",
"prefix": "sc",
"body": ["<script lang=\"ts\" setup>", "//$3", "</script>\n"],
"body": [
"<script lang=\"ts\" setup>",
"//$3",
"</script>\n"
],
},
"Print unibest template": {
"scope": "vue",
"prefix": "te",
"body": ["<template>", " <view class=\"\">$1</view>", "</template>\n"],
"body": [
"<template>",
" <view class=\"\">$1</view>",
"</template>\n"
],
},
}

View File

@ -63,21 +63,20 @@
## &#x1F4C2; 快速开始
执行 `pnpm create unibest` 创建项目
执行 `pnpm i` 安装依赖
执行 `pnpm dev` 运行 `H5`
执行 `pnpm dev:mp` 运行 `微信小程序`
## 📦 运行(支持热更新)
- web平台 `pnpm dev:h5`, 然后打开 [http://localhost:9000/](http://localhost:9000/)。
- weixin平台`pnpm dev:mp-weixin` 然后打开微信开发者工具,导入本地文件夹,选择本项目的`dist/dev/mp-weixin` 文件。
- weixin平台`pnpm dev:mp` 然后打开微信开发者工具,导入本地文件夹,选择本项目的`dist/dev/mp-weixin` 文件。
- APP平台`pnpm dev:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/dev/app` 文件夹,选择运行到模拟器(开发时优先使用),或者运行的安卓/ios基座。
## 🔗 发布
- web平台 `pnpm build:h5`,打包后的文件在 `dist/build/h5`可以放到web服务器如nginx运行。如果最终不是放在根目录可以在 `manifest.config.ts` 文件的 `h5.router.base` 属性进行修改。
- weixin平台`pnpm build:mp-weixin`, 打包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
- weixin平台`pnpm build:mp`, 打包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
- APP平台`pnpm build:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/build/app` 文件夹,选择发行 - APP云打包。
## 📄 License

5
env/.env vendored
View File

@ -1,12 +1,15 @@
VITE_APP_TITLE = 'unibest'
VITE_APP_PORT = 9000
VITE_UNI_APPID = 'H57F2ACE4'
VITE_UNI_APPID = '__UNI__D1E5001'
VITE_WX_APPID = 'wxa2abb91f64032a2b'
# h5部署网站的base配置到 manifest.config.ts 里的 h5.router.base
VITE_APP_PUBLIC_BASE=/
# 登录页面
VITE_LOGIN_URL = '/pages/login/index'
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'

22
eslint.config.mjs Normal file
View File

@ -0,0 +1,22 @@
import uniHelper from '@uni-helper/eslint-config'
export default uniHelper({
unocss: true,
vue: true,
markdown: false,
ignores: [
'src/uni_modules/',
'dist',
],
rules: {
'no-console': 'off',
'no-unused-vars': 'off',
'vue/no-unused-refs': 'off',
'unused-imports/no-unused-vars': 'off',
'eslint-comments/no-unlimited-disable': 'off',
'jsdoc/check-param-names': 'off',
'jsdoc/require-returns-description': 'off',
'ts/no-empty-object-type': 'off',
'no-extend-native': 'off',
},
})

View File

@ -1,6 +1,7 @@
import path from 'node:path'
import process from 'node:process'
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
import path from 'node:path'
import { loadEnv } from 'vite'
// 获取环境变量的范例
@ -14,14 +15,14 @@ const {
} = env
export default defineManifestConfig({
name: VITE_APP_TITLE,
appid: VITE_UNI_APPID,
description: '',
versionName: '1.0.0',
versionCode: '100',
transformPx: false,
locale: VITE_FALLBACK_LOCALE, // 'zh-Hans'
h5: {
'name': VITE_APP_TITLE,
'appid': VITE_UNI_APPID,
'description': '',
'versionName': '1.0.0',
'versionCode': '100',
'transformPx': false,
'locale': VITE_FALLBACK_LOCALE, // 'zh-Hans'
'h5': {
router: {
base: VITE_APP_PUBLIC_BASE,
},
@ -82,14 +83,14 @@ export default defineManifestConfig({
ios: {
appstore: 'static/app/icons/1024x1024.png',
ipad: {
app: 'static/app/icons/76x76.png',
'app': 'static/app/icons/76x76.png',
'app@2x': 'static/app/icons/152x152.png',
notification: 'static/app/icons/20x20.png',
'notification': 'static/app/icons/20x20.png',
'notification@2x': 'static/app/icons/40x40.png',
'proapp@2x': 'static/app/icons/167x167.png',
settings: 'static/app/icons/29x29.png',
'settings': 'static/app/icons/29x29.png',
'settings@2x': 'static/app/icons/58x58.png',
spotlight: 'static/app/icons/40x40.png',
'spotlight': 'static/app/icons/40x40.png',
'spotlight@2x': 'static/app/icons/80x80.png',
},
iphone: {
@ -107,12 +108,15 @@ export default defineManifestConfig({
},
},
/* 快应用特有相关 */
quickapp: {},
'quickapp': {},
/* 小程序特有相关 */
'mp-weixin': {
appid: VITE_WX_APPID,
setting: {
urlCheck: false,
// 是否启用 ES6 转 ES5
es6: true,
minified: true,
},
usingComponents: true,
// __usePrivacyCheck__: true,
@ -127,8 +131,8 @@ export default defineManifestConfig({
'mp-toutiao': {
usingComponents: true,
},
uniStatistics: {
'uniStatistics': {
enable: false,
},
vueVersion: '3',
'vueVersion': '3',
})

View File

@ -1,8 +1,9 @@
{
"name": "unibest",
"type": "commonjs",
"version": "2.10.1",
"version": "3.1.0",
"description": "unibest - 最好的 uniapp 开发模板",
"update-time": "2025-06-21",
"author": {
"name": "feige996",
"zhName": "菲鸽",
@ -11,13 +12,14 @@
"gitee": "https://gitee.com/feige996"
},
"license": "MIT",
"homepage": "https://unibest.tech",
"repository": "https://github.com/feige996/unibest",
"repository-gitee": "https://gitee.com/feige996/unibest",
"repository-deprecated": "https://github.com/codercup/unibest",
"repository-old": "https://github.com/codercup/unibest",
"bugs": {
"url": "https://github.com/feige996/unibest/issues"
"url": "https://github.com/feige996/unibest/issues",
"url-old": "https://github.com/codercup/unibest/issues"
},
"homepage": "https://feige996.github.io/unibest/",
"engines": {
"node": ">=18",
"pnpm": ">=7.30"
@ -68,18 +70,10 @@
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"type-check": "vue-tsc --noEmit",
"openapi-ts-request": "openapi-ts"
},
"lint-staged": {
"**/*.{html,vue,ts,cjs,json,md}": [
"prettier --write"
],
"**/*.{vue,js,ts,jsx,tsx,vue,css,scss,html}": [
"oxlint --fix"
]
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china"
"openapi-ts-request": "openapi-ts",
"prepare": "git init && husky",
"lint": "eslint",
"lint:fix": "eslint --fix"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-4060620250520001",
@ -104,12 +98,14 @@
"js-cookie": "^3.0.5",
"pinia": "2.0.36",
"pinia-plugin-persistedstate": "3.2.1",
"qs": "6.5.3",
"vue": "^3.5.15",
"vue": "^3.4.21",
"wot-design-uni": "^1.9.1",
"z-paging": "^2.8.4"
"z-paging": "2.8.7"
},
"devDependencies": {
"@antfu/eslint-config": "^4.15.0",
"@commitlint/cli": "^19.8.1",
"@commitlint/config-conventional": "^19.8.1",
"@dcloudio/types": "^3.4.8",
"@dcloudio/uni-automator": "3.0.0-4060620250520001",
"@dcloudio/uni-cli-shared": "3.0.0-4060620250520001",
@ -121,32 +117,42 @@
"@rollup/rollup-darwin-x64": "^4.28.0",
"@types/node": "^20.17.9",
"@types/wechat-miniprogram": "^3.4.8",
"@uni-helper/eslint-config": "^0.4.0",
"@uni-helper/uni-types": "1.0.0-alpha.3",
"@uni-helper/unocss-preset-uni": "^0.2.11",
"@uni-helper/vite-plugin-uni-components": "^0.2.0",
"@uni-helper/vite-plugin-uni-layouts": "^0.1.10",
"@uni-helper/vite-plugin-uni-manifest": "^0.2.8",
"@uni-helper/vite-plugin-uni-pages": "0.2.20",
"@uni-helper/vite-plugin-uni-platform": "^0.0.4",
"@uni-helper/vite-plugin-uni-components": "0.2.0",
"@uni-helper/vite-plugin-uni-layouts": "0.1.10",
"@uni-helper/vite-plugin-uni-manifest": "0.2.8",
"@uni-helper/vite-plugin-uni-pages": "0.2.28",
"@uni-helper/vite-plugin-uni-platform": "0.0.4",
"@uni-ku/bundle-optimizer": "^1.3.3",
"@unocss/eslint-plugin": "^66.2.3",
"@unocss/preset-legacy-compat": "^0.59.4",
"@vue/runtime-core": "^3.4.21",
"@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.20",
"eslint": "^9.29.0",
"husky": "^9.1.7",
"lint-staged": "^15.2.10",
"openapi-ts-request": "^1.1.2",
"oxlint": "^0.1.0",
"postcss": "^8.4.49",
"postcss-html": "^1.7.0",
"postcss-scss": "^4.0.9",
"prettier": "^3.5.3",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "1.77.8",
"terser": "^5.36.0",
"typescript": "^5.7.2",
"unocss": "^66.0.0",
"unocss": "65.4.2",
"unplugin-auto-import": "^0.17.8",
"vite": "6.3.5",
"vite": "5.2.8",
"vite-plugin-restart": "^0.4.2",
"vue-tsc": "^2.2.10"
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china"
},
"lint-staged": {
"*": "eslint --fix"
}
}

View File

@ -1,4 +1,5 @@
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
import { tabBar } from './src/layouts/fg-tabbar/tabbarList'
export default defineUniPages({
globalStyle: {
@ -17,35 +18,6 @@ export default defineUniPages({
'z-paging/components/z-paging$1/z-paging$1.vue',
},
},
// 如果不需要tabBar可以注释掉这个配置或者直接删除
tabBar: {
color: '#999999',
selectedColor: '#018d71',
backgroundColor: '#F8F8F8',
borderStyle: 'black',
height: '50px',
fontSize: '10px',
iconWidth: '24px',
spacing: '3px',
list: [
{
iconPath: 'static/tabbar/home.png',
selectedIconPath: 'static/tabbar/homeHL.png',
pagePath: 'pages/index/index',
text: '首页',
},
{
iconPath: 'static/tabbar/example.png',
selectedIconPath: 'static/tabbar/exampleHL.png',
pagePath: 'pages/about/about',
text: '关于',
},
{
iconPath: 'static/tabbar/personal.png',
selectedIconPath: 'static/tabbar/personalHL.png',
pagePath: 'pages/mine/index',
text: '我的',
},
],
},
// tabbar 的配置统一在 “./src/layouts/fg-tabbar/tabbarList.ts” 文件中
tabBar: tabBar as any,
})

View File

@ -0,0 +1,13 @@
diff --git a/dist/uni-h5.es.js b/dist/uni-h5.es.js
index 7421bad97d94ad34a3d4d94292a9ee9071430662..19c6071ee4036ceb8d1cfa09030e471c002d2cda 100644
--- a/dist/uni-h5.es.js
+++ b/dist/uni-h5.es.js
@@ -23410,7 +23410,7 @@ function useShowTabBar(emit2) {
const tabBar2 = useTabBar();
const showTabBar2 = computed(() => route.meta.isTabBar && tabBar2.shown);
updateCssVar({
- "--tab-bar-height": tabBar2.height
+ "--tab-bar-height": tabBar2?.height || 0
});
return showTabBar2;
}

3570
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

2
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,2 @@
patchedDependencies:
'@dcloudio/uni-h5': patches/@dcloudio__uni-h5.patch

View File

@ -2,8 +2,7 @@
// # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
// # 只需要执行下面的命令即可
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { exec } = require('child_process')
const { exec } = require('node:child_process')
// 定义要执行的命令
const dependencies = [

View File

@ -1,7 +1,10 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
import { onHide, onLaunch, onShow } from '@dcloudio/uni-app'
import { usePageAuth } from '@/hooks/usePageAuth'
import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only'
usePageAuth()
onLaunch(() => {
console.log('App Launch')
})
@ -14,7 +17,6 @@ onHide(() => {
</script>
<style lang="scss">
/* stylelint-disable selector-type-no-unknown */
button::after {
border: none;
}

View File

@ -1,4 +1,4 @@
import { ICaptcha, IUpdateInfo, IUpdatePassword, IUserInfoVo, IUserLogin } from './login.typings'
import type { ICaptcha, IUpdateInfo, IUpdatePassword, IUserInfoVo, IUserLogin } from './types/login'
import { http } from '@/utils/http'
/**
@ -15,7 +15,7 @@ export interface ILoginForm {
*
* @returns ICaptcha
*/
export const getCode = () => {
export function getCode() {
return http.get<ICaptcha>('/user/getCode')
}
@ -23,35 +23,35 @@ export const getCode = () => {
*
* @param loginForm
*/
export const login = (loginForm: ILoginForm) => {
export function login(loginForm: ILoginForm) {
return http.post<IUserLogin>('/user/login', loginForm)
}
/**
*
*/
export const getUserInfo = () => {
export function getUserInfo() {
return http.get<IUserInfoVo>('/user/info')
}
/**
* 退
*/
export const logout = () => {
export function logout() {
return http.get<void>('/user/logout')
}
/**
*
*/
export const updateInfo = (data: IUpdateInfo) => {
export function updateInfo(data: IUpdateInfo) {
return http.post('/user/updateInfo', data)
}
/**
*
*/
export const updateUserPassword = (data: IUpdatePassword) => {
export function updateUserPassword(data: IUpdatePassword) {
return http.post('/user/updatePassword', data)
}
@ -59,12 +59,12 @@ export const updateUserPassword = (data: IUpdatePassword) => {
*
* @returns Promise (code)
*/
export const getWxCode = () => {
export function getWxCode() {
return new Promise<UniApp.LoginRes>((resolve, reject) => {
uni.login({
provider: 'weixin',
success: (res) => resolve(res),
fail: (err) => reject(new Error(err)),
success: res => resolve(res),
fail: err => reject(new Error(err)),
})
})
}
@ -78,6 +78,6 @@ export const getWxCode = () => {
* @param params code
* @returns Promise
*/
export const wxLogin = (data: { code: string }) => {
export function wxLogin(data: { code: string }) {
return http.post<IUserLogin>('/user/wxLogin', data)
}

View File

@ -1,7 +1,7 @@
/**
*
*/
export type IUserInfoVo = {
export interface IUserInfoVo {
id: number
username: string
avatar: string
@ -11,7 +11,7 @@ export type IUserInfoVo = {
/**
*
*/
export type IUserLogin = {
export interface IUserLogin {
id: string
username: string
token: string
@ -20,7 +20,7 @@ export type IUserLogin = {
/**
*
*/
export type ICaptcha = {
export interface ICaptcha {
captchaEnabled: boolean
uuid: string
image: string
@ -28,7 +28,7 @@ export type ICaptcha = {
/**
*
*/
export type IUploadSuccessInfo = {
export interface IUploadSuccessInfo {
fileId: number
originalName: string
fileName: string
@ -41,7 +41,7 @@ export type IUploadSuccessInfo = {
/**
*
*/
export type IUpdateInfo = {
export interface IUpdateInfo {
id: number
name: string
sex: string
@ -49,7 +49,7 @@ export type IUpdateInfo = {
/**
*
*/
export type IUpdatePassword = {
export interface IUpdatePassword {
id: number
oldPassword: string
newPassword: string

View File

@ -1,47 +0,0 @@
@import 'wot-design-uni/components/wd-button/index.scss';
:deep(.wd-privacy-popup) {
width: 600rpx;
padding: 0 24rpx;
box-sizing: border-box;
border-radius: 32rpx;
overflow: hidden;
}
.wd-privacy-popup {
&__header {
width: 100%;
height: 128rpx;
line-height: 128rpx;
color: rgba(0, 0, 0, 0.85);
font-size: 30rpx;
padding: 0 12rpx;
box-sizing: border-box;
}
&__container {
width: 100%;
box-sizing: border-box;
padding: 0 12rpx;
margin-bottom: 32rpx;
font-size: 28rpx;
line-height: 1.8;
color: #3e3e3e;
text-align: left;
font-weight: 550;
&-protocol {
color: #4d80f0;
}
}
&__footer {
display: flex;
justify-content: space-between;
padding-bottom: 36rpx;
button {
border: none;
outline: none;
}
}
}

View File

@ -1,144 +0,0 @@
<template>
<view>
<wd-popup
v-model="showPopup"
:close-on-click-modal="false"
custom-class="wd-privacy-popup"
@close="handleClose"
>
<view class="wd-privacy-popup__header">
<!--标题-->
<view class="wd-picker__title">{{ title }}</view>
</view>
<view class="wd-privacy-popup__container">
<text>{{ desc }}</text>
<text class="wd-privacy-popup__container-protocol" @click="openPrivacyContract">
{{ protocol }}
</text>
<text>{{ subDesc }}</text>
</view>
<view class="wd-privacy-popup__footer">
<button
class="wd-privacy-popup__footer-disagree wd-button is-block is-round is-medium is-plain"
id="disagree-btn"
@click="handleDisagree"
>
拒绝
</button>
<button
class="wd-privacy-popup__footer-agree wd-button is-primary is-block is-round is-medium"
id="agree-btn"
open-type="agreePrivacyAuthorization"
@agreeprivacyauthorization="handleAgree"
>
同意
</button>
</view>
</wd-popup>
</view>
</template>
<script lang="ts">
export default {
name: 'privacy-popup',
options: {
virtualHost: true,
addGlobalClass: true,
styleIsolation: 'shared',
},
}
</script>
<script lang="ts" setup>
import { onBeforeMount, ref } from 'vue'
type Props = {
title?: string //
desc?: string //
subDesc?: string //
protocol?: string //
}
const props = withDefaults(defineProps<Props>(), {
title: '用户隐私保护提示',
desc: '感谢您使用本应用,您使用本应用的服务之前请仔细阅读并同意',
subDesc:
'。当您点击同意并开始时用产品服务时,即表示你已理解并同息该条款内容,该条款将对您产生法律约束力。如您拒绝,将无法使用相应服务。',
protocol: '《用户隐私保护指引》',
})
const showPopup = ref<boolean>(false) // popup
const privacyResolves = ref(new Set()) // onNeedPrivacyAuthorizationreslove
const privacyHandler = (resolve: any) => {
showPopup.value = true
privacyResolves.value.add(resolve)
}
const emit = defineEmits(['agree', 'disagree'])
onBeforeMount(() => {
//
if ((wx as any).onNeedPrivacyAuthorization) {
;(wx as any).onNeedPrivacyAuthorization((resolve: any) => {
if (typeof privacyHandler === 'function') {
privacyHandler(resolve)
}
})
}
})
/**
* 同意隐私协议
*/
function handleAgree() {
showPopup.value = false
privacyResolves.value.forEach((resolve: any) => {
resolve({
event: 'agree',
buttonId: 'agree-btn',
})
})
privacyResolves.value.clear()
emit('agree')
}
/**
* 拒绝隐私协议
*/
function handleDisagree() {
showPopup.value = false
privacyResolves.value.forEach((resolve: any) => {
resolve({
event: 'disagree',
})
})
privacyResolves.value.clear()
}
/**
* 打开隐私协议
*/
function openPrivacyContract() {
;(wx as any).openPrivacyContract({
success: (res) => {
console.log('openPrivacyContract success')
},
fail: (res) => {
console.error('openPrivacyContract fail', res)
},
})
}
/**
* 弹出框关闭时清空
*/
function handleClose() {
privacyResolves.value.clear()
}
</script>
<style lang="scss" scoped>
@import './index.scss';
</style>

7
src/env.d.ts vendored
View File

@ -2,8 +2,8 @@
/// <reference types="vite-svg-loader" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
import type { DefineComponent } from 'vue'
const component: DefineComponent<{}, {}, any>
export default component
}
@ -29,3 +29,6 @@ interface ImportMetaEnv {
interface ImportMeta {
readonly env: ImportMetaEnv
}
declare const __VITE_APP_PROXY__: 'true' | 'false'
declare const __UNI_PLATFORM__: 'app' | 'h5' | 'mp-alipay' | 'mp-baidu' | 'mp-kuaishou' | 'mp-lark' | 'mp-qq' | 'mp-tiktok' | 'mp-weixin' | 'mp-xiaochengxu'

50
src/hooks/usePageAuth.ts Normal file
View File

@ -0,0 +1,50 @@
import { onLoad } from '@dcloudio/uni-app'
import { useUserStore } from '@/store'
import { needLoginPages as _needLoginPages, getNeedLoginPages } from '@/utils'
const loginRoute = import.meta.env.VITE_LOGIN_URL
const isDev = import.meta.env.DEV
function isLogined() {
const userStore = useUserStore()
return !!userStore.userInfo.username
}
// 检查当前页面是否需要登录
export function usePageAuth() {
onLoad((options) => {
// 获取当前页面路径
const pages = getCurrentPages()
const currentPage = pages[pages.length - 1]
const currentPath = `/${currentPage.route}`
// 获取需要登录的页面列表
let needLoginPages: string[] = []
if (isDev) {
needLoginPages = getNeedLoginPages()
}
else {
needLoginPages = _needLoginPages
}
// 检查当前页面是否需要登录
const isNeedLogin = needLoginPages.includes(currentPath)
if (!isNeedLogin) {
return
}
const hasLogin = isLogined()
if (hasLogin) {
return true
}
// 构建重定向URL
const queryString = Object.entries(options || {})
.map(([key, value]) => `${key}=${encodeURIComponent(String(value))}`)
.join('&')
const currentFullPath = queryString ? `${currentPath}?${queryString}` : currentPath
const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(currentFullPath)}`
// 重定向到登录页
uni.redirectTo({ url: redirectRoute })
})
}

View File

@ -1,6 +1,6 @@
import { UnwrapRef } from 'vue'
import type { UnwrapRef } from 'vue'
type IUseRequestOptions<T> = {
interface IUseRequestOptions<T> {
/** 是否立即执行 */
immediate?: boolean
/** 初始化数据 */

View File

@ -1,69 +1,160 @@
// TODO: 别忘加更改环境变量的 VITE_UPLOAD_BASEURL 地址。
import { ref } from 'vue'
import { getEnvBaseUploadUrl } from '@/utils'
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
/**
* useUpload
* @param formData {name: '菲鸽'}
* @returns {loading, error, data, run}
*/
export default function useUpload<T = string>(formData: Record<string, any> = {}) {
type TfileType = 'image' | 'file'
type TImage = 'png' | 'jpg' | 'jpeg' | 'webp' | '*'
type TFile = 'doc' | 'docx' | 'ppt' | 'zip' | 'xls' | 'xlsx' | 'txt' | TImage
interface TOptions<T extends TfileType> {
formData?: Record<string, any>
maxSize?: number
accept?: T extends 'image' ? TImage[] : TFile[]
fileType?: T
success?: (params: any) => void
error?: (err: any) => void
}
export default function useUpload<T extends TfileType>(options: TOptions<T> = {} as TOptions<T>) {
const {
formData = {},
maxSize = 5 * 1024 * 1024,
accept = ['*'],
fileType = 'image',
success,
error: onError,
} = options
const loading = ref(false)
const error = ref(false)
const data = ref<T>()
const error = ref<Error | null>(null)
const data = ref<any>(null)
const handleFileChoose = ({ tempFilePath, size }: { tempFilePath: string, size: number }) => {
if (size > maxSize) {
uni.showToast({
title: `文件大小不能超过 ${maxSize / 1024 / 1024}MB`,
icon: 'none',
})
return
}
// const fileExtension = file?.tempFiles?.name?.split('.').pop()?.toLowerCase()
// const isTypeValid = accept.some((type) => type === '*' || type.toLowerCase() === fileExtension)
// if (!isTypeValid) {
// uni.showToast({
// title: `仅支持 ${accept.join(', ')} 格式的文件`,
// icon: 'none',
// })
// return
// }
loading.value = true
uploadFile({
tempFilePath,
formData,
onSuccess: (res) => {
const { data: _data } = JSON.parse(res)
data.value = _data
// console.log('上传成功', res)
success?.(_data)
},
onError: (err) => {
error.value = err
onError?.(err)
},
onComplete: () => {
loading.value = false
},
})
}
const run = () => {
// #ifdef MP-WEIXIN
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
// 微信小程序在2023年10月17日之后使用本API需要配置隐私协议
uni.chooseMedia({
const chooseFileOptions = {
count: 1,
mediaType: ['image'],
success: (res) => {
loading.value = true
const tempFilePath = res.tempFiles[0].tempFilePath
uploadFile<T>({ tempFilePath, formData, data, error, loading })
success: (res: any) => {
console.log('File selected successfully:', res)
// 小程序中res:{errMsg: "chooseImage:ok", tempFiles: [{fileType: "image", size: 48976, tempFilePath: "http://tmp/5iG1WpIxTaJf3ece38692a337dc06df7eb69ecb49c6b.jpeg"}]}
// h5中res:{errMsg: "chooseImage:ok", tempFilePaths: "blob:http://localhost:9000/f74ab6b8-a14d-4cb6-a10d-fcf4511a0de5", tempFiles: [File]}
// h5的File有以下字段{name: "girl.jpeg", size: 48976, type: "image/jpeg"}
// App中res:{errMsg: "chooseImage:ok", tempFilePaths: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", tempFiles: [File]}
// App的File有以下字段{path: "file:///Users/feige/xxx/gallery/1522437259-compressed-IMG_0006.jpg", size: 48976}
let tempFilePath = ''
let size = 0
// #ifdef MP-WEIXIN
tempFilePath = res.tempFiles[0].tempFilePath
size = res.tempFiles[0].size
// #endif
// #ifndef MP-WEIXIN
tempFilePath = res.tempFilePaths[0]
size = res.tempFiles[0].size
// #endif
handleFileChoose({ tempFilePath, size })
},
fail: (err) => {
console.error('uni.chooseMedia err->', err)
error.value = true
fail: (err: any) => {
console.error('File selection failed:', err)
error.value = err
onError?.(err)
},
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseImage({
count: 1,
success: (res) => {
loading.value = true
const tempFilePath = res.tempFilePaths[0]
uploadFile<T>({ tempFilePath, formData, data, error, loading })
},
fail: (err) => {
console.error('uni.chooseImage err->', err)
error.value = true
},
})
// #endif
}
if (fileType === 'image') {
// #ifdef MP-WEIXIN
uni.chooseMedia({
...chooseFileOptions,
mediaType: ['image'],
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseImage(chooseFileOptions)
// #endif
}
else {
uni.chooseFile({
...chooseFileOptions,
type: 'all',
})
}
}
return { loading, error, data, run }
}
function uploadFile<T>({ tempFilePath, formData, data, error, loading }) {
async function uploadFile({
tempFilePath,
formData,
onSuccess,
onError,
onComplete,
}: {
tempFilePath: string
formData: Record<string, any>
onSuccess: (data: any) => void
onError: (err: any) => void
onComplete: () => void
}) {
uni.uploadFile({
url: VITE_UPLOAD_BASEURL,
filePath: tempFilePath,
name: 'file',
formData,
success: (uploadFileRes) => {
data.value = uploadFileRes.data as T
try {
const data = uploadFileRes.data
onSuccess(data)
}
catch (err) {
onError(err)
}
},
fail: (err) => {
console.error('uni.uploadFile err->', err)
error.value = true
},
complete: () => {
loading.value = false
console.error('Upload failed:', err)
onError(err)
},
complete: onComplete,
})
}

View File

@ -1,3 +1,3 @@
export { routeInterceptor } from './route'
export { requestInterceptor } from './request'
export { prototypeInterceptor } from './prototype'
export { requestInterceptor } from './request'
export { routeInterceptor } from './route'

View File

@ -2,10 +2,11 @@ export const prototypeInterceptor = {
install() {
// 解决低版本手机不识别 array.at() 导致运行报错的问题
if (typeof Array.prototype.at !== 'function') {
// eslint-disable-next-line no-extend-native
Array.prototype.at = function (index: number) {
if (index < 0) return this[this.length + index]
if (index >= this.length) return undefined
if (index < 0)
return this[this.length + index]
if (index >= this.length)
return undefined
return this[index]
}
}

View File

@ -1,8 +1,7 @@
/* eslint-disable no-param-reassign */
import qs from 'qs'
import { useUserStore } from '@/store'
import { platform } from '@/utils/platform'
import { getEnvBaseUrl } from '@/utils'
import { platform } from '@/utils/platform'
import { stringifyQuery } from '@/utils/queryString'
export type CustomRequestOptions = UniApp.RequestOptions & {
query?: Record<string, any>
@ -19,10 +18,11 @@ const httpInterceptor = {
invoke(options: CustomRequestOptions) {
// 接口请求支持通过 query 参数配置 queryString
if (options.query) {
const queryStr = qs.stringify(options.query)
const queryStr = stringifyQuery(options.query)
if (options.url.includes('?')) {
options.url += `&${queryStr}`
} else {
}
else {
options.url += `?${queryStr}`
}
}
@ -33,7 +33,8 @@ const httpInterceptor = {
if (JSON.parse(__VITE_APP_PROXY__)) {
// 自动拼接代理前缀
options.url = import.meta.env.VITE_APP_PROXY_PREFIX + options.url
} else {
}
else {
options.url = baseUrl + options.url
}
// #endif

View File

@ -5,12 +5,12 @@
* 便使
*/
import { useUserStore } from '@/store'
import { needLoginPages as _needLoginPages, getNeedLoginPages, getLastPage } from '@/utils'
import { needLoginPages as _needLoginPages, getLastPage, getNeedLoginPages } from '@/utils'
// TODO Check
const loginRoute = '/pages/login/index'
const loginRoute = import.meta.env.VITE_LOGIN_URL
const isLogined = () => {
function isLogined() {
const userStore = useUserStore()
return !!userStore.userInfo.username
}
@ -37,7 +37,8 @@ const navigateToInterceptor = {
// 为了防止开发时出现BUG这里每次都获取一下。生产环境可以移到函数外性能更好
if (isDev) {
needLoginPages = getNeedLoginPages()
} else {
}
else {
needLoginPages = _needLoginPages
}
const isNeedLogin = needLoginPages.includes(path)

View File

@ -1,12 +1,3 @@
<template>
<wd-config-provider :themeVars="themeVars">
<slot />
<wd-toast />
<wd-message-box />
<privacy-popup />
</wd-config-provider>
</template>
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni'
@ -16,3 +7,11 @@ const themeVars: ConfigProviderThemeVars = {
// buttonPrimaryColor: '#07c160',
}
</script>
<template>
<wd-config-provider :theme-vars="themeVars">
<slot />
<wd-toast />
<wd-message-box />
</wd-config-provider>
</template>

View File

@ -1,11 +1,3 @@
<template>
<wd-config-provider :themeVars="themeVars">
<slot />
<wd-toast />
<wd-message-box />
</wd-config-provider>
</template>
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni'
@ -15,3 +7,11 @@ const themeVars: ConfigProviderThemeVars = {
// buttonPrimaryColor: '#07c160',
}
</script>
<template>
<wd-config-provider :theme-vars="themeVars">
<slot />
<wd-toast />
<wd-message-box />
</wd-config-provider>
</template>

View File

@ -0,0 +1,67 @@
<script setup lang="ts">
import { tabbarStore } from './tabbar'
// 'i-carbon-code',
import { tabbarList as _tabBarList, cacheTabbarEnable, selectedTabbarStrategy } from './tabbarList'
// @ts-expect-error
const customTabbarEnable = selectedTabbarStrategy === 1 || selectedTabbarStrategy === 2
/** tabbarList 里面的 path 从 pages.config.ts 得到 */
const tabbarList = _tabBarList.map(item => ({ ...item, path: `/${item.pagePath}` }))
function selectTabBar({ value: index }: { value: number }) {
const url = tabbarList[index].path
tabbarStore.setCurIdx(index)
if (cacheTabbarEnable) {
uni.switchTab({ url })
}
else {
uni.navigateTo({ url })
}
}
onLoad(() => {
// tabBar 2 tabBar
// @ts-expect-error
const hideRedundantTabbarEnable = selectedTabbarStrategy === 1
hideRedundantTabbarEnable
&& uni.hideTabBar({
fail(err) {
console.log('hideTabBar fail: ', err)
},
success(res) {
console.log('hideTabBar success: ', res)
},
})
})
</script>
<template>
<wd-tabbar
v-if="customTabbarEnable"
v-model="tabbarStore.curIdx"
bordered
safeareainsetbottom
placeholder
fixed
@change="selectTabBar"
>
<block v-for="(item, idx) in tabbarList" :key="item.path">
<wd-tabbar-item v-if="item.iconType === 'uiLib'" :title="item.text" :icon="item.icon" />
<wd-tabbar-item
v-else-if="item.iconType === 'unocss' || item.iconType === 'iconfont'"
:title="item.text"
>
<template #icon>
<view
h-40rpx
w-40rpx
:class="[item.icon, idx === tabbarStore.curIdx ? 'is-active' : 'is-inactive']"
/>
</template>
</wd-tabbar-item>
<wd-tabbar-item v-else-if="item.iconType === 'local'" :title="item.text">
<template #icon>
<image :src="item.icon" h-40rpx w-40rpx />
</template>
</wd-tabbar-item>
</block>
</wd-tabbar>
</template>

View File

@ -0,0 +1,16 @@
# tabbar 说明
`tabbar` 分为 `4 种` 情况:
- `完全原生 tabbar`,使用 `switchTab` 切换 tabbar`tabbar` 页面有缓存。
- 优势:原生自带的 tabbar最先渲染有缓存。
- 劣势:只能使用 2 组图片来切换选中和非选中状态,修改颜色只能重新换图片(或者用 iconfont
- `半自定义 tabbar`,使用 `switchTab` 切换 tabbar`tabbar` 页面有缓存。使用了第三方 UI 库的 `tabbar` 组件,并隐藏了原生 `tabbar` 的显示。
- 优势:可以随意配置自己想要的 `svg icon`,切换字体颜色方便。有缓存。可以实现各种花里胡哨的动效等。
- 劣势:首次点击 tababr 会闪烁。
- `全自定义 tabbar`,使用 `navigateTo` 切换 `tabbar``tabbar` 页面无缓存。使用了第三方 UI 库的 `tabbar` 组件。
- 优势:可以随意配置自己想要的 svg icon切换字体颜色方便。可以实现各种花里胡哨的动效等。
- 劣势:首次点击 `tababr` 会闪烁,无缓存。
- `无 tabbar`,只有一个页面入口,底部无 `tabbar` 显示;常用语临时活动页。
> 注意:花里胡哨的效果需要自己实现,本模版不提供。

View File

@ -0,0 +1,11 @@
/**
* tabbar storageSync tabbar
* 使reactive简单状态 pinia
*/
export const tabbarStore = reactive({
curIdx: uni.getStorageSync('app-tabbar-index') || 0,
setCurIdx(idx: number) {
this.curIdx = idx
uni.setStorageSync('app-tabbar-index', idx)
},
})

View File

@ -0,0 +1,65 @@
/**
* tabbar tabbar.md
* 0: 'NATIVE_TABBAR' `完全原生 tabbar`
* 2: 'FULL_CUSTOM_TABBAR' `全自定义 tabbar`
* 1: 'HALF_CUSTOM_TABBAR' `半自定义 tabbar`
* 3: 'NO_TABBAR' `无 tabbar`
*
* pages.json
*/
// TODO通过这里切换使用tabbar的策略
export const selectedTabbarStrategy = 0
// 0 和 1 时需要tabbar缓存
export const cacheTabbarEnable = selectedTabbarStrategy < 2
// selectedTabbarStrategy==0 时,需要填 iconPath 和 selectedIconPath
// selectedTabbarStrategy==1 or 2 时,需要填 icon 和 iconType
// selectedTabbarStrategy==3 时tabbarList 不生效
export const tabbarList = [
{
iconPath: 'static/tabbar/home.png',
selectedIconPath: 'static/tabbar/homeHL.png',
pagePath: 'pages/index/index',
text: '首页',
icon: 'home',
iconType: 'uiLib',
},
{
iconPath: 'static/tabbar/example.png',
selectedIconPath: 'static/tabbar/exampleHL.png',
pagePath: 'pages/about/about',
text: '关于',
icon: 'i-carbon-code',
// 注意 unocss 的图标需要在 页面上引入一下,或者配置到 unocss.config.ts 的 safelist 中
iconType: 'unocss',
},
// {
// pagePath: 'pages/my/index',
// text: '我的',
// icon: '/static/logo.svg',
// iconType: 'local',
// },
// {
// pagePath: 'pages/mine/index',
// text: '我的',
// icon: 'iconfont icon-my',
// iconType: 'iconfont',
// },
]
const _tabbar = {
color: '#999999',
selectedColor: '#018d71',
backgroundColor: '#F8F8F8',
borderStyle: 'black',
height: '50px',
fontSize: '10px',
iconWidth: '24px',
spacing: '3px',
list: tabbarList,
}
// 0和1 需要显示底部的tabbar的各种配置以利用缓存
export const tabBar = cacheTabbarEnable ? _tabbar : undefined

19
src/layouts/tabbar.vue Normal file
View File

@ -0,0 +1,19 @@
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni'
import FgTabbar from './fg-tabbar/fg-tabbar.vue'
const themeVars: ConfigProviderThemeVars = {
// colorTheme: 'red',
// buttonPrimaryBgColor: '#07c160',
// buttonPrimaryColor: '#07c160',
}
</script>
<template>
<wd-config-provider :theme-vars="themeVars">
<slot />
<FgTabbar />
<wd-toast />
<wd-message-box />
</wd-config-provider>
</template>

View File

@ -1,11 +1,11 @@
import '@/style/index.scss'
import { VueQueryPlugin } from '@tanstack/vue-query'
import 'uno.css'
import { createSSRApp } from 'vue'
import App from './App.vue'
import { prototypeInterceptor, requestInterceptor, routeInterceptor } from './interceptors'
import store from './store'
import '@/style/index.scss'
import 'virtual:uno.css'
export function createApp() {
const app = createSSRApp(App)

View File

@ -1,6 +1,6 @@
{
"name": "unibest",
"appid": "H57F2ACE4",
"appid": "__UNI__D1E5001",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
@ -85,7 +85,9 @@
"mp-weixin": {
"appid": "wxa2abb91f64032a2b",
"setting": {
"urlCheck": false
"urlCheck": false,
"es6": true,
"minified": true
},
"usingComponents": true
},

View File

@ -1,20 +1,27 @@
<route lang="json5" type="page">
{
style: { navigationBarTitleText: '分包页面 标题' },
style: {
navigationStyle: 'default',
navigationBarTitleText: '分包页面 标题',
},
}
</route>
<template>
<view class="text-center">
<view class="m-8">http://localhost:9000/#/pages-sub/demo/index</view>
<view class="text-green-500">分包页面demo</view>
</view>
</template>
<script lang="ts" setup>
// code here
</script>
<template>
<view class="text-center">
<view class="m-8">
http://localhost:9000/#/pages-sub/demo/index
</view>
<view class="text-green-500">
分包页面demo
</view>
</view>
</template>
<style lang="scss" scoped>
//
</style>

View File

@ -28,19 +28,17 @@
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/homeHL.png",
"pagePath": "pages/index/index",
"text": "首页"
"text": "首页",
"icon": "home",
"iconType": "uiLib"
},
{
"iconPath": "static/tabbar/example.png",
"selectedIconPath": "static/tabbar/exampleHL.png",
"pagePath": "pages/about/about",
"text": "关于"
},
{
"iconPath": "static/tabbar/personal.png",
"selectedIconPath": "static/tabbar/personalHL.png",
"pagePath": "pages/mine/index",
"text": "我的"
"text": "关于",
"icon": "i-carbon-code",
"iconType": "unocss"
}
]
},
@ -48,6 +46,7 @@
{
"path": "pages/index/index",
"type": "home",
"layout": "tabbar",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "首页"
@ -56,45 +55,9 @@
{
"path": "pages/about/about",
"type": "page",
"layout": "tabbar",
"style": {
"navigationBarTitleText": "关于",
"navigationStyle": "custom"
}
},
{
"path": "pages/login/index",
"type": "page",
"style": {
"navigationBarTitleText": "登录",
"navigationStyle": "custom"
}
},
{
"path": "pages/mine/index",
"type": "page",
"style": {
"navigationBarTitleText": "我的"
}
},
{
"path": "pages/mine/about/index",
"type": "page",
"style": {
"navigationBarTitleText": "关于我们"
}
},
{
"path": "pages/mine/info/index",
"type": "page",
"style": {
"navigationBarTitleText": "个人资料"
}
},
{
"path": "pages/mine/password/index",
"type": "page",
"style": {
"navigationBarTitleText": "修改密码"
"navigationBarTitleText": "关于"
}
}
],

View File

@ -1,38 +1,40 @@
<route lang="json5">
{
layout: 'tabbar',
style: {
navigationBarTitleText: '关于',
navigationStyle: 'custom', //
},
}
</route>
<template>
<view>
<fg-navbar>关于</fg-navbar>
<view
class="bg-white overflow-hidden pt-2 px-4"
:style="{ marginTop: safeAreaInsets?.top + 'px' }"
>
<view class="text-center text-3xl mt-8">
鸽友们好我是
<text class="text-red-500">菲鸽</text>
</view>
<view class="test-css">测试 scss 样式</view>
<RequestComp />
<UploadComp />
</view>
</view>
</template>
<script lang="ts" setup>
import RequestComp from './components/request.vue'
import UploadComp from './components/upload.vue'
//
const { safeAreaInsets } = uni.getSystemInfoSync()
// vue .ts
// const testOxlint = (name: string) => {
// console.log('oxlint')
// }
// testOxlint('oxlint')
console.log('about')
</script>
<template>
<view>
<view class="mt-8 text-center text-3xl">
鸽友们好我是
<text class="text-red-500">
菲鸽
</text>
</view>
<RequestComp />
<UploadComp />
</view>
</template>
<style lang="scss" scoped>
.test-css {
// 16rpx=>0.5rem

View File

@ -7,36 +7,9 @@
}
</route>
<template>
<view class="p-6 text-center">
<view class="my-2">使用的是 laf 云后台</view>
<view class="text-green-400">我的推荐码可以获得佣金</view>
<!-- #ifdef H5 -->
<view class="my-2">
<a class="my-2" :href="recommendUrl" target="_blank">{{ recommendUrl }}</a>
</view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="my-2 text-left text-sm">{{ recommendUrl }}</view>
<!-- #endif -->
<!-- http://localhost:9000/#/pages/index/request -->
<wd-button @click="run" class="my-6">发送请求</wd-button>
<view class="h-16">
<view v-if="loading">loading...</view>
<block v-else>
<view class="text-xl">请求数据如下</view>
<view class="text-green leading-8">{{ JSON.stringify(data) }}</view>
</block>
</view>
<wd-button type="error" @click="reset" class="my-6" :disabled="!data">重置数据</wd-button>
</view>
</template>
<script lang="ts" setup>
import { getFooAPI, postFooAPI, IFooItem } from '@/service/index/foo'
import type { IFooItem } from '@/service/index/foo'
import { getFooAPI } from '@/service/index/foo'
// import { findPetsByStatusQueryOptions } from '@/service/app'
// import { useQuery } from '@tanstack/vue-query'
@ -61,7 +34,51 @@ const { loading, error, data, run } = useRequest<IFooItem>(() => getFooAPI('菲
// refetch,
// } = useQuery(findPetsByStatusQueryOptions({ params: { status: ['available'] } }))
const reset = () => {
function reset() {
data.value = initialData
}
</script>
<template>
<view class="p-6 text-center">
<view class="my-2">
使用的是 laf 云后台
</view>
<view class="text-green-400">
我的推荐码可以获得佣金
</view>
<!-- #ifdef H5 -->
<view class="my-2">
<a class="my-2" :href="recommendUrl" target="_blank">{{ recommendUrl }}</a>
</view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="my-2 text-left text-sm">
{{ recommendUrl }}
</view>
<!-- #endif -->
<!-- http://localhost:9000/#/pages/index/request -->
<wd-button class="my-6" @click="run">
发送请求
</wd-button>
<view class="h-16">
<view v-if="loading">
loading...
</view>
<block v-else>
<view class="text-xl">
请求数据如下
</view>
<view class="text-green leading-8">
{{ JSON.stringify(data) }}
</view>
</block>
</view>
<wd-button type="error" class="my-6" :disabled="!data" @click="reset">
重置数据
</wd-button>
</view>
</template>

View File

@ -7,24 +7,32 @@
}
</route>
<script lang="ts" setup>
const { loading, data, run } = useUpload()
</script>
<template>
<view class="p-4 text-center">
<wd-button @click="run">选择图片并上传</wd-button>
<view v-if="loading" class="text-blue h-10">上传...</view>
<wd-button @click="run">
选择图片并上传
</wd-button>
<view v-if="loading" class="h-10 text-blue">
上传...
</view>
<template v-else>
<view class="m-2">上传后返回的接口数据</view>
<view class="m-2">{{ data }}</view>
<view class="h-80 w-full">
<image v-if="data" :src="data || data" mode="scaleToFill" />
<view class="m-2">
上传后返回的接口数据
</view>
<view class="m-2">
{{ data }}
</view>
<view v-if="data" class="h-80 w-full">
<image :src="data.url" mode="scaleToFill" />
</view>
</template>
</view>
</template>
<script lang="ts" setup>
const { loading, data, run } = useUpload({ user: '菲鸽' })
</script>
<style lang="scss" scoped>
//
</style>

View File

@ -1,34 +1,14 @@
<!-- 使用 type="home" 属性设置首页其他页面不需要设置默认为page推荐使用json5更强大且允许注释 -->
<route lang="json5" type="home">
{
layout: 'tabbar',
style: {
// 'custom' 'default'
navigationStyle: 'custom',
navigationBarTitleText: '首页',
},
}
</route>
<template>
<view
class="bg-white overflow-hidden pt-2 px-4"
:style="{ marginTop: safeAreaInsets?.top + 'px' }"
>
<view class="mt-12">
<image src="/static/logo.svg" alt="" class="w-28 h-28 block mx-auto" />
</view>
<view class="text-center text-4xl main-title-color mt-4">unibest</view>
<view class="text-center text-2xl mt-2 mb-8">最好用的 uniapp 开发模板</view>
<view class="text-justify max-w-100 m-auto text-4 indent mb-2">{{ description }}</view>
<view class="text-center mt-8">
当前平台是
<text class="text-green-500">{{ PLATFORM.platform }}</text>
</view>
<view class="text-center mt-4">
模板分支是
<text class="text-green-500">base</text>
</view>
</view>
</template>
<script lang="ts" setup>
import PLATFORM from '@/utils/platform'
@ -38,19 +18,78 @@ defineOptions({
})
//
const { safeAreaInsets } = uni.getSystemInfoSync()
let safeAreaInsets
let systemInfo
// #ifdef MP-WEIXIN
// 使API
systemInfo = uni.getWindowInfo()
safeAreaInsets = systemInfo.safeArea
? {
top: systemInfo.safeArea.top,
right: systemInfo.windowWidth - systemInfo.safeArea.right,
bottom: systemInfo.windowHeight - systemInfo.safeArea.bottom,
left: systemInfo.safeArea.left,
}
: null
// #endif
// #ifndef MP-WEIXIN
// 使uni API
systemInfo = uni.getSystemInfoSync()
safeAreaInsets = systemInfo.safeAreaInsets
// #endif
const author = ref('菲鸽')
const description = ref(
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite6 + UnoCss + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite5 + UnoCss + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
)
// uni API
onLoad(() => {
console.log('项目作者:', author.value)
})
console.log('index')
</script>
<style>
.main-title-color {
color: #d14328;
}
</style>
<template>
<view class="bg-white px-4 pt-2" :style="{ marginTop: `${safeAreaInsets?.top}px` }">
<view class="mt-10">
<image src="/static/logo.svg" alt="" class="mx-auto block h-28 w-28" />
</view>
<view class="mt-4 text-center text-4xl text-[#d14328]">
unibest
</view>
<view class="mb-8 mt-2 text-center text-2xl">
最好用的 uniapp 开发模板
</view>
<view class="m-auto mb-2 max-w-100 text-justify indent text-4">
{{ description }}
</view>
<view class="mt-4 text-center">
作者
<text class="text-green-500">
菲鸽
</text>
</view>
<view class="mt-4 text-center">
官网地址
<text class="text-green-500">
https://unibest.tech
</text>
</view>
<view class="mt-6 h-1px bg-#eee" />
<view class="mt-8 text-center">
当前平台是
<text class="text-green-500">
{{ PLATFORM.platform }}
</text>
</view>
<view class="mt-4 text-center">
模板分支是
<text class="text-green-500">
base
</text>
</view>
</view>
</template>

View File

@ -1,584 +0,0 @@
<route lang="json5" type="page">
{
style: {
navigationBarTitleText: '登录',
navigationStyle: 'custom',
},
}
</route>
<template>
<view class="login-container">
<!-- 背景装饰元素 -->
<view class="bg-decoration bg-circle-1"></view>
<view class="bg-decoration bg-circle-2"></view>
<view class="bg-decoration bg-circle-3"></view>
<view class="login-header">
<image class="login-logo" :src="appLogo" mode="aspectFit"></image>
<view class="login-title">{{ appTitle }}</view>
</view>
<view class="login-form">
<view class="welcome-text">欢迎登录</view>
<view class="login-desc">请输入您的账号和密码</view>
<view class="login-input-group">
<view class="input-wrapper">
<wd-input
v-model="loginForm.username"
prefix-icon="user"
placeholder="请输入用户名"
clearable
class="login-input"
:border="false"
required
></wd-input>
<view class="input-bottom-line"></view>
</view>
<view class="input-wrapper">
<wd-input
v-model="loginForm.password"
prefix-icon="lock-on"
placeholder="请输入密码"
clearable
show-password
class="login-input"
:border="false"
required
></wd-input>
<view class="input-bottom-line"></view>
</view>
<!-- 验证码区域 -->
<view class="input-wrapper captcha-wrapper">
<wd-input
v-if="captcha.captchaEnabled"
v-model="loginForm.code"
prefix-icon="secured"
placeholder="请输入验证码"
clearable
class="login-input captcha-input"
:border="false"
required
>
<template #suffix>
<image
class="captcha-image"
:src="'data:image/gif;base64,' + captcha.image"
mode="aspectFit"
@click="refreshCaptcha"
></image>
</template>
</wd-input>
<view class="input-bottom-line"></view>
</view>
</view>
<!-- 登录按钮组 -->
<view class="login-buttons">
<!-- 账号密码登录按钮 -->
<wd-button
type="primary"
size="large"
block
@click="handleAccountLogin"
class="account-login-btn"
>
<wd-icon name="right" size="18px" class="login-icon"></wd-icon>
登录
</wd-button>
<!-- 微信小程序一键登录按钮 -->
<!-- #ifdef MP-WEIXIN -->
<view class="divider">
<view class="divider-line"></view>
<view class="divider-text"></view>
<view class="divider-line"></view>
</view>
<wd-button
type="info"
size="large"
block
plain
@click="handleWechatLogin"
class="wechat-login-btn"
>
微信一键登录
</wd-button>
<!-- #endif -->
</view>
</view>
<!-- 隐私协议勾选 -->
<view class="privacy-agreement">
<wd-checkbox
v-model="agreePrivacy"
shape="square"
class="privacy-checkbox"
active-color="var(--wot-color-theme, #1989fa)"
>
<view class="agreement-text">
我已阅读并同意
<text class="agreement-link" @click.stop="handleAgreement('user')">用户协议</text>
<text class="agreement-link" @click.stop="handleAgreement('privacy')">隐私政策</text>
</view>
</wd-checkbox>
</view>
<view class="login-footer"></view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { useUserStore } from '@/store/user'
import { isMpWeixin } from '@/utils/platform'
import { getCode, ILoginForm } from '@/api/login'
import { toast } from '@/utils/toast'
import { isTableBar } from '@/utils/index'
import { ICaptcha } from '@/api/login.typings'
const redirectRoute = ref('')
//
const appTitle = ref(import.meta.env.VITE_APP_TITLE || 'Unibest Login')
const appLogo = ref(import.meta.env.VITE_APP_LOGO || '/static/logo.svg')
// store
const userStore = useUserStore()
//
//
const captcha = ref<ICaptcha>({
captchaEnabled: false,
uuid: '',
image: '',
})
//
const loginForm = ref<ILoginForm>({
username: 'admin',
password: '123456',
code: '',
uuid: '',
})
//
const agreePrivacy = ref(true)
//
onLoad((option) => {
//
captcha.value.captchaEnabled && refreshCaptcha()
//
if (option.redirect) {
redirectRoute.value = option.redirect
}
})
//
const handleAccountLogin = async () => {
if (!agreePrivacy.value) {
toast.error('请阅读同意协议')
return
}
//
if (!loginForm.value.username) {
toast.error('请输入用户名')
return
}
if (!loginForm.value.password) {
toast.error('请输入密码')
return
}
if (captcha.value.captchaEnabled && !loginForm.value.code) {
toast.error('请输入验证码')
return
}
//
await userStore.login(loginForm.value)
//
const targetUrl = redirectRoute.value || '/pages/index/index'
if (isTableBar(targetUrl)) {
uni.switchTab({ url: targetUrl })
} else {
uni.redirectTo({ url: targetUrl })
}
}
//
const handleWechatLogin = async () => {
if (!isMpWeixin) {
toast.info('请在微信小程序中使用此功能')
return
}
//
if (!agreePrivacy.value) {
toast.error('请先阅读并同意用户协议和隐私政策')
return
}
//
await userStore.wxLogin()
//
const targetUrl = redirectRoute.value || '/pages/index/index'
if (isTableBar(targetUrl)) {
uni.switchTab({ url: targetUrl })
} else {
uni.redirectTo({ url: targetUrl })
}
}
//
const refreshCaptcha = () => {
//
getCode().then((res) => {
const { data } = res
loginForm.value.uuid = data.uuid
captcha.value = data
})
}
//
const handleAgreement = (type: 'user' | 'privacy') => {
const title = type === 'user' ? '用户协议' : '隐私政策'
// showToast(`${title}`)
//
// uni.navigateTo({
// url: `/pages/agreement/${type}`
// })
}
</script>
<style lang="scss" scoped>
/* 验证码输入框样式 */
.captcha-wrapper {
.captcha-input {
:deep(.wd-input__suffix) {
margin-right: 0;
padding-right: 0;
}
}
.captcha-image {
width: 100px;
height: 36px;
margin-left: 10px;
border-radius: 8px;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to bottom, rgba(255, 255, 255, 0.1), transparent);
pointer-events: none;
}
&:active {
opacity: 0.8;
transform: scale(0.96);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
}
}
}
.login-container {
box-sizing: border-box;
display: flex;
flex-direction: column;
min-height: 100vh;
padding: 0 70rpx;
background-color: #ffffff;
background-image: linear-gradient(
135deg,
rgba(25, 137, 250, 0.05) 0%,
rgba(255, 255, 255, 0) 100%
);
position: relative;
overflow: hidden;
}
/* 背景装饰元素 */
.bg-decoration {
position: absolute;
border-radius: 50%;
background: linear-gradient(135deg, rgba(25, 137, 250, 0.05), rgba(25, 137, 250, 0.1));
z-index: 0;
pointer-events: none;
}
.bg-circle-1 {
width: 500rpx;
height: 500rpx;
top: -200rpx;
right: -200rpx;
opacity: 0.6;
}
.bg-circle-2 {
width: 400rpx;
height: 400rpx;
bottom: 10%;
left: -200rpx;
opacity: 0.4;
}
.bg-circle-3 {
width: 300rpx;
height: 300rpx;
bottom: -100rpx;
right: 10%;
opacity: 0.3;
background: linear-gradient(135deg, rgba(7, 193, 96, 0.05), rgba(7, 193, 96, 0.1));
}
.login-header {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-top: 120rpx;
animation: fadeInDown 0.8s ease-out;
.login-logo {
width: 200rpx;
height: 200rpx;
border-radius: 36rpx;
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.12);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
box-shadow: 0 6rpx 15rpx rgba(0, 0, 0, 0.1);
}
}
.login-title {
margin-top: 30rpx;
font-size: 46rpx;
font-weight: bold;
color: #333333;
letter-spacing: 3rpx;
text-shadow: 0 2rpx 4rpx rgba(0, 0, 0, 0.05);
}
}
.login-form {
flex: 1;
margin-top: 70rpx;
animation: fadeIn 0.8s ease-out 0.2s both;
.welcome-text {
margin-bottom: 16rpx;
font-size: 48rpx;
font-weight: bold;
color: #333333;
text-align: center;
letter-spacing: 1rpx;
}
.login-desc {
margin-bottom: 70rpx;
font-size: 28rpx;
color: #888888;
text-align: center;
}
.login-input-group {
margin-bottom: 60rpx;
position: relative;
z-index: 1;
.input-wrapper {
position: relative;
margin-bottom: 50rpx;
transition: all 0.3s ease;
border-radius: 16rpx;
overflow: hidden;
&:last-child {
margin-bottom: 0;
}
.login-input {
padding: 12rpx 20rpx;
background-color: rgba(245, 247, 250, 0.7);
border-radius: 16rpx;
transition: all 0.3s ease;
:deep(.wd-input__inner) {
font-size: 30rpx;
color: #333333;
}
:deep(.wd-input__placeholder) {
font-size: 28rpx;
color: #aaaaaa;
}
&:focus-within {
background-color: rgba(245, 247, 250, 0.95);
box-shadow: 0 6rpx 16rpx rgba(0, 0, 0, 0.06);
transform: translateY(-3rpx);
}
}
.input-bottom-line {
position: absolute;
bottom: -2rpx;
left: 5%;
width: 90%;
height: 2rpx;
background: linear-gradient(
to right,
transparent,
var(--wot-color-theme, #1989fa),
transparent
);
transition: transform 0.4s ease;
transform: scaleX(0);
opacity: 0.8;
}
&:focus-within .input-bottom-line {
transform: scaleX(1);
}
.input-icon {
margin-right: 16rpx;
color: #666666;
transition: color 0.3s ease;
}
&:focus-within .input-icon {
color: var(--wot-color-theme, #1989fa);
}
}
}
.login-buttons {
display: flex;
flex-direction: column;
gap: 36rpx;
.account-login-btn {
height: 96rpx;
margin-top: 20rpx;
font-size: 32rpx;
font-weight: 500;
letter-spacing: 2rpx;
border-radius: 48rpx;
box-shadow: 0 10rpx 20rpx rgba(25, 137, 250, 0.25);
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
.login-icon {
margin-right: 8rpx;
opacity: 0.8;
transition: all 0.3s ease;
}
&:active {
box-shadow: 0 5rpx 10rpx rgba(25, 137, 250, 0.2);
transform: scale(0.98);
.login-icon {
transform: translateX(3rpx);
}
}
}
.divider {
display: flex;
align-items: center;
margin: 24rpx 0;
.divider-line {
flex: 1;
height: 1px;
background-color: #eeeeee;
}
.divider-text {
padding: 0 24rpx;
font-size: 24rpx;
color: #999999;
}
}
.wechat-login-btn {
height: 96rpx;
font-size: 32rpx;
color: #07c160;
border-color: #07c160;
border-radius: 48rpx;
transition: all 0.3s ease;
.wechat-icon {
margin-right: 12rpx;
}
&:active {
background-color: rgba(7, 193, 96, 0.08);
transform: scale(0.98);
}
}
}
}
.privacy-agreement {
display: flex;
justify-content: center;
margin: 30rpx 0 40rpx;
animation: fadeIn 0.8s ease-out 0.4s both;
.privacy-checkbox {
display: flex;
align-items: center;
}
.agreement-text {
font-size: 26rpx;
line-height: 1.6;
color: #666666;
.agreement-link {
padding: 0 4rpx;
font-weight: 500;
color: var(--wot-color-theme, #1989fa);
transition: all 0.3s ease;
&:active {
opacity: 0.8;
transform: scale(0.98);
}
}
}
}
.login-footer {
padding: 50rpx 0;
margin-top: auto;
}
/* 添加动画效果 */
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
@keyframes fadeInDown {
from {
opacity: 0;
transform: translateY(-20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>

View File

@ -1,173 +0,0 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '关于我们',
},
}
</route>
<template>
<view class="about-container">
<view class="about-card">
<!-- 应用信息 -->
<view class="app-info">
<view class="logo-wrapper">
<wd-img :src="appLogo" width="120px" height="120px" radius="24rpx"></wd-img>
</view>
<view class="app-name">{{ appTitle }}</view>
<view class="app-version">版本 {{ packageJson.version }}</view>
</view>
<!-- 联系方式 -->
<view class="info-section">
<view class="section-title">联系我们</view>
<view class="section-content">
<view class="contact-item">
<wd-icon name="phone" size="20px" class="contact-icon"></wd-icon>
<text class="contact-text">客服电话400-XXX-XXXX</text>
</view>
<view class="contact-item">
<wd-icon name="mail" size="20px" class="contact-icon"></wd-icon>
<text class="contact-text">邮箱support@unibest.tech</text>
</view>
<view class="contact-item">
<wd-icon name="location" size="20px" class="contact-icon"></wd-icon>
<text class="contact-text">地址中国·深圳</text>
</view>
</view>
</view>
<!-- 版权信息 -->
<view class="copyright">
<text>Copyright © 2025-{{ currentYear }} {{ appTitle }}</text>
<text>All Rights Reserved</text>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
import packageJson from '@/../package.json'
const appTitle = ref(import.meta.env.VITE_APP_TITLE || 'unibest')
const appLogo = ref(import.meta.env.VITE_APP_LOGO || '/static/logo.svg')
//
const currentYear = computed(() => new Date().getFullYear())
</script>
<style lang="scss" scoped>
.about-container {
background-color: #f5f7fa;
padding: 30rpx;
}
.about-card {
background-color: #ffffff;
border-radius: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
overflow: hidden;
padding: 40rpx 30rpx;
}
/* 应用信息 */
.app-info {
display: flex;
flex-direction: column;
align-items: center;
padding: 30rpx 0 50rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.logo-wrapper {
margin-bottom: 20rpx;
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.08);
border-radius: 24rpx;
}
.app-name {
font-size: 40rpx;
font-weight: 600;
color: #333;
margin-bottom: 10rpx;
}
.app-version {
font-size: 28rpx;
color: #999;
}
/* 信息区块 */
.info-section {
padding: 40rpx 0;
border-bottom: 2rpx solid #f0f0f0;
}
.section-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
margin-bottom: 20rpx;
position: relative;
padding-left: 24rpx;
&::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 8rpx;
height: 32rpx;
background: linear-gradient(135deg, #4a7bff, #6a5acd);
border-radius: 4rpx;
}
}
.section-content {
padding: 0 10rpx;
}
.content-text {
font-size: 30rpx;
color: #666;
line-height: 1.6;
text-align: justify;
}
/* 联系方式 */
.contact-item {
display: flex;
align-items: center;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
}
.contact-icon {
margin-right: 20rpx;
color: #4a7bff;
}
.contact-text {
font-size: 30rpx;
color: #666;
}
/* 版权信息 */
.copyright {
padding-top: 40rpx;
display: flex;
flex-direction: column;
align-items: center;
text {
font-size: 26rpx;
color: #999;
line-height: 1.6;
}
}
</style>

View File

@ -1,367 +0,0 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '我的',
},
}
</route>
<template>
<view class="profile-container">
{{ JSON.stringify(userStore.userInfo) }}
<!-- 用户信息区域 -->
<view class="user-info-section">
<!-- #ifdef MP-WEIXIN -->
<button class="avatar-button" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
<wd-img :src="userStore.userInfo.avatar" width="80px" height="80px" radius="50%"></wd-img>
</button>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="avatar-wrapper" @click="run">
<wd-img :src="userStore.userInfo.avatar" width="100%" height="100%" radius="50%"></wd-img>
</view>
<!-- #endif -->
<view class="user-details">
<!-- #ifdef MP-WEIXIN -->
<input
type="nickname"
class="weui-input"
placeholder="请输入昵称"
v-model="userStore.userInfo.username"
/>
<!-- #endif -->
<!-- #ifndef MP-WEIXIN -->
<view class="username">{{ userStore.userInfo.username }}</view>
<!-- #endif -->
<view class="user-id">ID: {{ userStore.userInfo.id }}</view>
</view>
</view>
<!-- 功能区块 -->
<view class="function-section">
<view class="cell-group">
<view class="group-title">账号管理</view>
<wd-cell title="个人资料" is-link @click="handleProfileInfo">
<template #icon>
<wd-icon name="user" size="20px"></wd-icon>
</template>
</wd-cell>
<wd-cell title="账号安全" is-link @click="handlePassword">
<template #icon>
<wd-icon name="lock-on" size="20px"></wd-icon>
</template>
</wd-cell>
</view>
<view class="cell-group">
<view class="group-title">通用设置</view>
<wd-cell title="消息通知" is-link @click="handleInform">
<template #icon>
<wd-icon name="notification" size="20px"></wd-icon>
</template>
</wd-cell>
<wd-cell title="清理缓存" is-link @click="handleClearCache">
<template #icon>
<wd-icon name="clear" size="20px"></wd-icon>
</template>
</wd-cell>
<wd-cell title="应用更新" is-link @click="handleAppUpdate">
<template #icon>
<wd-icon name="refresh1" size="20px"></wd-icon>
</template>
</wd-cell>
<wd-cell title="关于我们" is-link @click="handleAbout">
<template #icon>
<wd-icon name="info-circle" size="20px"></wd-icon>
</template>
</wd-cell>
</view>
<view class="logout-button-wrapper">
<wd-button type="error" v-if="hasLogin" block @click="handleLogout">退出登录</wd-button>
<wd-button type="primary" v-else block @click="handleLogin">登录</wd-button>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { useUserStore } from '@/store'
import { useToast } from 'wot-design-uni'
import { uploadFileUrl, useUpload } from '@/utils/uploadFile'
import { storeToRefs } from 'pinia'
import { IUploadSuccessInfo } from '@/api/login.typings'
const userStore = useUserStore()
const toast = useToast()
const hasLogin = ref(false)
onShow((options) => {
hasLogin.value = !!uni.getStorageSync('token')
console.log('个人中心onShow', hasLogin.value, options)
hasLogin.value && useUserStore().getUserInfo()
})
// #ifndef MP-WEIXIN
//
const { run } = useUpload<IUploadSuccessInfo>(
uploadFileUrl.USER_AVATAR,
{},
{
onSuccess: (res) => useUserStore().getUserInfo(),
},
)
// #endif
//
const handleLogin = async () => {
// #ifdef MP-WEIXIN
//
await userStore.wxLogin()
hasLogin.value = true
// #endif
// #ifndef MP-WEIXIN
uni.navigateTo({ url: '/pages/login/index' })
// #endif
}
// #ifdef MP-WEIXIN
//
const onChooseAvatar = (e: any) => {
console.log('选择头像', e.detail)
const { avatarUrl } = e.detail
const { run } = useUpload<IUploadSuccessInfo>(
uploadFileUrl.USER_AVATAR,
{},
{
onSuccess: (res) => useUserStore().getUserInfo(),
},
avatarUrl,
)
run()
}
// #endif
// #ifdef MP-WEIXIN
//
const getUserInfo = (e: any) => {
console.log(e.detail)
}
// #endif
//
const handleProfileInfo = () => {
uni.navigateTo({ url: `/pages/mine/info/index` })
}
//
const handlePassword = () => {
uni.navigateTo({ url: `/pages/mine/password/index` })
}
//
const handleInform = () => {
// uni.navigateTo({ url: `/pages/mine/inform/index` })
toast.success('功能开发中')
}
//
const handleAppUpdate = () => {
// #ifdef MP
// #ifndef MP-HARMONY
const updateManager = uni.getUpdateManager()
updateManager.onCheckForUpdate(function (res) {
//
// console.log(res.hasUpdate)
if (res.hasUpdate) {
toast.success('检测到新版本,正在下载中...')
} else {
toast.success('已是最新版本')
}
})
updateManager.onUpdateReady(function (res) {
uni.showModal({
title: '更新提示',
content: '新版本已经准备好,是否重启应用?',
success(res) {
if (res.confirm) {
// applyUpdate
updateManager.applyUpdate()
}
},
})
})
updateManager.onUpdateFailed(function (res) {
//
toast.error('新版本下载失败')
})
// #endif
// #endif
// #ifndef MP
toast.success('功能开发中')
// #endif
}
//
const handleAbout = () => {
uni.navigateTo({ url: `/pages/mine/about/index` })
}
//
const handleClearCache = () => {
uni.showModal({
title: '清除缓存',
content: '确定要清除所有缓存吗?\n清除后需要重新登录',
success: (res) => {
if (res.confirm) {
try {
//
uni.clearStorageSync()
//
useUserStore().logout()
toast.success('清除缓存成功')
} catch (err) {
console.error('清除缓存失败:', err)
toast.error('清除缓存失败')
}
}
},
})
}
// 退
const handleLogout = () => {
uni.showModal({
title: '提示',
content: '确定要退出登录吗?',
success: (res) => {
if (res.confirm) {
//
useUserStore().logout()
hasLogin.value = false
// 退
toast.success('退出登录成功')
// #ifdef MP-WEIXIN
//
// uni.reLaunch({ url: '/pages/index/index' })
// #endif
// #ifndef MP-WEIXIN
//
// uni.reLaunch({ url: '/pages/login/index' })
// #endif
}
},
})
}
</script>
<style lang="scss" scoped>
/* 基础样式 */
.profile-container {
overflow: hidden;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', sans-serif;
background-color: #f7f8fa;
}
/* 用户信息区域 */
.user-info-section {
display: flex;
align-items: center;
padding: 40rpx;
margin: 30rpx 30rpx 20rpx;
background-color: #fff;
border-radius: 24rpx;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
}
.avatar-wrapper {
width: 160rpx;
height: 160rpx;
margin-right: 40rpx;
overflow: hidden;
border: 4rpx solid #f5f5f5;
border-radius: 50%;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.avatar-button {
height: 160rpx;
padding: 0;
margin-right: 40rpx;
overflow: hidden;
border: 4rpx solid #f5f5f5;
border-radius: 50%;
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
}
.user-details {
flex: 1;
}
.username {
margin-bottom: 12rpx;
font-size: 38rpx;
font-weight: 600;
color: #333;
letter-spacing: 0.5rpx;
}
.user-id {
font-size: 28rpx;
color: #666;
}
.user-created {
margin-top: 8rpx;
font-size: 24rpx;
color: #999;
}
/* 功能区块 */
.function-section {
padding: 0 20rpx;
margin-top: 20rpx;
}
.cell-group {
margin-bottom: 20rpx;
overflow: hidden;
background-color: #fff;
border-radius: 16rpx;
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
}
.group-title {
padding: 24rpx 30rpx 16rpx;
font-size: 30rpx;
font-weight: 500;
color: #999;
background-color: #fafafa;
}
:deep(.wd-cell) {
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.wd-cell__title {
margin-left: 5px;
font-size: 32rpx;
color: #333;
}
.cell-icon {
margin-right: 20rpx;
font-size: 36rpx;
}
}
/* 退出登录按钮 */
.logout-button-wrapper {
padding: 40rpx 30rpx;
}
:deep(.wd-button--danger) {
height: 88rpx;
font-size: 32rpx;
line-height: 88rpx;
color: #fff;
background-color: #f53f3f;
border-radius: 44rpx;
}
</style>

View File

@ -1,190 +0,0 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '个人资料',
},
}
</route>
<template>
<view class="profile-info-container">
<view class="profile-card">
<view class="form-wrapper">
<wd-form ref="formRef" :model="formData" label-width="160rpx" class="profile-form">
<wd-cell-group class="form-group">
<!-- 昵称 -->
<view class="sex-field">
<text class="field-label">昵称</text>
<wd-input
prop="name"
clearable
v-model="formData.name"
placeholder="请输入昵称"
:rules="[{ required: true, message: '请填写昵称' }]"
class="form-input"
/>
</view>
<!-- 性别 -->
<view class="sex-field">
<text class="field-label">性别</text>
<wd-radio-group
v-model="formData.sex"
shape="button"
:rules="[{ required: true, message: '请选择性别' }]"
>
<wd-radio :value="'1'"></wd-radio>
<wd-radio :value="'0'"></wd-radio>
</wd-radio-group>
</view>
</wd-cell-group>
</wd-form>
<!-- 操作按钮 -->
<view class="form-actions">
<wd-button type="primary" size="large" @click="handleSubmit">保存修改</wd-button>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { useUserStore } from '@/store'
import { storeToRefs } from 'pinia'
import { toast } from '@/utils/toast'
import { updateInfo } from '@/api/login'
//
const formRef = ref()
//
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
//
const formData = ref({
id: userInfo.value.id,
name: userInfo.value.name,
sex: userInfo.value.sex,
})
//
const handleSubmit = async () => {
//
const valid = await formRef.value.validate()
if (!valid) return
const { message } = await updateInfo(formData.value)
await useUserStore().getUserInfo()
toast.success(message)
}
</script>
<style lang="scss" scoped>
.profile-info-container {
min-height: 100vh;
background-color: #f5f7fa;
padding: 30rpx;
}
.profile-card {
background-color: #ffffff;
border-radius: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.card-header {
padding: 40rpx 30rpx 20rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.card-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
position: relative;
display: inline-block;
padding-bottom: 16rpx;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 60rpx;
height: 6rpx;
background: linear-gradient(90deg, #4a7bff, #6a5acd);
border-radius: 6rpx;
}
}
.form-wrapper {
padding: 30rpx;
}
.form-group {
border-radius: 16rpx;
overflow: hidden;
margin-bottom: 40rpx;
}
.form-input {
font-size: 30rpx;
}
.sex-field {
display: flex;
align-items: center;
padding: 24rpx 30rpx;
background-color: #ffffff;
}
.field-label {
width: 160rpx;
font-size: 30rpx;
color: #333;
}
.radio-group {
flex: 1;
display: flex;
gap: 20rpx;
}
.radio-btn {
flex: 1;
height: 80rpx;
line-height: 80rpx;
text-align: center;
font-size: 30rpx;
border-radius: 12rpx;
background-color: #f5f7fa;
&:active {
opacity: 0.8;
}
}
.form-actions {
display: flex;
flex-direction: row;
gap: 20rpx;
}
.submit-btn {
height: 90rpx;
border-radius: 45rpx;
font-size: 32rpx;
font-weight: 500;
background: linear-gradient(135deg, #4a7bff, #6a5acd);
box-shadow: 0 8rpx 16rpx rgba(74, 123, 255, 0.2);
transition: all 0.3s ease;
&:active {
transform: translateY(2rpx);
box-shadow: 0 4rpx 8rpx rgba(74, 123, 255, 0.15);
}
}
</style>

View File

@ -1,203 +0,0 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '修改密码',
},
}
</route>
<template>
<view class="profile-info-container">
<view class="profile-card">
<view class="form-wrapper">
<wd-form ref="formRef" :model="formData" label-width="160rpx" class="profile-form">
<wd-cell-group class="form-group">
<!-- 昵称 -->
<view class="sex-field">
<text class="field-label">旧密码</text>
<wd-input
prop="oldPassword"
clearable
v-model="formData.oldPassword"
placeholder="请输入旧密码"
show-password
:rules="[{ required: true, message: '请填写旧密码' }]"
class="form-input"
/>
</view>
<view class="sex-field">
<text class="field-label">新密码</text>
<wd-input
prop="newPassword"
clearable
v-model="formData.newPassword"
placeholder="请输入新密码"
show-password
:rules="[{ required: true, message: '请填写新密码' }]"
class="form-input"
/>
</view>
<view class="sex-field">
<text class="field-label">确认密码</text>
<wd-input
prop="confirmPassword"
clearable
v-model="formData.confirmPassword"
placeholder="请输入新密码"
show-password
:rules="[{ required: true, message: '请填写新密码' }]"
class="form-input"
/>
</view>
</wd-cell-group>
</wd-form>
<!-- 操作按钮 -->
<view class="form-actions">
<wd-button type="primary" size="large" @click="handleSubmit">保存修改</wd-button>
</view>
</view>
</view>
</view>
</template>
<script lang="ts" setup>
import { ref } from 'vue'
import { useUserStore } from '@/store'
import { storeToRefs } from 'pinia'
import { toast } from '@/utils/toast'
import { updateInfo, updateUserPassword } from '@/api/login'
//
const formRef = ref()
//
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
//
const formData = ref({
id: userInfo.value.id,
oldPassword: '',
newPassword: '',
confirmPassword: '',
})
//
const handleSubmit = async () => {
//
const valid = await formRef.value.validate()
if (!valid) return
const { message } = await updateUserPassword(formData.value)
await useUserStore().logout()
toast.success('修改成功,请重新登录')
}
</script>
<style lang="scss" scoped>
.profile-info-container {
min-height: 100vh;
background-color: #f5f7fa;
padding: 30rpx;
}
.profile-card {
background-color: #ffffff;
border-radius: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
overflow: hidden;
}
.card-header {
padding: 40rpx 30rpx 20rpx;
border-bottom: 2rpx solid #f0f0f0;
}
.card-title {
font-size: 36rpx;
font-weight: 600;
color: #333;
position: relative;
display: inline-block;
padding-bottom: 16rpx;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 60rpx;
height: 6rpx;
background: linear-gradient(90deg, #4a7bff, #6a5acd);
border-radius: 6rpx;
}
}
.form-wrapper {
padding: 30rpx;
}
.form-group {
border-radius: 16rpx;
overflow: hidden;
margin-bottom: 40rpx;
}
.form-input {
font-size: 30rpx;
}
.sex-field {
display: flex;
align-items: center;
padding: 24rpx 30rpx;
background-color: #ffffff;
}
.field-label {
width: 160rpx;
font-size: 30rpx;
color: #333;
}
.radio-group {
flex: 1;
display: flex;
gap: 20rpx;
}
.radio-btn {
flex: 1;
height: 80rpx;
line-height: 80rpx;
text-align: center;
font-size: 30rpx;
border-radius: 12rpx;
background-color: #f5f7fa;
&:active {
opacity: 0.8;
}
}
.form-actions {
display: flex;
flex-direction: row;
gap: 20rpx;
}
.submit-btn {
height: 90rpx;
border-radius: 45rpx;
font-size: 32rpx;
font-weight: 500;
background: linear-gradient(135deg, #4a7bff, #6a5acd);
box-shadow: 0 8rpx 16rpx rgba(74, 123, 255, 0.2);
transition: all 0.3s ease;
&:active {
transform: translateY(2rpx);
box-shadow: 0 4rpx 8rpx rgba(74, 123, 255, 0.15);
}
}
</style>

View File

@ -1,27 +1,28 @@
import { http } from '@/utils/http'
export interface IFooItem {
id: string
name: string
}
/** GET 请求 */
export const getFooAPI = (name: string) => {
export function getFooAPI(name: string) {
return http.get<IFooItem>('/foo', { name })
}
/** GET 请求;支持 传递 header 的范例 */
export const getFooAPI2 = (name: string) => {
export function getFooAPI2(name: string) {
return http.get<IFooItem>('/foo', { name }, { 'Content-Type-100': '100' })
}
/** POST 请求 */
export const postFooAPI = (name: string) => {
export function postFooAPI(name: string) {
return http.post<IFooItem>('/foo', { name })
}
/** POST 请求;需要传递 query 参数的范例微信小程序经常有同时需要query参数和body参数的场景 */
export const postFooAPI2 = (name: string) => {
export function postFooAPI2(name: string) {
return http.post<IFooItem>('/foo', { name })
}
/** POST 请求;支持 传递 header 的范例 */
export const postFooAPI3 = (name: string) => {
export function postFooAPI3(name: string) {
return http.post<IFooItem>('/foo', { name }, { name }, { 'Content-Type-100': '100' })
}

View File

@ -1,14 +1,14 @@
import {
login as _login,
getUserInfo as _getUserInfo,
wxLogin as _wxLogin,
logout as _logout,
getWxCode,
} from '@/api/login'
import type { IUserInfoVo } from '@/api/types/login'
import { defineStore } from 'pinia'
import { ref } from 'vue'
import {
getUserInfo as _getUserInfo,
login as _login,
logout as _logout,
wxLogin as _wxLogin,
getWxCode,
} from '@/api/login'
import { toast } from '@/utils/toast'
import { IUserInfoVo } from '@/api/login.typings'
// 初始化状态
const userInfoState: IUserInfoVo = {
@ -29,17 +29,35 @@ export const useUserStore = defineStore(
// 若头像为空 则使用默认头像
if (!val.avatar) {
val.avatar = userInfoState.avatar
} else {
}
else {
val.avatar = 'https://oss.laf.run/ukw0y1-site/avatar.jpg?feige'
}
userInfo.value = val
}
const setUserAvatar = (avatar: string) => {
userInfo.value.avatar = avatar
console.log('设置用户头像', avatar)
console.log('userInfo', userInfo.value)
}
// 删除用户信息
const removeUserInfo = () => {
userInfo.value = { ...userInfoState }
uni.removeStorageSync('userInfo')
uni.removeStorageSync('token')
}
/**
*
*/
const getUserInfo = async () => {
const res = await _getUserInfo()
const userInfo = res.data
setUserInfo(userInfo)
uni.setStorageSync('userInfo', userInfo)
uni.setStorageSync('token', userInfo.token)
// TODO 这里可以增加获取用户路由的方法 根据用户的角色动态生成路由
return res
}
/**
*
* @param credentials
@ -54,21 +72,10 @@ export const useUserStore = defineStore(
const res = await _login(credentials)
console.log('登录信息', res)
toast.success('登录成功')
getUserInfo()
return res
}
/**
*
*/
const getUserInfo = async () => {
const res = await _getUserInfo()
const userInfo = res.data
setUserInfo(userInfo)
uni.setStorageSync('userInfo', userInfo)
uni.setStorageSync('token', userInfo.token)
// TODO 这里可以增加获取用户路由的方法 根据用户的角色动态生成路由
await getUserInfo()
return res
}
/**
* 退
*/
@ -85,7 +92,7 @@ export const useUserStore = defineStore(
console.log('微信登录code', data)
const res = await _wxLogin(data)
getUserInfo()
await getUserInfo()
return res
}
@ -94,6 +101,7 @@ export const useUserStore = defineStore(
login,
wxLogin,
getUserInfo,
setUserAvatar,
logout,
}
},

View File

@ -1,4 +1,4 @@
// @import './iconfont.css';
@import './iconfont.css';
.test {
// 可以通过 @apply 多个样式封装整体样式

6
src/typings.d.ts vendored
View File

@ -1,14 +1,14 @@
// 全局要用的类型放到这里
declare global {
type IResData<T> = {
interface IResData<T> {
code: number
msg: string
data: T
}
// uni.uploadFile文件上传参数
type IUniUploadFileOptions = {
interface IUniUploadFileOptions {
file?: File
files?: UniApp.UploadFileOptionFiles[]
filePath?: string
@ -16,7 +16,7 @@ declare global {
formData?: any
}
type IUserInfo = {
interface IUserInfo {
nickname?: string
avatar?: string
/** 微信的 openid非微信没有这个字段 */

View File

@ -6,7 +6,7 @@ export enum TestEnum {
}
// uni.uploadFile文件上传参数
export type IUniUploadFileOptions = {
export interface IUniUploadFileOptions {
file?: File
files?: UniApp.UploadFileOptionFiles[]
filePath?: string

View File

@ -1,6 +1,6 @@
import { CustomRequestOptions } from '@/interceptors/request'
import type { CustomRequestOptions } from '@/interceptors/request'
export const http = <T>(options: CustomRequestOptions) => {
export function http<T>(options: CustomRequestOptions) {
// 1. 返回 Promise 对象
return new Promise<IResData<T>>((resolve, reject) => {
uni.request({
@ -15,18 +15,20 @@ export const http = <T>(options: CustomRequestOptions) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
// 2.1 提取核心数据 res.data
resolve(res.data as IResData<T>)
} else if (res.statusCode === 401) {
}
else if (res.statusCode === 401) {
// 401错误 -> 清理用户信息,跳转到登录页
// userStore.clearUserInfo()
// uni.navigateTo({ url: '/pages/login/login' })
reject(res)
} else {
}
else {
// 其他错误 -> 根据后端错误信息轻提示
!options.hideErrorToast &&
uni.showToast({
icon: 'none',
title: (res.data as IResData<T>).msg || '请求错误',
})
!options.hideErrorToast
&& uni.showToast({
icon: 'none',
title: (res.data as IResData<T>).msg || '请求错误',
})
reject(res)
}
},
@ -49,16 +51,13 @@ export const http = <T>(options: CustomRequestOptions) => {
* @param header json格式
* @returns
*/
export const httpGet = <T>(
url: string,
query?: Record<string, any>,
header?: Record<string, any>,
) => {
export function httpGet<T>(url: string, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
return http<T>({
url,
query,
method: 'GET',
header,
...options,
})
}
@ -70,51 +69,40 @@ export const httpGet = <T>(
* @param header json格式
* @returns
*/
export const httpPost = <T>(
url: string,
data?: Record<string, any>,
query?: Record<string, any>,
header?: Record<string, any>,
) => {
export function httpPost<T>(url: string, data?: Record<string, any>, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
return http<T>({
url,
query,
data,
method: 'POST',
header,
...options,
})
}
/**
* PUT
*/
export const httpPut = <T>(
url: string,
data?: Record<string, any>,
query?: Record<string, any>,
header?: Record<string, any>,
) => {
export function httpPut<T>(url: string, data?: Record<string, any>, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
return http<T>({
url,
data,
query,
method: 'PUT',
header,
...options,
})
}
/**
* DELETE query
*/
export const httpDelete = <T>(
url: string,
query?: Record<string, any>,
header?: Record<string, any>,
) => {
export function httpDelete<T>(url: string, query?: Record<string, any>, header?: Record<string, any>, options?: Partial<CustomRequestOptions>) {
return http<T>({
url,
query,
method: 'DELETE',
header,
...options,
})
}

View File

@ -1,9 +1,7 @@
import pagesConfig from '@/pages.json'
import { pages, subPackages } from '@/pages.json'
import { isMpWeixin } from './platform'
const { pages, subPackages, tabBar = { list: [] } } = { ...pagesConfig }
export const getLastPage = () => {
export function getLastPage() {
// getCurrentPages() 至少有1个元素所以不再额外判断
// const lastPage = getCurrentPages().at(-1)
// 上面那个在低版本安卓中打包会报错,所以改用下面这个【虽然我加了 src/interceptions/prototype.ts但依然报错】
@ -11,44 +9,12 @@ export const getLastPage = () => {
return pages[pages.length - 1]
}
/** 判断当前页面是否是 tabbar 页 */
export const getIsTabbar = () => {
try {
const lastPage = getLastPage()
const currPath = lastPage?.route
return Boolean(tabBar?.list?.some((item) => item.pagePath === currPath))
} catch {
return false
}
}
/**
* tabbar
* @param path
* @returns true: tabbar false: tabbar
*/
export const isTableBar = (path: string) => {
if (!tabBar) {
return false
}
if (!tabBar.list.length) {
// 通常有 tabBar 的话list 不能有空且至少有2个元素这里其实不用处理
return false
}
// 这里需要处理一下 path因为 tabBar 中的 pagePath 是不带 /pages 前缀的
if (path.startsWith('/')) {
path = path.substring(1)
}
return !!tabBar.list.find((e) => e.pagePath === path)
}
/**
* path redirectPath
* path '/pages/login/index'
* redirectPath '/pages/demo/base/route-interceptor'
*/
export const currRoute = () => {
export function currRoute() {
const lastPage = getLastPage()
const currRoute = (lastPage as any).$page
// console.log('lastPage.$page:', currRoute)
@ -63,7 +29,7 @@ export const currRoute = () => {
return getUrlObj(fullPath)
}
const ensureDecodeURIComponent = (url: string) => {
function ensureDecodeURIComponent(url: string) {
if (url.startsWith('%')) {
return ensureDecodeURIComponent(decodeURIComponent(url))
}
@ -74,7 +40,7 @@ const ensureDecodeURIComponent = (url: string) => {
* url: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor
* : {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
*/
export const getUrlObj = (url: string) => {
export function getUrlObj(url: string) {
const [path, queryStr] = url.split('?')
// console.log(path, queryStr)
@ -97,16 +63,15 @@ export const getUrlObj = (url: string) => {
* key needLogin, route-block 使
* key pages key, key
*/
export const getAllPages = (key = 'needLogin') => {
export function getAllPages(key = 'needLogin') {
// 这里处理主包
const mainPages = [
...pages
.filter((page) => !key || page[key])
.map((page) => ({
...page,
path: `/${page.path}`,
})),
]
const mainPages = pages
.filter(page => !key || page[key])
.map(page => ({
...page,
path: `/${page.path}`,
}))
// 这里处理分包
const subPages: any[] = []
subPackages.forEach((subPageObj) => {
@ -114,7 +79,7 @@ export const getAllPages = (key = 'needLogin') => {
const { root } = subPageObj
subPageObj.pages
.filter((page) => !key || page[key])
.filter(page => !key || page[key])
.forEach((page: { path: string } & Record<string, any>) => {
subPages.push({
...page,
@ -131,18 +96,18 @@ export const getAllPages = (key = 'needLogin') => {
* pages
* path
*/
export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map((page) => page.path)
export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map(page => page.path)
/**
* pages
* path
*/
export const needLoginPages: string[] = getAllPages('needLogin').map((page) => page.path)
export const needLoginPages: string[] = getAllPages('needLogin').map(page => page.path)
/**
* baseUrl
*/
export const getEnvBaseUrl = () => {
export function getEnvBaseUrl() {
// 请求基准地址
let baseUrl = import.meta.env.VITE_SERVER_BASEURL
@ -171,7 +136,7 @@ export const getEnvBaseUrl = () => {
/**
* UPLOAD_BASEURL
*/
export const getEnvBaseUploadUrl = () => {
export function getEnvBaseUploadUrl() {
// 请求基准地址
let baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL

29
src/utils/queryString.ts Normal file
View File

@ -0,0 +1,29 @@
/**
* URL查询字符串 qs
*
* @param obj
* @returns
*/
export function stringifyQuery(obj: Record<string, any>): string {
if (!obj || typeof obj !== 'object' || Array.isArray(obj))
return ''
return Object.entries(obj)
.filter(([_, value]) => value !== undefined && value !== null)
.map(([key, value]) => {
// 对键进行编码
const encodedKey = encodeURIComponent(key)
// 处理数组类型
if (Array.isArray(value)) {
return value
.filter(item => item !== undefined && item !== null)
.map(item => `${encodedKey}=${encodeURIComponent(item)}`)
.join('&')
}
// 处理基本类型
return `${encodedKey}=${encodeURIComponent(value)}`
})
.join('&')
}

View File

@ -1,11 +1,11 @@
import { CustomRequestOptions } from '@/interceptors/request'
import type { CustomRequestOptions } from '@/interceptors/request'
/**
* 请求方法: 主要是对 uni.request openapi-ts-request request
* @param options
* @returns Promise
*/
const http = <T>(options: CustomRequestOptions) => {
function http<T>(options: CustomRequestOptions) {
// 1. 返回 Promise 对象
return new Promise<T>((resolve, reject) => {
uni.request({
@ -20,18 +20,20 @@ const http = <T>(options: CustomRequestOptions) => {
if (res.statusCode >= 200 && res.statusCode < 300) {
// 2.1 提取核心数据 res.data
resolve(res.data as T)
} else if (res.statusCode === 401) {
}
else if (res.statusCode === 401) {
// 401错误 -> 清理用户信息,跳转到登录页
// userStore.clearUserInfo()
// uni.navigateTo({ url: '/pages/login/login' })
reject(res)
} else {
}
else {
// 其他错误 -> 根据后端错误信息轻提示
!options.hideErrorToast &&
uni.showToast({
icon: 'none',
title: (res.data as T & { msg?: string })?.msg || '请求错误',
})
!options.hideErrorToast
&& uni.showToast({
icon: 'none',
title: (res.data as T & { msg?: string })?.msg || '请求错误',
})
reject(res)
}
},

View File

@ -21,8 +21,8 @@ export function showToast(options: ToastOptions | string) {
position: 'middle',
message: '',
}
const mergedOptions =
typeof options === 'string'
const mergedOptions
= typeof options === 'string'
? { ...defaultOptions, message: options }
: { ...defaultOptions, ...options }
// 映射position到uniapp支持的格式

View File

@ -21,7 +21,7 @@ import { toast } from './toast'
*/
export const uploadFileUrl = {
/** 用户头像上传地址 */
USER_AVATAR: import.meta.env.VITE_SERVER_BASEURL + '/user/avatar',
USER_AVATAR: `${import.meta.env.VITE_SERVER_BASEURL}/user/avatar`,
}
/**
@ -31,12 +31,7 @@ export const uploadFileUrl = {
* @param formData
* @param options
*/
export const useFileUpload = <T = string>(
url: string,
filePath: string,
formData: Record<string, any> = {},
options: Omit<UploadOptions, 'sourceType' | 'sizeType' | 'count'> = {},
) => {
export function useFileUpload<T = string>(url: string, filePath: string, formData: Record<string, any> = {}, options: Omit<UploadOptions, 'sourceType' | 'sizeType' | 'count'> = {}) {
return useUpload<T>(
url,
formData,
@ -61,7 +56,7 @@ export interface UploadOptions {
/** 上传进度回调函数 */
onProgress?: (progress: number) => void
/** 上传成功回调函数 */
onSuccess?: (res: UniApp.UploadFileSuccessCallbackResult) => void
onSuccess?: (res: Record<string, any>) => void
/** 上传失败回调函数 */
onError?: (err: Error | UniApp.GeneralCallbackResult) => void
/** 上传完成回调函数(无论成功失败) */
@ -76,13 +71,9 @@ export interface UploadOptions {
* @param options
* @returns
*/
export const useUpload = <T = string>(
url: string,
formData: Record<string, any> = {},
options: UploadOptions = {},
export function useUpload<T = string>(url: string, formData: Record<string, any> = {}, options: UploadOptions = {},
/** 直接传入文件路径,跳过选择器 */
directFilePath?: string,
) => {
directFilePath?: string) {
/** 上传中状态 */
const loading = ref(false)
/** 上传错误状态 */
@ -161,7 +152,8 @@ export const useUpload = <T = string>(
success: (res) => {
const file = res.tempFiles[0]
// 检查文件大小是否符合限制
if (!checkFileSize(file.size)) return
if (!checkFileSize(file.size))
return
// 开始上传
loading.value = true
@ -248,7 +240,7 @@ interface UploadFileOptions<T> {
/** 上传进度回调 */
onProgress?: (progress: number) => void
/** 上传成功回调 */
onSuccess?: (res: UniApp.UploadFileSuccessCallbackResult) => void
onSuccess?: (res: Record<string, any>) => void
/** 上传失败回调 */
onError?: (err: Error | UniApp.GeneralCallbackResult) => void
/** 上传完成回调 */
@ -288,20 +280,15 @@ function uploadFile<T>({
},
// 确保文件名称合法
success: (uploadFileRes) => {
console.log('上传文件成功:', uploadFileRes)
try {
// 解析响应数据
const result = JSON.parse(uploadFileRes.data)
if (result.code === 1) {
// 上传成功
data.value = result.data as T
onSuccess?.(uploadFileRes)
} else {
// 业务错误
const err = new Error(result.message || '上传失败')
error.value = true
onError?.(err)
}
} catch (err) {
const { data: _data } = JSON.parse(uploadFileRes.data)
// 上传成功
data.value = _data as T
onSuccess?.(_data)
}
catch (err) {
// 响应解析错误
console.error('解析上传响应失败:', err)
error.value = true
@ -326,7 +313,8 @@ function uploadFile<T>({
progress.value = res.progress
onProgress?.(res.progress)
})
} catch (err) {
}
catch (err) {
// 创建上传任务失败
console.error('创建上传任务失败:', err)
error.value = true

View File

@ -1,20 +1,15 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"lib": ["esnext", "dom"],
"baseUrl": ".",
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"noImplicitThis": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
"@/*": ["./src/*"],
"@img/*": ["./src/static/*"]
},
"outDir": "dist",
"lib": ["esnext", "dom"],
"resolveJsonModule": true,
"types": [
"@dcloudio/types",
"@uni-helper/uni-types",
@ -22,12 +17,17 @@
"wot-design-uni/global.d.ts",
"z-paging/types",
"./src/typings.d.ts"
]
],
"allowJs": true,
"noImplicitThis": true,
"outDir": "dist",
"sourceMap": true,
"allowSyntheticDefaultImports": true,
"skipLibCheck": true
},
"vueCompilerOptions": {
"plugins": ["@uni-helper/uni-types/volar-plugin"]
},
"exclude": ["node_modules"],
"include": [
"src/**/*.ts",
"src/**/*.js",
@ -36,5 +36,6 @@
"src/**/*.jsx",
"src/**/*.vue",
"src/**/*.json"
]
],
"exclude": ["node_modules"]
}

View File

@ -1,20 +1,26 @@
// https://www.npmjs.com/package/@uni-helper/unocss-preset-uni
import { presetUni } from '@uni-helper/unocss-preset-uni'
import {
defineConfig,
presetIcons,
presetAttributify,
presetIcons,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
export default defineConfig({
presets: [
presetUni(),
presetUni({
attributify: {
// prefix: 'fg-', // 如果加前缀,则需要在代码里面使用 `fg-` 前缀,如:<div fg-border="1px solid #000"></div>
prefixedOnly: true,
},
}),
presetIcons({
scale: 1.2,
warn: true,
extraProperties: {
display: 'inline-block',
'display': 'inline-block',
'vertical-align': 'middle',
},
}),
@ -33,6 +39,7 @@ export default defineConfig({
center: 'flex justify-center items-center',
},
],
safelist: [],
rules: [
[
'p-safe',

View File

@ -1,5 +1,6 @@
import path from 'node:path'
import process from 'node:process'
import fs from 'fs-extra'
import path from 'path'
export function copyNativeRes() {
const waitPath = path.resolve(__dirname, '../src/nativeResources')
@ -31,7 +32,8 @@ export function copyNativeRes() {
console.log(
`[copyNativeRes] 成功将 nativeResources 目录中的资源移动到构建目录:${buildPath}`,
)
} catch (error) {
}
catch (error) {
console.error(`[copyNativeRes] 复制资源失败:`, error)
}
},

View File

@ -0,0 +1,37 @@
// src/plugins/updatePackageJson.ts
import type { Plugin } from 'vite'
import fs from 'node:fs/promises'
import path from 'node:path'
import process from 'node:process'
function updatePackageJson(): Plugin {
return {
name: 'update-package-json',
async buildStart() {
// 只在生产环境构建时执行
if (process.env.NODE_ENV !== 'production')
return
const packageJsonPath = path.resolve(process.cwd(), 'package.json')
try {
// 读取并解析 package.json
const content = await fs.readFile(packageJsonPath, 'utf-8')
const packageJson = JSON.parse(content)
// 更新时间戳(使用 ISO 格式或自定义格式)
packageJson['update-time'] = new Date().toISOString().split('T')[0] // YYYY-MM-DD
// 写回文件(保持 2 空格缩进)
await fs.writeFile(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`, 'utf-8')
console.log(`[update-package-json] 更新时间戳: ${packageJson['update-time']}`)
}
catch (error) {
console.error('[update-package-json] 插件执行失败:', error)
}
},
}
}
export default updatePackageJson

View File

@ -1,26 +1,27 @@
import Uni from '@dcloudio/vite-plugin-uni'
import dayjs from 'dayjs'
import path from 'node:path'
import { defineConfig, loadEnv } from 'vite'
// @see https://uni-helper.js.org/vite-plugin-uni-pages
import UniPages from '@uni-helper/vite-plugin-uni-pages'
import process from 'node:process'
import Uni from '@dcloudio/vite-plugin-uni'
import Components from '@uni-helper/vite-plugin-uni-components'
// @see https://uni-helper.js.org/vite-plugin-uni-layouts
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
// @see https://github.com/uni-helper/vite-plugin-uni-manifest
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
// @see https://uni-helper.js.org/vite-plugin-uni-pages
import UniPages from '@uni-helper/vite-plugin-uni-pages'
// @see https://github.com/uni-helper/vite-plugin-uni-platform
// 需要与 @uni-helper/vite-plugin-uni-pages 插件一起使用
import UniPlatform from '@uni-helper/vite-plugin-uni-platform'
// @see https://github.com/uni-helper/vite-plugin-uni-manifest
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
/**
*
* @see https://github.com/uni-ku/bundle-optimizer
*/
import Optimization from '@uni-ku/bundle-optimizer'
import dayjs from 'dayjs'
import { visualizer } from 'rollup-plugin-visualizer'
import AutoImport from 'unplugin-auto-import/vite'
import { defineConfig, loadEnv } from 'vite'
import ViteRestart from 'vite-plugin-restart'
import { copyNativeRes } from './vite-plugins/copyNativeRes'
import Components from '@uni-helper/vite-plugin-uni-components'
import updatePackageJson from './vite-plugins/updatePackageJson'
// https://vitejs.dev/config/
export default async ({ command, mode }) => {
@ -74,7 +75,7 @@ export default async ({ command, mode }) => {
// 自定义插件禁用 vite:vue 插件的 devToolsEnabled强制编译 vue 模板时 inline 为 true
name: 'fix-vite-plugin-vue',
configResolved(config) {
const plugin = config.plugins.find((p) => p.name === 'vite:vue')
const plugin = config.plugins.find(p => p.name === 'vite:vue')
if (plugin && plugin.api && plugin.api.options) {
plugin.api.options.devToolsEnabled = false
}
@ -90,7 +91,7 @@ export default async ({ command, mode }) => {
// Optimization 插件需要 page.json 文件,故应在 UniPages 插件之后执行
Optimization({
enable: {
optimization: true,
'optimization': true,
'async-import': true,
'async-component': true,
},
@ -112,16 +113,16 @@ export default async ({ command, mode }) => {
},
},
// 打包分析插件h5 + 生产环境才弹出
UNI_PLATFORM === 'h5' &&
mode === 'production' &&
visualizer({
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
}),
UNI_PLATFORM === 'h5'
&& mode === 'production'
&& visualizer({
filename: './node_modules/.cache/visualizer/stats.html',
open: true,
gzipSize: true,
brotliSize: true,
}),
// 只有在 app 平台时才启用 copyNativeRes 插件
UNI_PLATFORM === 'app' && copyNativeRes(),
// UNI_PLATFORM === 'app' && copyNativeRes(),
Components({
extensions: ['vue'],
deep: true, // 是否递归扫描子目录,
@ -129,6 +130,7 @@ export default async ({ command, mode }) => {
dts: 'src/types/components.d.ts', // 自动生成的组件类型声明文件路径(用于 TypeScript 支持)
}),
Uni(),
updatePackageJson(),
],
define: {
__UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
@ -161,14 +163,15 @@ export default async ({ command, mode }) => {
[VITE_APP_PROXY_PREFIX]: {
target: VITE_SERVER_BASEURL,
changeOrigin: true,
rewrite: (path) => path.replace(new RegExp(`^${VITE_APP_PROXY_PREFIX}`), ''),
rewrite: path => path.replace(new RegExp(`^${VITE_APP_PROXY_PREFIX}`), ''),
},
}
: undefined,
},
build: {
sourcemap: false,
// 方便非h5端调试
sourcemap: VITE_SHOW_SOURCEMAP === 'true', // 默认是false
// sourcemap: VITE_SHOW_SOURCEMAP === 'true', // 默认是false
target: 'es6',
// 开发环境不用压缩
minify: mode === 'development' ? false : 'terser',