├── .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 | "\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": ["\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 |

8 | unibest - 最好的 uniapp 开发框架 9 |

10 | 11 |
12 | 旧仓库 codercup 进不去了,star 也拿不回来,这里也展示一下那个地址的 star. 13 | 14 | [![GitHub Repo stars](https://img.shields.io/github/stars/codercup/unibest?style=flat&logo=github)](https://github.com/codercup/unibest) 15 | [![GitHub forks](https://img.shields.io/github/forks/codercup/unibest?style=flat&logo=github)](https://github.com/codercup/unibest) 16 | 17 |
18 | 19 |
20 | 21 | [![GitHub Repo stars](https://img.shields.io/github/stars/feige996/unibest?style=flat&logo=github)](https://github.com/feige996/unibest) 22 | [![GitHub forks](https://img.shields.io/github/forks/feige996/unibest?style=flat&logo=github)](https://github.com/feige996/unibest) 23 | [![star](https://gitee.com/feige996/unibest/badge/star.svg?theme=dark)](https://gitee.com/feige996/unibest/stargazers) 24 | [![fork](https://gitee.com/feige996/unibest/badge/fork.svg?theme=dark)](https://gitee.com/feige996/unibest/members) 25 | ![node version](https://img.shields.io/badge/node-%3E%3D18-green) 26 | ![pnpm version](https://img.shields.io/badge/pnpm-%3E%3D7.30-green) 27 | ![GitHub package.json version (subfolder of monorepo)](https://img.shields.io/github/package-json/v/feige996/unibest) 28 | ![GitHub License](https://img.shields.io/github/license/feige996/unibest) 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 | ![](https://raw.githubusercontent.com/andreasbm/readme/master/screenshots/lines/rainbow.png) 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 | special sponsor appwrite 106 | special sponsor appwrite 107 |

108 | -------------------------------------------------------------------------------- /docs/.vitepress/components/BuildInfo.vue: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /docs/.vitepress/layouts/Default.vue: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 19 | 20 | 27 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/components/FreshImage.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 22 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/components/HomeStar.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 51 | -------------------------------------------------------------------------------- /docs/.vitepress/theme/components/NavBarTitleAfter.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 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 | ![微信公众号](./screenshots/wx-gzh.png) 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 | special sponsor appwrite 7 | special sponsor appwrite 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 | ![alt text](https://oss.laf.run/ukw0y1-site/unibest-discussion-group/qq.jpg) 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 | ![alt text](./assets/10-1.png) 42 | 43 | `非H5端` 效果如下,异常显示: 44 | 45 | ![alt text](./assets/10-2.png) 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 | ![alt text](./assets/10-3.png) 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 | - ![alt text](./assets/11-1.png) 8 | 9 | - `APP 平台`:`pnpm dev:app`,然后打开 `HBuilderX`,导入刚刚生成的 `dist/dev/app` 文件夹,选择运行到 `模拟器`( `开发时优先使用` ),或者 `运行到安卓/ios 基座` (真机调试时使用) 。 10 | 11 | ![alt text](./assets/11-2.png) 12 | 13 | ![alt text](./assets/11-3.png) 14 | ![alt text](./assets/11-4.png) 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 | ![alt text](./assets/11-13.png) 27 | ![alt text](./assets/11-5.png) 28 | ![alt text](./assets/11-6.png) 29 | 30 | > 熟悉原生 APP 开发的开发者也可以使用 `原生APP-本地打包`。 31 | 32 | ## APP 打包注意事项(上) 33 | 34 | 很多开发者发现打包失败,或者打包白屏,这里简单说明一下。 35 | 36 | - 1. 重新获取自己的 `AppId` 37 | 38 | ![alt text](./assets/11-7.png) 39 | 40 | - 2. 根据上面获取到的 `AppId` 修改 `env/.env` 文件的 `VITE_UNI_APPID` 字段 41 | 42 | ![alt text](./assets/11-8.png) 43 | 44 | - 3. (可选)云打包如果有出现解析时出问题的,把 `minSdkVersion` 版本改低一点就好了,比如 `21`。(最低 `21`,不能低于 `21`;我模板里面设置的是 `30`)。 45 | 46 | ![alt text](./assets/11-9.png) 47 | 48 | ## APP 打包注意事项 (下) 49 | 50 | ### `uni-app SDK` 版本 51 | 52 | > 特别备注:`2024-05-03`,新的 `base` 模板的 `uni-app SDK` 版本已经升级到 `4.14` 了。 53 | > 54 | > ![alt text](./assets/11-100.png) 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 | ![alt text](./assets/11-10.png) 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 | ![alt text](./assets/11-11.png) 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 | ![alt text](./assets/11-12.png) 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 | ![alt text](./assets/13-1.png) 29 | 30 | ![alt text](./assets/13-2.png) 31 | 32 | 需要执行如下 2 步: 33 | 34 | - 项目下执行 `pnpm i` 35 | - 右键项目,选择 `重新识别项目类型` 36 | 37 | ![alt text](./assets/13-3.png) 38 | 39 | ## 运行效果 40 | 41 | 经过上面的操作后,就可以正常运行了。 42 | 43 | - ios 模拟器运行效果如下: 44 | 45 | ![alt text](./assets/13-4.png) 46 | 47 | ![alt text](./assets/13-5.png) 48 | 49 | ![alt text](./assets/13-6.png) 50 | 51 | - 微信小程序运行效果如下: 52 | 53 | ![alt text](./assets/13-7.png) 54 | 55 | > 目前微信小程序静态资源还有点问题,如下图 `logo 不见了`,后续会修复。 56 | 57 | ![alt text](./assets/13-8.png) 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 | ![alt text](./assets/14-1.png) 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 | ![alt text](./assets/14-2.png) 92 | 93 | - 只需要勾上 `本地开发跳过 ES5 转译` 即可正常运行,如下图 94 | ![alt text](./assets/14-3.png) 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 | ![alt text](./assets/14-4.png) 113 | 114 | 真实运行报错截图如下:(分别是 `小程序` 和 `APP`, 都会报错 ) 115 | 116 | ![alt text](./assets/14-5.png) 117 | 118 | ![alt text](./assets/14-6.png) 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 | ![alt text](image-18.png) 28 | 29 | 点击左侧下面的 `源码视图` 就可以看到增加了如下内容: 30 | ![alt text](image-18-2.png) 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 | ![create project](./assets/2-1.png) 33 | 34 | `create-unibest` 在 `v1.10.0` 开始会有版本号,如下: 35 | 36 | ![alt text](./assets/2-2.png) 37 | 38 | ![unibest templates](https://oss.laf.run/ukw0y1-site/xmind/unibest模板.png) 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 | ![alt text](./assets/2-4.gif) 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 | ![unibest templates](https://oss.laf.run/ukw0y1-site/xmind/unibest模板.png) 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 | 35 | 36 | 39 | ``` 40 | 41 | ### 3. `线上地址` 图标 42 | 43 | 这种方式直接使用,代码编写如下: 44 | 45 | ```html 46 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | 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 | ![alt text](image.png) 36 | ![alt text](image-1.png) 37 | ![alt text](image-2.png) 38 | ![alt text](image-3.png) 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 | ![alt text](image-1.png) 7 | 8 | **配置后效果:** 9 | ![alt text](image-2.png) 10 | 11 | **相关代码:** 12 | ![alt text](image-3.png) 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 | ![alt text](./assets/5-9.png) 9 | 10 | ![alt text](./assets/5-10.png) 11 | 12 | ![alt text](./assets/5-11.png) 13 | 14 | > 初次接触 `iconfont` 的同学,可能会找不到自己的项目,如下图:资源管理 -- 我的项目 15 | 16 | ![alt text](./assets/5-12.png) 17 | 18 | - 3.图标方式选择,如下图有 `Unicode` `Font class` `Symbol` 三种方式,分别预览和使用如下: 19 | 20 | ![alt text](./assets/5-13.png) 21 | 22 | ![alt text](./assets/5-14.png) 23 | 24 | ![alt text](./assets/5-15.png) 25 | 26 | - `Unicode` 的方式太落后,语义化不明显,不推荐; 27 | - `Symbol` 的方式太先进(背后原理是生成了 `SVG` 雪碧图),先进到 `小程序` 和 `APP` 都不支持,只能无奈放弃。 28 | 29 | > `Symbol` 的方式生成 `svg` 雪碧图,如下所示: 30 | > 31 | > ![alt text](./assets/5-16.png) 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 | ![alt text](./assets/5-17.png) 38 | 39 | 我们使用的是 `Font class` 的方式,只需要这一个 `css` 链接就行,无需 `下载至本地`,想要本地预览的话才需要 `下载至本地`。 40 | 41 | > `iconfont` 有默认的前缀 `icon-`,可以设置为其他的,如我的一个项目设置为 `bap-icon-`,以防跟其他的冲突。 42 | 43 | ![alt text](./assets/5-18.png) 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 | ![alt text](./assets/5-23.png) 84 | 85 | - 6. 预览,`h5 `端正常,APP 端不正常,小程序端看着正常,控制台也会报错,如下图: 86 | 87 | ![alt text](./assets/5-22.png) 88 | 89 | - 7. 这个怎么处理呢?转成 `base64` 是最快捷的,`iconfont` 本身就支持, `3`步搞定: 90 | 91 | - 7.1 如下图,勾选 `Base64` 92 | 93 | ![alt text](./assets/5-21.png) 94 | 95 | - 7.2 生成新链接,并得到新的 `css` 代码 96 | 97 | ![alt text](./assets/5-20.png) 98 | 99 | - 7.3 引入新代码,刷新界面,小程序不报错了,APP 也正常了! 100 | 101 | ![alt text](./assets/5-19.png) 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 | ![alt text](./assets/image-1.png) 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 | ![alt text](./assets/image-2.png) 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 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 27 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /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 | 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 | 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 | 9 | 10 | 19 | -------------------------------------------------------------------------------- /src/layouts/demo.vue: -------------------------------------------------------------------------------- 1 | 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 | 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 | 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 | 37 | 38 | 68 | -------------------------------------------------------------------------------- /src/pages/about/components/upload.vue: -------------------------------------------------------------------------------- 1 | 2 | { 3 | layout: 'default', 4 | style: { 5 | navigationBarTitleText: '上传-状态一体化', 6 | }, 7 | } 8 | 9 | 10 | 23 | 24 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /src/pages/index/index.vue: -------------------------------------------------------------------------------- 1 | 2 | 3 | { 4 | style: { 5 | navigationStyle: 'custom', 6 | navigationBarTitleText: '首页', 7 | }, 8 | } 9 | 10 | 32 | 33 | 51 | 52 | 57 | -------------------------------------------------------------------------------- /src/pages/mine/about/index.vue: -------------------------------------------------------------------------------- 1 | 2 | { 3 | style: { 4 | navigationBarTitleText: '关于我们', 5 | }, 6 | } 7 | 8 | 9 | 48 | 49 | 59 | 60 | 174 | -------------------------------------------------------------------------------- /src/pages/mine/info/index.vue: -------------------------------------------------------------------------------- 1 | 2 | { 3 | style: { 4 | navigationBarTitleText: '个人资料', 5 | }, 6 | } 7 | 8 | 9 | 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 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 27 | 29 | 30 | 31 | 32 | 33 | 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 | --------------------------------------------------------------------------------