├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── release.yml
└── workflows
│ ├── auto-merge-to-aliyun.yml
│ ├── deploy.yml
│ └── release-log.yml
├── .gitignore
├── .npmrc
├── .prettierignore
├── .prettierrc.cjs
├── .vscode
├── extensions.json
├── settings.json
└── vue3.code-snippets
├── CNAME
├── LICENSE
├── README.md
├── docs
├── .vitepress
│ ├── components
│ │ └── BuildInfo.vue
│ ├── config.mts
│ ├── layouts
│ │ └── Default.vue
│ └── theme
│ │ ├── components
│ │ ├── FreshImage.vue
│ │ ├── HomeStar.vue
│ │ └── NavBarTitleAfter.vue
│ │ ├── custom.css
│ │ └── index.ts
├── advanced
│ ├── me
│ │ ├── image-2.png
│ │ ├── image-3.png
│ │ ├── me.md
│ │ └── screenshots
│ │ │ ├── pay-wx.png
│ │ │ ├── wx-gzh.png
│ │ │ └── wx-me.png
│ ├── rewards
│ │ ├── assets
│ │ │ ├── group-qq.jpg
│ │ │ ├── group-wx.jpg
│ │ │ ├── pay-1.png
│ │ │ ├── pay-2.png
│ │ │ ├── pay-ali.png
│ │ │ ├── pay-w-5.png
│ │ │ ├── pay-wx-10.png
│ │ │ ├── pay-wx-2.png
│ │ │ ├── pay-wx-5-doc.png
│ │ │ ├── pay-wx-5.png
│ │ │ └── pay-wx.png
│ │ └── rewards.md
│ ├── sponsor
│ │ └── sponsor.md
│ └── wechat
│ │ └── wechat.md
├── base
│ ├── 1-introduction.md
│ ├── 10-i18n.md
│ ├── 11-build.md
│ ├── 12-env.md
│ ├── 13-hbx.md
│ ├── 14-faq.md
│ ├── 15-faq.md
│ ├── 16-terminology.md
│ ├── 17-generate.md
│ ├── 18-app.md
│ ├── 2-start.md
│ ├── 20-best.md
│ ├── 3-plugin.md
│ ├── 4-style.md
│ ├── 4-style2.md
│ ├── 5-icons.md
│ ├── 6-svg.md
│ ├── 7-ui.md
│ ├── 8-request.md
│ ├── 9-state.md
│ ├── assets
│ │ ├── 1-1.png
│ │ ├── 10-1.png
│ │ ├── 10-2.png
│ │ ├── 10-3.png
│ │ ├── 10-android.mp4
│ │ ├── 10-ios.mp4
│ │ ├── 11-1.png
│ │ ├── 11-10.png
│ │ ├── 11-100.png
│ │ ├── 11-11.png
│ │ ├── 11-12.png
│ │ ├── 11-13.png
│ │ ├── 11-2.png
│ │ ├── 11-3.png
│ │ ├── 11-4.png
│ │ ├── 11-5.png
│ │ ├── 11-6.png
│ │ ├── 11-7.png
│ │ ├── 11-8.png
│ │ ├── 11-9.png
│ │ ├── 13-1.png
│ │ ├── 13-2.png
│ │ ├── 13-3.png
│ │ ├── 13-4.png
│ │ ├── 13-5.png
│ │ ├── 13-6.png
│ │ ├── 13-7.png
│ │ ├── 13-8.png
│ │ ├── 14-1.png
│ │ ├── 14-2.png
│ │ ├── 14-3.png
│ │ ├── 14-4.png
│ │ ├── 14-5.png
│ │ ├── 14-6.png
│ │ ├── 15-1.png
│ │ ├── 15-2.png
│ │ ├── 15-3.png
│ │ ├── 15-4.png
│ │ ├── 15-5.png
│ │ ├── 15-6.png
│ │ ├── 2-1.png
│ │ ├── 2-2.png
│ │ ├── 2-3.png
│ │ ├── 2-4.gif
│ │ ├── 3-1.png
│ │ ├── 4-1.png
│ │ ├── 4-2.png
│ │ ├── 4-3.png
│ │ ├── 4-4.png
│ │ ├── 4-5.png
│ │ ├── 5-1.png
│ │ ├── 5-10.png
│ │ ├── 5-100.png
│ │ ├── 5-11.png
│ │ ├── 5-2.png
│ │ ├── 5-3.png
│ │ ├── 5-4.png
│ │ ├── 5-5.png
│ │ ├── 5-6.png
│ │ ├── 5-7.png
│ │ ├── 5-8.png
│ │ ├── 5-9.png
│ │ └── 8-1.png
│ ├── image-18-2.png
│ ├── image-18.png
│ ├── image.png
│ └── ui
│ │ ├── image-1.png
│ │ ├── image-2.png
│ │ ├── image.png
│ │ └── ui.md
├── changelog
│ ├── CHANGELOG.md
│ ├── image-1.png
│ ├── image-2.png
│ ├── image-3.png
│ └── image.png
├── gif
│ ├── assets
│ │ ├── auto-page.gif
│ │ ├── auto-sort.gif
│ │ ├── commit.gif
│ │ ├── i18n.gif
│ │ ├── ios-run-app.gif
│ │ ├── lottery2.gif
│ │ ├── lottery3.gif
│ │ ├── snippets.gif
│ │ ├── snippets2.gif
│ │ ├── snippets3.gif
│ │ ├── unocss-icons.gif
│ │ └── unocss.gif
│ └── index.md
├── index.md
├── other
│ ├── blog.md
│ ├── files
│ │ ├── files.md
│ │ ├── image-1.png
│ │ ├── image-2.png
│ │ └── image-3.png
│ ├── iconfont
│ │ ├── assets
│ │ │ ├── 5-10.png
│ │ │ ├── 5-100.png
│ │ │ ├── 5-11.png
│ │ │ ├── 5-12.png
│ │ │ ├── 5-13.png
│ │ │ ├── 5-14.png
│ │ │ ├── 5-15.png
│ │ │ ├── 5-16.png
│ │ │ ├── 5-17.png
│ │ │ ├── 5-18.png
│ │ │ ├── 5-19.png
│ │ │ ├── 5-20.png
│ │ │ ├── 5-21.png
│ │ │ ├── 5-22.png
│ │ │ ├── 5-23.png
│ │ │ └── 5-9.png
│ │ └── iconfont.md
│ ├── image
│ │ ├── assets
│ │ │ ├── image-1.png
│ │ │ ├── image-2.png
│ │ │ └── unibest-项目架构.png
│ │ └── image.md
│ └── links
│ │ └── links.md
└── public
│ ├── favicon.ico
│ └── logo.svg
├── env
├── .env
├── .env.development
├── .env.production
└── .env.test
├── favicon.ico
├── index.html
├── manifest.config.ts
├── openapi-ts-request.config.ts
├── package.json
├── pages.config.ts
├── pnpm-lock.yaml
├── scripts
└── postupgrade.js
├── src
├── App.vue
├── api
│ ├── login.ts
│ └── login.typings.ts
├── components
│ ├── .gitkeep
│ ├── fg-navbar
│ │ └── fg-navbar.vue
│ └── privacy-popup
│ │ ├── index.scss
│ │ └── privacy-popup.vue
├── env.d.ts
├── hooks
│ ├── .gitkeep
│ ├── useRequest.ts
│ └── useUpload.ts
├── interceptors
│ ├── index.ts
│ ├── prototype.ts
│ ├── request.ts
│ └── route.ts
├── layouts
│ ├── default.vue
│ └── demo.vue
├── main.ts
├── manifest.json
├── pages-sub
│ └── demo
│ │ └── index.vue
├── pages.json
├── pages
│ ├── about
│ │ ├── about.vue
│ │ └── components
│ │ │ ├── request.vue
│ │ │ └── upload.vue
│ ├── index
│ │ └── index.vue
│ ├── login
│ │ └── index.vue
│ └── mine
│ │ ├── about
│ │ └── index.vue
│ │ ├── index.vue
│ │ ├── info
│ │ └── index.vue
│ │ └── password
│ │ └── index.vue
├── service
│ ├── app
│ │ ├── displayEnumLabel.ts
│ │ ├── index.ts
│ │ ├── pet.ts
│ │ ├── pet.vuequery.ts
│ │ ├── store.ts
│ │ ├── store.vuequery.ts
│ │ ├── types.ts
│ │ ├── user.ts
│ │ └── user.vuequery.ts
│ └── index
│ │ └── foo.ts
├── static
│ ├── app
│ │ └── icons
│ │ │ ├── 1024x1024.png
│ │ │ ├── 120x120.png
│ │ │ ├── 144x144.png
│ │ │ ├── 152x152.png
│ │ │ ├── 167x167.png
│ │ │ ├── 180x180.png
│ │ │ ├── 192x192.png
│ │ │ ├── 20x20.png
│ │ │ ├── 29x29.png
│ │ │ ├── 40x40.png
│ │ │ ├── 58x58.png
│ │ │ ├── 60x60.png
│ │ │ ├── 72x72.png
│ │ │ ├── 76x76.png
│ │ │ ├── 80x80.png
│ │ │ ├── 87x87.png
│ │ │ └── 96x96.png
│ ├── images
│ │ ├── .gitkeep
│ │ ├── avatar.jpg
│ │ └── default-avatar.png
│ ├── logo.svg
│ └── tabbar
│ │ ├── example.png
│ │ ├── exampleHL.png
│ │ ├── home.png
│ │ ├── homeHL.png
│ │ ├── personal.png
│ │ └── personalHL.png
├── store
│ ├── index.ts
│ └── user.ts
├── style
│ ├── iconfont.css
│ └── index.scss
├── typings.d.ts
├── typings.ts
├── uni.scss
├── uni_modules
│ └── .gitkeep
└── utils
│ ├── http.ts
│ ├── index.ts
│ ├── platform.ts
│ ├── request.ts
│ ├── toast.ts
│ └── uploadFile.ts
├── tsconfig.json
├── uno.config.ts
├── vite-plugins
└── copyNativeRes.ts
└── vite.config.ts
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*] # 表示所有文件适用
4 | charset = utf-8 # 设置文件字符集为 utf-8
5 | indent_style = space # 缩进风格(tab | space)
6 | indent_size = 2 # 缩进大小
7 | end_of_line = lf # 控制换行类型(lf | cr | crlf)
8 | trim_trailing_whitespace = true # 去除行首的任意空白字符
9 | insert_final_newline = true # 始终在文件末尾插入一个新行
10 |
11 | [*.md] # 表示仅 md 文件适用以下规则
12 | max_line_length = off # 关闭最大行长度限制
13 | trim_trailing_whitespace = false # 关闭末尾空格修剪
14 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report(报告问题)
3 | about: Create a report to help us improve
4 | ---
5 |
6 |
11 |
12 | # Bug report(问题描述)
13 |
14 | 写清楚 unibest 版本号,平台是什么(小程序、APP还是h5),以及具体问题描述。期望是什么,实际是什么。
15 |
16 | ## Steps to reproduce(问题复现步骤)
17 |
18 | 这里写问题复现步骤
19 |
20 |
25 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request(新功能建议)
3 | about: Suggest an idea for this project
4 | ---
5 |
6 | # Feature request(新功能建议)
7 |
--------------------------------------------------------------------------------
/.github/release.yml:
--------------------------------------------------------------------------------
1 | categories:
2 | - title: '🚀 新功能'
3 | labels: ['feat', 'feature']
4 | - title: '🛠️ 修复'
5 | labels: ['fix', 'bugfix']
6 | - title: '💅 样式'
7 | labels: ['style']
8 | - title: '📄 文档'
9 | labels: ['docs']
10 | - title: '⚡️ 性能'
11 | labels: ['perf']
12 | - title: '🧪 测试'
13 | labels: ['test']
14 | - title: '♻️ 重构'
15 | labels: ['refactor']
16 | - title: '📦 构建'
17 | labels: ['build']
18 | - title: '🚨 补丁'
19 | labels: ['patch', 'hotfix']
20 | - title: '🌐 发布'
21 | labels: ['release', 'publish']
22 | - title: '🔧 流程'
23 | labels: ['ci', 'cd', 'workflow']
24 | - title: '⚙️ 配置'
25 | labels: ['config', 'chore']
26 | - title: '📁 文件'
27 | labels: ['file']
28 | - title: '🎨 格式化'
29 | labels: ['format']
30 | - title: '🔀 其他'
31 | labels: ['other', 'misc']
32 |
--------------------------------------------------------------------------------
/.github/workflows/auto-merge-to-aliyun.yml:
--------------------------------------------------------------------------------
1 | name: Auto Merge aliyun
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | workflow_dispatch: # 手动触发
8 |
9 | jobs:
10 | auto-merge:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Checkout repository
14 | uses: actions/checkout@v4
15 | with:
16 | fetch-depth: 0
17 | token: ${{ secrets.GH_TOKEN_AUTO_MERGE }}
18 |
19 | - name: Merge main into aliyun
20 | run: |
21 | git config user.name "GitHub Actions"
22 | git config user.email "actions@github.com"
23 | git checkout aliyun
24 | git merge main --no-ff -m "Auto merge main into aliyun"
25 | git push origin aliyun
26 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | # 构建 VitePress 站点并将其部署到 GitHub Pages 的示例工作流程
2 | #
3 | name: Deploy VitePress site to Pages
4 |
5 | on:
6 | push:
7 | branches: [main]
8 |
9 | # 允许你从 Actions 选项卡手动运行此工作流程
10 | workflow_dispatch:
11 |
12 | # 设置 GITHUB_TOKEN 的权限,以允许部署到 GitHub Pages
13 | permissions:
14 | contents: read
15 | pages: write
16 | id-token: write
17 |
18 | # 只允许同时进行一次部署,跳过正在运行和最新队列之间的运行队列
19 | # 但是,不要取消正在进行的运行,因为我们希望允许这些生产部署完成
20 | concurrency:
21 | group: pages
22 | cancel-in-progress: false
23 |
24 | jobs:
25 | # 构建工作
26 | build:
27 | runs-on: ubuntu-latest
28 | steps:
29 | - name: Checkout
30 | uses: actions/checkout@v4
31 | with:
32 | fetch-depth: 0 # 如果未启用 lastUpdated,则不需要
33 | - uses: pnpm/action-setup@v3 # 如果使用 pnpm,请取消此区域注释
34 | with:
35 | version: 9
36 | - name: Setup Node
37 | uses: actions/setup-node@v4
38 | with:
39 | node-version: 18
40 | cache: pnpm
41 | - name: Setup Pages
42 | uses: actions/configure-pages@v4
43 | - name: Install dependencies
44 | run: pnpm i
45 | - name: Build with VitePress
46 | run: pnpm run docs:build
47 | - name: Upload artifact
48 | uses: actions/upload-pages-artifact@v3
49 | with:
50 | path: docs/.vitepress/dist
51 |
52 | # 部署工作
53 | deploy:
54 | environment:
55 | name: github-pages
56 | url: ${{ steps.deployment.outputs.page_url }}
57 | needs: build
58 | runs-on: ubuntu-latest
59 | name: Deploy
60 | steps:
61 | - name: Deploy to GitHub Pages
62 | id: deployment
63 | uses: actions/deploy-pages@v4
64 |
--------------------------------------------------------------------------------
/.github/workflows/release-log.yml:
--------------------------------------------------------------------------------
1 | name: Auto Release
2 |
3 | on:
4 | push:
5 | tags:
6 | - 'v*'
7 |
8 | permissions:
9 | contents: write
10 | pull-requests: read
11 | issues: read
12 |
13 | jobs:
14 | build:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: Checkout code
18 | uses: actions/checkout@v4
19 | with:
20 | fetch-depth: 0
21 | token: ${{ secrets.GITHUB_TOKEN }}
22 |
23 | - name: Install yq
24 | run: sudo snap install yq
25 |
26 | - name: Generate changelog
27 | id: changelog
28 | env:
29 | CONFIG_FILE: .github/release.yml
30 | run: |
31 | # 解析配置文件
32 | declare -A category_map
33 | while IFS=";" read -r title labels; do
34 | for label in $labels; do
35 | category_map[$label]="$title"
36 | done
37 | done < <(yq -o=tsv '.categories[] | [.title, (.labels | join(" "))] | join(";")' $CONFIG_FILE)
38 | # 获取版本范围
39 | mapfile -t tags < <(git tag -l --sort=-version:refname)
40 | current_tag=${tags[0]}
41 | previous_tag=${tags[1]:-}
42 | if [[ -z "$previous_tag" ]]; then
43 | commit_range="$current_tag"
44 | echo "首次发布版本: $current_tag"
45 | else
46 | commit_range="$previous_tag..$current_tag"
47 | echo "版本范围: $commit_range"
48 | fi
49 | # 获取所有符合规范的提交
50 | commits=$(git log --pretty=format:"%s|%h" "$commit_range")
51 | # 生成分类日志
52 | declare -A log_entries
53 | while IFS="|" read -r subject hash; do
54 | # type=$(echo "$subject" | cut -d':' -f1 | tr -d ' ')
55 | type=$(echo "$subject" | sed -E 's/^([[:alnum:]]+)(\(.*\))?:.*/\1/' | tr -d ' ')
56 | found=0
57 | for label in "${!category_map[@]}"; do
58 | if [[ "$type" == "$label" ]]; then
59 | entry="- ${subject} (${hash:0:7})"
60 | log_entries[${category_map[$label]}]+="$entry"$'\n'
61 | found=1
62 | break
63 | fi
64 | done
65 | if [[ $found -eq 0 ]]; then
66 | entry="- ${subject} (${hash:0:7})"
67 | log_entries["其他"]+="$entry"$'\n'
68 | fi
69 | done <<< "$commits"
70 |
71 | # 统计提交数量
72 | commit_count=$(git log --oneline "$commit_range" | wc -l)
73 | # 统计受影响的文件数量
74 | file_count=$(git diff --name-only "$commit_range" | wc -l)
75 | # 统计贡献者信息
76 | contributor_stats=$(git shortlog -sn "$commit_range")
77 | contributor_notes=""
78 | while IFS= read -r line; do
79 | commits=$(echo "$line" | awk '{print $1}')
80 | name=$(echo "$line" | awk '{$1=""; print $0}' | sed 's/^ //')
81 | contributor_notes+="- @${name} (${commits} commits)\n"
82 | done <<< "$contributor_stats"
83 | # 构建输出内容
84 | release_notes="## 版本更新日志 ($current_tag)\n\n"
85 | while IFS= read -r category; do
86 | if [[ -n "${log_entries[$category]}" ]]; then
87 | release_notes+="### $category\n${log_entries[$category]}\n"
88 | fi
89 | done < <(yq '.categories[].title' $CONFIG_FILE)
90 | # 构建输出内容
91 | release_notes="## 版本更新日志 ($current_tag)\n\n"
92 | current_date=$(date +"%Y-%m-%d")
93 | # 添加发布日期和下载统计信息
94 | release_notes+=" ### 📅 发布日期: ${current_date}\n"
95 | while IFS= read -r category; do
96 | if [[ -n "${log_entries[$category]}" ]]; then
97 | release_notes+="### $category\n${log_entries[$category]}\n"
98 | fi
99 | done < <(yq '.categories[].title' $CONFIG_FILE)
100 |
101 | # 添加统计信息
102 | release_notes+="### 📊 统计信息\n"
103 | release_notes+="- 本次发布包含 ${commit_count} 个提交\n"
104 | release_notes+="- 影响 ${file_count} 个文件\n\n"
105 | # 添加贡献者信息
106 | release_notes+="### 👥 贡献者\n"
107 | release_notes+="感谢这些优秀的贡献者(按提交次数排序):\n"
108 | release_notes+="${contributor_notes}\n"
109 | release_notes+="---\n"
110 | # 写入文件
111 | echo -e "$release_notes" > changelog.md
112 | echo "生成日志内容:"
113 | cat changelog.md
114 | - name: Create Release
115 | uses: ncipollo/release-action@v1
116 | with:
117 | generateReleaseNotes: false
118 | bodyFile: changelog.md
119 | tag: ${{ github.ref_name }}
120 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | .DS_Store
12 | dist
13 | *.local
14 |
15 | # Editor directories and files
16 | .idea
17 | *.suo
18 | *.ntvs*
19 | *.njsproj
20 | *.sln
21 | *.sw?
22 | .hbuilderx
23 |
24 | .stylelintcache
25 | .eslintcache
26 |
27 | docs/.vitepress/dist
28 | docs/.vitepress/cache
29 |
30 | types
31 |
32 | # lock 文件还是不要了,我主要的版本写死就好了
33 | # pnpm-lock.yaml
34 | # package-lock.json
35 |
36 | # TIPS:如果某些文件已经加入了版本管理,现在重新加入 .gitignore 是不生效的,需要执行下面的操作
37 | # `git rm -r --cached .` 然后提交 commit 即可。
38 |
39 | # git rm -r --cached file1 file2 ## 针对某些文件
40 | # git rm -r --cached dir1 dir2 ## 针对某些文件夹
41 | # git rm -r --cached . ## 针对所有文件
42 |
43 | # 更新 uni-app 官方版本
44 | # npx @dcloudio/uvm@latest
45 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | # registry = https://registry.npmjs.org
2 | registry = https://registry.npmmirror.com
3 |
4 | strict-peer-dependencies=false
5 | auto-install-peers=true
6 | shamefully-hoist=true
7 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | # unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
2 | auto-import.d.ts
3 |
4 | # vite-plugin-uni-pages 生成的类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
5 | uni-pages.d.ts
6 |
7 | # 插件生成的文件
8 | src/pages.json
9 | src/manifest.json
10 |
11 | # 忽略自动生成文件
12 | src/service/app/**
13 |
--------------------------------------------------------------------------------
/.prettierrc.cjs:
--------------------------------------------------------------------------------
1 | // @see https://prettier.io/docs/en/options
2 | module.exports = {
3 | singleQuote: true,
4 | printWidth: 100,
5 | tabWidth: 2,
6 | useTabs: false,
7 | semi: false,
8 | trailingComma: 'all',
9 | endOfLine: 'auto',
10 | htmlWhitespaceSensitivity: 'ignore',
11 | overrides: [
12 | {
13 | files: '*.json',
14 | options: {
15 | trailingComma: 'none',
16 | },
17 | },
18 | ],
19 | }
20 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": [
3 | "vue.volar",
4 | "stylelint.vscode-stylelint",
5 | "esbenp.prettier-vscode",
6 | "dbaeumer.vscode-eslint",
7 | "antfu.unocss",
8 | "antfu.iconify",
9 | "evils.uniapp-vscode",
10 | "uni-helper.uni-helper-vscode",
11 | "uni-helper.uni-app-schemas-vscode",
12 | "uni-helper.uni-highlight-vscode",
13 | "uni-helper.uni-ui-snippets-vscode",
14 | "uni-helper.uni-app-snippets-vscode",
15 | "mrmlnc.vscode-json5",
16 | "streetsidesoftware.code-spell-checker"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | // 默认格式化工具选择prettier
3 | "editor.defaultFormatter": "esbenp.prettier-vscode",
4 | // 保存的时候自动格式化
5 | "editor.formatOnSave": true,
6 | //开启自动修复
7 | "editor.codeActionsOnSave": {
8 | "source.fixAll": "explicit",
9 | "source.fixAll.eslint": "explicit",
10 | "source.fixAll.stylelint": "explicit"
11 | },
12 | // 配置stylelint检查的文件类型范围
13 | "stylelint.validate": ["css", "scss", "vue", "html"], // 与package.json的scripts对应
14 | "stylelint.enable": true,
15 | "css.validate": false,
16 | "less.validate": false,
17 | "scss.validate": false,
18 | "[shellscript]": {
19 | "editor.defaultFormatter": "foxundermoon.shell-format"
20 | },
21 | "[dotenv]": {
22 | "editor.defaultFormatter": "foxundermoon.shell-format"
23 | },
24 | "[vue]": {
25 | "editor.defaultFormatter": "esbenp.prettier-vscode"
26 | },
27 | "[typescript]": {
28 | "editor.defaultFormatter": "esbenp.prettier-vscode"
29 | },
30 | "[jsonc]": {
31 | "editor.defaultFormatter": "esbenp.prettier-vscode"
32 | },
33 | // 配置语言的文件关联
34 | "files.associations": {
35 | "pages.json": "jsonc", // pages.json 可以写注释
36 | "manifest.json": "jsonc" // manifest.json 可以写注释
37 | },
38 | "cSpell.words": [
39 | "aliyun",
40 | "Aplipay",
41 | "climblee",
42 | "commitlint",
43 | "dcloudio",
44 | "iconfont",
45 | "qrcode",
46 | "refresherrefresh",
47 | "scrolltolower",
48 | "tabbar",
49 | "Toutiao",
50 | "unibest",
51 | "uvui",
52 | "vitepress",
53 | "Wechat",
54 | "WechatMiniprogram",
55 | "Weixin"
56 | ],
57 | "typescript.tsdk": "node_modules\\typescript\\lib",
58 | "explorer.fileNesting.enabled": true,
59 | "explorer.fileNesting.expand": false,
60 | "explorer.fileNesting.patterns": {
61 | "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,.npmrc,.browserslistrc",
62 | ".eslintrc.cjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,.stylelintrc.*,.eslintrc-auto-import.json,.editorconfig,.commitlint.cjs"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/.vscode/vue3.code-snippets:
--------------------------------------------------------------------------------
1 | {
2 | // Place your unibest 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
3 | // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
4 | // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
5 | // used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
6 | // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
7 | // Placeholders with the same ids are connected.
8 | // Example:
9 | // "Print to console": {
10 | // "scope": "javascript,typescript",
11 | // "prefix": "log",
12 | // "body": [
13 | // "console.log('$1');",
14 | // "$2"
15 | // ],
16 | // "description": "Log output to console"
17 | // }
18 | "Print unibest Vue3 SFC": {
19 | "scope": "vue",
20 | "prefix": "v3",
21 | "body": [
22 | "",
23 | "{",
24 | " layout: 'default',",
25 | " style: {",
26 | " navigationBarTitleText: '$1',",
27 | " },",
28 | "}",
29 | "\n",
30 | "",
31 | " $2",
32 | "\n",
33 | "\n",
36 | "\n",
39 | ],
40 | },
41 | "Print unibest style": {
42 | "scope": "vue",
43 | "prefix": "st",
44 | "body": ["\n"],
45 | },
46 | "Print unibest script": {
47 | "scope": "vue",
48 | "prefix": "sc",
49 | "body": ["\n"],
50 | },
51 | "Print unibest template": {
52 | "scope": "vue",
53 | "prefix": "te",
54 | "body": ["", " $1", "\n"],
55 | },
56 | }
57 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | unibest.tech
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2024 菲鸽
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
10 |
11 |
12 | 旧仓库 codercup 进不去了,star 也拿不回来,这里也展示一下那个地址的 star.
13 |
14 | [](https://github.com/codercup/unibest)
15 | [](https://github.com/codercup/unibest)
16 |
17 |
18 |
19 |
20 |
21 | [](https://github.com/feige996/unibest)
22 | [](https://github.com/feige996/unibest)
23 | [](https://gitee.com/feige996/unibest/stargazers)
24 | [](https://gitee.com/feige996/unibest/members)
25 | 
26 | 
27 | 
28 | 
29 |
30 |
31 |
32 | `unibest` —— 最好的 `uniapp` 开发模板,由 `uniapp` + `Vue3` + `Ts` + `Vite5` + `UnoCss` + `wot-ui` + `z-paging` 构成,使用了最新的前端技术栈,无需依靠 `HBuilderX`,通过命令行方式运行 `web`、`小程序` 和 `App`(编辑器推荐 `VSCode`,可选 `webstorm`)。
33 |
34 | `unibest` 内置了 `约定式路由`、`layout布局`、`请求封装`、`请求拦截`、`登录拦截`、`UnoCSS`、`i18n多语言` 等基础功能,提供了 `代码提示`、`自动格式化`、`统一配置`、`代码片段` 等辅助功能,让你编写 `uniapp` 拥有 `best` 体验 ( `unibest 的由来`)。
35 |
36 | 
37 |
38 |
39 | 📖 文档地址(new)
40 | |
41 | 📱 DEMO 地址
42 |
43 |
44 | ---
45 |
46 | 注意旧的地址 [codercup](https://github.com/codercup/unibest) 我进不去了,使用新的 [feige996](https://github.com/feige996/unibest)。PR和 issue 也请使用新地址,否则无法合并。
47 |
48 | ## 平台兼容性
49 |
50 | | H5 | IOS | 安卓 | 微信小程序 | 字节小程序 | 快手小程序 | 支付宝小程序 | 钉钉小程序 | 百度小程序 |
51 | | --- | --- | ---- | ---------- | ---------- | ---------- | ------------ | ---------- | ---------- |
52 | | √ | √ | √ | √ | √ | √ | √ | √ | √ |
53 |
54 | 注意每种 `UI框架` 支持的平台有所不同,详情请看各 `UI框架` 的官网,也可以看 `unibest` 文档。
55 |
56 | ## ⚙️ 环境
57 |
58 | - node>=18
59 | - pnpm>=8
60 | - Vue Official>=2.1.10
61 | - TypeScript>=5.0
62 |
63 | ## 📂 快速开始
64 |
65 | 执行 `pnpm create unibest` 创建项目
66 | 执行 `pnpm i` 安装依赖
67 | 执行 `pnpm dev` 运行 `H5`
68 | 执行 `pnpm dev:mp` 运行 `微信小程序`
69 |
70 | ## 📦 运行(支持热更新)
71 |
72 | - web平台: `pnpm dev:h5`, 然后打开 [http://localhost:9000/](http://localhost:9000/)。
73 | - weixin平台:`pnpm dev:mp-weixin` 然后打开微信开发者工具,导入本地文件夹,选择本项目的`dist/dev/mp-weixin` 文件。
74 | - APP平台:`pnpm dev:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/dev/app` 文件夹,选择运行到模拟器(开发时优先使用),或者运行的安卓/ios基座。
75 |
76 | ## 🔗 发布
77 |
78 | - web平台: `pnpm build:h5`,打包后的文件在 `dist/build/h5`,可以放到web服务器,如nginx运行。如果最终不是放在根目录,可以在 `manifest.config.ts` 文件的 `h5.router.base` 属性进行修改。
79 | - weixin平台:`pnpm build:mp-weixin`, 打包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
80 | - APP平台:`pnpm build:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/build/app` 文件夹,选择发行 - APP云打包。
81 |
82 | ## 🤔 如何贡献
83 |
84 | 非常欢迎您的加入!提一个 [Issue](https://github.com/feige996/unibest/issues) 或者提交一个 [Pull Request](https://github.com/feige996/unibest/pulls)
85 |
86 | **Pull Request:**
87 |
88 | - 1. `Fork` 代码到自己的项目下,不要直接在仓库下建分支
89 | - 2. 请选择 `base` 分支,进行 `PR`
90 | - 3. 提交 `PR` 前请 `rebase`,确保 `commit` 记录的整洁
91 | - 4. 注意 `commit` 信息规范,要以 `type: 描述信息` 的形式填写,注意 `type` 得是下面规范之中的一个
92 | - 5. 示例 `commit 信息`:`fix: 修复样式问题`
93 | - 6. 可以使用项目中的 `pnpm cz` 进行 `commit` 提交
94 | - 7. 等待作者 `review` 通过后,即可合并
95 |
96 | ## 📄 License
97 |
98 | [MIT](https://opensource.org/license/mit/)
99 |
100 | Copyright (c) 2025 菲鸽
101 |
102 | ## 捐赠
103 |
104 |
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/docs/.vitepress/components/BuildInfo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
构建时间: {{ buildTime }}
5 |
版本号: {{ version }}
6 |
7 |
8 |
9 |
15 |
16 |
24 |
--------------------------------------------------------------------------------
/docs/.vitepress/layouts/Default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
27 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/components/FreshImage.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
22 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/components/HomeStar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
33 |
34 |
35 |
38 |
39 |
40 |
41 |
51 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/components/NavBarTitleAfter.vue:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
18 | {{ version }}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/custom.css:
--------------------------------------------------------------------------------
1 | /**
2 | * Colors
3 | * -------------------------------------------------------------------------- */
4 | :root {
5 | --vp-c-brand-1: hsl(128, 56%, 38%);
6 | --vp-c-brand-2: hsl(128, 56%, 55%);
7 | --vp-c-brand-3: hsl(128, 56%, 45%);
8 | --vp-c-brand-soft: rgba(98, 133, 208, 0.16);
9 | }
10 | /**
11 | * Component: Home
12 | * -------------------------------------------------------------------------- */
13 |
14 | :root {
15 | --vp-home-hero-name-color: transparent;
16 | --vp-home-hero-name-background: -webkit-linear-gradient(
17 | 120deg,
18 | hsl(128, 56%, 38%) 30%,
19 | hsl(128, 56%, 60%)
20 | );
21 | --vp-home-hero-image-background-image: linear-gradient(
22 | 120deg,
23 | hsl(100, 56%, 45%) 30%,
24 | hsl(120, 56%, 38%)
25 | );
26 | --vp-home-hero-image-filter: blur(40px);
27 | }
28 | @media (min-width: 640px) {
29 | :root {
30 | --vp-home-hero-image-filter: blur(56px);
31 | }
32 | }
33 |
34 | @media (min-width: 960px) {
35 | :root {
36 | --vp-home-hero-image-filter: blur(72px);
37 | }
38 | }
39 |
40 | .md-center > p {
41 | display: flex;
42 | /* justify-content: center; */
43 | flex-wrap: wrap;
44 | margin-top: -4px;
45 | margin-right: -4px;
46 | }
47 | .md-center img {
48 | display: inline-block;
49 | height: 1.4em;
50 | margin-top: 4px;
51 | margin-right: 4px;
52 | line-height: 1.6;
53 | }
54 | .md-center2 img {
55 | display: inline-block;
56 | margin-top: 4px;
57 | margin-right: 4px;
58 | }
59 |
60 | .busuanzi_container {
61 | display: flex;
62 | justify-content: space-around;
63 | opacity: 0.3;
64 | }
65 |
66 | @media (max-width: 960px) {
67 | .busuanzi_container {
68 | flex-direction: column;
69 | padding-top: 12px;
70 | padding-left: 30px;
71 | }
72 | }
73 | @media (min-width: 961px) {
74 | .busuanzi_container {
75 | height: 60px;
76 | line-height: 60px;
77 | }
78 | }
79 |
80 | .icp_container {
81 | display: flex;
82 | align-items: center;
83 | justify-content: center;
84 | }
85 |
--------------------------------------------------------------------------------
/docs/.vitepress/theme/index.ts:
--------------------------------------------------------------------------------
1 | // https://vitepress.dev/guide/custom-theme
2 | import type { Theme } from 'vitepress'
3 | import DefaultTheme from 'vitepress/theme'
4 | import { h } from 'vue'
5 | import './custom.css'
6 |
7 | import HomeStar from './components/HomeStar.vue'
8 | import NavBarTitleAfter from './components/NavBarTitleAfter.vue'
9 | import FreshImage from './components/FreshImage.vue'
10 |
11 | export default {
12 | extends: DefaultTheme,
13 | Layout: () => {
14 | return h(DefaultTheme.Layout, null, {
15 | // https://vitepress.dev/guide/extending-default-theme#layout-slots
16 | 'home-hero-info-after': () => h(HomeStar),
17 | 'nav-bar-title-after': () => h(NavBarTitleAfter),
18 | })
19 | },
20 | enhanceApp({ app, router, siteData }) {
21 | // ...
22 | app.component('FreshImage', FreshImage)
23 | },
24 | } satisfies Theme
25 |
--------------------------------------------------------------------------------
/docs/advanced/me/image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/me/image-2.png
--------------------------------------------------------------------------------
/docs/advanced/me/image-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/me/image-3.png
--------------------------------------------------------------------------------
/docs/advanced/me/me.md:
--------------------------------------------------------------------------------
1 | # 关于我
2 |
3 | 我叫 `菲鸽`,江西宋城人。前端工程师,全栈实践者,精通 `vue`、`react`、`uniapp`、`typescript`、`小程序`、`Nodejs` 等。
4 |
5 | 热爱编程,喜欢分享,平时比较宅,喜欢撸代码,偶尔打篮球和玩王者荣耀。
6 |
7 |
8 |
9 | ## 找到我
10 |
11 | - Github: [feige996](https://github.com/feige996)
12 | - Gitee: [feige996](https://gitee.com/feige996)
13 | - 掘金:[菲鸽](https://juejin.cn/user/3263006241460792/posts)
14 | - 微信:`feige996` = `菲鸽`,大家都叫我 `鸽鸽`
15 | - QQ:`1020103647`
16 | - 邮箱:`1020103647@qq.com`
17 |
18 | ## 微信公众号
19 |
20 | 微信公众号:`菲鸽爱编程`
21 |
22 | 
23 |
24 |
31 |
--------------------------------------------------------------------------------
/docs/advanced/me/screenshots/pay-wx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/me/screenshots/pay-wx.png
--------------------------------------------------------------------------------
/docs/advanced/me/screenshots/wx-gzh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/me/screenshots/wx-gzh.png
--------------------------------------------------------------------------------
/docs/advanced/me/screenshots/wx-me.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/me/screenshots/wx-me.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/group-qq.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/group-qq.jpg
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/group-wx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/group-wx.jpg
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-1.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-2.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-ali.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-ali.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-w-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-w-5.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-wx-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-wx-10.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-wx-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-wx-2.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-wx-5-doc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-wx-5-doc.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-wx-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-wx-5.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/assets/pay-wx.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/advanced/rewards/assets/pay-wx.png
--------------------------------------------------------------------------------
/docs/advanced/rewards/rewards.md:
--------------------------------------------------------------------------------
1 | # 🥤 打赏
2 |
3 | 如果本项目对你的工作起到了帮助,加快了您的项目进展,解决了您的问题,欢迎 `打赏` !
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/docs/advanced/sponsor/sponsor.md:
--------------------------------------------------------------------------------
1 | # 赞助榜
2 |
3 | 感谢所有赞助者!
4 |
5 | 如需更改展示信息,或者需要展示更详细的信息,请联系我。
6 |
7 | 如果需要展示产品、博客、友链啥的,也可以联系我,很乐意为您展示。
8 |
9 | > 金牌赞助者将额外获得首页产品展示位。
10 |
11 | ## 200 元
12 |
13 | - 麦可
14 | - 程序员云创 [https://www.codecommitter.com/](https://www.codecommitter.com/)
15 | - 海鲜™ 深圳金济福科技有限公司[https://www.jinjifu.com/](https://www.jinjifu.com/)
16 |
17 | ## 50 元
18 |
19 | - \*皮
20 | - 暗月隐落 [飞鸟快验 - 一个通用网络验证后台](https://www.fnkuaiyan.cn)
21 |
22 | ## 20 元
23 |
24 | - \*捷
25 | - \*度
26 | - \*恼
27 |
28 | ## 10-20 元
29 |
30 | - 薛柳(15)
31 | - 是魔王哒(12)
32 |
33 | ## 10 元
34 |
35 | - \*辛
36 | - \*y
37 | - \*边
38 |
39 | ## 5-10 元
40 |
41 | - Leo (9.90)
42 | - \*熙 (6.66)
43 | - 阿森纳 (6.66)
44 | - I am 小萝北 ²º²4 (6.60)
45 | - SUMMER (5)
46 | - \*峰 (5)
47 | - 阿云 (5)
48 | - nuYoah (5)
49 | - 许志成 (5)
50 | - JY_en_ke_hao (5)
51 |
52 | ## 2 元及以下
53 |
54 | 有 `15` 人,这里不一一列出。(如果希望展示,请联系我)
55 |
56 | ## 红包打赏
57 |
58 | 还有一部分群友是发 `专属红包` 打赏的,这里没有统计,如果需要展示,请联系我。
59 |
60 | ---
61 |
62 | > 再次感谢所有赞助者、打赏者!
63 |
--------------------------------------------------------------------------------
/docs/advanced/wechat/wechat.md:
--------------------------------------------------------------------------------
1 | # 微信交流群
2 |
3 | 千万记得 `先看一遍文档`,可以解决大部分基础疑问。
4 |
5 |
6 |
7 |
8 |
9 | > 如果上面的微信群满了,请使用下面的 QQ 群,QQ 群不会过期,长期有效。
10 |
11 | ①②③ 群已满,下面是 ④ 群。
12 | 
13 |
--------------------------------------------------------------------------------
/docs/base/10-i18n.md:
--------------------------------------------------------------------------------
1 | # 多语言篇
2 |
3 | `多语言` 是一个常见的需求, `unibest` 专门开发了一个 `i18n`模板,可以直接生成 `多语言模板项目`。
4 |
5 | ```sh
6 | pnpm create unibest my-project -t i18n
7 | ```
8 |
9 | `vue组件` 里面使用方式如下:
10 |
11 | ```html
12 | {{ $t('app.name') }}
13 | ```
14 |
15 | `非vue组件` 里面怎么使用呢?比如 `ts` 文件。
16 |
17 | 这时需要用到作者编写的 `translate` 函数,使用方式如下:
18 |
19 | ```ts
20 | import { translate as t } from '@/locale/index'
21 |
22 | /** 非vue 文件使用 i18n */
23 | export const testI18n = () => {
24 | console.log(t('app.name'))
25 | // 下面同样生效
26 | uni.showModal({
27 | title: 'i18n 测试',
28 | content: t('app.name'),
29 | })
30 | }
31 | ```
32 |
33 | 上面基本的使用都是没问题的,但是传递参数时,只有 `H5端` 生效,`其他端` 是不生效的,代码如下:
34 |
35 | ```html
36 | {{ $t('weight', { heavy: 100 }) }}
37 | ```
38 |
39 | `H5端` 效果如下,正常显示:
40 |
41 | 
42 |
43 | `非H5端` 效果如下,异常显示:
44 |
45 | 
46 |
47 | 下面我们就来处理这个问题。
48 |
49 | ## 多语言传参
50 |
51 | 上面提到 `vue-i18n` 在 `非H5端` 传参时显示异常,那我们就来处理一下,主要方式就是通过 `正则` 替换 `多语言字符串`。
52 |
53 | 编写一个函数 `formatI18n`,如下:
54 |
55 | ```ts
56 | /**
57 | * formatI18n('我是{name},身高{detail.height},体重{detail.weight}',{name:'张三',detail:{height:178,weight:'75kg'}})
58 | * 暂不支持数组
59 | * @param template 多语言模板字符串,eg: `我是{name}`
60 | * @param obj 需要传递的对象,里面的key与多语言字符串对应,eg: `{name:'菲鸽'}`
61 | * @returns
62 | */
63 | export function formatI18n(template, data) {
64 | const match = /\{(.*?)\}/g.exec(template)
65 | if (match) {
66 | const variableList = match[0].replace('{', '').replace('}', '').split('.')
67 | let result = data
68 | for (let i = 0; i < variableList.length; i++) {
69 | result = result[variableList[i]] || ''
70 | }
71 | return formatStr(template.replace(match[0], result), data)
72 | } else {
73 | return template
74 | }
75 | }
76 | ```
77 |
78 | `vue组件` 里面使用方式如下:
79 |
80 | ```html
81 | {{ formatI18n(translate('introduction'), user) }}
82 | ```
83 |
84 | 用到的函数引入如下:
85 |
86 | ```js
87 | import { formatI18n, translate } from '@/locale/index'
88 | ```
89 |
90 | 对应的 en.json 文件如下:
91 |
92 | ```json
93 | { "introduction": "I am {name},height:{detail.height},weight:{detail.weight}" }
94 | ```
95 |
96 | `user` 对象如下:
97 |
98 | ```js
99 | {name:'张三',detail:{height:178,weight:'75kg'}}
100 | ```
101 |
102 | 这样,在 `H5端` 和 `非H5端` 都能正常显示,如下:
103 |
104 | 
105 |
106 | very good !
107 |
108 | ## 导航栏标题
109 |
110 | 目前发现 `导航栏标题` 在 `小程序端` 不会跟随多语言切换而切换,比如说刚开始是中文,切换成英文后,页面内容都变成英文了,标题栏还是中文。
111 |
112 | > `App端` 说明:`App模拟器`,以我的 `mac电脑` `ios模拟器` 来说,是正常的,可以直接切换,多语言也是生效的。
113 | >
114 | > 但是 `安卓真机` 会出现`切换多语言后,自动重启,然后界面多语言是生效的`。
115 | >
116 | > 既然 `App 正常`,这里主要说 `小程序端` 不正常的处理。
117 |
118 | `小程序端` 需要使用 `uni.setNavigationBarTitle` 来手动处理,`API` 使用如下:
119 |
120 | ```js
121 | uni.setNavigationBarTitle({
122 | title: '新的标题',
123 | })
124 | ```
125 |
126 | 结合 `translate` 函数,则:
127 |
128 | ```js
129 | uni.setNavigationBarTitle({
130 | title: translate('app.name'),
131 | })
132 | ```
133 |
134 | 可以满足大部分场景。
135 |
136 | ## tabbar 标题
137 |
138 | 同 `导航栏标题`。
139 |
140 | ## App 端视频
141 |
142 | 这里给出 `2` 个 `App端` 的视频,加深开发者的认识和印象。
143 |
144 | :::details
145 |
146 | ### `ios模拟器` 多语言直接就是生效的
147 |
148 |
149 |
150 | ### `安卓真机` 会自动重启,重启后界面多语言是生效的
151 |
152 |
153 | :::
154 |
155 | ## 总结
156 |
157 | 本文介绍了 `unibest` 里面使用 `多语言` 的基本方式,还处理了 `3` 个多端异常的问题:
158 |
159 | - `多语言传参` 不生效 BUG
160 | - `导航栏标题` 切换多语言不生效 BUG
161 | - `tabbar标题` 切换多语言不生效 BUG
162 |
163 | 全文完~
164 |
--------------------------------------------------------------------------------
/docs/base/11-build.md:
--------------------------------------------------------------------------------
1 | # 运行发布
2 |
3 | ## 运行
4 |
5 | - `h5 平台`: `pnpm dev:h5`( 或者简单点 `pnpm dev` ),然后浏览器打开 `http://localhost:9000/`。
6 | - `wx 小程序`:`pnpm dev:mp-weixin`,然后打开微信开发者工具,导入本地文件夹,选择本项目的 `dist/dev/mp-weixin` 文件。
7 | - 
8 |
9 | - `APP 平台`:`pnpm dev:app`,然后打开 `HBuilderX`,导入刚刚生成的 `dist/dev/app` 文件夹,选择运行到 `模拟器`( `开发时优先使用` ),或者 `运行到安卓/ios 基座` (真机调试时使用) 。
10 |
11 | 
12 |
13 | 
14 | 
15 |
16 | > 如果需要配置其他模拟器,可以参考:[安装模拟器](https://uniapp.dcloud.net.cn/tutorial/run/installSimulator.html)
17 |
18 | > 这样操作的话,开发时都会有热更新,开发体验很爽!
19 |
20 | ## 发布
21 |
22 | - `h5 平台`: `pnpm build:h5`,打包后的文件在 `dist/build/h5`,可以放到 web 服务器,如 nginx 运行。如果最终不是放在根目录,可以在 `manifest.config.ts` 文件的 `h5.router.base` 属性进行修改。
23 | - `wx 小程序`:`pnpm build:mp-weixin`,打包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
24 | - `APP 平台`:`pnpm build:app`,然后打开 `HBuilderX`,导入刚刚生成的 `dist/build/app` 文件夹,选择 `发行` - `原生APP-云打包`。
25 |
26 | 
27 | 
28 | 
29 |
30 | > 熟悉原生 APP 开发的开发者也可以使用 `原生APP-本地打包`。
31 |
32 | ## APP 打包注意事项(上)
33 |
34 | 很多开发者发现打包失败,或者打包白屏,这里简单说明一下。
35 |
36 | - 1. 重新获取自己的 `AppId`
37 |
38 | 
39 |
40 | - 2. 根据上面获取到的 `AppId` 修改 `env/.env` 文件的 `VITE_UNI_APPID` 字段
41 |
42 | 
43 |
44 | - 3. (可选)云打包如果有出现解析时出问题的,把 `minSdkVersion` 版本改低一点就好了,比如 `21`。(最低 `21`,不能低于 `21`;我模板里面设置的是 `30`)。
45 |
46 | 
47 |
48 | ## APP 打包注意事项 (下)
49 |
50 | ### `uni-app SDK` 版本
51 |
52 | > 特别备注:`2024-05-03`,新的 `base` 模板的 `uni-app SDK` 版本已经升级到 `4.14` 了。
53 | >
54 | > 
55 |
56 | `2024-04-14`,新的 `base` 模板的 `uni-app SDK` 版本已经升级到 `4.08` ,记得更新您的 `HBuilderx` 版本。
57 |
58 | `"@dcloudio/uni-app": "3.0.0-4000820240401001"` 表示 `uni-app` 为 `3.0.0` 版本,对应的 `HBuilderx` 版本为 `4.08`,后面的 `20240401001` 是发布日期。
59 |
60 | > `40008` 第一个数字 `4` 表示主要版本,后面每 `2` 位数为一组,所以代表 `4.0.8`。
61 | >
62 | > 类似的,`30812` 代表 `3.8.12` 版本,`30909` 代表 `3.9.9` 版本。
63 | >
64 | > 另外,从 `3.99` 开始,后面 2 个小版本合并书写,于是 `3.9.9` 变成 `3.99`,`4.0.8` 变成 `4.08`。
65 |
66 | `unibest` 历史用过的 `@dcloudio/uni-app` 版本:
67 |
68 | ```text
69 | "@dcloudio/uni-app": "3.0.0-3081220230817001", => 3.8.12
70 | "@dcloudio/uni-app": "3.0.0-3090920231225001", => 3.99
71 | "@dcloudio/uni-app": "3.0.0-4000820240401001", => 4.08
72 | "@dcloudio/uni-app": "3.0.0-4010420240430001", => 4.14
73 | ```
74 |
75 | 
76 |
77 | ### `uni-app SDK` 版本匹配 `HBuilderX`
78 |
79 | > 温馨提示:下面的部分是使用 `uni-app` 版本为 `3.8.12` 时写的文档,适当参考~
80 |
81 | 本模板使用的是 `3.8.12` 的库版本(`"@dcloudio/uni-app": "3.0.0-3081220230817001",`),所以尽量使用 `3.8.12` 版本的 `HBuilderX` 来打包,否则可能有未知的风险,出现情况如下图。
82 |
83 | > 原来的图不见了,重新补了一张。
84 |
85 | 
86 |
87 | 上图表示您的 `HBuilderX` 版本是 `4.08`,但是代码 `uni-app SDK` (即 `"@dcloudio/uni-app": "3.0.0-4010420240430001"` ) 是 `4.14`,版本不匹配。
88 |
89 | - 点击 `ignore`(忽略) 后若可以正常使用,那就不用管。(可选添加如下配置)
90 |
91 | ```json
92 | "app-plus" : { "compatible": { "ignoreVersion": true } }
93 | ```
94 |
95 | - 如果出现白屏啥的,请更新您的 `HBuilderX` 到 `uni-app SDK` 相同版本(这里是 `4.14` )。
96 |
97 | ### 多个 `HBuilderX` 版本安装
98 |
99 | > 温馨提示:下面的部分是使用 `uni-app` 版本为 `3.8.12` 时写的文档,适当参考~
100 |
101 | `MAC` 可以安装多个版本的软件,如下图我安装了 `3.8.12` (3.8.12.20230817) 和最新的 `3.99` (3.99.2023122611) 两个版本,平时的项目使用 `3.99`, 打包 `unibest` 的时候使用 `3.8.12`。
102 |
103 | 
104 |
105 | > `window` 系统也可以同时安装多个 `HBuilderX` 版本,安装时选择安装到不同目录下即可。
106 |
107 | ## 总结
108 |
109 | 本文描述了多端的运行和发布,希望对您有帮助。
110 |
111 | 全文完~
112 |
--------------------------------------------------------------------------------
/docs/base/12-env.md:
--------------------------------------------------------------------------------
1 | # 环境变量
2 |
3 | 主要介绍 `vite` 环境变量和 `uni` 环境变量。
4 |
5 | ## `vite` 环境变量
6 |
7 | `Vite` 在一个特殊的 `import.meta.env` 对象上暴露环境变量。环境变量定义在 `.env` 文件里。
8 |
9 | ### .env 文件
10 |
11 | 创建环境文件:
12 |
13 | ```yml
14 | .env # 所有情况下都会加载
15 | .env.local # 所有情况下都会加载,但会被 git 忽略
16 | .env.[mode] # 只在指定模式下加载
17 | .env.[mode].local # 只在指定模式下加载,但会被 git 忽略
18 |
19 | # 注意 .env.local 无法覆盖 .env.[mode]
20 | ```
21 |
22 | 环境文件只包含环境变量的 `键值对` :
23 |
24 | ```yml
25 | VITE_SOME_KEY=123
26 | DB_PASSWORD=foobar
27 | ```
28 |
29 | > 为了防止意外地将一些环境变量泄漏到客户端,只有以 `VITE_` 为前缀的变量才会暴露给经过 `vite` 处理的代码。
30 |
31 | 如上配置,只有 `VITE_SOME_KEY` 会被暴露为 `import.meta.env.VITE_SOME_KEY` 提供给客户端源码,而 `DB_PASSWORD` 则不会。
32 |
33 | ```js
34 | console.log(import.meta.env.VITE_SOME_KEY) // "123"
35 | console.log(import.meta.env.DB_PASSWORD) // undefined
36 | ```
37 |
38 | ### mode 模式
39 |
40 | Vite 允许你为不同的构建环境指定不同的模式。通常在 `npm scripts` 里面指定 `mode` 参数:
41 |
42 | ```sh
43 | "scripts": {
44 | "dev": "uni",
45 | "dev-dev": "uni --mode development",
46 | "dev-test": "uni --mode test",
47 | "dev-prod": "uni --mode production",
48 | }
49 | ```
50 |
51 | 运行不同模式的脚本时,`Vite` 会自动加载对应的 `.env.[mode]` 文件,就能获取到不同的环境变量。
52 |
53 | `Vite` 运行 `dev` 时默认会加载 `.env.development` 文件(若有)。
54 |
55 | `Vite` 运行 `build` 时默认会加载 `.env.production` 文件(若有)。
56 |
57 | 故,如上配置 `pnpm dev` 与 `pnpm dev-dev` 是一个效果。
58 |
59 | ## `uni` 环境变量
60 |
61 | `uni` 环境变量这里指运行 `uni` 的平台变量,通过 `vite` 的 `define` 配置可以暴露出来。
62 |
63 | ```
64 | define: {
65 | __UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
66 | },
67 | ```
68 |
69 | 代码里面使用:
70 |
71 | ```js
72 | export const platform = __UNI_PLATFORM__
73 | export const isH5 = __UNI_PLATFORM__ === 'h5'
74 | export const isApp = __UNI_PLATFORM__ === 'app'
75 | export const isMp = __UNI_PLATFORM__.startsWith('mp-')
76 | const PLATFORM = {
77 | platform,
78 | isH5,
79 | isApp,
80 | isMp,
81 | }
82 | export default PLATFORM
83 | ```
84 |
85 | ## 总结
86 |
87 | 本文描写了 `vite` 环境变量和 `uni` 环境变量如何配置和使用。
88 |
89 | 全文完~
90 |
--------------------------------------------------------------------------------
/docs/base/13-hbx.md:
--------------------------------------------------------------------------------
1 | # hbx 模板
2 |
3 | 为了方便使用 `HBuilderX` 的开发者,`unibest` 也提供 `hbx` 模板。
4 |
5 | `hbx 模板` 适用于 `2 类用户`
6 |
7 | - 使用 `uniCloud` 云开发的用户,必须使用 `hbx 版本`,因为 `uniCloud` 跟 `HBuilderX` 是绑定的。
8 | - 开发 `App` 的用户,可选使用 `hbx 版本`。
9 |
10 | ## 仓库地址
11 |
12 | - gitee: [https://gitee.com/feige996/unibest-hbx.git](https://gitee.com/feige996/unibest-hbx.git)
13 | - github: [https://github.com/feige996/unibest-hbx.git](https://github.com/feige996/unibest-hbx.git)
14 |
15 | 没有梯子的用户优先推荐使用 `gitee` 仓库,速度更快。(两个仓库会实时同步,无差别。)
16 |
17 | ## 导入项目
18 |
19 | 有 2 种方式导入项目:
20 |
21 | - 从 `Git` 导入...
22 | - 从本地目录导入...
23 |
24 | ## 运行项目
25 |
26 | 此时运行菜单会提示 `本项目类型无法运行`,如下图
27 |
28 | 
29 |
30 | 
31 |
32 | 需要执行如下 2 步:
33 |
34 | - 项目下执行 `pnpm i`
35 | - 右键项目,选择 `重新识别项目类型`
36 |
37 | 
38 |
39 | ## 运行效果
40 |
41 | 经过上面的操作后,就可以正常运行了。
42 |
43 | - ios 模拟器运行效果如下:
44 |
45 | 
46 |
47 | 
48 |
49 | 
50 |
51 | - 微信小程序运行效果如下:
52 |
53 | 
54 |
55 | > 目前微信小程序静态资源还有点问题,如下图 `logo 不见了`,后续会修复。
56 |
57 | 
58 |
59 | > 另外还发现 `UnoCSS Icon` 不生效,原因未知。
60 |
61 | ## 总结
62 |
63 | 本文描述了 `hbx` 模板的由来,使用方式。
64 |
65 | 有需要的可以试试,但是不太建议使用。另外精力有限,该模板不再维护。
66 |
67 | 全文完~
68 |
--------------------------------------------------------------------------------
/docs/base/14-faq.md:
--------------------------------------------------------------------------------
1 | # 常见问题
2 |
3 | 本篇介绍一些常见的问题,会持续更新。
4 |
5 | ## 1. 如何设置/修改首页?
6 |
7 | `vue` 文件的 `route-block` 块里面设置 `type="home"` 即可,请确保项目里面 `只有一个页面` 是这个配置。
8 |
9 | > 注意:如果有多个,会按照字母顺序排列,第一个是首页。(可能不是您的想要的效果。)
10 |
11 | ## 2. 修改 `pages.json`、`manifest.json` 被覆盖问题
12 |
13 | - `pages.json`
14 |
15 | 本项目引入了 `@uni-helper/vite-plugin-uni-pages`,`pages.json` 文件将会自动生成,手动修改 `pages.json` 将会被覆盖。
16 |
17 | 全局的东西请在 `pages.config.ts` 里面配置,页面的东西请在 `vue` 文件的 `route-block` 配置。
18 |
19 | - `manifest.json`
20 |
21 | 与上面类似。本项目引入了 `@uni-helper/vite-plugin-uni-manifest`,`manifest.json` 文件将会自动生成,手动修改 `manifest.json` 将会被覆盖。
22 |
23 | 如需修改,请在 `manifest.config.ts` 里面修改。
24 |
25 | ## 3. 怎么分包?
26 |
27 | `vite.config.ts` 里面有一个配置,如下:(其中 `subPackages` 就是用来分包的)
28 |
29 | ```ts [vite.config.ts]{3}
30 | UniPages({
31 | exclude: ['**/components/**/**.*'],
32 | subPackages: ['src/pages-sub'], // 是个数组,可以配置多个
33 | }),
34 | ```
35 |
36 | ## 4. 首次运行 `pnpm:mp` 时报错。
37 |
38 | 首次运行 `pnpm:mp` 时报错,报错如下:
39 |
40 | ```text
41 | Error: ENOENT: no such file or directory, open '/Users/burtlai/unibest-projects/unibest/src/manifest.json'
42 | ```
43 |
44 | 首次运行 `非h5端` 时都可能出现上面的问题,需要先执行一下 `pnpm i` 以生成 `src/manifest.json` 文件,后面就不会报错了。
45 |
46 | ## 5. `git commit` 报错。
47 |
48 | 请看 `commitlint.config.ts` 里面的配置,需要满足对应的设定。根据自己的需要,可以修改 `commitlint.config.ts` 里面的配置。
49 |
50 | 如果是一次的(比如引入了某个第三方库),可以通过 `--no-verify` 参数跳过校验:
51 |
52 | ```sh
53 | git commit -m "feat: xxx" --no-verify
54 | ```
55 |
56 | 第三方库还有另外一种处理方式,放到特定的文件夹,然后在 `.eslintignore` 和 `.styleintignore` 里面加上该文件夹。
57 |
58 | ## 6. 不想要严格的 `git` 提交检测,怎么办?
59 |
60 | 直接把 `.husky` 这个文件删掉即可。(或者不删除,只把里面的文件内容注释掉。)
61 |
62 | ## 7. `uni-app` 无法使用 `process.env` 变量,怎么办?
63 |
64 | 使用 `import.meta.env` 替代!
65 |
66 | ## 8. 如何跟随 `uni-app` 官方升级?
67 |
68 | 项目下,执行 `npx @dcloudio/uvm@latest` 即可更新。
69 |
70 | 
71 |
72 | > 注意,上面的命令会自动安装 `vue-i18n`,可以手动删除(`pnpm un vue-i18n`),也可以不理它(没多大影响)。
73 |
74 | ## 9. 如何把已经加入 `git` 管理的文件移出 `git` 管理?
75 |
76 | - 第一步,先把文件移出`git` 管理,操作如下:
77 |
78 | ```text
79 | # git rm -r --cached file1 file2 ## 针对某些文件
80 | # git rm -r --cached dir1 dir2 ## 针对某些文件夹
81 | # git rm -r --cached . ## 针对所有文件
82 | ```
83 |
84 | - 第二步,提交 `commit` 以正式删除的文件
85 |
86 | > 总结:`git rm -r --cached .` + `git commit` 即可。
87 |
88 | ## 10. 支付宝小程序运行报错。
89 |
90 | - 默认运行是会报错的,如下图
91 | 
92 |
93 | - 只需要勾上 `本地开发跳过 ES5 转译` 即可正常运行,如下图
94 | 
95 |
96 | > 总结:勾上 `本地开发跳过 ES5 转译` 即可。
97 |
98 | ## 11. 支持 `uni-app x` 吗?
99 |
100 | 不支持。但我们一直保持关注。[uni-app x 传送门](https://doc.dcloud.net.cn/uni-app-x/)
101 |
102 | 目前 `unibest` 已经有 `hbx` 模板,后续接入 `uni-app x` 会很容易,坐等官方发布。
103 |
104 | ## 12. 为啥 `package.json` 中 `vue` 已经 `3.4+` 了,还不支持 `defineModel` ?
105 |
106 | `uni-app` 官方虽然已经把 `vue` 升级到 `3.4+` 了,但是目前只有 `H5端` 支持 `defineModel`,其他端目前运行报错,详情请看 `uni-app` 官网的发布日志:
107 |
108 | [HBuilder X - Release Notes](https://3085868976.hiecheimaetu.com:22443/qn-GO8xCsKgpKDZWIBAkVCUkI1EnGmQUMT4.update.dcloud.net.cn/hbuilderx/changelog/4.14.2024043013.html)
109 |
110 | 关键截图如下:(仅支持 `H5端`)
111 |
112 | 
113 |
114 | 真实运行报错截图如下:(分别是 `小程序` 和 `APP`, 都会报错 )
115 |
116 | 
117 |
118 | 
119 |
120 | ## 13. `base` 模板如何接 `uniCloud` ?
121 |
122 | - 1. 操作方案:直接在原始项目目录上右键,重新识别项目类型,就可以关联 `uniCloud` 了,然后用原始项目直接运行就可以了,不需要再 `pnpm dev:app` 后导入 `dist/dev/app` 再运行了。
123 |
124 | - 2. 问:其他模板可以吗?答:其他模板也可以,操作同上。
125 |
126 | - 3. 我写的文章链接:[【unibest】可以去掉 hbx 模版了,base 模板一统天下](https://mp.weixin.qq.com/s?__biz=MzUxMzAwNzMwNw==&mid=2247484792&idx=1&sn=b6116198f265384e5a51bd2bd95bea90&chksm=f95a8edcce2d07caba60782e17e48d766612c0ad85c019379fd5ac37890e31b6ca7049e670f7&scene=178&cur_album_id=3438500614009782275#rd)
127 |
128 | 全文完~
129 |
--------------------------------------------------------------------------------
/docs/base/16-terminology.md:
--------------------------------------------------------------------------------
1 | # 小程序的标识
2 |
3 | 目前有以下 `9` 种小程序标识,对应小程序平台类型如下:
4 |
5 | | 类型 | 标识 |
6 | | ------------ | ----------- |
7 | | 微信小程序 | mp-weixin |
8 | | 支付宝小程序 | mp-alipay |
9 | | 抖音小程序 | mp-toutiao |
10 | | 飞书小程序 | mp-lark |
11 | | QQ小程序 | mp-qq |
12 | | 京东小程序 | mp-jd |
13 | | 小红书小程序 | mp-xhs |
14 | | 百度小程序 | mp-baidu |
15 | | 快手小程序 | mp-kuaishou |
16 |
17 | > 注意: `mp-toutiao` 就是抖音小程序,其他的都很好辨别。
18 |
--------------------------------------------------------------------------------
/docs/base/17-generate.md:
--------------------------------------------------------------------------------
1 | # 自动生成代码
2 |
3 | 集成 [openapi-ts-request](https://github.com/openapi-ui/openapi-ts-request) 插件,可以根据接口文档自动生成 js,ts,uni.request,vue-query 代码。
4 |
5 | 支持 apifox/swagger/opeanpi/yapi 等接口文档,更多配置详情请查看 [openapi-ts-request](https://github.com/openapi-ui/openapi-ts-request) 插件。
6 |
7 |
8 | ## 如何使用
9 |
10 | 你只需要将接口文档对应的接口配置url,复制到根目录的 `openapi-ts-request.config.ts` 插件的配置文件中的 `schemaPath` 字段中,然后运行 `npm run openapi-ts-request` 命令,就可以生成代码。
11 | 支持同时配置多个接口文档url,生成的代码默认会放在 `src/service/app` 目录下,你可以自己调整生成代码的目录。
12 |
13 | 配置如下:
14 |
15 | ```ts
16 | import type { GenerateServiceProps } from 'openapi-ts-request'
17 |
18 | export default [
19 | {
20 | schemaPath: 'http://petstore.swagger.io/v2/swagger.json',
21 | serversPath: './src/service/app',
22 | requestLibPath: `import request from '@/utils/request';\n import { CustomRequestOptions } from '@/interceptors/request';`,
23 | requestOptionsType: 'CustomRequestOptions',
24 | isGenReactQuery: true,
25 | reactQueryMode: 'vue',
26 | isGenJavaScript: false,
27 | },
28 | ] as GenerateServiceProps[]
29 |
30 | ```
31 |
32 |
33 | ## 生成 ts 代码
34 |
35 | ts 的 type 类型会默认生成在 `src/service/app/types.ts` 文件,你可以通过引入它们进行使用。
36 |
37 | ```ts
38 | import { type Category } from '@/service/app'
39 |
40 | const category: Category = {
41 | id: 1,
42 | name: '张三',
43 | }
44 | ```
45 |
46 |
47 | ## 生成 uni.request 代码
48 |
49 | ts 的 uni.request 客户端会默认生成在 `src/service/app` 目录下,以模块名进行分类,你可以通过引入它们进行使用。
50 |
51 | ```ts
52 | import { getPetById } from '@/service/app'
53 |
54 | onShow(() => {
55 | const res = await getPetById({ id: 1 });
56 | console.log('res: ', res)
57 | })
58 | ```
59 |
60 |
61 | ## 生成 vue-query 代码
62 |
63 | vue-query 的代码会默认生成在 `src/service/app` 目录下,以模块名进行分类,后缀为 `moduleName.vuequery.ts`,你可以通过引入它们进行使用。
64 |
65 | ```ts
66 | import { useQuery } from '@tanstack/vue-query'
67 | import { findPetsByStatusQueryOptions, usePlaceOrderMutation } from '@/service/app'
68 |
69 | // get请求使用,findPetsByStatusQueryOptions 方法为自动生成 react-query 函数
70 | export function findPetsByStatusQueryOptions(options: {
71 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
72 | params: API.findPetsByStatusParams;
73 | options?: CustomRequestOptions;
74 | }) {
75 | return queryOptions({
76 | queryFn: async ({ queryKey }) => {
77 | return apis.findPetsByStatus(queryKey[1] as typeof options);
78 | },
79 | queryKey: ['findPetsByStatus', options],
80 | });
81 | }
82 |
83 | // vue-query useQuery 默认使用
84 | const {
85 | data,
86 | error
87 | isLoading,
88 | refetch,
89 | } = useQuery(findPetsByStatusQueryOptions({ params: { status: ['available'] } }))
90 |
91 | // vue-query useQuery 额外配置
92 | const {
93 | data,
94 | error
95 | isLoading,
96 | refetch,
97 | } = useQuery({
98 | ...findPetsByStatusQueryOptions({ params: { status: ['available'] } }),
99 | enabled: !!token,
100 | })
101 |
102 | // post, delete, patch请求使用,usePlaceOrderMutation 为自动生成 vue-query hook函数
103 | export function usePlaceOrderMutation(options?: {
104 | onSuccess?: (value?: API.Order) => void;
105 | onError?: (error?: DefaultError) => void;
106 | }) {
107 | const { onSuccess, onError } = options || {};
108 |
109 | const response = useMutation({
110 | mutationFn: apis.placeOrder,
111 | onSuccess(data: API.Order) {
112 | onSuccess?.(data);
113 | },
114 | onError(error) {
115 | onError?.(error);
116 | },
117 | });
118 |
119 | return response;
120 | }
121 |
122 | // 定义请求
123 | const { mutate, isPending } = usePlaceOrderMutation({
124 | onSuccess: (data) => {
125 | console.log('success data: ', data)
126 | },
127 | })
128 |
129 | // 提交请求
130 | mutate({
131 | body: {
132 | status: 'placed',
133 | complete: true,
134 | }
135 | })
136 | ```
--------------------------------------------------------------------------------
/docs/base/18-app.md:
--------------------------------------------------------------------------------
1 | # App 专题
2 |
3 | ## 1. 其他端正常,`App` 白屏
4 |
5 | 请检查 `useXxxStore` 的调用,需要在函数内部调用,而不是在函数外部调用。(估计是顶层调用的时候 `pinia` 没有初始化,导致的问题,`app` 端独有的问题。)
6 |
7 | ```ts
8 | // 错误写法
9 | const userStore = useUserStore()
10 | function foo() {
11 | userStore.xxx
12 | }
13 | // 正确写法
14 | function foo() {
15 | const userStore = useUserStore()
16 | userStore.xxx
17 | }
18 | ```
19 |
20 | ## 2.unibest 的 `App` 模块配置
21 |
22 | > 核心解决办法就是把 `manifest.json` 的内容搬运到 `manifest.config.ts` 中。
23 |
24 | 我们默认的的 `manifest.config.ts` 只包含了比较基础的 `uniapp` 配置,有的时候我们需要在打包 `app` 时在 `hbuilderx` 里面额外设置一些配置,那么就需要配置好后把 `manifest.json` 中的内容拷贝到 `manifest.config.ts` 中,后面运行就不会丢失了。
25 |
26 | 举例子,我在 `manifest.json` 里面配置了 2个模块配置,如下:
27 | 
28 |
29 | 点击左侧下面的 `源码视图` 就可以看到增加了如下内容:
30 | 
31 |
32 | 只需要把对应的内容拷贝到 `manifest.config.ts` 中的 `distribute.plugins` 里面即可。
33 |
--------------------------------------------------------------------------------
/docs/base/2-start.md:
--------------------------------------------------------------------------------
1 | # 快速开始
2 |
3 | - 前置依赖
4 |
5 | - **Node.js** - `>=v18`
6 | - **pnpm** - `>=7.30`(推荐使用 `8.12+`)
7 | - **`VSCode`** - 可选 `WebStrom`
8 | - **`HBuilderX`** - `APP` 的运行和发布还是离不开它
9 |
10 | ## 创建项目
11 |
12 | 通过下面的命令可以快速生成项目模板,`pnpm create unibest <项目名称>` ,如果不写 `<项目名称>` 会进入命令行交互模式。
13 |
14 | ```bash
15 | # 如果没有 pnpm,请先安装: npm i -g pnpm
16 | pnpm create unibest my-project
17 | ```
18 |
19 | npm 创建如下(不推荐)
20 | :::details
21 | 如果使用 `npm`,可能有缓存,需要加上 `@latest` 标识,如果创建失败,请使用 `pnpm` 安装。
22 |
23 | ```bash
24 | npm create unibest my-project
25 | # 如果提示报错,或者生成的项目版本太旧,请使用下面的命令,增加 @latest 标识
26 | npm create unibest@latest my-project
27 | ```
28 |
29 | :::
30 | 实际操作截图如下:
31 |
32 | 
33 |
34 | `create-unibest` 在 `v1.10.0` 开始会有版本号,如下:
35 |
36 | 
37 |
38 | 
39 |
40 | `create unibest` 支持 `-t` 参数选择模板,目前已有两大类 `8` 个模板
41 |
42 | - `普通` 模板( `4个` ):分别是 `base`、`tabbar`、`i18n`、`demo`、~~`js`~~
43 | - `hbx` 模板(`2个` ):分别是 `hbx-base`、`hbx-demo`。
44 |
45 | 不带 `-t` 参数时会默认生成 `base` 模板。
46 |
47 | `base` 模板是最基本的模板,更新最及时,推荐使用 `base` 模板创建新项目。其他几个模板也是基于 `base` 模板得到的。 `demo` 模板则作为参考用。
48 |
49 | `js` 模板不推荐使用,可以使用 `base` 模板替代,里面已经做了兼容配置,可以直接编写 `js`,原本的 `ts` 文件还能提供部分类型,何乐而不为?
50 |
51 | ```sh
52 | # VS Code 模板
53 | pnpm create unibest my-project # 默认用 base 模板
54 |
55 | pnpm create unibest my-project -t base # 基础模板
56 | pnpm create unibest my-project -t tabbar # 自定义 tabbar 模板
57 | pnpm create unibest my-project -t i18n # 多语言模板
58 | pnpm create unibest my-project -t demo # 所有demo的模板(包括i18n)
59 | # pnpm create unibest my-project -t js # js 模板
60 |
61 | # HBuilderX 模板,方便使用 uniCloud 云开发 (未来可以对接 uni-app x)
62 | pnpm create unibest my-project -t hbx-base # hbx的base模板
63 | pnpm create unibest my-project -t hbx-demo # hbx的demo模板,包含所有的demo
64 | ```
65 |
66 | > 2024-12-29<周日> 发表了一篇文章:[【unibest】可以去掉hbx模版了,base模板一统天下](https://mp.weixin.qq.com/s/ybunFNkjKfV5yVLOMvqscg?token=1696234630&lang=zh_CN)
67 | >
68 | > 就是说 hbx 模板可以退出历史舞台了。
69 |
70 | ## 项目仓库地址
71 |
72 | `github` 和 `gitee` 实时同步,代码一致。
73 |
74 | ### 普通模板:
75 |
76 | - https://github.com/feige996/unibest
77 | - https://gitee.com/feige996/unibest
78 |
79 | > `demo` 模板是在 `hello-unibest` 项目中,仓库地址如下:
80 |
81 | - https://github.com/feige996/hello-unibest
82 | - https://gitee.com/feige996/hello-unibest
83 |
84 | ### hbx 模板
85 |
86 | - https://github.com/uni-run/unibest-hbx
87 |
88 | > `hbx` 目前由 `青谷` 大佬维护,微信号:`qingguxixi`,[青谷 github 地址](https://github.com/Xiphin) 。
89 |
90 | ## 安装、运行
91 |
92 | ```bash [pnpm]
93 | pnpm i
94 | pnpm dev
95 | # dev默认运行的是h5,其他平台执行dev:,如:
96 | pnpm dev:mp-weixin
97 | ```
98 |
99 | `pnpm dev` 之后在浏览器打开 `http://localhost:9000/`。
100 |
101 | > 其他平台构建和发布,查看 [运行发布篇](./11-build)。
102 |
103 | ## 第一次 `commit`
104 |
105 | ```bash
106 | git add .
107 | git commit -m "feat: init project"
108 | ```
109 |
110 | ## `v3` 代码块
111 |
112 | 在 `vue` 文件中,输入 `v3` 按 `tab` 即可快速生成页面模板,可以大大加快页面生成。
113 |
114 | > 原理:基于 `VSCode` 代码块生成。
115 |
116 | 
117 |
118 | ## 注意事项
119 |
120 | - 若代码里面自动引入的 `API` 报错,只需要 `pnpm dev` 即可。
121 | - 若代码运行后,`H5端` 浏览器界面底部没有 `tabbar`, 刷新浏览器或者再次 `pnpm dev` 即可。
122 |
--------------------------------------------------------------------------------
/docs/base/20-best.md:
--------------------------------------------------------------------------------
1 | # 最佳实践
2 |
3 | 新项目使用 `base` 模板,可选 `tabbar` 模板。如果需要多语言,可以选 `i18n` 模板。
4 |
5 | 同时参考 `demo` 模板,可以直接 `clone` `demo` 项目,用来参考用。
6 |
7 | 
8 |
9 | ## 创建项目
10 |
11 | 推荐使用 `pnpm` :
12 |
13 | ```sh
14 | # 新项目创建
15 | pnpm create unibest my-project -t base
16 | ```
17 |
18 | ## DEMO 模板
19 |
20 | `demo` 模版-在线地址:
21 |
22 | 推荐先全部体验一下 `demo` 的示例
23 |
24 | ## 必看章节
25 |
26 | - [介绍](/base/1-introduction)
27 | - [快速开始](/base/2-start)
28 | - [uni 插件](/base/3-plugin)
29 | - [常见问题](/base/14-faq)
30 | - [常见问题 2](/base/15-faq)
31 | - [运行发布](/base/11-build)
32 |
--------------------------------------------------------------------------------
/docs/base/4-style2.md:
--------------------------------------------------------------------------------
1 | # 关于使用 TailwindCSS
2 |
3 | 对于 unibest 项目使用 TailwindCSS 的评估如下:
4 |
5 | 1. **直接使用TailwindCSS的限制**:
6 |
7 | - 原生TailwindCSS是为Web设计的,在小程序环境下会有兼容性问题
8 | - 不支持rpx单位,在小程序适配上有困难
9 | - 需要额外配置postcss和purgeCSS才能在小程序工作
10 |
11 | 2. **当前UnoCSS的优势**:
12 |
13 | - 您的项目已经配置了`unocss-applet`,专门为小程序优化
14 | - 支持rpx单位转换(通过presetRemRpx)
15 | - 支持小程序属性化写法(transformerAttributify)
16 | - 体积更小,按需生成样式
17 |
18 | 3. **替代方案建议**:
19 |
20 | - 保持使用UnoCSS,它已经实现了Tailwind的大部分功能
21 | - 可以通过安装`@unocss/preset-wind`来获得Tailwind风格的类名:
22 |
23 | ```bash
24 | pnpm add -D @unocss/preset-wind
25 | ```
26 |
27 | 然后在uno.config.ts中添加:
28 |
29 | ```typescript
30 | import { presetWind } from '@unocss/preset-wind'
31 |
32 | export default defineConfig({
33 | presets: [
34 | presetWind(),
35 | // 其他presets...
36 | ],
37 | })
38 | ```
39 |
40 | 4. **结论**:
41 | 在uni-app项目中,UnoCSS是比原生TailwindCSS更合适的选择,特别是针对小程序开发。通过`preset-wind`可以获得类似Tailwind的开发体验。
42 |
--------------------------------------------------------------------------------
/docs/base/6-svg.md:
--------------------------------------------------------------------------------
1 | # SVG 篇
2 |
3 | 上一章《五、图标篇》主要介绍了 `线上图标` 的使用,今天带给大家本地 `SVG` 图标的使用。
4 |
5 | 本地 `SVG` 图标使用方式主要有:
6 |
7 | - `image + src` 方式
8 |
9 | - `static目录` 图标
10 | - `相对目录` 图标
11 | - `线上地址` 图标
12 |
13 | > **`图片`** 也是使用上面几种方式。
14 |
15 | ## `image + src` 方式
16 |
17 | 根据图片地址不同,分为 2 种:`static目录`图标 , `相对目录`图标。
18 |
19 | ### 1. `static目录` 图标
20 |
21 | 这种方式直接编写代码即可,如下:
22 |
23 | ```html
24 |
25 | ```
26 |
27 | ### 2. `相对目录` 图标
28 |
29 | 这种方式需要先引入,再使用,代码编写如下:
30 |
31 | ```html
32 |
33 |
34 |
35 |
36 |
39 | ```
40 |
41 | ### 3. `线上地址` 图标
42 |
43 | 这种方式直接使用,代码编写如下:
44 |
45 | ```html
46 |
47 |
48 |
49 | ```
50 |
51 | ## 其他
52 |
53 | > `SvgComponent` 方式 和 `SvgIcon` 方式,仅 `H5端` 适用,感兴趣的可以阅读下
54 |
55 | :::details
56 |
57 | ### `SvgComponent` 方式
58 |
59 | 从 `Web端` 过来的同学都知道 `SvgComponent` 这种方式,只需要引入 `vite-svg-loader` 插件即可,支持 `3种` 方式引入 `svg`: `url`, `raw`, `component`。
60 |
61 | - URL
62 |
63 | SVGs can be imported as URLs using the `?url` suffix:
64 |
65 | ```js
66 | import iconUrl from './my-icon.svg?url'
67 | // 'data:image/svg+xml...'
68 | ```
69 |
70 | Used in template:
71 |
72 | ```html
73 |
74 |
75 |
76 | ```
77 |
78 | - Raw
79 |
80 | SVGs can be imported as strings using the `?raw` suffix:
81 |
82 | ```js
83 | import iconRaw from './my-icon.svg?raw'
84 | // '...'
85 | ```
86 |
87 | Used in template:
88 |
89 | ```html
90 | {{ iconRaw }}
91 | ```
92 |
93 | - Component
94 |
95 | SVGs can be explicitly imported as Vue components using the `?component` suffix:
96 |
97 | ```js
98 | import IconComponent from './my-icon.svg?component'
99 | //
100 | ```
101 |
102 | Used in template:
103 |
104 | ```html
105 |
106 |
107 |
108 | ```
109 |
110 | 但是目前经过测试,只有 `url` 的方式所有端可以使用,与上面的 `image + src - 相对目录 图标` 是一个效果。至于 `component` 只有 `H5端生效`,其他端不行。
111 |
112 | ### `SvgIcon` 方式
113 |
114 | 从 `Web端` 过来的同学都知道 `SvgIcon` 这种方式,只需要引入 `vite-plugin-svg-icons` 插件 + `vite 配置`,再编写一个通用的 `SvgIcon` 即可,但是同样只有 `H5端生效`,其他端不行。
115 |
116 | `vite` 配置如下:
117 |
118 | ```
119 | createSvgIconsPlugin({
120 | // 指定要缓存的文件夹
121 | iconDirs: [path.resolve(process.cwd(), 'src/assets')],
122 | // 指定symbolId格式
123 | symbolId: 'icon-[dir]-[name]',
124 | }),
125 | ```
126 |
127 | 如上,只需要把 `svg` 放到 `src/assets` 目录即可。
128 |
129 | `SvgIcon` 代码如下:
130 |
131 | ```html
132 |
133 |
136 |
137 |
138 |
153 | ```
154 |
155 | 使用方式如下:
156 |
157 | ```html
158 |
159 |
160 |
161 |
162 |
163 | ```
164 |
165 | > `SvgComponent` 依赖 `vite-svg-loader` 插件
166 | >
167 | > `SvgIcon` 依赖 `vite-plugin-svg-icons` 插件
168 |
169 | :::
170 |
171 | ## 总结
172 |
173 | 本地 `svg` 的使用方式,如果要全端适配,那就只能使用 `image + src` 的方式。
174 |
175 | > `SvgComponent` 依赖 `vite-svg-loader` 插件
176 | >
177 | > `SvgIcon` 依赖 `vite-plugin-svg-icons` 插件
178 |
179 | 其他 2 种方式 —— `SvgComponent` + `SvgIcon` 仅 `h5` 端生效,其他端都不能用,既然不能使用,那就删了,对应的 2 个插件也一起删了,目前 `base` 分支已经删了。
180 |
181 | 全文完~
182 |
--------------------------------------------------------------------------------
/docs/base/7-ui.md:
--------------------------------------------------------------------------------
1 | # UI 库替换篇
2 |
3 | ## 默认 UI 库
4 |
5 | `unibest` 经过几次更迭,先后使用 `uni-ui`、`uv-ui`作为默认 UI 库,目前使用 `wot-ui` 为默认 UI 库。
6 |
7 | `wot-ui` 是 `vue3+ts` 编写的全端支持的 UI 库,编码体验比 `uv-ui` 更好;而官方维护的 `uni-ui` 则样式略丑,组件较少,故弃之。
8 |
9 | > `wot-ui` 全称 `wot-design-uni`,是 `wot-design` 的 `uniapp` 版本,文档地址:[https://wot-design-uni.netlify.app/](https://wot-design-uni.netlify.app/).
10 |
11 | ---
12 |
13 | 很多群友反馈有其他 `UI` 库的需求,那么更换 `UI 库` 需要哪些步骤呢?
14 |
15 | - 先卸载原有的 `wot-ui` 库
16 | - 再安装其他 `UI 库`
17 |
18 | 下面我们简单描述一下更换 2 个主流 `UI库` —— `uni-ui` + `uv-ui` 的过程。
19 |
20 | > 当然也支持同时存在多个 `UI 库`,有 ES 摇树特性,不必担心打包后的体积。
21 |
22 | ## 卸载 wot-ui 库
23 |
24 | 卸载 `wot-ui` 过程如下:
25 |
26 | - 1. 删除 `wot-ui` 库:
27 |
28 | ```sh
29 | pnpm un wot-design-uni
30 | ```
31 |
32 | - 2. `pages.config.ts` 文件 `easycom.custom` 删除相关配置:
33 |
34 | ```diff
35 | easycom: {
36 | autoscan: true,
37 | custom: {
38 | - '^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
39 | },
40 | },
41 | ```
42 |
43 | - 3. ` tsconfig.json` 文件 `compilerOptions.types` 删除相关配置:
44 |
45 | ```diff
46 | "types": [
47 | "@dcloudio/types",
48 | "@types/wechat-miniprogram",
49 | - "wot-design-uni/global.d.ts",
50 | "./components.d.ts",
51 | "./global.d.ts"
52 | ]
53 | ```
54 |
55 | ## 安装 `uni-ui` 库
56 |
57 | - 1. 安装 `uni-ui` 库:
58 |
59 | ```sh
60 | pnpm add @dcloudio/uni-ui
61 | ```
62 |
63 | - 2. `pages.config.ts` 文件 `easycom.custom` 添加相关配置:
64 |
65 | ```diff
66 | easycom: {
67 | autoscan: true,
68 | custom: {
69 | + '^uni-(.*)': '@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue',
70 | },
71 | },
72 | ```
73 |
74 | - 3. ` tsconfig.json` 文件 `compilerOptions.types` 添加相关配置:
75 |
76 | ```diff
77 | "types": [
78 | "@dcloudio/types",
79 | "@types/wechat-miniprogram",
80 | + "@uni-helper/uni-ui-types",
81 | "./components.d.ts",
82 | "./global.d.ts"
83 | ]
84 | ```
85 |
86 | ## 安装 `uv-ui` 库
87 |
88 | - 1. 安装 `uv-ui` 库:
89 |
90 | ```sh
91 | pnpm add @climblee/uv-ui
92 | ```
93 |
94 | - 2. `pages.config.ts` 文件 `easycom.custom` 添加相关配置:
95 |
96 | ```diff
97 | easycom: {
98 | autoscan: true,
99 | custom: {
100 | + '^uv-(.*)': '@climblee/uv-ui/components/uv-$1/uv-$1.vue',
101 | },
102 | },
103 | ```
104 |
105 | - 3. ` tsconfig.json` 文件 `compilerOptions.types` 添加相关配置:
106 |
107 | ```diff
108 | "types": [
109 | "@dcloudio/types",
110 | "@types/wechat-miniprogram",
111 | + "@ttou/uv-typings/shim",
112 | + "@ttou/uv-typings/v2",
113 | "./components.d.ts",
114 | "./global.d.ts"
115 | ]
116 | ```
117 |
118 | > 其他 UI 库的安装类似,不再赘述。
119 |
120 | 全文完~
121 |
--------------------------------------------------------------------------------
/docs/base/9-state.md:
--------------------------------------------------------------------------------
1 | # 状态篇
2 |
3 | 本文主要介绍了全局状态管理 `pinia` 和 简单状态 `ref` + `reactive`。
4 |
5 | ## pinia
6 |
7 | `unibest` 已经内置了 `Pinia` + `pinia-plugin-persistedstate`(数据持久化插件),并提供了开箱即用的示例。
8 |
9 | ### 兼容性处理
10 |
11 | 本身 `pinia-plugin-persistedstate` 是不支持 `uniapp` 的,但是 `pinia-plugin-persistedstate` 提供了修改 `storage` 存储 API 的方式(默认是 `localStorage`,是一个 `WEB API`,`非H5端` 不支持),目前 `unibest` 已经处理好了。关键代码如下:
12 |
13 | ```ts
14 | import { createPinia } from 'pinia'
15 | import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化
16 |
17 | const store = createPinia()
18 | store.use(
19 | createPersistedState({
20 | storage: {
21 | getItem: uni.getStorageSync, // 看这里
22 | setItem: uni.setStorageSync, // 看这里
23 | },
24 | }),
25 | )
26 | ```
27 |
28 | ### 定义 `pinia` 全局状态
29 |
30 | `src/store/xxx.ts` 里面编写代码,如下是 `src/store/count.ts` 文件。
31 |
32 | 注意 `defineStore` 第三个参数可以设置是否需要持久化,默认不需要。
33 |
34 | ```ts [src/store/count.ts]{26}
35 | import { defineStore } from 'pinia'
36 | import { ref } from 'vue'
37 |
38 | export const useCountStore = defineStore(
39 | 'count',
40 | () => {
41 | const count = ref(0)
42 | const increment = () => {
43 | count.value++
44 | }
45 | const decrement = () => {
46 | count.value--
47 | }
48 | const reset = () => {
49 | count.value = 0
50 | }
51 | return {
52 | count,
53 | decrement,
54 | increment,
55 | reset,
56 | }
57 | },
58 | {
59 | // 如果需要持久化就写 true, 不需要持久化就写 false(或者去掉这个配置项)
60 | persist: true,
61 | },
62 | )
63 | ```
64 |
65 | > 请不要随意把数据丢到 `pinia`,能不用就不用。简单状态尽量使用 `ref` 或者 `reactive`。
66 |
67 | ### 使用 `pinia` 全局状态
68 |
69 | 在 `vue` 文件中就可以使用了,如下是 `src/pages/demo.vue` 文件:
70 |
71 | ```vue
72 |
73 |
74 | Count: {{ countStore.count }}
75 |
76 |
77 |
78 |
79 |
80 |
81 |
86 | ```
87 |
88 | ## 简单状态
89 |
90 | 你可以直接使用 `Vue` 提供的 `ref` 或 `reactive` 方法来做简单状态管理。
91 |
92 | ### ref
93 |
94 | 如下是 `src/pages/demo/useCount.ts` 文件,定义简单状态。
95 |
96 | ```ts [src/pages/demo/useCount.ts]
97 | // 全局状态
98 | const globalCount = ref(1)
99 | export function useCount() {
100 | // 本地状态
101 | const localCount = ref(1)
102 | function increment() {
103 | globalCount.value++
104 | localCount.value++
105 | }
106 | return {
107 | globalCount,
108 | localCount,
109 | increment,
110 | }
111 | }
112 | ```
113 |
114 | 如下是 `src/pages/demo/index.vue`,与 `ref` 简单状态文件放到同一个目录下,方便管理。
115 |
116 | ```vue [src/pages/demo/index.vue]
117 |
121 |
122 |
123 |
127 |
128 | ```
129 |
130 | ## reactive
131 |
132 | `reactive` 与 `ref` 类似。
133 |
134 | 如下是 `src/pages/demo/count.ts` 文件,定义状态。
135 |
136 | ```ts [src/pages/demo/count.ts]
137 | export const countStore = reactive({
138 | count: 0,
139 | increment() {
140 | this.count++
141 | },
142 | })
143 | ```
144 |
145 | 如下是 `src/pages/demo/index.vue`,与 `reactive` 简单状态文件放到同一个目录下,方便管理。
146 |
147 | ```vue [src/pages/demo/index.vue]
148 |
151 |
152 |
153 |
156 |
157 | ```
158 |
159 | ## 总结
160 |
161 | 本文介绍了 `unibest` 里面状态管理的 `2` 种方式:`pinia` 全局状态 和 `ref\reactive` 简单状态,分别演示了如何定义状态和使用状态。
162 |
163 | 注意需要灵活使用 `pinia` 和 `简单状态`,局部的状态尽量使用 `简单状态` 的方式来处理,减少 `pinia` 里面全局变量的数量。
164 |
165 | 全文完~
166 |
--------------------------------------------------------------------------------
/docs/base/assets/1-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/1-1.png
--------------------------------------------------------------------------------
/docs/base/assets/10-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/10-1.png
--------------------------------------------------------------------------------
/docs/base/assets/10-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/10-2.png
--------------------------------------------------------------------------------
/docs/base/assets/10-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/10-3.png
--------------------------------------------------------------------------------
/docs/base/assets/10-android.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/10-android.mp4
--------------------------------------------------------------------------------
/docs/base/assets/10-ios.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/10-ios.mp4
--------------------------------------------------------------------------------
/docs/base/assets/11-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-1.png
--------------------------------------------------------------------------------
/docs/base/assets/11-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-10.png
--------------------------------------------------------------------------------
/docs/base/assets/11-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-100.png
--------------------------------------------------------------------------------
/docs/base/assets/11-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-11.png
--------------------------------------------------------------------------------
/docs/base/assets/11-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-12.png
--------------------------------------------------------------------------------
/docs/base/assets/11-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-13.png
--------------------------------------------------------------------------------
/docs/base/assets/11-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-2.png
--------------------------------------------------------------------------------
/docs/base/assets/11-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-3.png
--------------------------------------------------------------------------------
/docs/base/assets/11-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-4.png
--------------------------------------------------------------------------------
/docs/base/assets/11-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-5.png
--------------------------------------------------------------------------------
/docs/base/assets/11-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-6.png
--------------------------------------------------------------------------------
/docs/base/assets/11-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-7.png
--------------------------------------------------------------------------------
/docs/base/assets/11-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-8.png
--------------------------------------------------------------------------------
/docs/base/assets/11-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/11-9.png
--------------------------------------------------------------------------------
/docs/base/assets/13-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/13-1.png
--------------------------------------------------------------------------------
/docs/base/assets/13-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/13-2.png
--------------------------------------------------------------------------------
/docs/base/assets/13-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/13-3.png
--------------------------------------------------------------------------------
/docs/base/assets/13-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/13-4.png
--------------------------------------------------------------------------------
/docs/base/assets/13-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/13-5.png
--------------------------------------------------------------------------------
/docs/base/assets/13-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/13-6.png
--------------------------------------------------------------------------------
/docs/base/assets/13-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/13-7.png
--------------------------------------------------------------------------------
/docs/base/assets/13-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/13-8.png
--------------------------------------------------------------------------------
/docs/base/assets/14-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/14-1.png
--------------------------------------------------------------------------------
/docs/base/assets/14-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/14-2.png
--------------------------------------------------------------------------------
/docs/base/assets/14-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/14-3.png
--------------------------------------------------------------------------------
/docs/base/assets/14-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/14-4.png
--------------------------------------------------------------------------------
/docs/base/assets/14-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/14-5.png
--------------------------------------------------------------------------------
/docs/base/assets/14-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/14-6.png
--------------------------------------------------------------------------------
/docs/base/assets/15-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/15-1.png
--------------------------------------------------------------------------------
/docs/base/assets/15-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/15-2.png
--------------------------------------------------------------------------------
/docs/base/assets/15-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/15-3.png
--------------------------------------------------------------------------------
/docs/base/assets/15-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/15-4.png
--------------------------------------------------------------------------------
/docs/base/assets/15-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/15-5.png
--------------------------------------------------------------------------------
/docs/base/assets/15-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/15-6.png
--------------------------------------------------------------------------------
/docs/base/assets/2-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/2-1.png
--------------------------------------------------------------------------------
/docs/base/assets/2-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/2-2.png
--------------------------------------------------------------------------------
/docs/base/assets/2-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/2-3.png
--------------------------------------------------------------------------------
/docs/base/assets/2-4.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/2-4.gif
--------------------------------------------------------------------------------
/docs/base/assets/3-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/3-1.png
--------------------------------------------------------------------------------
/docs/base/assets/4-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/4-1.png
--------------------------------------------------------------------------------
/docs/base/assets/4-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/4-2.png
--------------------------------------------------------------------------------
/docs/base/assets/4-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/4-3.png
--------------------------------------------------------------------------------
/docs/base/assets/4-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/4-4.png
--------------------------------------------------------------------------------
/docs/base/assets/4-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/4-5.png
--------------------------------------------------------------------------------
/docs/base/assets/5-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-1.png
--------------------------------------------------------------------------------
/docs/base/assets/5-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-10.png
--------------------------------------------------------------------------------
/docs/base/assets/5-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-100.png
--------------------------------------------------------------------------------
/docs/base/assets/5-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-11.png
--------------------------------------------------------------------------------
/docs/base/assets/5-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-2.png
--------------------------------------------------------------------------------
/docs/base/assets/5-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-3.png
--------------------------------------------------------------------------------
/docs/base/assets/5-4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-4.png
--------------------------------------------------------------------------------
/docs/base/assets/5-5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-5.png
--------------------------------------------------------------------------------
/docs/base/assets/5-6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-6.png
--------------------------------------------------------------------------------
/docs/base/assets/5-7.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-7.png
--------------------------------------------------------------------------------
/docs/base/assets/5-8.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-8.png
--------------------------------------------------------------------------------
/docs/base/assets/5-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/5-9.png
--------------------------------------------------------------------------------
/docs/base/assets/8-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/assets/8-1.png
--------------------------------------------------------------------------------
/docs/base/image-18-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/image-18-2.png
--------------------------------------------------------------------------------
/docs/base/image-18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/image-18.png
--------------------------------------------------------------------------------
/docs/base/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/image.png
--------------------------------------------------------------------------------
/docs/base/ui/image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/ui/image-1.png
--------------------------------------------------------------------------------
/docs/base/ui/image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/ui/image-2.png
--------------------------------------------------------------------------------
/docs/base/ui/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/base/ui/image.png
--------------------------------------------------------------------------------
/docs/changelog/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # CHANGELOG 更新日志
2 |
3 | ## v2.10.1(2025-05-28)
4 |
5 | ### 新功能
6 |
7 | - 实现基础的”登录“功能,后端接口数据是mock的。
8 |
9 | ## v2.9.3(2025-05-27)
10 |
11 | ### 新功能
12 |
13 | - 支持 `spa` 模板,属于单页应用,完全自定义 `tabbar` 的形式。
14 |
15 | ### 依赖升级
16 |
17 | - 将 `unocss` 从 `0.58` 升级到 `66.0.0`。
18 | - 将 `wot-design-uni` 从 `^1.4.0` 升级到 `^1.9.0`。
19 | - 将 `vue` 从 `3.4.21` 升级到 `^3.5.15`。
20 | - 将 `vite` 从 `5.2.8` 升级到 `6.3.5`。
21 |
22 | ## v2.8.0(2025-05-20)
23 |
24 | ### 架构优化
25 |
26 | - 移除 `stylelint` 和 `eslint` 配置,统一采用 `oxlint` 进行代码检查,提升代码校验的速度(比 `eslint` 快 `50-100` 倍)。
27 | - 移除 `husky` 和 `commitlint` 配置(使用编辑器的AI生成commit信息)。
28 |
29 | ::: details 对于 `v2.8.0` 以下版本,需按以下步骤操作:
30 |
31 | - 把 `husky, stylelint, eslint` 相关依赖包删除
32 | - 安装 `oxlint`,设置 `lint-staged` 配置为 `oxlint`
33 | - 删除 `husky, stylelint, eslint` 相关文件
34 |
35 | 
36 | 
37 | 
38 | 
39 |
40 | :::
41 |
42 | ## v2.7.0(2025-05-19)
43 |
44 | ### 依赖升级
45 |
46 | - 将 `@dcloudio/uni-app` 从 `3.0.0-4020920240930001` 升级到 `3.0.0-4060520250512001`,获取最新功能和性能优化。
47 |
48 | ### 新功能
49 |
50 | - 支持 `无 TabBar` 模式,用户只需删除 `pages.config.ts` 中的 `tabBar` 配置即可。
51 |
52 | ::: details 对于 `v2.7.0` 以下版本,需按以下步骤操作:
53 |
54 | - 执行 `pnpm uvm` 升级 `@dcloudio/uni-app`。
55 | - 修改 `src/utils/index.ts` 部分代码:
56 |
57 | ```ts
58 | import pagesConfig from '@/pages.json'
59 | const { pages, subPackages, tabBar = { list: [] } } = { ...pagesConfig }
60 |
61 | /** 判断当前页面是否是 tabbar 页 */
62 | export const getIsTabbar = () => {
63 | try {
64 | const lastPage = getLastPage()
65 | const currPath = lastPage?.route
66 |
67 | return Boolean(tabBar?.list?.some((item) => item.pagePath === currPath))
68 | } catch {
69 | return false
70 | }
71 | }
72 | ```
73 |
74 | :::
75 |
--------------------------------------------------------------------------------
/docs/changelog/image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/changelog/image-1.png
--------------------------------------------------------------------------------
/docs/changelog/image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/changelog/image-2.png
--------------------------------------------------------------------------------
/docs/changelog/image-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/changelog/image-3.png
--------------------------------------------------------------------------------
/docs/changelog/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/changelog/image.png
--------------------------------------------------------------------------------
/docs/gif/assets/auto-page.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/auto-page.gif
--------------------------------------------------------------------------------
/docs/gif/assets/auto-sort.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/auto-sort.gif
--------------------------------------------------------------------------------
/docs/gif/assets/commit.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/commit.gif
--------------------------------------------------------------------------------
/docs/gif/assets/i18n.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/i18n.gif
--------------------------------------------------------------------------------
/docs/gif/assets/ios-run-app.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/ios-run-app.gif
--------------------------------------------------------------------------------
/docs/gif/assets/lottery2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/lottery2.gif
--------------------------------------------------------------------------------
/docs/gif/assets/lottery3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/lottery3.gif
--------------------------------------------------------------------------------
/docs/gif/assets/snippets.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/snippets.gif
--------------------------------------------------------------------------------
/docs/gif/assets/snippets2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/snippets2.gif
--------------------------------------------------------------------------------
/docs/gif/assets/snippets3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/snippets3.gif
--------------------------------------------------------------------------------
/docs/gif/assets/unocss-icons.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/unocss-icons.gif
--------------------------------------------------------------------------------
/docs/gif/assets/unocss.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/assets/unocss.gif
--------------------------------------------------------------------------------
/docs/gif/index.md:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/gif/index.md
--------------------------------------------------------------------------------
/docs/other/blog.md:
--------------------------------------------------------------------------------
1 | # 博客列表
2 |
3 | `unibest` 相关文章主要发布在 `掘金`,我的 [掘金 unibest 专栏](https://juejin.cn/column/7307183009604894735)。
4 |
5 | - [🔥2024 年最好用的 uniapp 开发模板,近一个月 star 数飙升!🔥](https://juejin.cn/post/7329034439408615451)
6 | - [【unibest】uniapp + vue3 超实用模板](https://juejin.cn/post/7315246744158191666)
7 | - [【unibest】uniapp + vue3 超实用模板(续)](https://juejin.cn/post/7315461542697500682)
8 | - [【unibest】uniapp + vue3 超实用模板(终)](https://juejin.cn/post/7321930742400188453)
9 | - [【unibest】uniapp + vue3 超实用模板(番外篇)](https://juejin.cn/editor/drafts/7315308701051519030)
10 |
--------------------------------------------------------------------------------
/docs/other/files/files.md:
--------------------------------------------------------------------------------
1 | # 文件资源展示优化
2 |
3 | > 本功能由 `⑤群` 群友 `Collapsar` 提供,感谢 `Collapsar` 的贡献。
4 |
5 | **未配置前的默认效果:**
6 | 
7 |
8 | **配置后效果:**
9 | 
10 |
11 | **相关代码:**
12 | 
13 |
14 | > 如果觉得不需要这种查看方式,可以删除 or 注释掉 `.vscode/setting.json` 里面 `explorer.fileNesting.patterns` 配置。
15 |
--------------------------------------------------------------------------------
/docs/other/files/image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/files/image-1.png
--------------------------------------------------------------------------------
/docs/other/files/image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/files/image-2.png
--------------------------------------------------------------------------------
/docs/other/files/image-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/files/image-3.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-10.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-10.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-100.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-11.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-11.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-12.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-12.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-13.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-13.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-14.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-14.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-15.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-15.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-16.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-17.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-17.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-18.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-18.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-19.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-19.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-20.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-21.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-21.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-22.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-22.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-23.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-23.png
--------------------------------------------------------------------------------
/docs/other/iconfont/assets/5-9.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/iconfont/assets/5-9.png
--------------------------------------------------------------------------------
/docs/other/iconfont/iconfont.md:
--------------------------------------------------------------------------------
1 | ## iconfont 图标库
2 |
3 | `iconfont` 同样有海量免费的图标,同时支持上传自己的图标。公司项目通常会有自己的图标,由专业的 `UI设计师` 设计,这时通常会使用 `iconfont` 方式使用图标。
4 |
5 | - 1. 打开`阿里巴巴矢量图标库 iconfont`,地址:https://www.iconfont.cn/,并登录。
6 | - 2. 寻找需要的图标,加入项目,也可以上传自己的图标。
7 |
8 | 
9 |
10 | 
11 |
12 | 
13 |
14 | > 初次接触 `iconfont` 的同学,可能会找不到自己的项目,如下图:资源管理 -- 我的项目
15 |
16 | 
17 |
18 | - 3.图标方式选择,如下图有 `Unicode` `Font class` `Symbol` 三种方式,分别预览和使用如下:
19 |
20 | 
21 |
22 | 
23 |
24 | 
25 |
26 | - `Unicode` 的方式太落后,语义化不明显,不推荐;
27 | - `Symbol` 的方式太先进(背后原理是生成了 `SVG` 雪碧图),先进到 `小程序` 和 `APP` 都不支持,只能无奈放弃。
28 |
29 | > `Symbol` 的方式生成 `svg` 雪碧图,如下所示:
30 | >
31 | > 
32 |
33 | - `Font class` 则是我们最合适的选择,有 `Symbol` 一样的语义化(都是`icon-xxx`方式),引入和使用也方便( `Symbol` 是一个 `js` 文件,`Font class` 是一个 `css` 文件)。
34 |
35 | - 3. 点击选中 `Font class` 后再点击 `查看在线连接` 按钮,可以拿到一个 `css` 的链接,如 [//at.alicdn.com/t/c/font_4032028_mbcuy517h6.css](//at.alicdn.com/t/c/font_4032028_mbcuy517h6.css) ,如果期间新加入了图标,记得点击更新链接,会重新生成一个链接,只有最后面一串 hash 有改变,并且旧的链接依然可以访问。
36 |
37 | 
38 |
39 | 我们使用的是 `Font class` 的方式,只需要这一个 `css` 链接就行,无需 `下载至本地`,想要本地预览的话才需要 `下载至本地`。
40 |
41 | > `iconfont` 有默认的前缀 `icon-`,可以设置为其他的,如我的一个项目设置为 `bap-icon-`,以防跟其他的冲突。
42 |
43 | 
44 |
45 | > 注意 `uniapp` 项目拿到 `css` 链接放到 `index.html` 是不对的,这样做只在 `h5` 中生效,`小程序` 和 `APP` 都不生效,正确的做法是放到代码里面显示引入。下面会讲:
46 |
47 | - 4.在 `style/index.scss` 中写上上面的 `css` 链接里面的内容(`style/index.scss` 已经在 `main.ts` 引入了,`unibest` 模板已经内置),如下
48 |
49 | > 注意: `url(//at.alicdn.com)` 里面的路径要改为 `url(https://at.alicdn.com)`,因为 APP 里面 `//` 是文件协议。 —— 设定 `https` 协议
50 |
51 | ```css
52 | @font-face {
53 | font-family: iconfont; /* Project id 4032028 */
54 | src:
55 | url('//at.alicdn.com/t/c/font_4032028_mbcuy517h6.woff2?t=1713685013355') format('woff2'),
56 | url('//at.alicdn.com/t/c/font_4032028_mbcuy517h6.woff?t=1713685013355') format('woff'),
57 | url('//at.alicdn.com/t/c/font_4032028_mbcuy517h6.ttf?t=1713685013355') format('truetype');
58 | }
59 |
60 | .iconfont {
61 | font-family: iconfont !important;
62 | font-size: 16px;
63 | font-style: normal;
64 | -webkit-font-smoothing: antialiased;
65 | -moz-osx-font-smoothing: grayscale;
66 | }
67 |
68 | .icon-facebook::before {
69 | content: '\e87d';
70 | }
71 |
72 | .icon-twitter::before {
73 | content: '\e646';
74 | }
75 |
76 | .icon-telegram::before {
77 | content: '\f245';
78 | }
79 | ```
80 |
81 | - 5. 编写代码,``
82 |
83 | 
84 |
85 | - 6. 预览,`h5 `端正常,APP 端不正常,小程序端看着正常,控制台也会报错,如下图:
86 |
87 | 
88 |
89 | - 7. 这个怎么处理呢?转成 `base64` 是最快捷的,`iconfont` 本身就支持, `3`步搞定:
90 |
91 | - 7.1 如下图,勾选 `Base64`
92 |
93 | 
94 |
95 | - 7.2 生成新链接,并得到新的 `css` 代码
96 |
97 | 
98 |
99 | - 7.3 引入新代码,刷新界面,小程序不报错了,APP 也正常了!
100 |
101 | 
102 |
103 |
109 |
--------------------------------------------------------------------------------
/docs/other/image/assets/image-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/image/assets/image-1.png
--------------------------------------------------------------------------------
/docs/other/image/assets/image-2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/image/assets/image-2.png
--------------------------------------------------------------------------------
/docs/other/image/assets/unibest-项目架构.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/other/image/assets/unibest-项目架构.png
--------------------------------------------------------------------------------
/docs/other/image/image.md:
--------------------------------------------------------------------------------
1 | # 图片占位图
2 |
3 | - 色块占位图
4 | - 真实随机图片
5 |
6 | ## 色块占位图
7 |
8 | 下面是一个 `400x200 - 宽高,3c9cff - 背景颜色,fff - 文本颜色` 的色块占位图。
9 |
10 | 
11 |
12 | 可以通过下面几种方式生成,效果一样:
13 |
14 | | 官网地址 | 占位图片示例 |
15 | | :-------------------------------------------------- | :------------------------------------------------------------------------------------------------------- |
16 | | [https://placeholder.com](https://placeholder.com/) | [https://via.placeholder.com/400x200.png/3c9cff/fff](https://via.placeholder.com/400x200.png/3c9cff/fff) |
17 | | [https://dummyimage.com](https://dummyimage.com/) | [https://dummyimage.com/400x200/3c9cff/fff](https://dummyimage.com/400x200/3c9cff/fff) |
18 | | [https://fakeimg.pl](https://fakeimg.pl/) | [https://fakeimg.pl/400x200/3c9cff/fff](https://fakeimg.pl/400x200/3c9cff/fff) |
19 |
20 | 代码编写举例:
21 |
22 | ```vue
23 |
24 | ```
25 |
26 | ## 真实随机图片
27 |
28 | 如果想生成某个宽高的随机图片,可以使用 [https://picsum.photos](https://picsum.photos)。
29 |
30 | 格式如:`https://picsum.photos//?random=1`
31 |
32 | 举例:[https://picsum.photos/400/200?random=1](https://picsum.photos/400/200?random=1)
33 |
34 | 生成效果如下:
35 |
36 | 
37 |
38 | 代码编写举例:
39 |
40 | ```vue
41 |
42 | ```
43 |
--------------------------------------------------------------------------------
/docs/other/links/links.md:
--------------------------------------------------------------------------------
1 | # 相关链接
2 |
3 | ## Unibest Demo 分支演示地址
4 |
5 | - [演示地址](https://feige996.github.io/hello-unibest/#/)
6 | - [仓库地址-github](https://github.com/feige996/hello-unibest)
7 | - [仓库地址-gitee](https://gitee.com/feige996/hello-unibest)
8 |
9 | ## UI 组件库
10 |
11 | - [wot-ui](https://wot-design-uni.cn) -- `五星推荐⭐⭐⭐⭐⭐,unibest默认内置`
12 | > [wot-ui 备用地址](https://wot-design-uni.netlify.app)
13 | - [uni-ui](https://uniapp.dcloud.net.cn/component/uniui/uni-ui.html)
14 | - [uv-ui](https://www.uvui.cn/)
15 | - [uview-plus](https://uiadmin.net/uview-plus/)
16 | - [TuniaoUI](https://vue3.tuniaokj.com/zh-CN/)
17 | - [Sard uniapp](https://sard.wzt.zone/sard-uniapp-docs/)
18 | - [FirstUI](https://doc.firstui.cn/)(部分组件收费)
19 | - [ThorUI](https://thorui.cn/doc/)(部分组件收费)
20 |
21 | ## 原子类 CSS
22 |
23 | - [UnoCSS](https://unocss.dev/) -- `五星推荐⭐⭐⭐⭐⭐`
24 | - [tailwindcss](https://tailwindcss.com/)
25 |
26 | ## icons
27 |
28 | - [icones](https://icones.js.org/) -- `五星推荐⭐⭐⭐⭐⭐` used in `UnoCSS Icons`
29 | - [iconfont](https://www.iconfont.cn/)
30 | - [IconPark](https://iconpark.oceanengine.com)
31 |
32 | ## 优质组件
33 |
34 | - [z-paging](https://z-paging.zxlee.cn/) -- `五星推荐⭐⭐⭐⭐⭐`
35 |
36 | > 一个 `uni-app` 分页组件。
37 | >
38 | > 全平台兼容,支持自定义下拉刷新、上拉加载更多,支持虚拟列表,支持自动管理空数据图、点击返回顶部,支持聊天分页、本地分页,支持展示最后更新时间,支持国际化等等。
39 |
40 | - [mescroll](https://www.mescroll.com/)
41 |
42 | > 精致的下拉刷新和上拉加载 js 框架,一套代码多端运行,支持 `uni-app`。
43 |
44 | ## uni-app
45 |
46 | - [uni-app 官网](https://uniapp.dcloud.net.cn/)
47 | - [uni-app x 官网](https://doc.dcloud.net.cn/uni-app-x/)
48 |
49 | ## 图表库
50 |
51 | - [ucharts](https://www.ucharts.cn/v2/#/)
52 | - [lime-echart](https://gitee.com/liangei/lime-echart)
53 |
54 | > 其他可以在 `uni-app` 插件市场找:[uniapp chart](https://ext.dcloud.net.cn/search?q=chart)
55 |
56 | ## vue 相关
57 |
58 | - [Vue](https://cn.vuejs.org/)
59 | - [Vite](https://cn.vitejs.dev/)
60 | - [Pinia](https://pinia.vuejs.org/zh/)
61 |
62 | ## 请求库
63 |
64 | - [Alova.js](https://alova.js.org/zh-CN)
65 |
--------------------------------------------------------------------------------
/docs/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/docs/public/favicon.ico
--------------------------------------------------------------------------------
/docs/public/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/env/.env:
--------------------------------------------------------------------------------
1 | VITE_APP_TITLE = 'unibest'
2 | VITE_APP_PORT = 9000
3 |
4 | VITE_UNI_APPID = 'H57F2ACE4'
5 | VITE_WX_APPID = 'wxa2abb91f64032a2b'
6 |
7 | # h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
8 | VITE_APP_PUBLIC_BASE=/
9 |
10 | VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
11 | VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
12 |
13 | # 有些同学可能需要在微信小程序里面根据 develop、trial、release 分别设置上传地址,参考代码如下。
14 | # 下面的变量如果没有设置,会默认使用 VITE_SERVER_BASEURL or VITE_UPLOAD_BASEURL
15 | VITE_SERVER_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run'
16 | VITE_SERVER_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run'
17 | VITE_SERVER_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run'
18 |
19 | VITE_UPLOAD_BASEURL__WEIXIN_DEVELOP = 'https://ukw0y1.laf.run/upload'
20 | VITE_UPLOAD_BASEURL__WEIXIN_TRIAL = 'https://ukw0y1.laf.run/upload'
21 | VITE_UPLOAD_BASEURL__WEIXIN_RELEASE = 'https://ukw0y1.laf.run/upload'
22 |
23 | # h5是否需要配置代理
24 | VITE_APP_PROXY=false
25 | VITE_APP_PROXY_PREFIX = '/api'
26 |
--------------------------------------------------------------------------------
/env/.env.development:
--------------------------------------------------------------------------------
1 | # 变量必须以 VITE_ 为前缀才能暴露给外部读取
2 | NODE_ENV = 'development'
3 | # 是否去除console 和 debugger
4 | VITE_DELETE_CONSOLE = false
5 | # 是否开启sourcemap
6 | VITE_SHOW_SOURCEMAP = true
7 |
--------------------------------------------------------------------------------
/env/.env.production:
--------------------------------------------------------------------------------
1 | # 变量必须以 VITE_ 为前缀才能暴露给外部读取
2 | NODE_ENV = 'production'
3 | # 是否去除console 和 debugger
4 | VITE_DELETE_CONSOLE = true
5 | # 是否开启sourcemap
6 | VITE_SHOW_SOURCEMAP = false
7 |
--------------------------------------------------------------------------------
/env/.env.test:
--------------------------------------------------------------------------------
1 | # 变量必须以 VITE_ 为前缀才能暴露给外部读取
2 | NODE_ENV = 'development'
3 | # 是否去除console 和 debugger
4 | VITE_DELETE_CONSOLE = false
5 |
--------------------------------------------------------------------------------
/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/favicon.ico
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
17 | unibest
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/openapi-ts-request.config.ts:
--------------------------------------------------------------------------------
1 | import type { GenerateServiceProps } from 'openapi-ts-request'
2 |
3 | export default [
4 | {
5 | schemaPath: 'http://petstore.swagger.io/v2/swagger.json',
6 | serversPath: './src/service/app',
7 | requestLibPath: `import request from '@/utils/request';\n import { CustomRequestOptions } from '@/interceptors/request';`,
8 | requestOptionsType: 'CustomRequestOptions',
9 | isGenReactQuery: true,
10 | reactQueryMode: 'vue',
11 | isGenJavaScript: false,
12 | },
13 | ] as GenerateServiceProps[]
14 |
--------------------------------------------------------------------------------
/pages.config.ts:
--------------------------------------------------------------------------------
1 | import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
2 |
3 | export default defineUniPages({
4 | globalStyle: {
5 | navigationStyle: 'default',
6 | navigationBarTitleText: 'unibest',
7 | navigationBarBackgroundColor: '#f8f8f8',
8 | navigationBarTextStyle: 'black',
9 | backgroundColor: '#FFFFFF',
10 | },
11 | easycom: {
12 | autoscan: true,
13 | custom: {
14 | '^fg-(.*)': '@/components/fg-$1/fg-$1.vue',
15 | '^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
16 | '^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
17 | 'z-paging/components/z-paging$1/z-paging$1.vue',
18 | },
19 | },
20 | // 如果不需要tabBar,请使用 spa 模板。(pnpm create xxx -t spa)
21 | tabBar: {
22 | color: '#999999',
23 | selectedColor: '#018d71',
24 | backgroundColor: '#F8F8F8',
25 | borderStyle: 'black',
26 | height: '50px',
27 | fontSize: '10px',
28 | iconWidth: '24px',
29 | spacing: '3px',
30 | list: [
31 | {
32 | iconPath: 'static/tabbar/home.png',
33 | selectedIconPath: 'static/tabbar/homeHL.png',
34 | pagePath: 'pages/index/index',
35 | text: '首页',
36 | },
37 | {
38 | iconPath: 'static/tabbar/example.png',
39 | selectedIconPath: 'static/tabbar/exampleHL.png',
40 | pagePath: 'pages/about/about',
41 | text: '关于',
42 | },
43 | {
44 | iconPath: 'static/tabbar/personal.png',
45 | selectedIconPath: 'static/tabbar/personalHL.png',
46 | pagePath: 'pages/mine/index',
47 | text: '我的',
48 | },
49 | ],
50 | },
51 | })
52 |
--------------------------------------------------------------------------------
/scripts/postupgrade.js:
--------------------------------------------------------------------------------
1 | // # 执行 `pnpm upgrade` 后会升级 `uniapp` 相关依赖
2 | // # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
3 | // # 只需要执行下面的命令即可
4 |
5 | // eslint-disable-next-line @typescript-eslint/no-var-requires
6 | const { exec } = require('child_process')
7 |
8 | // 定义要执行的命令
9 | const dependencies = [
10 | '@dcloudio/uni-app-harmony',
11 | // TODO: 如果需要某个平台的小程序,请手动删除或注释掉
12 | '@dcloudio/uni-mp-alipay',
13 | '@dcloudio/uni-mp-baidu',
14 | '@dcloudio/uni-mp-jd',
15 | '@dcloudio/uni-mp-kuaishou',
16 | '@dcloudio/uni-mp-lark',
17 | '@dcloudio/uni-mp-qq',
18 | '@dcloudio/uni-mp-toutiao',
19 | '@dcloudio/uni-mp-xhs',
20 | '@dcloudio/uni-quickapp-webview',
21 | // i18n模板要注释掉下面的
22 | 'vue-i18n',
23 | ]
24 |
25 | // 使用exec执行命令
26 | exec(`pnpm un ${dependencies.join(' ')}`, (error, stdout, stderr) => {
27 | if (error) {
28 | // 如果有错误,打印错误信息
29 | console.error(`执行出错: ${error}`)
30 | return
31 | }
32 | // 打印正常输出
33 | console.log(`stdout: ${stdout}`)
34 | // 如果有错误输出,也打印出来
35 | console.error(`stderr: ${stderr}`)
36 | })
37 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
15 |
16 |
60 |
--------------------------------------------------------------------------------
/src/api/login.ts:
--------------------------------------------------------------------------------
1 | import { ICaptcha, IUpdateInfo, IUpdatePassword, IUserInfoVo, IUserLogin } from './login.typings'
2 | import { http } from '@/utils/http'
3 |
4 | /**
5 | * 登录表单
6 | */
7 | export interface ILoginForm {
8 | username: string
9 | password: string
10 | code: string
11 | uuid: string
12 | }
13 |
14 | /**
15 | * 获取验证码
16 | * @returns ICaptcha 验证码
17 | */
18 | export const getCode = () => {
19 | return http.get('/user/getCode')
20 | }
21 |
22 | /**
23 | * 用户登录
24 | * @param loginForm 登录表单
25 | */
26 | export const login = (loginForm: ILoginForm) => {
27 | return http.post('/user/login', loginForm)
28 | }
29 |
30 | /**
31 | * 获取用户信息
32 | */
33 | export const getUserInfo = () => {
34 | return http.get('/user/info')
35 | }
36 |
37 | /**
38 | * 退出登录
39 | */
40 | export const logout = () => {
41 | return http.get('/user/logout')
42 | }
43 |
44 | /**
45 | * 修改用户信息
46 | */
47 | export const updateInfo = (data: IUpdateInfo) => {
48 | return http.post('/user/updateInfo', data)
49 | }
50 |
51 | /**
52 | * 修改用户密码
53 | */
54 | export const updateUserPassword = (data: IUpdatePassword) => {
55 | return http.post('/user/updatePassword', data)
56 | }
57 |
58 | /**
59 | * 获取微信登录凭证
60 | * @returns Promise 包含微信登录凭证(code)
61 | */
62 | export const getWxCode = () => {
63 | return new Promise((resolve, reject) => {
64 | uni.login({
65 | provider: 'weixin',
66 | success: (res) => resolve(res),
67 | fail: (err) => reject(new Error(err)),
68 | })
69 | })
70 | }
71 |
72 | /**
73 | * 微信登录参数
74 | */
75 |
76 | /**
77 | * 微信登录
78 | * @param params 微信登录参数,包含code
79 | * @returns Promise 包含登录结果
80 | */
81 | export const wxLogin = (data: { code: string }) => {
82 | return http.post('/user/wxLogin', data)
83 | }
84 |
--------------------------------------------------------------------------------
/src/api/login.typings.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * 用户信息
3 | */
4 | export type IUserInfoVo = {
5 | id: number
6 | username: string
7 | avatar: string
8 | token: string
9 | }
10 |
11 | /**
12 | * 登录返回的信息
13 | */
14 | export type IUserLogin = {
15 | id: string
16 | username: string
17 | token: string
18 | }
19 |
20 | /**
21 | * 获取验证码
22 | */
23 | export type ICaptcha = {
24 | captchaEnabled: boolean
25 | uuid: string
26 | image: string
27 | }
28 | /**
29 | * 上传成功的信息
30 | */
31 | export type IUploadSuccessInfo = {
32 | fileId: number
33 | originalName: string
34 | fileName: string
35 | storagePath: string
36 | fileHash: string
37 | fileType: string
38 | fileBusinessType: string
39 | fileSize: number
40 | }
41 | /**
42 | * 更新用户信息
43 | */
44 | export type IUpdateInfo = {
45 | id: number
46 | name: string
47 | sex: string
48 | }
49 | /**
50 | * 更新用户信息
51 | */
52 | export type IUpdatePassword = {
53 | id: number
54 | oldPassword: string
55 | newPassword: string
56 | confirmPassword: string
57 | }
58 |
--------------------------------------------------------------------------------
/src/components/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/components/.gitkeep
--------------------------------------------------------------------------------
/src/components/fg-navbar/fg-navbar.vue:
--------------------------------------------------------------------------------
1 |
39 |
40 |
41 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/src/components/privacy-popup/index.scss:
--------------------------------------------------------------------------------
1 | @import 'wot-design-uni/components/wd-button/index.scss';
2 | :deep(.wd-privacy-popup) {
3 | width: 600rpx;
4 | padding: 0 24rpx;
5 | box-sizing: border-box;
6 | border-radius: 32rpx;
7 | overflow: hidden;
8 | }
9 |
10 | .wd-privacy-popup {
11 | &__header {
12 | width: 100%;
13 | height: 128rpx;
14 | line-height: 128rpx;
15 | color: rgba(0, 0, 0, 0.85);
16 | font-size: 30rpx;
17 | padding: 0 12rpx;
18 | box-sizing: border-box;
19 | }
20 |
21 | &__container {
22 | width: 100%;
23 | box-sizing: border-box;
24 | padding: 0 12rpx;
25 | margin-bottom: 32rpx;
26 |
27 | font-size: 28rpx;
28 | line-height: 1.8;
29 | color: #3e3e3e;
30 | text-align: left;
31 | font-weight: 550;
32 | &-protocol {
33 | color: #4d80f0;
34 | }
35 | }
36 |
37 | &__footer {
38 | display: flex;
39 | justify-content: space-between;
40 | padding-bottom: 36rpx;
41 |
42 | button {
43 | border: none;
44 | outline: none;
45 | }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/components/privacy-popup/privacy-popup.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
13 |
20 |
37 |
38 |
39 |
40 |
41 |
51 |
52 |
141 |
142 |
145 |
--------------------------------------------------------------------------------
/src/env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | declare module '*.vue' {
5 | import { DefineComponent } from 'vue'
6 | // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
7 | const component: DefineComponent<{}, {}, any>
8 | export default component
9 | }
10 |
11 | interface ImportMetaEnv {
12 | /** 网站标题,应用名称 */
13 | readonly VITE_APP_TITLE: string
14 | /** 服务端口号 */
15 | readonly VITE_SERVER_PORT: string
16 | /** 后台接口地址 */
17 | readonly VITE_SERVER_BASEURL: string
18 | /** H5是否需要代理 */
19 | readonly VITE_APP_PROXY: 'true' | 'false'
20 | /** H5是否需要代理,需要的话有个前缀 */
21 | readonly VITE_APP_PROXY_PREFIX: string // 一般是/api
22 | /** 上传图片地址 */
23 | readonly VITE_UPLOAD_BASEURL: string
24 | /** 是否清除console */
25 | readonly VITE_DELETE_CONSOLE: string
26 | // 更多环境变量...
27 | }
28 |
29 | interface ImportMeta {
30 | readonly env: ImportMetaEnv
31 | }
32 |
--------------------------------------------------------------------------------
/src/hooks/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/hooks/.gitkeep
--------------------------------------------------------------------------------
/src/hooks/useRequest.ts:
--------------------------------------------------------------------------------
1 | import { UnwrapRef } from 'vue'
2 |
3 | type IUseRequestOptions = {
4 | /** 是否立即执行 */
5 | immediate?: boolean
6 | /** 初始化数据 */
7 | initialData?: T
8 | }
9 |
10 | /**
11 | * useRequest是一个定制化的请求钩子,用于处理异步请求和响应。
12 | * @param func 一个执行异步请求的函数,返回一个包含响应数据的Promise。
13 | * @param options 包含请求选项的对象 {immediate, initialData}。
14 | * @param options.immediate 是否立即执行请求,默认为false。
15 | * @param options.initialData 初始化数据,默认为undefined。
16 | * @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
17 | */
18 | export default function useRequest(
19 | func: () => Promise>,
20 | options: IUseRequestOptions = { immediate: false },
21 | ) {
22 | const loading = ref(false)
23 | const error = ref(false)
24 | const data = ref(options.initialData)
25 | const run = async () => {
26 | loading.value = true
27 | return func()
28 | .then((res) => {
29 | data.value = res.data as UnwrapRef
30 | error.value = false
31 | return data.value
32 | })
33 | .catch((err) => {
34 | error.value = err
35 | throw err
36 | })
37 | .finally(() => {
38 | loading.value = false
39 | })
40 | }
41 |
42 | options.immediate && run()
43 | return { loading, error, data, run }
44 | }
45 |
--------------------------------------------------------------------------------
/src/hooks/useUpload.ts:
--------------------------------------------------------------------------------
1 | // TODO: 别忘加更改环境变量的 VITE_UPLOAD_BASEURL 地址。
2 | import { getEnvBaseUploadUrl } from '@/utils'
3 |
4 | const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
5 |
6 | /**
7 | * useUpload 是一个定制化的请求钩子,用于处理上传图片。
8 | * @param formData 额外传递给后台的数据,如{name: '菲鸽'}。
9 | * @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
10 | */
11 | export default function useUpload(formData: Record = {}) {
12 | const loading = ref(false)
13 | const error = ref(false)
14 | const data = ref()
15 | const run = () => {
16 | // #ifdef MP-WEIXIN
17 | // 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
18 | // 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
19 | uni.chooseMedia({
20 | count: 1,
21 | mediaType: ['image'],
22 | success: (res) => {
23 | loading.value = true
24 | const tempFilePath = res.tempFiles[0].tempFilePath
25 | uploadFile({ tempFilePath, formData, data, error, loading })
26 | },
27 | fail: (err) => {
28 | console.error('uni.chooseMedia err->', err)
29 | error.value = true
30 | },
31 | })
32 | // #endif
33 | // #ifndef MP-WEIXIN
34 | uni.chooseImage({
35 | count: 1,
36 | success: (res) => {
37 | loading.value = true
38 | const tempFilePath = res.tempFilePaths[0]
39 | uploadFile({ tempFilePath, formData, data, error, loading })
40 | },
41 | fail: (err) => {
42 | console.error('uni.chooseImage err->', err)
43 | error.value = true
44 | },
45 | })
46 | // #endif
47 | }
48 |
49 | return { loading, error, data, run }
50 | }
51 |
52 | function uploadFile({ tempFilePath, formData, data, error, loading }) {
53 | uni.uploadFile({
54 | url: VITE_UPLOAD_BASEURL,
55 | filePath: tempFilePath,
56 | name: 'file',
57 | formData,
58 | success: (uploadFileRes) => {
59 | data.value = uploadFileRes.data as T
60 | },
61 | fail: (err) => {
62 | console.error('uni.uploadFile err->', err)
63 | error.value = true
64 | },
65 | complete: () => {
66 | loading.value = false
67 | },
68 | })
69 | }
70 |
--------------------------------------------------------------------------------
/src/interceptors/index.ts:
--------------------------------------------------------------------------------
1 | export { routeInterceptor } from './route'
2 | export { requestInterceptor } from './request'
3 | export { prototypeInterceptor } from './prototype'
4 |
--------------------------------------------------------------------------------
/src/interceptors/prototype.ts:
--------------------------------------------------------------------------------
1 | export const prototypeInterceptor = {
2 | install() {
3 | // 解决低版本手机不识别 array.at() 导致运行报错的问题
4 | if (typeof Array.prototype.at !== 'function') {
5 | // eslint-disable-next-line no-extend-native
6 | Array.prototype.at = function (index: number) {
7 | if (index < 0) return this[this.length + index]
8 | if (index >= this.length) return undefined
9 | return this[index]
10 | }
11 | }
12 | },
13 | }
14 |
--------------------------------------------------------------------------------
/src/interceptors/request.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-param-reassign */
2 | import qs from 'qs'
3 | import { useUserStore } from '@/store'
4 | import { platform } from '@/utils/platform'
5 | import { getEnvBaseUrl } from '@/utils'
6 |
7 | export type CustomRequestOptions = UniApp.RequestOptions & {
8 | query?: Record
9 | /** 出错时是否隐藏错误提示 */
10 | hideErrorToast?: boolean
11 | } & IUniUploadFileOptions // 添加uni.uploadFile参数类型
12 |
13 | // 请求基准地址
14 | const baseUrl = getEnvBaseUrl()
15 |
16 | // 拦截器配置
17 | const httpInterceptor = {
18 | // 拦截前触发
19 | invoke(options: CustomRequestOptions) {
20 | // 接口请求支持通过 query 参数配置 queryString
21 | if (options.query) {
22 | const queryStr = qs.stringify(options.query)
23 | if (options.url.includes('?')) {
24 | options.url += `&${queryStr}`
25 | } else {
26 | options.url += `?${queryStr}`
27 | }
28 | }
29 | // 非 http 开头需拼接地址
30 | if (!options.url.startsWith('http')) {
31 | // #ifdef H5
32 | // console.log(__VITE_APP_PROXY__)
33 | if (JSON.parse(__VITE_APP_PROXY__)) {
34 | // 自动拼接代理前缀
35 | options.url = import.meta.env.VITE_APP_PROXY_PREFIX + options.url
36 | } else {
37 | options.url = baseUrl + options.url
38 | }
39 | // #endif
40 | // 非H5正常拼接
41 | // #ifndef H5
42 | options.url = baseUrl + options.url
43 | // #endif
44 | // TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
45 | }
46 | // 1. 请求超时
47 | options.timeout = 10000 // 10s
48 | // 2. (可选)添加小程序端请求头标识
49 | options.header = {
50 | platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
51 | ...options.header,
52 | }
53 | // 3. 添加 token 请求头标识
54 | const userStore = useUserStore()
55 | const { token } = userStore.userInfo as unknown as IUserInfo
56 | if (token) {
57 | options.header.Authorization = `Bearer ${token}`
58 | }
59 | },
60 | }
61 |
62 | export const requestInterceptor = {
63 | install() {
64 | // 拦截 request 请求
65 | uni.addInterceptor('request', httpInterceptor)
66 | // 拦截 uploadFile 文件上传
67 | uni.addInterceptor('uploadFile', httpInterceptor)
68 | },
69 | }
70 |
--------------------------------------------------------------------------------
/src/interceptors/route.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * by 菲鸽 on 2024-03-06
3 | * 路由拦截,通常也是登录拦截
4 | * 可以设置路由白名单,或者黑名单,看业务需要选哪一个
5 | * 我这里应为大部分都可以随便进入,所以使用黑名单
6 | */
7 | import { useUserStore } from '@/store'
8 | import { needLoginPages as _needLoginPages, getNeedLoginPages, getLastPage } from '@/utils'
9 |
10 | // TODO Check
11 | const loginRoute = '/pages/login/index'
12 |
13 | const isLogined = () => {
14 | const userStore = useUserStore()
15 | return !!userStore.userInfo.username
16 | }
17 |
18 | const isDev = import.meta.env.DEV
19 |
20 | // 黑名单登录拦截器 - (适用于大部分页面不需要登录,少部分页面需要登录)
21 | const navigateToInterceptor = {
22 | // 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
23 | // 增加对相对路径的处理,BY 网友 @ideal
24 | invoke({ url }: { url: string }) {
25 | // console.log(url) // /pages/route-interceptor/index?name=feige&age=30
26 | let path = url.split('?')[0]
27 |
28 | // 处理相对路径
29 | if (!path.startsWith('/')) {
30 | const currentPath = getLastPage().route
31 | const normalizedCurrentPath = currentPath.startsWith('/') ? currentPath : `/${currentPath}`
32 | const baseDir = normalizedCurrentPath.substring(0, normalizedCurrentPath.lastIndexOf('/'))
33 | path = `${baseDir}/${path}`
34 | }
35 |
36 | let needLoginPages: string[] = []
37 | // 为了防止开发时出现BUG,这里每次都获取一下。生产环境可以移到函数外,性能更好
38 | if (isDev) {
39 | needLoginPages = getNeedLoginPages()
40 | } else {
41 | needLoginPages = _needLoginPages
42 | }
43 | const isNeedLogin = needLoginPages.includes(path)
44 | if (!isNeedLogin) {
45 | return true
46 | }
47 | const hasLogin = isLogined()
48 | if (hasLogin) {
49 | return true
50 | }
51 | const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
52 | uni.navigateTo({ url: redirectRoute })
53 | return false
54 | },
55 | }
56 |
57 | export const routeInterceptor = {
58 | install() {
59 | uni.addInterceptor('navigateTo', navigateToInterceptor)
60 | uni.addInterceptor('reLaunch', navigateToInterceptor)
61 | uni.addInterceptor('redirectTo', navigateToInterceptor)
62 | uni.addInterceptor('switchTab', navigateToInterceptor)
63 | },
64 | }
65 |
--------------------------------------------------------------------------------
/src/layouts/default.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
19 |
--------------------------------------------------------------------------------
/src/layouts/demo.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
18 |
--------------------------------------------------------------------------------
/src/main.ts:
--------------------------------------------------------------------------------
1 | import '@/style/index.scss'
2 | import { VueQueryPlugin } from '@tanstack/vue-query'
3 | import 'uno.css'
4 | import { createSSRApp } from 'vue'
5 |
6 | import App from './App.vue'
7 | import { prototypeInterceptor, requestInterceptor, routeInterceptor } from './interceptors'
8 | import store from './store'
9 |
10 | export function createApp() {
11 | const app = createSSRApp(App)
12 | app.use(store)
13 | app.use(routeInterceptor)
14 | app.use(requestInterceptor)
15 | app.use(prototypeInterceptor)
16 | app.use(VueQueryPlugin)
17 |
18 | return {
19 | app,
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "unibest",
3 | "appid": "H57F2ACE4",
4 | "description": "",
5 | "versionName": "1.0.0",
6 | "versionCode": "100",
7 | "transformPx": false,
8 | "app-plus": {
9 | "usingComponents": true,
10 | "nvueStyleCompiler": "uni-app",
11 | "compilerVersion": 3,
12 | "splashscreen": {
13 | "alwaysShowBeforeRender": true,
14 | "waiting": true,
15 | "autoclose": true,
16 | "delay": 0
17 | },
18 | "modules": {},
19 | "distribute": {
20 | "android": {
21 | "permissions": [
22 | "",
23 | "",
24 | "",
25 | "",
26 | "",
27 | "",
28 | "",
29 | "",
30 | "",
31 | "",
32 | "",
33 | "",
34 | "",
35 | "",
36 | ""
37 | ],
38 | "minSdkVersion": 30,
39 | "targetSdkVersion": 30,
40 | "abiFilters": [
41 | "armeabi-v7a",
42 | "arm64-v8a"
43 | ]
44 | },
45 | "ios": {},
46 | "sdkConfigs": {},
47 | "icons": {
48 | "android": {
49 | "hdpi": "static/app/icons/72x72.png",
50 | "xhdpi": "static/app/icons/96x96.png",
51 | "xxhdpi": "static/app/icons/144x144.png",
52 | "xxxhdpi": "static/app/icons/192x192.png"
53 | },
54 | "ios": {
55 | "appstore": "static/app/icons/1024x1024.png",
56 | "ipad": {
57 | "app": "static/app/icons/76x76.png",
58 | "app@2x": "static/app/icons/152x152.png",
59 | "notification": "static/app/icons/20x20.png",
60 | "notification@2x": "static/app/icons/40x40.png",
61 | "proapp@2x": "static/app/icons/167x167.png",
62 | "settings": "static/app/icons/29x29.png",
63 | "settings@2x": "static/app/icons/58x58.png",
64 | "spotlight": "static/app/icons/40x40.png",
65 | "spotlight@2x": "static/app/icons/80x80.png"
66 | },
67 | "iphone": {
68 | "app@2x": "static/app/icons/120x120.png",
69 | "app@3x": "static/app/icons/180x180.png",
70 | "notification@2x": "static/app/icons/40x40.png",
71 | "notification@3x": "static/app/icons/60x60.png",
72 | "settings@2x": "static/app/icons/58x58.png",
73 | "settings@3x": "static/app/icons/87x87.png",
74 | "spotlight@2x": "static/app/icons/80x80.png",
75 | "spotlight@3x": "static/app/icons/120x120.png"
76 | }
77 | }
78 | }
79 | },
80 | "compatible": {
81 | "ignoreVersion": true
82 | }
83 | },
84 | "quickapp": {},
85 | "mp-weixin": {
86 | "appid": "wxa2abb91f64032a2b",
87 | "setting": {
88 | "urlCheck": false
89 | },
90 | "usingComponents": true
91 | },
92 | "mp-alipay": {
93 | "usingComponents": true,
94 | "styleIsolation": "shared"
95 | },
96 | "mp-baidu": {
97 | "usingComponents": true
98 | },
99 | "mp-toutiao": {
100 | "usingComponents": true
101 | },
102 | "uniStatistics": {
103 | "enable": false
104 | },
105 | "vueVersion": "3",
106 | "h5": {
107 | "router": {
108 | "base": "/"
109 | }
110 | }
111 | }
--------------------------------------------------------------------------------
/src/pages-sub/demo/index.vue:
--------------------------------------------------------------------------------
1 |
2 | {
3 | style: { navigationBarTitleText: '分包页面 标题' },
4 | }
5 |
6 |
7 |
8 |
9 | http://localhost:9000/#/pages-sub/demo/index
10 | 分包页面demo
11 |
12 |
13 |
14 |
17 |
18 |
21 |
--------------------------------------------------------------------------------
/src/pages.json:
--------------------------------------------------------------------------------
1 | {
2 | "globalStyle": {
3 | "navigationStyle": "default",
4 | "navigationBarTitleText": "unibest",
5 | "navigationBarBackgroundColor": "#f8f8f8",
6 | "navigationBarTextStyle": "black",
7 | "backgroundColor": "#FFFFFF"
8 | },
9 | "easycom": {
10 | "autoscan": true,
11 | "custom": {
12 | "^fg-(.*)": "@/components/fg-$1/fg-$1.vue",
13 | "^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
14 | "^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
15 | }
16 | },
17 | "tabBar": {
18 | "color": "#999999",
19 | "selectedColor": "#018d71",
20 | "backgroundColor": "#F8F8F8",
21 | "borderStyle": "black",
22 | "height": "50px",
23 | "fontSize": "10px",
24 | "iconWidth": "24px",
25 | "spacing": "3px",
26 | "list": [
27 | {
28 | "iconPath": "static/tabbar/home.png",
29 | "selectedIconPath": "static/tabbar/homeHL.png",
30 | "pagePath": "pages/index/index",
31 | "text": "首页"
32 | },
33 | {
34 | "iconPath": "static/tabbar/example.png",
35 | "selectedIconPath": "static/tabbar/exampleHL.png",
36 | "pagePath": "pages/about/about",
37 | "text": "关于"
38 | },
39 | {
40 | "iconPath": "static/tabbar/personal.png",
41 | "selectedIconPath": "static/tabbar/personalHL.png",
42 | "pagePath": "pages/mine/index",
43 | "text": "我的"
44 | }
45 | ]
46 | },
47 | "pages": [
48 | {
49 | "path": "pages/index/index",
50 | "type": "home",
51 | "style": {
52 | "navigationStyle": "custom",
53 | "navigationBarTitleText": "首页"
54 | }
55 | },
56 | {
57 | "path": "pages/about/about",
58 | "type": "page",
59 | "style": {
60 | "navigationBarTitleText": "关于",
61 | "navigationStyle": "custom"
62 | }
63 | },
64 | {
65 | "path": "pages/login/index",
66 | "type": "page",
67 | "style": {
68 | "navigationBarTitleText": "登录",
69 | "navigationStyle": "custom"
70 | }
71 | },
72 | {
73 | "path": "pages/mine/index",
74 | "type": "page",
75 | "style": {
76 | "navigationBarTitleText": "我的"
77 | }
78 | },
79 | {
80 | "path": "pages/mine/about/index",
81 | "type": "page",
82 | "style": {
83 | "navigationBarTitleText": "关于我们"
84 | }
85 | },
86 | {
87 | "path": "pages/mine/info/index",
88 | "type": "page",
89 | "style": {
90 | "navigationBarTitleText": "个人资料"
91 | }
92 | },
93 | {
94 | "path": "pages/mine/password/index",
95 | "type": "page",
96 | "style": {
97 | "navigationBarTitleText": "修改密码"
98 | }
99 | }
100 | ],
101 | "subPackages": []
102 | }
--------------------------------------------------------------------------------
/src/pages/about/about.vue:
--------------------------------------------------------------------------------
1 |
2 | {
3 | style: {
4 | navigationBarTitleText: '关于',
5 | navigationStyle: 'custom', // 开启自定义导航栏
6 | },
7 | }
8 |
9 |
10 |
11 |
12 | 关于
13 |
17 |
18 | 鸽友们好,我是
19 | 菲鸽
20 |
21 | 测试 scss 样式
22 |
23 |
24 |
25 |
26 |
27 |
28 |
35 |
36 |
45 |
--------------------------------------------------------------------------------
/src/pages/about/components/request.vue:
--------------------------------------------------------------------------------
1 |
2 | {
3 | layout: 'demo',
4 | style: {
5 | navigationBarTitleText: '请求',
6 | },
7 | }
8 |
9 |
10 |
11 |
12 | 使用的是 laf 云后台
13 | 我的推荐码,可以获得佣金
14 |
15 |
16 |
17 | {{ recommendUrl }}
18 |
19 |
20 |
21 |
22 | {{ recommendUrl }}
23 |
24 |
25 |
26 | 发送请求
27 |
28 | loading...
29 |
30 | 请求数据如下
31 | {{ JSON.stringify(data) }}
32 |
33 |
34 | 重置数据
35 |
36 |
37 |
38 |
68 |
--------------------------------------------------------------------------------
/src/pages/about/components/upload.vue:
--------------------------------------------------------------------------------
1 |
2 | {
3 | layout: 'default',
4 | style: {
5 | navigationBarTitleText: '上传-状态一体化',
6 | },
7 | }
8 |
9 |
10 |
11 |
12 | 选择图片并上传
13 | 上传...
14 |
15 | 上传后返回的接口数据:
16 | {{ data }}
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
27 |
28 |
31 |
--------------------------------------------------------------------------------
/src/pages/index/index.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 | {
4 | style: {
5 | navigationStyle: 'custom',
6 | navigationBarTitleText: '首页',
7 | },
8 | }
9 |
10 |
11 |
15 |
16 |
17 |
18 | unibest
19 | 最好用的 uniapp 开发模板
20 |
21 | {{ description }}
22 |
23 | 当前平台是:
24 | {{ PLATFORM.platform }}
25 |
26 |
27 | 模板分支是:
28 | base
29 |
30 |
31 |
32 |
33 |
51 |
52 |
57 |
--------------------------------------------------------------------------------
/src/pages/mine/about/index.vue:
--------------------------------------------------------------------------------
1 |
2 | {
3 | style: {
4 | navigationBarTitleText: '关于我们',
5 | },
6 | }
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | {{ appTitle }}
18 | 版本 {{ packageJson.version }}
19 |
20 |
21 |
22 |
23 | 联系我们
24 |
25 |
26 |
27 | 客服电话:400-XXX-XXXX
28 |
29 |
30 |
31 | 邮箱:support@unibest.tech
32 |
33 |
34 |
35 | 地址:中国·深圳
36 |
37 |
38 |
39 |
40 |
41 |
42 | Copyright © 2025-{{ currentYear }} {{ appTitle }}
43 | All Rights Reserved
44 |
45 |
46 |
47 |
48 |
49 |
59 |
60 |
174 |
--------------------------------------------------------------------------------
/src/pages/mine/info/index.vue:
--------------------------------------------------------------------------------
1 |
2 | {
3 | style: {
4 | navigationBarTitleText: '个人资料',
5 | },
6 | }
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | 昵称
18 |
26 |
27 |
28 |
29 |
30 | 性别
31 |
36 | 男
37 | 女
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | 保存修改
46 |
47 |
48 |
49 |
50 |
51 |
52 |
83 |
84 |
191 |
--------------------------------------------------------------------------------
/src/service/app/displayEnumLabel.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-ignore
3 | import * as API from './types';
4 |
5 | export function displayStatusEnum(field: API.IStatusEnum) {
6 | return { available: 'available', pending: 'pending', sold: 'sold' }[field];
7 | }
8 |
9 | export function displayStatusEnum2(field: API.IStatusEnum2) {
10 | return { placed: 'placed', approved: 'approved', delivered: 'delivered' }[
11 | field
12 | ];
13 | }
14 |
--------------------------------------------------------------------------------
/src/service/app/index.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-ignore
3 | export * from './types';
4 | export * from './displayEnumLabel';
5 |
6 | export * from './pet';
7 | export * from './pet.vuequery';
8 | export * from './store';
9 | export * from './store.vuequery';
10 | export * from './user';
11 | export * from './user.vuequery';
12 |
--------------------------------------------------------------------------------
/src/service/app/pet.vuequery.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-ignore
3 | import { queryOptions, useMutation } from '@tanstack/vue-query';
4 | import type { DefaultError } from '@tanstack/vue-query';
5 | import request from '@/utils/request';
6 | import { CustomRequestOptions } from '@/interceptors/request';
7 |
8 | import * as apis from './pet';
9 | import * as API from './types';
10 |
11 | /** Update an existing pet PUT /pet */
12 | export function useUpdatePetMutation(options?: {
13 | onSuccess?: (value?: unknown) => void;
14 | onError?: (error?: DefaultError) => void;
15 | }) {
16 | const { onSuccess, onError } = options || {};
17 |
18 | const response = useMutation({
19 | mutationFn: apis.updatePet,
20 | onSuccess(data: unknown) {
21 | onSuccess?.(data);
22 | },
23 | onError(error) {
24 | onError?.(error);
25 | },
26 | });
27 |
28 | return response;
29 | }
30 |
31 | /** Add a new pet to the store POST /pet */
32 | export function useAddPetMutation(options?: {
33 | onSuccess?: (value?: unknown) => void;
34 | onError?: (error?: DefaultError) => void;
35 | }) {
36 | const { onSuccess, onError } = options || {};
37 |
38 | const response = useMutation({
39 | mutationFn: apis.addPet,
40 | onSuccess(data: unknown) {
41 | onSuccess?.(data);
42 | },
43 | onError(error) {
44 | onError?.(error);
45 | },
46 | });
47 |
48 | return response;
49 | }
50 |
51 | /** Find pet by ID Returns a single pet GET /pet/${param0} */
52 | export function getPetByIdQueryOptions(options: {
53 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
54 | params: API.getPetByIdParams;
55 | options?: CustomRequestOptions;
56 | }) {
57 | return queryOptions({
58 | queryFn: async ({ queryKey }) => {
59 | return apis.getPetById(queryKey[1] as typeof options);
60 | },
61 | queryKey: ['getPetById', options],
62 | });
63 | }
64 |
65 | /** Updates a pet in the store with form data POST /pet/${param0} */
66 | export function useUpdatePetWithFormMutation(options?: {
67 | onSuccess?: (value?: unknown) => void;
68 | onError?: (error?: DefaultError) => void;
69 | }) {
70 | const { onSuccess, onError } = options || {};
71 |
72 | const response = useMutation({
73 | mutationFn: apis.updatePetWithForm,
74 | onSuccess(data: unknown) {
75 | onSuccess?.(data);
76 | },
77 | onError(error) {
78 | onError?.(error);
79 | },
80 | });
81 |
82 | return response;
83 | }
84 |
85 | /** Deletes a pet DELETE /pet/${param0} */
86 | export function useDeletePetMutation(options?: {
87 | onSuccess?: (value?: unknown) => void;
88 | onError?: (error?: DefaultError) => void;
89 | }) {
90 | const { onSuccess, onError } = options || {};
91 |
92 | const response = useMutation({
93 | mutationFn: apis.deletePet,
94 | onSuccess(data: unknown) {
95 | onSuccess?.(data);
96 | },
97 | onError(error) {
98 | onError?.(error);
99 | },
100 | });
101 |
102 | return response;
103 | }
104 |
105 | /** uploads an image POST /pet/${param0}/uploadImage */
106 | export function useUploadFileMutation(options?: {
107 | onSuccess?: (value?: API.ApiResponse) => void;
108 | onError?: (error?: DefaultError) => void;
109 | }) {
110 | const { onSuccess, onError } = options || {};
111 |
112 | const response = useMutation({
113 | mutationFn: apis.uploadFile,
114 | onSuccess(data: API.ApiResponse) {
115 | onSuccess?.(data);
116 | },
117 | onError(error) {
118 | onError?.(error);
119 | },
120 | });
121 |
122 | return response;
123 | }
124 |
125 | /** Finds Pets by status Multiple status values can be provided with comma separated strings GET /pet/findByStatus */
126 | export function findPetsByStatusQueryOptions(options: {
127 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
128 | params: API.findPetsByStatusParams;
129 | options?: CustomRequestOptions;
130 | }) {
131 | return queryOptions({
132 | queryFn: async ({ queryKey }) => {
133 | return apis.findPetsByStatus(queryKey[1] as typeof options);
134 | },
135 | queryKey: ['findPetsByStatus', options],
136 | });
137 | }
138 |
139 | /** Finds Pets by tags Multiple tags can be provided with comma separated strings. Use tag1, tag2, tag3 for testing. GET /pet/findByTags */
140 | export function findPetsByTagsQueryOptions(options: {
141 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
142 | params: API.findPetsByTagsParams;
143 | options?: CustomRequestOptions;
144 | }) {
145 | return queryOptions({
146 | queryFn: async ({ queryKey }) => {
147 | return apis.findPetsByTags(queryKey[1] as typeof options);
148 | },
149 | queryKey: ['findPetsByTags', options],
150 | });
151 | }
152 |
--------------------------------------------------------------------------------
/src/service/app/store.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-ignore
3 | import request from '@/utils/request';
4 | import { CustomRequestOptions } from '@/interceptors/request';
5 |
6 | import * as API from './types';
7 |
8 | /** Returns pet inventories by status Returns a map of status codes to quantities GET /store/inventory */
9 | export async function getInventory({
10 | options,
11 | }: {
12 | options?: CustomRequestOptions;
13 | }) {
14 | return request>('/store/inventory', {
15 | method: 'GET',
16 | ...(options || {}),
17 | });
18 | }
19 |
20 | /** Place an order for a pet POST /store/order */
21 | export async function placeOrder({
22 | body,
23 | options,
24 | }: {
25 | body: API.Order;
26 | options?: CustomRequestOptions;
27 | }) {
28 | return request('/store/order', {
29 | method: 'POST',
30 | headers: {
31 | 'Content-Type': 'application/json',
32 | },
33 | data: body,
34 | ...(options || {}),
35 | });
36 | }
37 |
38 | /** Find purchase order by ID For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions GET /store/order/${param0} */
39 | export async function getOrderById({
40 | params,
41 | options,
42 | }: {
43 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
44 | params: API.getOrderByIdParams;
45 | options?: CustomRequestOptions;
46 | }) {
47 | const { orderId: param0, ...queryParams } = params;
48 |
49 | return request(`/store/order/${param0}`, {
50 | method: 'GET',
51 | params: { ...queryParams },
52 | ...(options || {}),
53 | });
54 | }
55 |
56 | /** Delete purchase order by ID For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors DELETE /store/order/${param0} */
57 | export async function deleteOrder({
58 | params,
59 | options,
60 | }: {
61 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
62 | params: API.deleteOrderParams;
63 | options?: CustomRequestOptions;
64 | }) {
65 | const { orderId: param0, ...queryParams } = params;
66 |
67 | return request(`/store/order/${param0}`, {
68 | method: 'DELETE',
69 | params: { ...queryParams },
70 | ...(options || {}),
71 | });
72 | }
73 |
--------------------------------------------------------------------------------
/src/service/app/store.vuequery.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-ignore
3 | import { queryOptions, useMutation } from '@tanstack/vue-query';
4 | import type { DefaultError } from '@tanstack/vue-query';
5 | import request from '@/utils/request';
6 | import { CustomRequestOptions } from '@/interceptors/request';
7 |
8 | import * as apis from './store';
9 | import * as API from './types';
10 |
11 | /** Returns pet inventories by status Returns a map of status codes to quantities GET /store/inventory */
12 | export function getInventoryQueryOptions(options: {
13 | options?: CustomRequestOptions;
14 | }) {
15 | return queryOptions({
16 | queryFn: async ({ queryKey }) => {
17 | return apis.getInventory(queryKey[1] as typeof options);
18 | },
19 | queryKey: ['getInventory', options],
20 | });
21 | }
22 |
23 | /** Place an order for a pet POST /store/order */
24 | export function usePlaceOrderMutation(options?: {
25 | onSuccess?: (value?: API.Order) => void;
26 | onError?: (error?: DefaultError) => void;
27 | }) {
28 | const { onSuccess, onError } = options || {};
29 |
30 | const response = useMutation({
31 | mutationFn: apis.placeOrder,
32 | onSuccess(data: API.Order) {
33 | onSuccess?.(data);
34 | },
35 | onError(error) {
36 | onError?.(error);
37 | },
38 | });
39 |
40 | return response;
41 | }
42 |
43 | /** Find purchase order by ID For valid response try integer IDs with value >= 1 and <= 10. Other values will generated exceptions GET /store/order/${param0} */
44 | export function getOrderByIdQueryOptions(options: {
45 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
46 | params: API.getOrderByIdParams;
47 | options?: CustomRequestOptions;
48 | }) {
49 | return queryOptions({
50 | queryFn: async ({ queryKey }) => {
51 | return apis.getOrderById(queryKey[1] as typeof options);
52 | },
53 | queryKey: ['getOrderById', options],
54 | });
55 | }
56 |
57 | /** Delete purchase order by ID For valid response try integer IDs with positive integer value. Negative or non-integer values will generate API errors DELETE /store/order/${param0} */
58 | export function useDeleteOrderMutation(options?: {
59 | onSuccess?: (value?: unknown) => void;
60 | onError?: (error?: DefaultError) => void;
61 | }) {
62 | const { onSuccess, onError } = options || {};
63 |
64 | const response = useMutation({
65 | mutationFn: apis.deleteOrder,
66 | onSuccess(data: unknown) {
67 | onSuccess?.(data);
68 | },
69 | onError(error) {
70 | onError?.(error);
71 | },
72 | });
73 |
74 | return response;
75 | }
76 |
--------------------------------------------------------------------------------
/src/service/app/types.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-ignore
3 |
4 | export type ApiResponse = {
5 | code?: number;
6 | type?: string;
7 | message?: string;
8 | };
9 |
10 | export type Category = {
11 | id?: number;
12 | name?: string;
13 | };
14 |
15 | export type deleteOrderParams = {
16 | /** ID of the order that needs to be deleted */
17 | orderId: number;
18 | };
19 |
20 | export type deletePetParams = {
21 | /** Pet id to delete */
22 | petId: number;
23 | };
24 |
25 | export type deleteUserParams = {
26 | /** The name that needs to be deleted */
27 | username: string;
28 | };
29 |
30 | export type findPetsByStatusParams = {
31 | /** Status values that need to be considered for filter */
32 | status: ('available' | 'pending' | 'sold')[];
33 | };
34 |
35 | export type findPetsByTagsParams = {
36 | /** Tags to filter by */
37 | tags: string[];
38 | };
39 |
40 | export type getOrderByIdParams = {
41 | /** ID of pet that needs to be fetched */
42 | orderId: number;
43 | };
44 |
45 | export type getPetByIdParams = {
46 | /** ID of pet to return */
47 | petId: number;
48 | };
49 |
50 | export type getUserByNameParams = {
51 | /** The name that needs to be fetched. Use user1 for testing. */
52 | username: string;
53 | };
54 |
55 | export type loginUserParams = {
56 | /** The user name for login */
57 | username: string;
58 | /** The password for login in clear text */
59 | password: string;
60 | };
61 |
62 | export type Order = {
63 | id?: number;
64 | petId?: number;
65 | quantity?: number;
66 | shipDate?: string;
67 | /** Order Status */
68 | status?: 'placed' | 'approved' | 'delivered';
69 | complete?: boolean;
70 | };
71 |
72 | export type Pet = {
73 | id?: number;
74 | category?: Category;
75 | name: string;
76 | photoUrls: string[];
77 | tags?: Tag[];
78 | /** pet status in the store */
79 | status?: 'available' | 'pending' | 'sold';
80 | };
81 |
82 | export enum StatusEnum {
83 | available = 'available',
84 | pending = 'pending',
85 | sold = 'sold',
86 | }
87 |
88 | export type IStatusEnum = keyof typeof StatusEnum;
89 |
90 | export enum StatusEnum2 {
91 | placed = 'placed',
92 | approved = 'approved',
93 | delivered = 'delivered',
94 | }
95 |
96 | export type IStatusEnum2 = keyof typeof StatusEnum2;
97 |
98 | export type Tag = {
99 | id?: number;
100 | name?: string;
101 | };
102 |
103 | export type updatePetWithFormParams = {
104 | /** ID of pet that needs to be updated */
105 | petId: number;
106 | };
107 |
108 | export type updateUserParams = {
109 | /** name that need to be updated */
110 | username: string;
111 | };
112 |
113 | export type uploadFileParams = {
114 | /** ID of pet to update */
115 | petId: number;
116 | };
117 |
118 | export type User = {
119 | id?: number;
120 | username?: string;
121 | firstName?: string;
122 | lastName?: string;
123 | email?: string;
124 | password?: string;
125 | phone?: string;
126 | /** User Status */
127 | userStatus?: number;
128 | };
129 |
--------------------------------------------------------------------------------
/src/service/app/user.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-ignore
3 | import request from '@/utils/request';
4 | import { CustomRequestOptions } from '@/interceptors/request';
5 |
6 | import * as API from './types';
7 |
8 | /** Create user This can only be done by the logged in user. 返回值: successful operation POST /user */
9 | export async function createUser({
10 | body,
11 | options,
12 | }: {
13 | body: API.User;
14 | options?: CustomRequestOptions;
15 | }) {
16 | return request('/user', {
17 | method: 'POST',
18 | headers: {
19 | 'Content-Type': 'application/json',
20 | },
21 | data: body,
22 | ...(options || {}),
23 | });
24 | }
25 |
26 | /** Get user by user name GET /user/${param0} */
27 | export async function getUserByName({
28 | params,
29 | options,
30 | }: {
31 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
32 | params: API.getUserByNameParams;
33 | options?: CustomRequestOptions;
34 | }) {
35 | const { username: param0, ...queryParams } = params;
36 |
37 | return request(`/user/${param0}`, {
38 | method: 'GET',
39 | params: { ...queryParams },
40 | ...(options || {}),
41 | });
42 | }
43 |
44 | /** Updated user This can only be done by the logged in user. PUT /user/${param0} */
45 | export async function updateUser({
46 | params,
47 | body,
48 | options,
49 | }: {
50 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
51 | params: API.updateUserParams;
52 | body: API.User;
53 | options?: CustomRequestOptions;
54 | }) {
55 | const { username: param0, ...queryParams } = params;
56 |
57 | return request(`/user/${param0}`, {
58 | method: 'PUT',
59 | headers: {
60 | 'Content-Type': 'application/json',
61 | },
62 | params: { ...queryParams },
63 | data: body,
64 | ...(options || {}),
65 | });
66 | }
67 |
68 | /** Delete user This can only be done by the logged in user. DELETE /user/${param0} */
69 | export async function deleteUser({
70 | params,
71 | options,
72 | }: {
73 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
74 | params: API.deleteUserParams;
75 | options?: CustomRequestOptions;
76 | }) {
77 | const { username: param0, ...queryParams } = params;
78 |
79 | return request(`/user/${param0}`, {
80 | method: 'DELETE',
81 | params: { ...queryParams },
82 | ...(options || {}),
83 | });
84 | }
85 |
86 | /** Creates list of users with given input array 返回值: successful operation POST /user/createWithArray */
87 | export async function createUsersWithArrayInput({
88 | body,
89 | options,
90 | }: {
91 | body: API.User[];
92 | options?: CustomRequestOptions;
93 | }) {
94 | return request('/user/createWithArray', {
95 | method: 'POST',
96 | headers: {
97 | 'Content-Type': 'application/json',
98 | },
99 | data: body,
100 | ...(options || {}),
101 | });
102 | }
103 |
104 | /** Creates list of users with given input array 返回值: successful operation POST /user/createWithList */
105 | export async function createUsersWithListInput({
106 | body,
107 | options,
108 | }: {
109 | body: API.User[];
110 | options?: CustomRequestOptions;
111 | }) {
112 | return request('/user/createWithList', {
113 | method: 'POST',
114 | headers: {
115 | 'Content-Type': 'application/json',
116 | },
117 | data: body,
118 | ...(options || {}),
119 | });
120 | }
121 |
122 | /** Logs user into the system GET /user/login */
123 | export async function loginUser({
124 | params,
125 | options,
126 | }: {
127 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
128 | params: API.loginUserParams;
129 | options?: CustomRequestOptions;
130 | }) {
131 | return request('/user/login', {
132 | method: 'GET',
133 | params: {
134 | ...params,
135 | },
136 | ...(options || {}),
137 | });
138 | }
139 |
140 | /** Logs out current logged in user session 返回值: successful operation GET /user/logout */
141 | export async function logoutUser({
142 | options,
143 | }: {
144 | options?: CustomRequestOptions;
145 | }) {
146 | return request('/user/logout', {
147 | method: 'GET',
148 | ...(options || {}),
149 | });
150 | }
151 |
--------------------------------------------------------------------------------
/src/service/app/user.vuequery.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable */
2 | // @ts-ignore
3 | import { queryOptions, useMutation } from '@tanstack/vue-query';
4 | import type { DefaultError } from '@tanstack/vue-query';
5 | import request from '@/utils/request';
6 | import { CustomRequestOptions } from '@/interceptors/request';
7 |
8 | import * as apis from './user';
9 | import * as API from './types';
10 |
11 | /** Create user This can only be done by the logged in user. 返回值: successful operation POST /user */
12 | export function useCreateUserMutation(options?: {
13 | onSuccess?: (value?: unknown) => void;
14 | onError?: (error?: DefaultError) => void;
15 | }) {
16 | const { onSuccess, onError } = options || {};
17 |
18 | const response = useMutation({
19 | mutationFn: apis.createUser,
20 | onSuccess(data: unknown) {
21 | onSuccess?.(data);
22 | },
23 | onError(error) {
24 | onError?.(error);
25 | },
26 | });
27 |
28 | return response;
29 | }
30 |
31 | /** Get user by user name GET /user/${param0} */
32 | export function getUserByNameQueryOptions(options: {
33 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
34 | params: API.getUserByNameParams;
35 | options?: CustomRequestOptions;
36 | }) {
37 | return queryOptions({
38 | queryFn: async ({ queryKey }) => {
39 | return apis.getUserByName(queryKey[1] as typeof options);
40 | },
41 | queryKey: ['getUserByName', options],
42 | });
43 | }
44 |
45 | /** Updated user This can only be done by the logged in user. PUT /user/${param0} */
46 | export function useUpdateUserMutation(options?: {
47 | onSuccess?: (value?: unknown) => void;
48 | onError?: (error?: DefaultError) => void;
49 | }) {
50 | const { onSuccess, onError } = options || {};
51 |
52 | const response = useMutation({
53 | mutationFn: apis.updateUser,
54 | onSuccess(data: unknown) {
55 | onSuccess?.(data);
56 | },
57 | onError(error) {
58 | onError?.(error);
59 | },
60 | });
61 |
62 | return response;
63 | }
64 |
65 | /** Delete user This can only be done by the logged in user. DELETE /user/${param0} */
66 | export function useDeleteUserMutation(options?: {
67 | onSuccess?: (value?: unknown) => void;
68 | onError?: (error?: DefaultError) => void;
69 | }) {
70 | const { onSuccess, onError } = options || {};
71 |
72 | const response = useMutation({
73 | mutationFn: apis.deleteUser,
74 | onSuccess(data: unknown) {
75 | onSuccess?.(data);
76 | },
77 | onError(error) {
78 | onError?.(error);
79 | },
80 | });
81 |
82 | return response;
83 | }
84 |
85 | /** Creates list of users with given input array 返回值: successful operation POST /user/createWithArray */
86 | export function useCreateUsersWithArrayInputMutation(options?: {
87 | onSuccess?: (value?: unknown) => void;
88 | onError?: (error?: DefaultError) => void;
89 | }) {
90 | const { onSuccess, onError } = options || {};
91 |
92 | const response = useMutation({
93 | mutationFn: apis.createUsersWithArrayInput,
94 | onSuccess(data: unknown) {
95 | onSuccess?.(data);
96 | },
97 | onError(error) {
98 | onError?.(error);
99 | },
100 | });
101 |
102 | return response;
103 | }
104 |
105 | /** Creates list of users with given input array 返回值: successful operation POST /user/createWithList */
106 | export function useCreateUsersWithListInputMutation(options?: {
107 | onSuccess?: (value?: unknown) => void;
108 | onError?: (error?: DefaultError) => void;
109 | }) {
110 | const { onSuccess, onError } = options || {};
111 |
112 | const response = useMutation({
113 | mutationFn: apis.createUsersWithListInput,
114 | onSuccess(data: unknown) {
115 | onSuccess?.(data);
116 | },
117 | onError(error) {
118 | onError?.(error);
119 | },
120 | });
121 |
122 | return response;
123 | }
124 |
125 | /** Logs user into the system GET /user/login */
126 | export function loginUserQueryOptions(options: {
127 | // 叠加生成的Param类型 (非body参数openapi默认没有生成对象)
128 | params: API.loginUserParams;
129 | options?: CustomRequestOptions;
130 | }) {
131 | return queryOptions({
132 | queryFn: async ({ queryKey }) => {
133 | return apis.loginUser(queryKey[1] as typeof options);
134 | },
135 | queryKey: ['loginUser', options],
136 | });
137 | }
138 |
139 | /** Logs out current logged in user session 返回值: successful operation GET /user/logout */
140 | export function logoutUserQueryOptions(options: {
141 | options?: CustomRequestOptions;
142 | }) {
143 | return queryOptions({
144 | queryFn: async ({ queryKey }) => {
145 | return apis.logoutUser(queryKey[1] as typeof options);
146 | },
147 | queryKey: ['logoutUser', options],
148 | });
149 | }
150 |
--------------------------------------------------------------------------------
/src/service/index/foo.ts:
--------------------------------------------------------------------------------
1 | import { http } from '@/utils/http'
2 | export interface IFooItem {
3 | id: string
4 | name: string
5 | }
6 |
7 | /** GET 请求 */
8 | export const getFooAPI = (name: string) => {
9 | return http.get('/foo', { name })
10 | }
11 | /** GET 请求;支持 传递 header 的范例 */
12 | export const getFooAPI2 = (name: string) => {
13 | return http.get('/foo', { name }, { 'Content-Type-100': '100' })
14 | }
15 |
16 | /** POST 请求 */
17 | export const postFooAPI = (name: string) => {
18 | return http.post('/foo', { name })
19 | }
20 | /** POST 请求;需要传递 query 参数的范例;微信小程序经常有同时需要query参数和body参数的场景 */
21 | export const postFooAPI2 = (name: string) => {
22 | return http.post('/foo', { name })
23 | }
24 | /** POST 请求;支持 传递 header 的范例 */
25 | export const postFooAPI3 = (name: string) => {
26 | return http.post('/foo', { name }, { name }, { 'Content-Type-100': '100' })
27 | }
28 |
--------------------------------------------------------------------------------
/src/static/app/icons/1024x1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/1024x1024.png
--------------------------------------------------------------------------------
/src/static/app/icons/120x120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/120x120.png
--------------------------------------------------------------------------------
/src/static/app/icons/144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/144x144.png
--------------------------------------------------------------------------------
/src/static/app/icons/152x152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/152x152.png
--------------------------------------------------------------------------------
/src/static/app/icons/167x167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/167x167.png
--------------------------------------------------------------------------------
/src/static/app/icons/180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/180x180.png
--------------------------------------------------------------------------------
/src/static/app/icons/192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/192x192.png
--------------------------------------------------------------------------------
/src/static/app/icons/20x20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/20x20.png
--------------------------------------------------------------------------------
/src/static/app/icons/29x29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/29x29.png
--------------------------------------------------------------------------------
/src/static/app/icons/40x40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/40x40.png
--------------------------------------------------------------------------------
/src/static/app/icons/58x58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/58x58.png
--------------------------------------------------------------------------------
/src/static/app/icons/60x60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/60x60.png
--------------------------------------------------------------------------------
/src/static/app/icons/72x72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/72x72.png
--------------------------------------------------------------------------------
/src/static/app/icons/76x76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/76x76.png
--------------------------------------------------------------------------------
/src/static/app/icons/80x80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/80x80.png
--------------------------------------------------------------------------------
/src/static/app/icons/87x87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/87x87.png
--------------------------------------------------------------------------------
/src/static/app/icons/96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/app/icons/96x96.png
--------------------------------------------------------------------------------
/src/static/images/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/images/.gitkeep
--------------------------------------------------------------------------------
/src/static/images/avatar.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/images/avatar.jpg
--------------------------------------------------------------------------------
/src/static/images/default-avatar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/images/default-avatar.png
--------------------------------------------------------------------------------
/src/static/logo.svg:
--------------------------------------------------------------------------------
1 |
2 |
34 |
--------------------------------------------------------------------------------
/src/static/tabbar/example.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/tabbar/example.png
--------------------------------------------------------------------------------
/src/static/tabbar/exampleHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/tabbar/exampleHL.png
--------------------------------------------------------------------------------
/src/static/tabbar/home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/tabbar/home.png
--------------------------------------------------------------------------------
/src/static/tabbar/homeHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/tabbar/homeHL.png
--------------------------------------------------------------------------------
/src/static/tabbar/personal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/tabbar/personal.png
--------------------------------------------------------------------------------
/src/static/tabbar/personalHL.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/static/tabbar/personalHL.png
--------------------------------------------------------------------------------
/src/store/index.ts:
--------------------------------------------------------------------------------
1 | import { createPinia } from 'pinia'
2 | import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化
3 |
4 | const store = createPinia()
5 | store.use(
6 | createPersistedState({
7 | storage: {
8 | getItem: uni.getStorageSync,
9 | setItem: uni.setStorageSync,
10 | },
11 | }),
12 | )
13 |
14 | export default store
15 |
16 | // 模块统一导出
17 | export * from './user'
18 |
--------------------------------------------------------------------------------
/src/store/user.ts:
--------------------------------------------------------------------------------
1 | import {
2 | login as _login,
3 | getUserInfo as _getUserInfo,
4 | wxLogin as _wxLogin,
5 | logout as _logout,
6 | getWxCode,
7 | } from '@/api/login'
8 | import { defineStore } from 'pinia'
9 | import { ref } from 'vue'
10 | import { toast } from '@/utils/toast'
11 | import { IUserInfoVo } from '@/api/login.typings'
12 |
13 | // 初始化状态
14 | const userInfoState: IUserInfoVo = {
15 | id: 0,
16 | username: '',
17 | avatar: '/static/images/default-avatar.png',
18 | token: '',
19 | }
20 |
21 | export const useUserStore = defineStore(
22 | 'user',
23 | () => {
24 | // 定义用户信息
25 | const userInfo = ref({ ...userInfoState })
26 | // 设置用户信息
27 | const setUserInfo = (val: IUserInfoVo) => {
28 | console.log('设置用户信息', val)
29 | // 若头像为空 则使用默认头像
30 | if (!val.avatar) {
31 | val.avatar = userInfoState.avatar
32 | } else {
33 | val.avatar = 'https://oss.laf.run/ukw0y1-site/avatar.jpg?feige'
34 | }
35 | userInfo.value = val
36 | }
37 | // 删除用户信息
38 | const removeUserInfo = () => {
39 | userInfo.value = { ...userInfoState }
40 | uni.removeStorageSync('userInfo')
41 | uni.removeStorageSync('token')
42 | }
43 | /**
44 | * 用户登录
45 | * @param credentials 登录参数
46 | * @returns R
47 | */
48 | const login = async (credentials: {
49 | username: string
50 | password: string
51 | code: string
52 | uuid: string
53 | }) => {
54 | const res = await _login(credentials)
55 | console.log('登录信息', res)
56 | toast.success('登录成功')
57 | getUserInfo()
58 | return res
59 | }
60 | /**
61 | * 获取用户信息
62 | */
63 | const getUserInfo = async () => {
64 | const res = await _getUserInfo()
65 | const userInfo = res.data
66 | setUserInfo(userInfo)
67 | uni.setStorageSync('userInfo', userInfo)
68 | uni.setStorageSync('token', userInfo.token)
69 | // TODO 这里可以增加获取用户路由的方法 根据用户的角色动态生成路由
70 | return res
71 | }
72 | /**
73 | * 退出登录 并 删除用户信息
74 | */
75 | const logout = async () => {
76 | _logout()
77 | removeUserInfo()
78 | }
79 | /**
80 | * 微信登录
81 | */
82 | const wxLogin = async () => {
83 | // 获取微信小程序登录的code
84 | const data = await getWxCode()
85 | console.log('微信登录code', data)
86 |
87 | const res = await _wxLogin(data)
88 | getUserInfo()
89 | return res
90 | }
91 |
92 | return {
93 | userInfo,
94 | login,
95 | wxLogin,
96 | getUserInfo,
97 | logout,
98 | }
99 | },
100 | {
101 | persist: true,
102 | },
103 | )
104 |
--------------------------------------------------------------------------------
/src/style/iconfont.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'iconfont'; /* Project id 4543091 */
3 | src:
4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAOwAAsAAAAAB9AAAANjAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHFQGYACDHAqDBIJqATYCJAMQCwoABCAFhGcHPRvnBsgusG3kMyE15/44PsBX09waBHv0REDt97oHAQDFrOIyPirRiULQ+TJcXV0hCYTuVFcBC915/2vX/32Q80hkZ5PZGZ9snvwruVLloidKqYN6iKC53bOtbKwVLSIi3W6zCWZbs3VbER3j9JpGX3ySYcc94IQRTK5s4epS/jSqIgvg37qlY2/jwQN7D9ADpfRCmIknQByTscVZPTBr+hnnCKg2o4bjakvXEPjuY65DJGeJNtBUhn1JxOBuB2UZmUpBOXdsFp4oxOv4GHgs3h/+wRDcicqSZJG1q9kK1z/Af9NpqxjpC2QaAdpHlCFh4spcYXs5sMWpSk5wUj31G2dLQKVKkZ/w7f/8/i/A3JVUSZK9f7xIKJeU14IFpBI/Qfkkz46GT/CuaGREfCtKJUougWeQWHvVC5Lcz2BGS+SePR99vj3yjJx7h574tp7uWcOh4yfaTjS/245TT/vkQrN+a7RLkK8+Vd+bz+FSGh+9srDQKPeJ2s29z7ah4+efdoxefRbbGwfy7ht+SuIWukzsu1b6ePP+6kN1aamb47qsPim1Ia3xdEpDcl1dckPKGYnneI23+57r2W1Mmkqs6ajrChRCs5qyQ66rTVWhgZaG7toOeHm5cxn0sSQuNDEgcUTdNTSupKI1JRZih/JssAUKezPeOJJzbNozF6zWJuuVavVU5Tgtkop/SDzHa7ytvnCTq0PhkEfi4xLLtb0PuwyOAYqmrYQApFJyoJjTnfz+ve94vvv2f/yWgxl8Jd8Di2DRDPuob59mU/+VfDCROQyR8xSnmP9fXm7liagmN39OlmbvjqG0sMsJKrU0EFXogaRSH5bNY1CmxhyUq7QC1cY1T67RwuQk5CoM2RUQNLoEUb03kDS6h2XzcyjT7iOUa/QXqq1Hn6/GUBAaGcGcWJFlGUmCoVOp8kLvABHnVczGYiOE2SVEUH5OXj/TSnTCDjHAviAWcE4RZYaGWszNiKoayGSGTASeY+PcrMjNpVMvyREMDRoxBMYRVojFMkQiMOhohubdzxtAiOapMMbERpKMnQT9SL4ceQysVdJZVa9kEbsFogIcRyEUE2kN0mL7CDVIGhBzupWMEHA5bDvipgq5hKJcKef8ivbx1kC15KgcYkghhzLxYNntxoKCReJ82jAHAAA=')
5 | format('woff2'),
6 | url('//at.alicdn.com/t/c/font_4543091_njpo5b95nl.woff?t=1715485842402') format('woff'),
7 | url('//at.alicdn.com/t/c/font_4543091_njpo5b95nl.ttf?t=1715485842402') format('truetype');
8 | }
9 |
10 | .iconfont {
11 | font-family: 'iconfont' !important;
12 | font-size: 16px;
13 | font-style: normal;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | }
17 |
18 | .icon-my:before {
19 | content: '\e78c';
20 | }
21 |
22 | .icon-package:before {
23 | content: '\e9c2';
24 | }
25 |
26 | .icon-chat:before {
27 | content: '\e600';
28 | }
29 |
--------------------------------------------------------------------------------
/src/style/index.scss:
--------------------------------------------------------------------------------
1 | // @import './iconfont.css';
2 |
3 | .test {
4 | // 可以通过 @apply 多个样式封装整体样式
5 | @apply mt-4 ml-4;
6 |
7 | padding-top: 4px;
8 | color: red;
9 | }
10 |
11 | :root,
12 | page {
13 | // 修改按主题色
14 | // --wot-color-theme: #37c2bc;
15 |
16 | // 修改按钮背景色
17 | // --wot-button-primary-bg-color: green;
18 | }
19 |
--------------------------------------------------------------------------------
/src/typings.d.ts:
--------------------------------------------------------------------------------
1 | // 全局要用的类型放到这里
2 |
3 | declare global {
4 | type IResData = {
5 | code: number
6 | msg: string
7 | data: T
8 | }
9 |
10 | // uni.uploadFile文件上传参数
11 | type IUniUploadFileOptions = {
12 | file?: File
13 | files?: UniApp.UploadFileOptionFiles[]
14 | filePath?: string
15 | name?: string
16 | formData?: any
17 | }
18 |
19 | type IUserInfo = {
20 | nickname?: string
21 | avatar?: string
22 | /** 微信的 openid,非微信没有这个字段 */
23 | openid?: string
24 | token?: string
25 | }
26 | }
27 |
28 | export {} // 防止模块污染
29 |
--------------------------------------------------------------------------------
/src/typings.ts:
--------------------------------------------------------------------------------
1 | // 枚举定义
2 |
3 | export enum TestEnum {
4 | A = '1',
5 | B = '2',
6 | }
7 |
8 | // uni.uploadFile文件上传参数
9 | export type IUniUploadFileOptions = {
10 | file?: File
11 | files?: UniApp.UploadFileOptionFiles[]
12 | filePath?: string
13 | name?: string
14 | formData?: any
15 | }
16 |
--------------------------------------------------------------------------------
/src/uni.scss:
--------------------------------------------------------------------------------
1 | /* stylelint-disable comment-empty-line-before */
2 | /**
3 | * 这里是uni-app内置的常用样式变量
4 | *
5 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
6 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
7 | *
8 | */
9 |
10 | /**
11 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
12 | *
13 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
14 | */
15 |
16 | /* 颜色变量 */
17 |
18 | /* 行为相关颜色 */
19 | $uni-color-primary: #007aff;
20 | $uni-color-success: #4cd964;
21 | $uni-color-warning: #f0ad4e;
22 | $uni-color-error: #dd524d;
23 |
24 | /* 文字基本颜色 */
25 | $uni-text-color: #333; // 基本色
26 | $uni-text-color-inverse: #fff; // 反色
27 | $uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息
28 | $uni-text-color-placeholder: #808080;
29 | $uni-text-color-disable: #c0c0c0;
30 |
31 | /* 背景颜色 */
32 | $uni-bg-color: #fff;
33 | $uni-bg-color-grey: #f8f8f8;
34 | $uni-bg-color-hover: #f1f1f1; // 点击状态颜色
35 | $uni-bg-color-mask: rgb(0 0 0 / 40%); // 遮罩颜色
36 |
37 | /* 边框颜色 */
38 | $uni-border-color: #c8c7cc;
39 |
40 | /* 尺寸变量 */
41 |
42 | /* 文字尺寸 */
43 | $uni-font-size-sm: 12px;
44 | $uni-font-size-base: 14px;
45 | $uni-font-size-lg: 16;
46 |
47 | /* 图片尺寸 */
48 | $uni-img-size-sm: 20px;
49 | $uni-img-size-base: 26px;
50 | $uni-img-size-lg: 40px;
51 |
52 | /* Border Radius */
53 | $uni-border-radius-sm: 2px;
54 | $uni-border-radius-base: 3px;
55 | $uni-border-radius-lg: 6px;
56 | $uni-border-radius-circle: 50%;
57 |
58 | /* 水平间距 */
59 | $uni-spacing-row-sm: 5px;
60 | $uni-spacing-row-base: 10px;
61 | $uni-spacing-row-lg: 15px;
62 |
63 | /* 垂直间距 */
64 | $uni-spacing-col-sm: 4px;
65 | $uni-spacing-col-base: 8px;
66 | $uni-spacing-col-lg: 12px;
67 |
68 | /* 透明度 */
69 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度
70 |
71 | /* 文章场景相关 */
72 | $uni-color-title: #2c405a; // 文章标题颜色
73 | $uni-font-size-title: 20px;
74 | $uni-color-subtitle: #555; // 二级标题颜色
75 | $uni-font-size-subtitle: 18px;
76 | $uni-color-paragraph: #3f536e; // 文章段落颜色
77 | $uni-font-size-paragraph: 15px;
78 |
--------------------------------------------------------------------------------
/src/uni_modules/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/feige996/unibest/5156a60985b54e66f9ae675e7025a45ed6eabb7c/src/uni_modules/.gitkeep
--------------------------------------------------------------------------------
/src/utils/http.ts:
--------------------------------------------------------------------------------
1 | import { CustomRequestOptions } from '@/interceptors/request'
2 |
3 | export const http = (options: CustomRequestOptions) => {
4 | // 1. 返回 Promise 对象
5 | return new Promise>((resolve, reject) => {
6 | uni.request({
7 | ...options,
8 | dataType: 'json',
9 | // #ifndef MP-WEIXIN
10 | responseType: 'json',
11 | // #endif
12 | // 响应成功
13 | success(res) {
14 | // 状态码 2xx,参考 axios 的设计
15 | if (res.statusCode >= 200 && res.statusCode < 300) {
16 | // 2.1 提取核心数据 res.data
17 | resolve(res.data as IResData)
18 | } else if (res.statusCode === 401) {
19 | // 401错误 -> 清理用户信息,跳转到登录页
20 | // userStore.clearUserInfo()
21 | // uni.navigateTo({ url: '/pages/login/login' })
22 | reject(res)
23 | } else {
24 | // 其他错误 -> 根据后端错误信息轻提示
25 | !options.hideErrorToast &&
26 | uni.showToast({
27 | icon: 'none',
28 | title: (res.data as IResData).msg || '请求错误',
29 | })
30 | reject(res)
31 | }
32 | },
33 | // 响应失败
34 | fail(err) {
35 | uni.showToast({
36 | icon: 'none',
37 | title: '网络错误,换个网络试试',
38 | })
39 | reject(err)
40 | },
41 | })
42 | })
43 | }
44 |
45 | /**
46 | * GET 请求
47 | * @param url 后台地址
48 | * @param query 请求query参数
49 | * @param header 请求头,默认为json格式
50 | * @returns
51 | */
52 | export const httpGet = (
53 | url: string,
54 | query?: Record,
55 | header?: Record,
56 | ) => {
57 | return http({
58 | url,
59 | query,
60 | method: 'GET',
61 | header,
62 | })
63 | }
64 |
65 | /**
66 | * POST 请求
67 | * @param url 后台地址
68 | * @param data 请求body参数
69 | * @param query 请求query参数,post请求也支持query,很多微信接口都需要
70 | * @param header 请求头,默认为json格式
71 | * @returns
72 | */
73 | export const httpPost = (
74 | url: string,
75 | data?: Record,
76 | query?: Record,
77 | header?: Record,
78 | ) => {
79 | return http({
80 | url,
81 | query,
82 | data,
83 | method: 'POST',
84 | header,
85 | })
86 | }
87 | /**
88 | * PUT 请求
89 | */
90 | export const httpPut = (
91 | url: string,
92 | data?: Record,
93 | query?: Record,
94 | header?: Record,
95 | ) => {
96 | return http({
97 | url,
98 | data,
99 | query,
100 | method: 'PUT',
101 | header,
102 | })
103 | }
104 |
105 | /**
106 | * DELETE 请求(无请求体,仅 query)
107 | */
108 | export const httpDelete = (
109 | url: string,
110 | query?: Record,
111 | header?: Record,
112 | ) => {
113 | return http({
114 | url,
115 | query,
116 | method: 'DELETE',
117 | header,
118 | })
119 | }
120 |
121 | http.get = httpGet
122 | http.post = httpPost
123 | http.put = httpPut
124 | http.delete = httpDelete
125 |
--------------------------------------------------------------------------------
/src/utils/platform.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * @Author: 菲鸽
3 | * @Date: 2024-03-28 19:13:55
4 | * @Last Modified by: 菲鸽
5 | * @Last Modified time: 2024-03-28 19:24:55
6 | */
7 | export const platform = __UNI_PLATFORM__
8 | export const isH5 = __UNI_PLATFORM__ === 'h5'
9 | export const isApp = __UNI_PLATFORM__ === 'app'
10 | export const isMp = __UNI_PLATFORM__.startsWith('mp-')
11 | export const isMpWeixin = __UNI_PLATFORM__.startsWith('mp-weixin')
12 | export const isMpAplipay = __UNI_PLATFORM__.startsWith('mp-alipay')
13 | export const isMpToutiao = __UNI_PLATFORM__.startsWith('mp-toutiao')
14 |
15 | const PLATFORM = {
16 | platform,
17 | isH5,
18 | isApp,
19 | isMp,
20 | isMpWeixin,
21 | isMpAplipay,
22 | isMpToutiao,
23 | }
24 | export default PLATFORM
25 |
--------------------------------------------------------------------------------
/src/utils/request.ts:
--------------------------------------------------------------------------------
1 | import { CustomRequestOptions } from '@/interceptors/request'
2 |
3 | /**
4 | * 请求方法: 主要是对 uni.request 的封装,去适配 openapi-ts-request 的 request 方法
5 | * @param options 请求参数
6 | * @returns 返回 Promise 对象
7 | */
8 | const http = (options: CustomRequestOptions) => {
9 | // 1. 返回 Promise 对象
10 | return new Promise((resolve, reject) => {
11 | uni.request({
12 | ...options,
13 | dataType: 'json',
14 | // #ifndef MP-WEIXIN
15 | responseType: 'json',
16 | // #endif
17 | // 响应成功
18 | success(res) {
19 | // 状态码 2xx,参考 axios 的设计
20 | if (res.statusCode >= 200 && res.statusCode < 300) {
21 | // 2.1 提取核心数据 res.data
22 | resolve(res.data as T)
23 | } else if (res.statusCode === 401) {
24 | // 401错误 -> 清理用户信息,跳转到登录页
25 | // userStore.clearUserInfo()
26 | // uni.navigateTo({ url: '/pages/login/login' })
27 | reject(res)
28 | } else {
29 | // 其他错误 -> 根据后端错误信息轻提示
30 | !options.hideErrorToast &&
31 | uni.showToast({
32 | icon: 'none',
33 | title: (res.data as T & { msg?: string })?.msg || '请求错误',
34 | })
35 | reject(res)
36 | }
37 | },
38 | // 响应失败
39 | fail(err) {
40 | uni.showToast({
41 | icon: 'none',
42 | title: '网络错误,换个网络试试',
43 | })
44 | reject(err)
45 | },
46 | })
47 | })
48 | }
49 |
50 | /*
51 | * openapi-ts-request 工具的 request 跨客户端适配方法
52 | */
53 | export default function request(
54 | url: string,
55 | options: Omit & {
56 | params?: Record
57 | headers?: Record
58 | },
59 | ) {
60 | const requestOptions = {
61 | url,
62 | ...options,
63 | }
64 |
65 | if (options.params) {
66 | requestOptions.query = requestOptions.params
67 | delete requestOptions.params
68 | }
69 |
70 | if (options.headers) {
71 | requestOptions.header = options.headers
72 | delete requestOptions.headers
73 | }
74 |
75 | return http(requestOptions)
76 | }
77 |
--------------------------------------------------------------------------------
/src/utils/toast.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * toast 弹窗组件
3 | * 支持 success/error/warning/info 四种状态
4 | * 可配置 duration, position 等参数
5 | */
6 |
7 | type ToastType = 'success' | 'error' | 'warning' | 'info'
8 |
9 | interface ToastOptions {
10 | type?: ToastType
11 | duration?: number
12 | position?: 'top' | 'middle' | 'bottom'
13 | icon?: 'success' | 'error' | 'none' | 'loading' | 'fail' | 'exception'
14 | message: string
15 | }
16 |
17 | export function showToast(options: ToastOptions | string) {
18 | const defaultOptions: ToastOptions = {
19 | type: 'info',
20 | duration: 2000,
21 | position: 'middle',
22 | message: '',
23 | }
24 | const mergedOptions =
25 | typeof options === 'string'
26 | ? { ...defaultOptions, message: options }
27 | : { ...defaultOptions, ...options }
28 | // 映射position到uniapp支持的格式
29 | const positionMap: Record = {
30 | top: 'top',
31 | middle: 'center',
32 | bottom: 'bottom',
33 | }
34 |
35 | // 映射图标类型
36 | const iconMap: Record<
37 | ToastType,
38 | 'success' | 'error' | 'none' | 'loading' | 'fail' | 'exception'
39 | > = {
40 | success: 'success',
41 | error: 'error',
42 | warning: 'fail',
43 | info: 'none',
44 | }
45 |
46 | // 调用uni.showToast显示提示
47 | uni.showToast({
48 | title: mergedOptions.message,
49 | duration: mergedOptions.duration,
50 | position: positionMap[mergedOptions.position],
51 | icon: mergedOptions.icon || iconMap[mergedOptions.type],
52 | mask: true,
53 | })
54 | }
55 |
56 | export const toast = {
57 | success: (message: string, options?: Omit) =>
58 | showToast({ ...options, type: 'success', message }),
59 | error: (message: string, options?: Omit) =>
60 | showToast({ ...options, type: 'error', message }),
61 | warning: (message: string, options?: Omit) =>
62 | showToast({ ...options, type: 'warning', message }),
63 | info: (message: string, options?: Omit) =>
64 | showToast({ ...options, type: 'info', message }),
65 | }
66 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "composite": true,
4 | "skipLibCheck": true,
5 | "module": "ESNext",
6 | "moduleResolution": "Node",
7 | "resolveJsonModule": true,
8 | "noImplicitThis": true,
9 | "allowSyntheticDefaultImports": true,
10 | "allowJs": true,
11 | "sourceMap": true,
12 | "baseUrl": ".",
13 | "paths": {
14 | "@/*": ["./src/*"]
15 | },
16 | "outDir": "dist",
17 | "lib": ["esnext", "dom"],
18 | "types": [
19 | "@dcloudio/types",
20 | "@uni-helper/uni-types",
21 | "wot-design-uni/global.d.ts",
22 | "z-paging/types",
23 | "./src/typings.d.ts"
24 | ]
25 | },
26 | "vueCompilerOptions": {
27 | "plugins": ["@uni-helper/uni-types/volar-plugin"]
28 | },
29 | "exclude": ["node_modules"],
30 | "include": [
31 | "src/**/*.ts",
32 | "src/**/*.js",
33 | "src/**/*.d.ts",
34 | "src/**/*.tsx",
35 | "src/**/*.jsx",
36 | "src/**/*.vue",
37 | "src/**/*.json"
38 | ]
39 | }
40 |
--------------------------------------------------------------------------------
/uno.config.ts:
--------------------------------------------------------------------------------
1 | import { presetUni } from '@uni-helper/unocss-preset-uni'
2 | import {
3 | defineConfig,
4 | presetIcons,
5 | presetAttributify,
6 | transformerDirectives,
7 | transformerVariantGroup,
8 | } from 'unocss'
9 |
10 | export default defineConfig({
11 | presets: [
12 | presetUni(),
13 | presetIcons({
14 | scale: 1.2,
15 | warn: true,
16 | extraProperties: {
17 | display: 'inline-block',
18 | 'vertical-align': 'middle',
19 | },
20 | }),
21 | // 支持css class属性化
22 | presetAttributify(),
23 | ],
24 | transformers: [
25 | // 启用指令功能:主要用于支持 @apply、@screen 和 theme() 等 CSS 指令
26 | transformerDirectives(),
27 | // 启用 () 分组功能
28 | // 支持css class组合,eg: `测试 unocss
`
29 | transformerVariantGroup(),
30 | ],
31 | shortcuts: [
32 | {
33 | center: 'flex justify-center items-center',
34 | },
35 | ],
36 | rules: [
37 | [
38 | 'p-safe',
39 | {
40 | padding:
41 | 'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',
42 | },
43 | ],
44 | ['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
45 | ['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
46 | ],
47 | theme: {
48 | colors: {
49 | /** 主题色,用法如: text-primary */
50 | primary: 'var(--wot-color-theme,#0957DE)',
51 | },
52 | fontSize: {
53 | /** 提供更小号的字体,用法如:text-2xs */
54 | '2xs': ['20rpx', '28rpx'],
55 | '3xs': ['18rpx', '26rpx'],
56 | },
57 | },
58 | })
59 |
--------------------------------------------------------------------------------
/vite-plugins/copyNativeRes.ts:
--------------------------------------------------------------------------------
1 | import fs from 'fs-extra'
2 | import path from 'path'
3 |
4 | export function copyNativeRes() {
5 | const waitPath = path.resolve(__dirname, '../src/nativeResources')
6 | const buildPath = path.resolve(
7 | __dirname,
8 | '../dist',
9 | process.env.NODE_ENV === 'production' ? 'build' : 'dev',
10 | process.env.UNI_PLATFORM!,
11 | 'nativeResources',
12 | )
13 |
14 | return {
15 | enforce: 'post',
16 | async writeBundle() {
17 | try {
18 | // 检查源目录是否存在
19 | const sourceExists = await fs.pathExists(waitPath)
20 | if (!sourceExists) {
21 | console.warn(`[copyNativeRes] 警告:源目录 "${waitPath}" 不存在,跳过复制操作。`)
22 | return
23 | }
24 |
25 | // 确保目标目录及中间目录存在
26 | await fs.ensureDir(buildPath)
27 | console.log(`[copyNativeRes] 确保目标目录存在:${buildPath}`)
28 |
29 | // 执行文件夹复制
30 | await fs.copy(waitPath, buildPath)
31 | console.log(
32 | `[copyNativeRes] 成功将 nativeResources 目录中的资源移动到构建目录:${buildPath}`,
33 | )
34 | } catch (error) {
35 | console.error(`[copyNativeRes] 复制资源失败:`, error)
36 | }
37 | },
38 | }
39 | }
40 |
--------------------------------------------------------------------------------