first commit

This commit is contained in:
Your Name
2024-12-26 16:41:17 +08:00
commit f8abb65023
101 changed files with 15618 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
const fs = require('fs')
const path = require('path')
const { execSync } = require('child_process')
const scopes = fs
.readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
.filter((dirent) => dirent.isDirectory())
.map((dirent) => dirent.name.replace(/s$/, ''))
// precomputed scope
const scopeComplete = execSync('git status --porcelain || true')
.toString()
.trim()
.split('\n')
.find((r) => ~r.indexOf('M src'))
?.replace(/(\/)/g, '%%')
?.match(/src%%((\w|-)*)/)?.[1]
?.replace(/s$/, '')
module.exports = {
ignores: [(commit) => commit.includes('init')],
extends: ['@commitlint/config-conventional'],
rules: {
'body-leading-blank': [2, 'always'],
'footer-leading-blank': [1, 'always'],
'header-max-length': [2, 'always', 108],
'subject-empty': [2, 'never'],
'type-empty': [2, 'never'],
'subject-case': [0],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'wip',
'workflow',
'types',
'release',
],
],
},
prompt: {
/** @use `pnpm commit :f` */
alias: {
f: 'docs: fix typos',
r: 'docs: update README',
s: 'style: update code format',
b: 'build: bump dependencies',
c: 'chore: update config',
},
customScopesAlign: !scopeComplete ? 'top' : 'bottom',
defaultScope: scopeComplete,
scopes: [...scopes, 'mock'],
allowEmptyIssuePrefixs: false,
allowCustomIssuePrefixs: false,
// English
typesAppend: [
{ value: 'wip', name: 'wip: work in process' },
{ value: 'workflow', name: 'workflow: workflow improvements' },
{ value: 'types', name: 'types: type definition file changes' },
],
// 中英文对照版
// messages: {
// type: '选择你要提交的类型 :',
// scope: '选择一个提交范围 (可选):',
// customScope: '请输入自定义的提交范围 :',
// subject: '填写简短精炼的变更描述 :\n',
// body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
// breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
// footerPrefixsSelect: '选择关联issue前缀 (可选):',
// customFooterPrefixs: '输入自定义issue前缀 :',
// footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
// confirmCommit: '是否提交或修改commit ?',
// },
// types: [
// { value: 'feat', name: 'feat: 新增功能' },
// { value: 'fix', name: 'fix: 修复缺陷' },
// { value: 'docs', name: 'docs: 文档变更' },
// { value: 'style', name: 'style: 代码格式' },
// { value: 'refactor', name: 'refactor: 代码重构' },
// { value: 'perf', name: 'perf: 性能优化' },
// { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
// { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
// { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
// { value: 'revert', name: 'revert: 回滚 commit' },
// { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
// { value: 'wip', name: 'wip: 正在开发中' },
// { value: 'workflow', name: 'workflow: 工作流程改进' },
// { value: 'types', name: 'types: 类型定义文件修改' },
// ],
// emptyScopesAlias: 'empty: 不填写',
// customScopesAlias: 'custom: 自定义',
},
}
+13
View File
@@ -0,0 +1,13 @@
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off # 关闭最大行长度限制
trim_trailing_whitespace = false # 关闭末尾空格修剪
+1
View File
@@ -0,0 +1 @@
src/uni_modules/
+101
View File
@@ -0,0 +1,101 @@
{
"globals": {
"Component": true,
"ComponentPublicInstance": true,
"ComputedRef": true,
"EffectScope": true,
"ExtractDefaultPropTypes": true,
"ExtractPropTypes": true,
"ExtractPublicPropTypes": true,
"InjectionKey": true,
"PropType": true,
"Ref": true,
"VNode": true,
"WritableComputedRef": true,
"computed": true,
"createApp": true,
"customRef": true,
"defineAsyncComponent": true,
"defineComponent": true,
"effectScope": true,
"getCurrentInstance": true,
"getCurrentScope": true,
"h": true,
"inject": true,
"isProxy": true,
"isReactive": true,
"isReadonly": true,
"isRef": true,
"markRaw": true,
"nextTick": true,
"onActivated": true,
"onAddToFavorites": true,
"onBackPress": true,
"onBeforeMount": true,
"onBeforeUnmount": true,
"onBeforeUpdate": true,
"onDeactivated": true,
"onError": true,
"onErrorCaptured": true,
"onHide": true,
"onLaunch": true,
"onLoad": true,
"onMounted": true,
"onNavigationBarButtonTap": true,
"onNavigationBarSearchInputChanged": true,
"onNavigationBarSearchInputClicked": true,
"onNavigationBarSearchInputConfirmed": true,
"onNavigationBarSearchInputFocusChanged": true,
"onPageNotFound": true,
"onPageScroll": true,
"onPullDownRefresh": true,
"onReachBottom": true,
"onReady": true,
"onRenderTracked": true,
"onRenderTriggered": true,
"onResize": true,
"onScopeDispose": true,
"onServerPrefetch": true,
"onShareAppMessage": true,
"onShareTimeline": true,
"onShow": true,
"onTabItemTap": true,
"onThemeChange": true,
"onUnhandledRejection": true,
"onUnload": true,
"onUnmounted": true,
"onUpdated": true,
"provide": true,
"reactive": true,
"readonly": true,
"ref": true,
"resolveComponent": true,
"shallowReactive": true,
"shallowReadonly": true,
"shallowRef": true,
"toRaw": true,
"toRef": true,
"toRefs": true,
"toValue": true,
"triggerRef": true,
"unref": true,
"useAttrs": true,
"useCssModule": true,
"useCssVars": true,
"useRequest": true,
"useSlots": true,
"useUpload": true,
"useUpload2": true,
"watch": true,
"watchEffect": true,
"watchPostEffect": true,
"watchSyncEffect": true,
"DirectiveBinding": true,
"MaybeRef": true,
"MaybeRefOrGetter": true,
"onWatcherCleanup": true,
"useId": true,
"useModel": true,
"useTemplateRef": true
}
}
+97
View File
@@ -0,0 +1,97 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-essential',
// eslint-plugin-import 插件, @see https://www.npmjs.com/package/eslint-plugin-import
'plugin:import/recommended',
// eslint-config-airbnb-base 插件 已经改用 eslint-config-standard 插件
'standard',
// 1. 接入 prettier 的规则
'prettier',
'plugin:prettier/recommended',
'./.eslintrc-auto-import.json',
],
overrides: [
{
env: {
node: true,
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
],
parserOptions: {
ecmaVersion: 'latest',
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
plugins: [
'@typescript-eslint',
'vue',
// 2. 加入 prettier 的 eslint 插件
'prettier',
// eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
'import',
],
rules: {
// 3. 注意要加上这一句,开启 prettier 自动修复的功能
'prettier/prettier': 'error',
// turn on errors for missing imports
'import/no-unresolved': 'off',
// 对后缀的检测,否则 import 一个ts文件也会报错,需要手动添加'.ts', 增加了下面的配置后就不用了
'import/extensions': [
'error',
'ignorePackages',
{ js: 'never', jsx: 'never', ts: 'never', tsx: 'never' },
],
// 只允许1个默认导出,关闭,否则不能随意export xxx
'import/prefer-default-export': ['off'],
'no-console': ['off'],
// 'no-unused-vars': ['off'],
// '@typescript-eslint/no-unused-vars': ['off'],
// 解决vite.config.ts报错问题
'import/no-extraneous-dependencies': 'off',
'no-plusplus': 'off',
'no-shadow': 'off',
'vue/multi-word-component-names': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'no-underscore-dangle': 'off',
'no-use-before-define': 'off',
'no-undef': 'off',
'no-unused-vars': 'off',
'no-param-reassign': 'off',
'@typescript-eslint/no-unused-vars': 'off',
// 避免 `eslint` 对于 `typescript` 函数重载的误报
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'error',
},
// eslint-import-resolver-typescript 插件,@see https://www.npmjs.com/package/eslint-import-resolver-typescript
settings: {
'import/parsers': {
'@typescript-eslint/parser': ['.ts', '.tsx'],
},
'import/resolver': {
typescript: {},
},
},
globals: {
$t: true,
uni: true,
UniApp: true,
wx: true,
WechatMiniprogram: true,
getCurrentPages: true,
UniHelper: true,
Page: true,
App: true,
NodeJS: true,
},
}
+42
View File
@@ -0,0 +1,42 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
.DS_Store
dist
*.local
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
.hbuilderx
.stylelintcache
.eslintcache
docs/.vitepress/dist
docs/.vitepress/cache
# lock 文件还是不要了,我主要的版本写死就好了
# pnpm-lock.yaml
# package-lock.json
# TIPS:如果某些文件已经加入了版本管理,现在重新加入 .gitignore 是不生效的,需要执行下面的操作
# `git rm -r --cached .` 然后提交 commit 即可。
# git rm -r --cached file1 file2 ## 针对某些文件
# git rm -r --cached dir1 dir2 ## 针对某些文件夹
# git rm -r --cached . ## 针对所有文件
# 更新 uni-app 官方版本
# npx @dcloudio/uvm@latest
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# Run the commit-msg hook
npx --no-install commitlint --edit
+5
View File
@@ -0,0 +1,5 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
# Run the pre-commit hook
npx --no-install -- lint-staged
+6
View File
@@ -0,0 +1,6 @@
# registry = https://registry.npmjs.org
registry = https://registry.npmmirror.com
strict-peer-dependencies=false
auto-install-peers=true
shamefully-hoist=true
+9
View File
@@ -0,0 +1,9 @@
# unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
auto-import.d.ts
# vite-plugin-uni-pages 生成的类型文件,每次切换分支都一堆不同的,所以直接 .gitignore
uni-pages.d.ts
# 插件生成的文件
src/pages.json
src/manifest.json
+19
View File
@@ -0,0 +1,19 @@
// @see https://prettier.io/docs/en/options
module.exports = {
singleQuote: true,
printWidth: 100,
tabWidth: 2,
useTabs: false,
semi: false,
trailingComma: 'all',
endOfLine: 'auto',
htmlWhitespaceSensitivity: 'ignore',
overrides: [
{
files: '*.json',
options: {
trailingComma: 'none',
},
},
],
}
+1
View File
@@ -0,0 +1 @@
src/uni_modules/
+52
View File
@@ -0,0 +1,52 @@
// .stylelintrc.cjs
module.exports = {
root: true,
extends: [
// stylelint-config-standard 替换成了更宽松的 stylelint-config-recommended
'stylelint-config-recommended',
// stylelint-config-standard-scss 替换成了更宽松的 stylelint-config-recommended-scss
'stylelint-config-recommended-scss',
'stylelint-config-recommended-vue/scss',
'stylelint-config-html/vue',
'stylelint-config-recess-order',
],
plugins: ['stylelint-prettier'],
overrides: [
// 扫描 .vue/html 文件中的<style>标签内的样式
{
files: ['**/*.{vue,html}'],
customSyntax: 'postcss-html',
},
{
files: ['**/*.{css,scss}'],
customSyntax: 'postcss-scss',
},
],
// 自定义规则
rules: {
'prettier/prettier': true,
// 允许 global 、export 、v-deep等伪类
'selector-pseudo-class-no-unknown': [
true,
{
ignorePseudoClasses: ['global', 'export', 'v-deep', 'deep'],
},
],
// 处理小程序page标签不认识的问题
'selector-type-no-unknown': [
true,
{
ignoreTypes: ['page'],
},
],
'comment-empty-line-before': 'never', // never|always|always-multi-line|never-multi-line
'custom-property-empty-line-before': 'never',
'no-empty-source': null,
'comment-no-empty': null,
'no-duplicate-selectors': null,
'scss/comment-no-empty': null,
'selector-class-pattern': null,
'font-family-no-missing-generic-family-keyword': null,
},
}
+18
View File
@@ -0,0 +1,18 @@
{
"recommendations": [
"vue.volar",
"stylelint.vscode-stylelint",
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"antfu.unocss",
"antfu.iconify",
"evils.uniapp-vscode",
"uni-helper.uni-helper-vscode",
"uni-helper.uni-app-schemas-vscode",
"uni-helper.uni-highlight-vscode",
"uni-helper.uni-ui-snippets-vscode",
"uni-helper.uni-app-snippets-vscode",
"mrmlnc.vscode-json5",
"streetsidesoftware.code-spell-checker"
]
}
+64
View File
@@ -0,0 +1,64 @@
{
// 默认格式化工具选择prettier
"editor.defaultFormatter": "esbenp.prettier-vscode",
// 保存的时候自动格式化
"editor.formatOnSave": true,
//开启自动修复
"editor.codeActionsOnSave": {
"source.fixAll": "explicit",
"source.fixAll.eslint": "explicit",
"source.fixAll.stylelint": "explicit"
},
// 配置stylelint检查的文件类型范围
"stylelint.validate": ["css", "scss", "vue", "html"], // 与package.json的scripts对应
"stylelint.enable": true,
"css.validate": false,
"less.validate": false,
"scss.validate": false,
"[shellscript]": {
"editor.defaultFormatter": "foxundermoon.shell-format"
},
"[dotenv]": {
"editor.defaultFormatter": "foxundermoon.shell-format"
},
"[vue]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// 配置语言的文件关联
"files.associations": {
"pages.json": "jsonc", // pages.json 可以写注释
"manifest.json": "jsonc" // manifest.json 可以写注释
},
"cSpell.words": [
"climblee",
"commitlint",
"dcloudio",
"iconfont",
"qrcode",
"refresherrefresh",
"scrolltolower",
"tabbar",
"unibest",
"uvui",
"WechatMiniprogram"
],
"typescript.tsdk": "node_modules\\typescript\\lib",
// 控制相关文件嵌套展示
"explorer.fileNesting.enabled": true,
"explorer.fileNesting.expand": false,
"explorer.fileNesting.patterns": {
"*.ts": "$(capture).test.ts, $(capture).test.tsx",
"*.tsx": "$(capture).test.ts, $(capture).test.tsx",
// "*.env": "$(capture).env.*",
"CHANGELOG.md": "CHANGELOG*",
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,.npmrc,.browserslistrc",
".eslintrc.cjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,.stylelintrc.*,.eslintrc-auto-import.json,.editorconfig,.commitlint.cjs"
},
"i18n-ally.localesPaths": ["src/locale"]
}
+56
View File
@@ -0,0 +1,56 @@
{
// Place your unibest 工作区 snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and
// description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope
// is left empty or omitted, the snippet gets applied to all languages. The prefix is what is
// used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
// $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders.
// Placeholders with the same ids are connected.
// Example:
// "Print to console": {
// "scope": "javascript,typescript",
// "prefix": "log",
// "body": [
// "console.log('$1');",
// "$2"
// ],
// "description": "Log output to console"
// }
"Print unibest Vue3 SFC": {
"scope": "vue",
"prefix": "v3",
"body": [
"<route lang=\"json5\" type=\"page\">",
"{",
" layout: 'default',",
" style: {",
" navigationBarTitleText: '$1',",
" },",
"}",
"</route>\n",
"<template>",
" <view class=\"\">$2</view>",
"</template>\n",
"<script lang=\"ts\" setup>",
"//$3",
"</script>\n",
"<style lang=\"scss\" scoped>",
"//$4",
"</style>\n",
],
},
"Print unibest style": {
"scope": "vue",
"prefix": "st",
"body": ["<style lang=\"scss\" scoped>", "//", "</style>\n"],
},
"Print unibest script": {
"scope": "vue",
"prefix": "sc",
"body": ["<script lang=\"ts\" setup>", "//$3", "</script>\n"],
},
"Print unibest template": {
"scope": "vue",
"prefix": "te",
"body": ["<template>", " <view class=\"\">$1</view>", "</template>\n"],
},
}
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 菲鸽
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+86
View File
@@ -0,0 +1,86 @@
<p align="center">
<a href="https://github.com/feige996/unibest">
<img width="160" src="./src/static/logo.svg">
</a>
</p>
<h1 align="center">
<a href="https://github.com/feige996/unibest" target="_blank">unibest - 最好的 uniapp 开发框架</a>
</h1>
<div align="center">
旧仓库 codercup 进不去了,star 也拿不回来,这里也展示一下那个地址的 star.
[![GitHub Repo stars](https://img.shields.io/github/stars/codercup/unibest?style=flat&logo=github)](https://github.com/codercup/unibest)
[![GitHub forks](https://img.shields.io/github/forks/codercup/unibest?style=flat&logo=github)](https://github.com/codercup/unibest)
</div>
<div align="center">
[![GitHub Repo stars](https://img.shields.io/github/stars/feige996/unibest?style=flat&logo=github)](https://github.com/feige996/unibest)
[![GitHub forks](https://img.shields.io/github/forks/feige996/unibest?style=flat&logo=github)](https://github.com/feige996/unibest)
[![star](https://gitee.com/feige996/unibest/badge/star.svg?theme=dark)](https://gitee.com/feige996/unibest/stargazers)
[![fork](https://gitee.com/feige996/unibest/badge/fork.svg?theme=dark)](https://gitee.com/feige996/unibest/members)
![node version](https://img.shields.io/badge/node-%3E%3D18-green)
![pnpm version](https://img.shields.io/badge/pnpm-%3E%3D7.30-green)
![GitHub package.json version (subfolder of monorepo)](https://img.shields.io/github/package-json/v/feige996/unibest)
![GitHub License](https://img.shields.io/github/license/feige996/unibest)
</div>
`unibest` —— 最好的 `uniapp` 开发模板,由 `uniapp` + `Vue3` + `Ts` + `Vite5` + `UnoCss` + `wot-ui` + `z-paging` 构成,使用了最新的前端技术栈,无需依靠 `HBuilderX`,通过命令行方式运行 `web``小程序``App`(编辑器推荐 `VSCode`,可选 `webstorm`)。
`unibest` 内置了 `约定式路由``layout布局``请求封装``请求拦截``登录拦截``UnoCSS``i18n多语言` 等基础功能,提供了 `代码提示``自动格式化``统一配置``代码片段` 等辅助功能,让你编写 `uniapp` 拥有 `best` 体验 `unibest 的由来`)。
![](https://raw.githubusercontent.com/andreasbm/readme/master/screenshots/lines/rainbow.png)
<p align="center">
<a href="https://unibest.tech/" target="_blank">📖 文档地址(new)</a>
<span style="margin:0 10px;">|</span>
<a href="https://feige996.github.io/hello-unibest/" target="_blank">📱 DEMO 地址</a>
</p>
---
注意旧的地址 [codercup](https://github.com/codercup/unibest) 我进不去了,使用新的 [feige996](https://github.com/feige996/unibest)。PR和 issue 也请使用新地址,否则无法合并。
## ⚙️ 环境
- node>=18
- pnpm>=7.30
- Vue Official>=2.1.10
- TypeScript>=5.0
## &#x1F4C2; 快速开始
执行 `pnpm create unibest` 创建项目
执行 `pnpm i` 安装依赖
执行 `pnpm dev` 运行 `H5`
## 📦 运行(支持热更新)
- web平台: `pnpm dev:h5`, 然后打开 [http://localhost:9000/](http://localhost:9000/)。
- weixin平台:`pnpm dev:mp-weixin` 然后打开微信开发者工具,导入本地文件夹,选择本项目的`dist/dev/mp-weixin` 文件。
- APP平台:`pnpm dev:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/dev/app` 文件夹,选择运行到模拟器(开发时优先使用),或者运行的安卓/ios基座。
## 🔗 发布
- web平台: `pnpm build:h5`,打包后的文件在 `dist/build/h5`,可以放到web服务器,如nginx运行。如果最终不是放在根目录,可以在 `manifest.config.ts` 文件的 `h5.router.base` 属性进行修改。
- weixin平台:`pnpm build:mp-weixin`, 打包后的文件在 `dist/build/mp-weixin`,然后通过微信开发者工具导入,并点击右上角的“上传”按钮进行上传。
- APP平台:`pnpm build:app`, 然后打开 `HBuilderX`,导入刚刚生成的`dist/build/app` 文件夹,选择发行 - APP云打包。
## 📄 License
[MIT](https://opensource.org/license/mit/)
Copyright (c) 2024 菲鸽
## 捐赠
<p align='center'>
<img alt="special sponsor appwrite" src="./screenshots/pay-1.png" height="330" style="display:inline-block; height:330px;">
<img alt="special sponsor appwrite" src="./screenshots/pay-2.png" height="330" style="display:inline-block; height:330px; margin-left:10px;">
</p>
Vendored
+15
View File
@@ -0,0 +1,15 @@
VITE_APP_TITLE = 'unibest'
VITE_APP_PORT = 9000
VITE_UNI_APPID = 'H57F2ACE4'
VITE_WX_APPID = 'wxa2abb91f64032a2b'
# h5部署网站的base,配置到 manifest.config.ts 里的 h5.router.base
VITE_APP_PUBLIC_BASE=/
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
VITE_UPLOAD_BASEURL = 'https://ukw0y1.laf.run/upload'
# h5是否需要配置代理
VITE_APP_PROXY=false
VITE_APP_PROXY_PREFIX = '/api'
+6
View File
@@ -0,0 +1,6 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = false
# 是否开启sourcemap
VITE_SHOW_SOURCEMAP = true
+6
View File
@@ -0,0 +1,6 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = true
# 是否开启sourcemap
VITE_SHOW_SOURCEMAP = false
+4
View File
@@ -0,0 +1,4 @@
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
NODE_ENV = 'development'
# 是否去除console 和 debugger
VITE_DELETE_CONSOLE = false
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

+26
View File
@@ -0,0 +1,26 @@
<!doctype html>
<html build-time="%BUILD_TIME%">
<head>
<meta charset="UTF-8" />
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
<script>
var coverSupport =
'CSS' in window &&
typeof CSS.supports === 'function' &&
(CSS.supports('top: env(a)') || CSS.supports('top: constant(a)'))
document.write(
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
(coverSupport ? ', viewport-fit=cover' : '') +
'" />',
)
</script>
<title>unibest</title>
<!--preload-links-->
<!--app-context-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
+134
View File
@@ -0,0 +1,134 @@
// manifest.config.ts
import { defineManifestConfig } from '@uni-helper/vite-plugin-uni-manifest'
import path from 'node:path'
import { loadEnv } from 'vite'
// 获取环境变量的范例
const env = loadEnv(process.env.NODE_ENV!, path.resolve(process.cwd(), 'env'))
const {
VITE_APP_TITLE,
VITE_UNI_APPID,
VITE_WX_APPID,
VITE_APP_PUBLIC_BASE,
VITE_FALLBACK_LOCALE,
} = env
export default defineManifestConfig({
name: VITE_APP_TITLE,
appid: VITE_UNI_APPID,
description: '',
versionName: '1.0.0',
versionCode: '100',
transformPx: false,
locale: VITE_FALLBACK_LOCALE, // 'zh-Hans'
h5: {
router: {
base: VITE_APP_PUBLIC_BASE,
},
},
/* 5+App特有相关 */
'app-plus': {
usingComponents: true,
nvueStyleCompiler: 'uni-app',
compilerVersion: 3,
compatible: {
ignoreVersion: true,
},
splashscreen: {
alwaysShowBeforeRender: true,
waiting: true,
autoclose: true,
delay: 0,
},
/* 模块配置 */
modules: {},
/* 应用发布信息 */
distribute: {
/* android打包配置 */
android: {
minSdkVersion: 30,
targetSdkVersion: 30,
abiFilters: ['armeabi-v7a', 'arm64-v8a'],
permissions: [
'<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>',
'<uses-permission android:name="android.permission.VIBRATE"/>',
'<uses-permission android:name="android.permission.READ_LOGS"/>',
'<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>',
'<uses-feature android:name="android.hardware.camera.autofocus"/>',
'<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>',
'<uses-permission android:name="android.permission.CAMERA"/>',
'<uses-permission android:name="android.permission.GET_ACCOUNTS"/>',
'<uses-permission android:name="android.permission.READ_PHONE_STATE"/>',
'<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>',
'<uses-permission android:name="android.permission.WAKE_LOCK"/>',
'<uses-permission android:name="android.permission.FLASHLIGHT"/>',
'<uses-feature android:name="android.hardware.camera"/>',
'<uses-permission android:name="android.permission.WRITE_SETTINGS"/>',
],
},
/* ios打包配置 */
ios: {},
/* SDK配置 */
sdkConfigs: {},
/* 图标配置 */
icons: {
android: {
hdpi: 'static/app/icons/72x72.png',
xhdpi: 'static/app/icons/96x96.png',
xxhdpi: 'static/app/icons/144x144.png',
xxxhdpi: 'static/app/icons/192x192.png',
},
ios: {
appstore: 'static/app/icons/1024x1024.png',
ipad: {
app: 'static/app/icons/76x76.png',
'app@2x': 'static/app/icons/152x152.png',
notification: 'static/app/icons/20x20.png',
'notification@2x': 'static/app/icons/40x40.png',
'proapp@2x': 'static/app/icons/167x167.png',
settings: 'static/app/icons/29x29.png',
'settings@2x': 'static/app/icons/58x58.png',
spotlight: 'static/app/icons/40x40.png',
'spotlight@2x': 'static/app/icons/80x80.png',
},
iphone: {
'app@2x': 'static/app/icons/120x120.png',
'app@3x': 'static/app/icons/180x180.png',
'notification@2x': 'static/app/icons/40x40.png',
'notification@3x': 'static/app/icons/60x60.png',
'settings@2x': 'static/app/icons/58x58.png',
'settings@3x': 'static/app/icons/87x87.png',
'spotlight@2x': 'static/app/icons/80x80.png',
'spotlight@3x': 'static/app/icons/120x120.png',
},
},
},
},
},
/* 快应用特有相关 */
quickapp: {},
/* 小程序特有相关 */
'mp-weixin': {
appid: VITE_WX_APPID,
setting: {
urlCheck: false,
},
usingComponents: true,
// __usePrivacyCheck__: true,
},
'mp-alipay': {
usingComponents: true,
styleIsolation: 'shared',
},
'mp-baidu': {
usingComponents: true,
},
'mp-toutiao': {
usingComponents: true,
},
uniStatistics: {
enable: false,
},
vueVersion: '3',
})
+172
View File
@@ -0,0 +1,172 @@
{
"name": "马尔代夫国际钱包",
"type": "commonjs",
"version": "2.5.3",
"description": "unibest - 最好的 uniapp 开发模板",
"author": {
"name": "feige996",
"zhName": "菲鸽",
"email": "1020103647@qq.com",
"github": "https://github.com/feige996",
"gitee": "https://gitee.com/feige996"
},
"license": "MIT",
"repository": "https://github.com/feige996/unibest",
"repository-gitee": "https://gitee.com/feige996/unibest",
"repository-deprecated": "https://github.com/codercup/unibest",
"bugs": {
"url": "https://github.com/feige996/unibest/issues"
},
"homepage": "https://feige996.github.io/unibest/",
"engines": {
"node": ">=18",
"pnpm": ">=7.30"
},
"scripts": {
"dev": "uni",
"build": "uni build",
"preinstall": "npx only-allow pnpm",
"uvm": "npx @dcloudio/uvm@latest",
"uvm-rm": "node ./scripts/postupgrade.js",
"postuvm": "echo upgrade uni-app success!",
"dev:app": "uni -p app",
"dev:app-android": "uni -p app-android",
"dev:app-ios": "uni -p app-ios",
"dev:custom": "uni -p",
"dev:h5": "uni",
"dev:h5:ssr": "uni --ssr",
"dev:mp": "uni -p mp-weixin",
"dev:mp-alipay": "uni -p mp-alipay",
"dev:mp-baidu": "uni -p mp-baidu",
"dev:mp-jd": "uni -p mp-jd",
"dev:mp-kuaishou": "uni -p mp-kuaishou",
"dev:mp-lark": "uni -p mp-lark",
"dev:mp-qq": "uni -p mp-qq",
"dev:mp-toutiao": "uni -p mp-toutiao",
"dev:mp-weixin": "uni -p mp-weixin",
"dev:mp-xhs": "uni -p mp-xhs",
"dev:quickapp-webview": "uni -p quickapp-webview",
"dev:quickapp-webview-huawei": "uni -p quickapp-webview-huawei",
"dev:quickapp-webview-union": "uni -p quickapp-webview-union",
"build:app": "uni build -p app",
"build:app-android": "uni build -p app-android",
"build:app-ios": "uni build -p app-ios",
"build:custom": "uni build -p",
"build:h5": "uni build",
"build:h5:ssr": "uni build --ssr",
"build:mp-alipay": "uni build -p mp-alipay",
"build:mp": "uni build -p mp-weixin",
"build:mp-baidu": "uni build -p mp-baidu",
"build:mp-jd": "uni build -p mp-jd",
"build:mp-kuaishou": "uni build -p mp-kuaishou",
"build:mp-lark": "uni build -p mp-lark",
"build:mp-qq": "uni build -p mp-qq",
"build:mp-toutiao": "uni build -p mp-toutiao",
"build:mp-weixin": "uni build -p mp-weixin",
"build:mp-xhs": "uni build -p mp-xhs",
"build:quickapp-webview": "uni build -p quickapp-webview",
"build:quickapp-webview-huawei": "uni build -p quickapp-webview-huawei",
"build:quickapp-webview-union": "uni build -p quickapp-webview-union",
"prepare": "git init && husky install ",
"type-check": "vue-tsc --noEmit",
"cz": "czg"
},
"lint-staged": {
"**/*.{html,vue,ts,cjs,json,md}": [
"prettier --write"
],
"**/*.{vue,js,ts,jsx,tsx}": [
"eslint --cache --fix"
],
"**/*.{vue,css,scss,html}": [
"stylelint --fix"
]
},
"resolutions": {
"bin-wrapper": "npm:bin-wrapper-china"
},
"dependencies": {
"@dcloudio/uni-app": "3.0.0-4020920240930001",
"@dcloudio/uni-app-harmony": "3.0.0-4020920240930001",
"@dcloudio/uni-app-plus": "3.0.0-4020920240930001",
"@dcloudio/uni-components": "3.0.0-4020920240930001",
"@dcloudio/uni-h5": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-alipay": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-baidu": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-jd": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-kuaishou": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-lark": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-qq": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-toutiao": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-weixin": "3.0.0-4020920240930001",
"@dcloudio/uni-mp-xhs": "3.0.0-4020920240930001",
"@dcloudio/uni-quickapp-webview": "3.0.0-4020920240930001",
"dayjs": "1.11.10",
"pinia": "2.0.36",
"pinia-plugin-persistedstate": "3.2.1",
"qs": "6.5.3",
"vue": "3.4.21",
"vue-i18n": "9.1.9",
"wot-design-uni": "^1.4.0",
"z-paging": "^2.8.4"
},
"devDependencies": {
"@commitlint/cli": "^18.6.1",
"@commitlint/config-conventional": "^18.6.3",
"@dcloudio/types": "^3.4.14",
"@dcloudio/uni-automator": "3.0.0-4020920240930001",
"@dcloudio/uni-cli-shared": "3.0.0-4020920240930001",
"@dcloudio/uni-stacktracey": "3.0.0-4020920240930001",
"@dcloudio/vite-plugin-uni": "3.0.0-4020920240930001",
"@esbuild/darwin-arm64": "0.20.2",
"@esbuild/darwin-x64": "0.20.2",
"@iconify-json/carbon": "^1.2.4",
"@rollup/rollup-darwin-x64": "^4.28.0",
"@types/node": "^20.17.9",
"@types/wechat-miniprogram": "^3.4.8",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@uni-helper/uni-types": "1.0.0-alpha.3",
"@uni-helper/vite-plugin-uni-layouts": "^0.1.10",
"@uni-helper/vite-plugin-uni-manifest": "^0.2.7",
"@uni-helper/vite-plugin-uni-pages": "0.2.20",
"@uni-helper/vite-plugin-uni-platform": "^0.0.4",
"@unocss/preset-legacy-compat": "^0.59.4",
"@unocss/preset-rem-to-px": "^0.65.3",
"@vue/runtime-core": "^3.5.13",
"@vue/tsconfig": "^0.1.3",
"autoprefixer": "^10.4.20",
"commitlint": "^18.6.1",
"czg": "^1.9.4",
"eslint": "^8.57.1",
"eslint-config-prettier": "^9.1.0",
"eslint-config-standard": "^17.1.0",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-vue": "^9.32.0",
"husky": "^8.0.3",
"lint-staged": "^15.2.10",
"postcss": "^8.4.49",
"postcss-html": "^1.7.0",
"postcss-mobile-forever": "^4.3.1",
"postcss-scss": "^4.0.9",
"rollup-plugin-visualizer": "^5.12.0",
"sass": "^1.77.8",
"stylelint": "^16.11.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-recess-order": "^4.6.0",
"stylelint-config-recommended": "^14.0.1",
"stylelint-config-recommended-scss": "^14.1.0",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-prettier": "^5.0.2",
"terser": "^5.36.0",
"typescript": "^5.7.2",
"unocss": "^0.58.9",
"unocss-applet": "^0.7.8",
"unplugin-auto-import": "^0.17.8",
"vite": "5.2.8",
"vite-plugin-restart": "^0.4.2",
"vue-tsc": "^1.8.27"
}
}
+43
View File
@@ -0,0 +1,43 @@
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
export default defineUniPages({
globalStyle: {
navigationStyle: 'default',
navigationBarTitleText: 'unibest',
navigationBarBackgroundColor: '#f8f8f8',
navigationBarTextStyle: 'black',
backgroundColor: '#FFFFFF',
},
easycom: {
autoscan: true,
custom: {
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
'^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)':
'z-paging/components/z-paging$1/z-paging$1.vue',
},
},
tabBar: {
color: '#999999',
selectedColor: '#018d71',
backgroundColor: '#F8F8F8',
borderStyle: 'black',
height: '50px',
fontSize: '10px',
iconWidth: '24px',
spacing: '3px',
list: [
{
iconPath: 'static/tabbar/home.png',
selectedIconPath: 'static/tabbar/homeHL.png',
pagePath: 'pages/index/index',
text: '%tabbar.home%',
},
{
iconPath: 'static/tabbar/example.png',
selectedIconPath: 'static/tabbar/exampleHL.png',
pagePath: 'pages/about/about',
text: '%tabbar.about%',
},
],
},
})
+12383
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

+36
View File
@@ -0,0 +1,36 @@
// # 执行 `pnpm upgrade` 后会升级 `uniapp` 相关依赖
// # 在升级完后,会自动添加很多无用依赖,这需要删除以减小依赖包体积
// # 只需要执行下面的命令即可
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { exec } = require('child_process')
// 定义要执行的命令
const dependencies = [
'@dcloudio/uni-app-harmony',
// TODO: 如果需要某个平台的小程序,请手动删除或注释掉
'@dcloudio/uni-mp-alipay',
'@dcloudio/uni-mp-baidu',
'@dcloudio/uni-mp-jd',
'@dcloudio/uni-mp-kuaishou',
'@dcloudio/uni-mp-lark',
'@dcloudio/uni-mp-qq',
'@dcloudio/uni-mp-toutiao',
'@dcloudio/uni-mp-xhs',
'@dcloudio/uni-quickapp-webview',
// i18n模板要注释掉下面的
// 'vue-i18n',
]
// 使用exec执行命令
exec(`pnpm un ${dependencies.join(' ')}`, (error, stdout, stderr) => {
if (error) {
// 如果有错误,打印错误信息
console.error(`执行出错: ${error}`)
return
}
// 打印正常输出
console.log(`stdout: ${stdout}`)
// 如果有错误输出,也打印出来
console.error(`stderr: ${stderr}`)
})
+75
View File
@@ -0,0 +1,75 @@
<script setup lang="ts">
import { onLaunch, onShow, onHide } from '@dcloudio/uni-app'
onLaunch(() => {
console.log('App Launch')
})
onShow(() => {
console.log('App Show')
})
onHide(() => {
console.log('App Hide')
})
</script>
<style lang="scss">
/* 覆盖 uniapp 自带rpx效果 */
html {
font-size: 16px !important;
}
/* stylelint-disable-next-line selector-type-no-unknown */
#app {
max-width: 600px; /* apply-without-convert */
margin: 0 auto;
box-shadow: inherit;
}
/* stylelint-disable selector-type-no-unknown */
button::after {
border: none;
}
swiper,
scroll-view {
flex: 1;
height: 100%;
overflow: hidden;
}
image {
width: 100%;
height: 100%;
vertical-align: middle;
}
// 单行省略,优先使用 unocss: text-ellipsis
.ellipsis {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 两行省略
.ellipsis-2 {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
// 三行省略
.ellipsis-3 {
display: -webkit-box;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
}
image {
display: block;
}
view {
box-sizing: border-box;
}
</style>
View File
+31
View File
@@ -0,0 +1,31 @@
/// <reference types="vite/client" />
/// <reference types="vite-svg-loader" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
interface ImportMetaEnv {
/** 网站标题,应用名称 */
readonly VITE_APP_TITLE: string
/** 服务端口号 */
readonly VITE_SERVER_PORT: string
/** 后台接口地址 */
readonly VITE_SERVER_BASEURL: string
/** H5是否需要代理 */
readonly VITE_APP_PROXY: 'true' | 'false'
/** H5是否需要代理,需要的话有个前缀 */
readonly VITE_APP_PROXY_PREFIX: string // 一般是/api
/** 上传图片地址 */
readonly VITE_UPLOAD_BASEURL: string
/** 是否清除console */
readonly VITE_DELETE_CONSOLE: string
// 更多环境变量...
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
View File
+44
View File
@@ -0,0 +1,44 @@
import { UnwrapRef } from 'vue'
type IUseRequestOptions<T> = {
/** 是否立即执行 */
immediate?: boolean
/** 初始化数据 */
initialData?: T
}
/**
* useRequest是一个定制化的请求钩子,用于处理异步请求和响应。
* @param func 一个执行异步请求的函数,返回一个包含响应数据的Promise。
* @param options 包含请求选项的对象 {immediate, initialData}。
* @param options.immediate 是否立即执行请求,默认为false。
* @param options.initialData 初始化数据,默认为undefined。
* @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
*/
export default function useRequest<T>(
func: () => Promise<IResData<T>>,
options: IUseRequestOptions<T> = { immediate: false },
) {
const loading = ref(false)
const error = ref(false)
const data = ref<T>(options.initialData)
const run = async () => {
loading.value = true
return func()
.then((res) => {
data.value = res.data as UnwrapRef<T>
error.value = false
return data.value
})
.catch((err) => {
error.value = err
throw err
})
.finally(() => {
loading.value = false
})
}
options.immediate && run()
return { loading, error, data, run }
}
+69
View File
@@ -0,0 +1,69 @@
// TODO: 别忘加更改环境变量的 VITE_UPLOAD_BASEURL 地址。
import { getEnvBaseUploadUrl } from '@/utils'
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
/**
* useUpload 是一个定制化的请求钩子,用于处理上传图片。
* @param formData 额外传递给后台的数据,如{name: '菲鸽'}。
* @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
*/
export default function useUpload<T = string>(formData: Record<string, any> = {}) {
const loading = ref(false)
const error = ref(false)
const data = ref<T>()
const run = () => {
// #ifdef MP-WEIXIN
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
// 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
uni.chooseMedia({
count: 1,
mediaType: ['image'],
success: (res) => {
loading.value = true
const tempFilePath = res.tempFiles[0].tempFilePath
uploadFile<T>({ tempFilePath, formData, data, error, loading })
},
fail: (err) => {
console.error('uni.chooseMedia err->', err)
error.value = true
},
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseImage({
count: 1,
success: (res) => {
loading.value = true
const tempFilePath = res.tempFilePaths[0]
uploadFile<T>({ tempFilePath, formData, data, error, loading })
},
fail: (err) => {
console.error('uni.chooseImage err->', err)
error.value = true
},
})
// #endif
}
return { loading, error, data, run }
}
function uploadFile<T>({ tempFilePath, formData, data, error, loading }) {
uni.uploadFile({
url: VITE_UPLOAD_BASEURL,
filePath: tempFilePath,
name: 'file',
formData,
success: (uploadFileRes) => {
data.value = uploadFileRes.data as T
},
fail: (err) => {
console.error('uni.uploadFile err->', err)
error.value = true
},
complete: () => {
loading.value = false
},
})
}
+3
View File
@@ -0,0 +1,3 @@
export { routeInterceptor } from './route'
export { requestInterceptor } from './request'
export { prototypeInterceptor } from './prototype'
+13
View File
@@ -0,0 +1,13 @@
export const prototypeInterceptor = {
install() {
// 解决低版本手机不识别 array.at() 导致运行报错的问题
if (typeof Array.prototype.at !== 'function') {
// eslint-disable-next-line no-extend-native
Array.prototype.at = function (index: number) {
if (index < 0) return this[this.length + index]
if (index >= this.length) return undefined
return this[index]
}
}
},
}
+68
View File
@@ -0,0 +1,68 @@
/* eslint-disable no-param-reassign */
import qs from 'qs'
import { useUserStore } from '@/store'
import { platform } from '@/utils/platform'
import { getEnvBaseUrl } from '@/utils'
export type CustomRequestOptions = UniApp.RequestOptions & {
query?: Record<string, any>
/** 出错时是否隐藏错误提示 */
hideErrorToast?: boolean
} & IUniUploadFileOptions // 添加uni.uploadFile参数类型
// 请求基准地址
const baseUrl = getEnvBaseUrl()
// 拦截器配置
const httpInterceptor = {
// 拦截前触发
invoke(options: CustomRequestOptions) {
// 接口请求支持通过 query 参数配置 queryString
if (options.query) {
const queryStr = qs.stringify(options.query)
if (options.url.includes('?')) {
options.url += `&${queryStr}`
} else {
options.url += `?${queryStr}`
}
}
// 非 http 开头需拼接地址
if (!options.url.startsWith('http')) {
// #ifdef H5
// console.log(__VITE_APP_PROXY__)
if (JSON.parse(__VITE_APP_PROXY__)) {
// 啥都不需要做
} else {
options.url = baseUrl + options.url
}
// #endif
// 非H5正常拼接
// #ifndef H5
options.url = baseUrl + options.url
// #endif
// TIPS: 如果需要对接多个后端服务,也可以在这里处理,拼接成所需要的地址
}
// 1. 请求超时
options.timeout = 10000 // 10s
// 2. (可选)添加小程序端请求头标识
options.header = {
platform, // 可选,与 uniapp 定义的平台一致,告诉后台来源
...options.header,
}
// 3. 添加 token 请求头标识
const userStore = useUserStore()
const { token } = userStore.userInfo as unknown as IUserInfo
if (token) {
options.header.Authorization = `Bearer ${token}`
}
},
}
export const requestInterceptor = {
install() {
// 拦截 request 请求
uni.addInterceptor('request', httpInterceptor)
// 拦截 uploadFile 文件上传
uni.addInterceptor('uploadFile', httpInterceptor)
},
}
+53
View File
@@ -0,0 +1,53 @@
/**
* by 菲鸽 on 2024-03-06
* 路由拦截,通常也是登录拦截
* 可以设置路由白名单,或者黑名单,看业务需要选哪一个
* 我这里应为大部分都可以随便进入,所以使用黑名单
*/
import { useUserStore } from '@/store'
import { getNeedLoginPages, needLoginPages as _needLoginPages } from '@/utils'
// TODO Check
const loginRoute = '/pages/login/index'
const isLogined = () => {
const userStore = useUserStore()
return userStore.isLogined
}
const isDev = import.meta.env.DEV
// 黑名单登录拦截器 - (适用于大部分页面不需要登录,少部分页面需要登录)
const navigateToInterceptor = {
// 注意,这里的url是 '/' 开头的,如 '/pages/index/index',跟 'pages.json' 里面的 path 不同
invoke({ url }: { url: string }) {
// console.log(url) // /pages/route-interceptor/index?name=feige&age=30
const path = url.split('?')[0]
let needLoginPages: string[] = []
// 为了防止开发时出现BUG,这里每次都获取一下。生产环境可以移到函数外,性能更好
if (isDev) {
needLoginPages = getNeedLoginPages()
} else {
needLoginPages = _needLoginPages
}
const isNeedLogin = needLoginPages.includes(path)
if (!isNeedLogin) {
return true
}
const hasLogin = isLogined()
if (hasLogin) {
return true
}
const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
uni.navigateTo({ url: redirectRoute })
return false
},
}
export const routeInterceptor = {
install() {
uni.addInterceptor('navigateTo', navigateToInterceptor)
uni.addInterceptor('reLaunch', navigateToInterceptor)
uni.addInterceptor('redirectTo', navigateToInterceptor)
},
}
+17
View File
@@ -0,0 +1,17 @@
<template>
<wd-config-provider :themeVars="themeVars">
<slot />
<wd-toast />
<wd-message-box />
</wd-config-provider>
</template>
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni'
const themeVars: ConfigProviderThemeVars = {
// colorTheme: 'red',
// buttonPrimaryBgColor: '#07c160',
// buttonPrimaryColor: '#07c160',
}
</script>
+17
View File
@@ -0,0 +1,17 @@
<template>
<wd-config-provider :themeVars="themeVars">
<slot />
<wd-toast />
<wd-message-box />
</wd-config-provider>
</template>
<script lang="ts" setup>
import type { ConfigProviderThemeVars } from 'wot-design-uni'
const themeVars: ConfigProviderThemeVars = {
// colorTheme: 'red',
// buttonPrimaryBgColor: '#07c160',
// buttonPrimaryColor: '#07c160',
}
</script>
+12
View File
@@ -0,0 +1,12 @@
# 注意事项
> 文件夹名字必须为 `locale`, 这是 `uniapp` 官方约定的,如果改为别的,标题将不能正常切换多语言(其他内容还是正常)。
>
> `xxx.json` 的 `xxx` 多语言标识必须与 `uniapp` 官方约定的一致,否则也会出现 BUG。
>
> 查看截图 `screenshots/i18n.png`。
## 参考文档
[uniapp 国际化开发指南](https://uniapp.dcloud.net.cn/tutorial/i18n.html)
[uniapp 国际化-注意事项](https://uniapp.dcloud.net.cn/api/ui/locale.html#onlocalechange) 最下面的注意事项
+8
View File
@@ -0,0 +1,8 @@
{
"tabbar.home": "Home",
"tabbar.about": "About",
"app.name": "En Title",
"weight": "{heavy}KG",
"detail": "{0}cm, {1}KG",
"introduction": "I am {name},height:{detail.height},weight:{detail.weight}"
}
+80
View File
@@ -0,0 +1,80 @@
import { createI18n } from 'vue-i18n'
import en from './en.json'
import zhHans from './zh-Hans.json' // 简体中文
const messages = {
en,
'zh-Hans': zhHans, // key 不能乱写,查看截图 screenshots/i18n.png
}
const i18n = createI18n({
locale: uni.getLocale(), // 获取已设置的语言,fallback 语言需要再 manifest.config.ts 中设置
messages,
allowComposition: true,
})
console.log(uni.getLocale())
console.log(i18n.global.locale)
/**
* 可以拿到原始的语言模板,非 vue 文件使用这个方法,
* @param { string } key 多语言的keyeg: "app.name"
* @returns {string} 返回原始的多语言模板,eg: "{heavy}KG"
*/
export const getTemplateByKey = (key: string) => {
if (!key) {
console.error(`[i18n] Function getTemplateByKey(), key param is required`)
return ''
}
const locale = uni.getLocale()
console.log('locale:', locale)
const message = messages[locale] // 拿到某个多语言的所有模板(是一个对象)
if (Object.keys(message).includes(key)) {
return message[key]
}
try {
const keyList = key.split('.')
return keyList.reduce((pre, cur) => {
return pre[cur]
}, message)
} catch (error) {
console.error(`[i18n] Function getTemplateByKey(), key param ${key} is not existed.`)
return ''
}
}
/**
* formatI18n('我是{name},身高{detail.height},体重{detail.weight}',{name:'张三',detail:{height:178,weight:'75kg'}})
* 暂不支持数组
* @param template 多语言模板字符串,eg: `我是{name}`
* @param {Object|undefined} data 需要传递的数据对象,里面的key与多语言字符串对应,eg: `{name:'菲鸽'}`
* @returns
*/
function formatI18n(template: string, data?: any) {
return template.replace(/\{([^}]+)\}/g, function (match, key: string) {
// console.log( match, key) // => { detail.height } detail.height
const arr = key.trim().split('.')
let result = data
while (arr.length) {
const first = arr.shift()
result = result[first]
}
return result
})
}
/**
* t('introduction',{name:'张三',detail:{height:178,weight:'75kg'}})
* => formatI18n('我是{name},身高{detail.height},体重{detail.weight}',{name:'张三',detail:{height:178,weight:'75kg'}})
* 没有key的,可以不传 data;暂不支持数组
* @param template 多语言模板字符串,eg: `我是{name}`
* @param {Object|undefined} data 需要传递的数据对象,里面的key与多语言字符串对应,eg: `{name:'菲鸽'}`
* @returns
*/
export function t(key, data?) {
return formatI18n(getTemplateByKey(key), data)
}
export default i18n
+8
View File
@@ -0,0 +1,8 @@
{
"tabbar.home": "首页",
"tabbar.about": "关于",
"app.name": "中文标题",
"weight": "{heavy}公斤",
"detail": "{0}cm, {1}公斤",
"introduction": "我是 {name},身高:{detail.height},体重:{detail.weight}"
}
+19
View File
@@ -0,0 +1,19 @@
import { createSSRApp } from 'vue'
import App from './App.vue'
import store from './store'
import i18n from './locale/index'
import { routeInterceptor, requestInterceptor, prototypeInterceptor } from './interceptors'
import 'virtual:uno.css'
import '@/style/index.scss'
export function createApp() {
const app = createSSRApp(App)
app.use(store)
app.use(i18n)
app.use(routeInterceptor)
app.use(requestInterceptor)
app.use(prototypeInterceptor)
return {
app,
}
}
+111
View File
@@ -0,0 +1,111 @@
{
"name": "unibest",
"appid": "H57F2ACE4",
"description": "",
"versionName": "1.0.0",
"versionCode": "100",
"transformPx": false,
"app-plus": {
"usingComponents": true,
"nvueStyleCompiler": "uni-app",
"compilerVersion": 3,
"splashscreen": {
"alwaysShowBeforeRender": true,
"waiting": true,
"autoclose": true,
"delay": 0
},
"modules": {},
"distribute": {
"android": {
"permissions": [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
],
"minSdkVersion": 30,
"targetSdkVersion": 30,
"abiFilters": [
"armeabi-v7a",
"arm64-v8a"
]
},
"ios": {},
"sdkConfigs": {},
"icons": {
"android": {
"hdpi": "static/app/icons/72x72.png",
"xhdpi": "static/app/icons/96x96.png",
"xxhdpi": "static/app/icons/144x144.png",
"xxxhdpi": "static/app/icons/192x192.png"
},
"ios": {
"appstore": "static/app/icons/1024x1024.png",
"ipad": {
"app": "static/app/icons/76x76.png",
"app@2x": "static/app/icons/152x152.png",
"notification": "static/app/icons/20x20.png",
"notification@2x": "static/app/icons/40x40.png",
"proapp@2x": "static/app/icons/167x167.png",
"settings": "static/app/icons/29x29.png",
"settings@2x": "static/app/icons/58x58.png",
"spotlight": "static/app/icons/40x40.png",
"spotlight@2x": "static/app/icons/80x80.png"
},
"iphone": {
"app@2x": "static/app/icons/120x120.png",
"app@3x": "static/app/icons/180x180.png",
"notification@2x": "static/app/icons/40x40.png",
"notification@3x": "static/app/icons/60x60.png",
"settings@2x": "static/app/icons/58x58.png",
"settings@3x": "static/app/icons/87x87.png",
"spotlight@2x": "static/app/icons/80x80.png",
"spotlight@3x": "static/app/icons/120x120.png"
}
}
}
},
"compatible": {
"ignoreVersion": true
}
},
"quickapp": {},
"mp-weixin": {
"appid": "wxa2abb91f64032a2b",
"setting": {
"urlCheck": false
},
"usingComponents": true
},
"mp-alipay": {
"usingComponents": true,
"styleIsolation": "shared"
},
"mp-baidu": {
"usingComponents": true
},
"mp-toutiao": {
"usingComponents": true
},
"uniStatistics": {
"enable": false
},
"vueVersion": "3",
"h5": {
"router": {
"base": "/"
}
}
}
+20
View File
@@ -0,0 +1,20 @@
<route lang="json5" type="page">
{
style: { navigationBarTitleText: '分包页面 标题' },
}
</route>
<template>
<view class="text-center">
<view class="m-8">http://localhost:9000/#/pages-sub/demo/index</view>
<view class="text-green-500">分包页面demo</view>
</view>
</template>
<script lang="ts" setup>
// code here
</script>
<style lang="scss" scoped>
//
</style>
+78
View File
@@ -0,0 +1,78 @@
{
"globalStyle": {
"navigationStyle": "default",
"navigationBarTitleText": "unibest",
"navigationBarBackgroundColor": "#f8f8f8",
"navigationBarTextStyle": "black",
"backgroundColor": "#FFFFFF"
},
"easycom": {
"autoscan": true,
"custom": {
"^wd-(.*)": "wot-design-uni/components/wd-$1/wd-$1.vue",
"^(?!z-paging-refresh|z-paging-load-more)z-paging(.*)": "z-paging/components/z-paging$1/z-paging$1.vue"
}
},
"tabBar": {
"color": "#999999",
"selectedColor": "#018d71",
"backgroundColor": "#F8F8F8",
"borderStyle": "black",
"height": "50px",
"fontSize": "10px",
"iconWidth": "24px",
"spacing": "3px",
"list": [
{
"iconPath": "static/tabbar/home.png",
"selectedIconPath": "static/tabbar/homeHL.png",
"pagePath": "pages/index/index",
"text": "%tabbar.home%"
},
{
"iconPath": "static/tabbar/example.png",
"selectedIconPath": "static/tabbar/exampleHL.png",
"pagePath": "pages/about/about",
"text": "%tabbar.about%"
}
]
},
"pages": [
{
"path": "pages/index/index",
"type": "home",
"style": {
"navigationStyle": "custom",
"navigationBarTitleText": "首页"
}
},
{
"path": "pages/about/about",
"type": "page",
"style": {
"navigationBarTitleText": "关于"
}
},
{
"path": "pages/about/i18n",
"type": "page",
"style": {
"navigationBarTitleText": "%app.name%"
}
}
],
"subPackages": [
{
"root": "pages-sub",
"pages": [
{
"path": "demo/index",
"type": "page",
"style": {
"navigationBarTitleText": "分包页面 标题"
}
}
]
}
]
}
+45
View File
@@ -0,0 +1,45 @@
<route lang="json5">
{
style: {
navigationBarTitleText: '关于',
},
}
</route>
<template>
<view
class="bg-white overflow-hidden pt-2 px-4"
:style="{ marginTop: safeAreaInsets?.top + 'px' }"
>
<view class="text-center text-3xl mt-8">
鸽友们好我是
<text class="text-red-500">菲鸽</text>
</view>
<view class="text-center mt-8 text-#fff">
<wd-button type="success" @click="gotoPage('i18n')">进入多语言页面</wd-button>
</view>
<RequestComp />
<UploadComp />
</view>
</template>
<script lang="ts" setup>
import RequestComp from './components/request.vue'
import UploadComp from './components/upload.vue'
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
const gotoPage = (path) => {
uni.navigateTo({
url: `/pages/about/${path}`,
})
}
</script>
<style lang="scss" scoped>
.test-css {
// mt-4=>1rem=>16px;
margin-top: 16px;
}
</style>
+56
View File
@@ -0,0 +1,56 @@
<route lang="json5">
{
layout: 'demo',
style: {
navigationBarTitleText: '请求',
},
}
</route>
<template>
<view class="p-6 text-center">
<view class="my-2">使用的是 laf 云后台</view>
<view class="text-green-400">我的推荐码可以获得佣金</view>
<!-- #ifdef H5 -->
<view class="my-2">
<a class="my-2" :href="recommendUrl" target="_blank">{{ recommendUrl }}</a>
</view>
<!-- #endif -->
<!-- #ifndef H5 -->
<view class="my-2 text-left text-sm">{{ recommendUrl }}</view>
<!-- #endif -->
<!-- http://localhost:9000/#/pages/index/request -->
<wd-button @click="run" class="my-6">发送请求</wd-button>
<view class="h-12">
<view v-if="loading">loading...</view>
<block v-else>
<view class="text-xl">请求数据如下</view>
<view class="text-green leading-8">{{ JSON.stringify(data) }}</view>
</block>
</view>
<wd-button type="error" @click="reset" class="my-6" :disabled="!data">重置数据</wd-button>
</view>
</template>
<script lang="ts" setup>
import { getFooAPI, postFooAPI, IFooItem } from '@/service/index/foo'
const recommendUrl = ref('http://laf.run/signup?code=ohaOgIX')
// const initialData = {
// name: 'initialData',
// id: '1234',
// }
const initialData = undefined
// 适合少部分全局性的接口————多个页面都需要的请求接口,额外编写一个 Service 层
const { loading, error, data, run } = useRequest<IFooItem>(() => getFooAPI('菲鸽'), {
immediate: true,
initialData,
})
const reset = () => {
data.value = initialData
}
</script>
+30
View File
@@ -0,0 +1,30 @@
<route lang="json5" type="page">
{
layout: 'default',
style: {
navigationBarTitleText: '上传-状态一体化',
},
}
</route>
<template>
<view class="p-4 text-center">
<wd-button @click="run">选择图片并上传</wd-button>
<view v-if="loading" class="text-blue h-10">上传...</view>
<template v-else>
<view class="m-2">上传后返回的接口数据</view>
<view class="m-2">{{ data }}</view>
<view class="h-80 w-full">
<image v-if="data" :src="data || data" mode="scaleToFill" />
</view>
</template>
</view>
</template>
<script lang="ts" setup>
const { loading, data, run } = useUpload({ user: '菲鸽' })
</script>
<style lang="scss" scoped>
//
</style>
+97
View File
@@ -0,0 +1,97 @@
<route lang="json">
{
"style": {
"navigationBarTitleText": "%app.name%"
}
}
</route>
<template>
<view class="center flex-col mt-6">
<view class="text-red-500 p-4 leading-6">
经过我的测试发现小程序里面会有2处BUG
<view>
<text class="line-through">1. 页面标题多语言不生效</text>
<text class="ml-2 text-green-500">已解决</text>
</view>
<view>
<text class="line-through">2. 多语言传递的参数不生效如下 heavy</text>
<text class="ml-2 text-green-500">已解决</text>
<view class="ml-2 text-green-500"> $t 改为自定义的 t 即可</view>
</view>
</view>
<view class="text-green-500">多语言测试</view>
<view class="m-4">{{ $t('app.name') }}</view>
<view class="m-4">{{ $t('weight', { heavy: 100 }) }}</view>
<view class="m-4">{{ t('introduction', user) }}</view>
<view class="text-green-500 mt-12">切换语言</view>
<view class="uni-list">
<radio-group @change="radioChange" class="radio-group">
<label class="uni-list-cell uni-list-cell-pd" v-for="item in languages" :key="item.value">
<view>
<radio :value="item.value" :checked="item.value === current" />
</view>
<view>{{ item.name }}</view>
</label>
</radio-group>
</view>
<!-- http://localhost:9000/#/pages/index/i18n -->
<button @click="testI18n" class="mt-20 mb-44">测试弹窗</button>
</view>
</template>
<script lang="ts" setup>
import i18n, { t } from '@/locale/index'
import { testI18n } from '@/utils/i18n'
const current = ref(uni.getLocale())
const user = { name: '张三', detail: { height: 178, weight: '75kg' } }
const languages = [
{
value: 'zh-Hans',
name: '中文',
checked: 'true',
},
{
value: 'en',
name: '英文',
},
]
const radioChange = (evt) => {
// console.log(evt)
current.value = evt.detail.value
// 下面2句缺一不可!!!
uni.setLocale(evt.detail.value)
i18n.global.locale = evt.detail.value
}
</script>
<style lang="scss">
.uni-list {
position: relative;
display: flex;
flex-direction: column;
width: 100%;
background-color: #fff;
border-radius: 12px;
}
.radio-group {
width: 200px;
margin: 10px auto;
border-radius: 12px;
}
.uni-list-cell {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
padding: 10px;
background-color: #bcecd1;
}
</style>
+57
View File
@@ -0,0 +1,57 @@
<!-- 使用 type="home" 属性设置首页其他页面不需要设置默认为page推荐使用json5更强大且允许注释 -->
<route lang="json5" type="home">
{
style: {
navigationStyle: 'custom',
navigationBarTitleText: '首页',
},
}
</route>
<template>
<view
class="bg-white overflow-hidden pt-2 px-4"
:style="{ marginTop: safeAreaInsets?.top + 'px' }"
>
<view class="mt-12">
<image src="/static/logo.svg" alt="" class="w-28 h-28 block mx-auto" />
</view>
<view class="text-center text-4xl main-title-color mt-4">unibest</view>
<view class="text-center text-2xl mt-2 mb-8">最好用的 uniapp 开发模板</view>
<view class="text-justify max-w-100 m-auto text-4 indent mb-2">{{ description }}</view>
<view class="text-center mt-8">
当前平台是
<text class="text-green-500">{{ PLATFORM.platform }}</text>
</view>
<view class="text-center mt-4">
模板分支是
<text class="text-green-500">i18n</text>
</view>
</view>
<tabbar />
</template>
<script lang="ts" setup>
import PLATFORM from '@/utils/platform'
defineOptions({
name: 'Home',
})
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
const author = ref('菲鸽')
const description = ref(
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite4 + UnoCss + UniUI + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
)
// 测试 uni API 自动引入
onLoad(() => {
console.log(author)
})
</script>
<style>
.main-title-color {
color: #d14328;
}
</style>
+15
View File
@@ -0,0 +1,15 @@
import { http } from '@/utils/http'
export interface IFooItem {
id: string
name: string
}
/** GET 请求 */
export const getFooAPI = (name: string) => {
return http.get<IFooItem>('/foo', { name })
}
/** POST 请求 */
export const postFooAPI = (name: string) => {
return http.post<IFooItem>('/foo', { name }, { name })
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 574 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 780 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File
+33
View File
@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg id="_图层_2" data-name="图层 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 113.39 113.39">
<defs>
<style>
.cls-1 {
fill: none;
}
.cls-2 {
fill: #d14328;
}
.cls-3 {
fill: #2c8d3a;
}
</style>
</defs>
<g id="_图层_1-2" data-name="图层 1">
<g>
<rect class="cls-1" width="113.39" height="113.39" />
<g>
<path class="cls-3"
d="M86.31,11.34H25.08c-8.14,0-14.74,6.6-14.74,14.74v61.23c0,8.14,6.6,14.74,14.74,14.74h61.23c.12,0,.24-.02,.37-.02-9.76-.2-17.64-8.18-17.64-17.99,0-.56,.03-1.12,.08-1.67H34.1c-1.57,0-2.83-1.27-2.83-2.83V32.43c0-.78,.63-1.42,1.42-1.42h9.17c.78,0,1.42,.63,1.42,1.42v36.52c0,.78,.63,1.42,1.42,1.42h22.02c.78,0,1.42-.63,1.42-1.42V32.43c0-.78,.63-1.42,1.42-1.42h9.17c.78,0,1.42,.63,1.42,1.42v34.99c2.13-.89,4.47-1.39,6.92-1.39,5.66,0,10.7,2.63,14.01,6.72V26.08c0-8.14-6.6-14.74-14.74-14.74Z" />
<g>
<path class="cls-2"
d="M87.04,68.03c-8.83,0-16.01,7.18-16.01,16.01s7.18,16.01,16.01,16.01,16.01-7.18,16.01-16.01-7.18-16.01-16.01-16.01Zm-.27,24.84h-7.2v-3h1.18v-10.48h4.58v2.81h1.42c.84,0,1.46-.16,1.88-.48s.62-.87,.62-1.64c0-.69-.25-1.17-.74-1.45s-1.19-.42-2.09-.42h-6.84v-3h7.2c2.38,0,4.15,.38,5.31,1.15,1.16,.77,1.74,1.93,1.74,3.48,0,1.71-.83,2.93-2.5,3.64,1.07,.4,1.87,.95,2.39,1.65s.79,1.56,.79,2.58c0,3.44-2.58,5.16-7.73,5.16Z" />
<path class="cls-2"
d="M86.49,85.17h-1.16v4.7h1.8c.81,0,1.46-.18,1.94-.55s.72-.95,.72-1.73c0-.86-.25-1.48-.74-1.85s-1.35-.56-2.56-.56Z" />
</g>
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

+17
View File
@@ -0,0 +1,17 @@
import { createPinia } from 'pinia'
import { createPersistedState } from 'pinia-plugin-persistedstate' // 数据持久化
const store = createPinia()
store.use(
createPersistedState({
storage: {
getItem: uni.getStorageSync,
setItem: uni.setStorageSync,
},
}),
)
export default store
// 模块统一导出
export * from './user'
+35
View File
@@ -0,0 +1,35 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
const initState = { nickname: '', avatar: '' }
export const useUserStore = defineStore(
'user',
() => {
const userInfo = ref<IUserInfo>({ ...initState })
const setUserInfo = (val: IUserInfo) => {
userInfo.value = val
}
const clearUserInfo = () => {
userInfo.value = { ...initState }
}
// 一般没有reset需求,不需要的可以删除
const reset = () => {
userInfo.value = { ...initState }
}
const isLogined = computed(() => !!userInfo.value.token)
return {
userInfo,
setUserInfo,
clearUserInfo,
isLogined,
reset,
}
},
{
persist: true,
},
)
+28
View File
@@ -0,0 +1,28 @@
@font-face {
font-family: 'iconfont'; /* Project id 4543091 */
src:
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=')
format('woff2'),
url('//at.alicdn.com/t/c/font_4543091_njpo5b95nl.woff?t=1715485842402') format('woff'),
url('//at.alicdn.com/t/c/font_4543091_njpo5b95nl.ttf?t=1715485842402') format('truetype');
}
.iconfont {
font-family: 'iconfont' !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-my:before {
content: '\e78c';
}
.icon-package:before {
content: '\e9c2';
}
.icon-chat:before {
content: '\e600';
}
+18
View File
@@ -0,0 +1,18 @@
// @import './iconfont.css';
.test {
// 可以通过 @apply 多个样式封装整体样式
@apply mt-4 ml-4;
padding-top: 4px;
color: red;
}
:root,
page {
// 修改按主题色
// --wot-color-theme: #37c2bc;
// 修改按钮背景色
// --wot-button-primary-bg-color: green;
}
+187
View File
@@ -0,0 +1,187 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onAddToFavorites: typeof import('@dcloudio/uni-app')['onAddToFavorites']
const onBackPress: typeof import('@dcloudio/uni-app')['onBackPress']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onError: typeof import('@dcloudio/uni-app')['onError']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onHide: typeof import('@dcloudio/uni-app')['onHide']
const onLaunch: typeof import('@dcloudio/uni-app')['onLaunch']
const onLoad: typeof import('@dcloudio/uni-app')['onLoad']
const onMounted: typeof import('vue')['onMounted']
const onNavigationBarButtonTap: typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']
const onNavigationBarSearchInputChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']
const onNavigationBarSearchInputClicked: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']
const onNavigationBarSearchInputConfirmed: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']
const onNavigationBarSearchInputFocusChanged: typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']
const onPageNotFound: typeof import('@dcloudio/uni-app')['onPageNotFound']
const onPageScroll: typeof import('@dcloudio/uni-app')['onPageScroll']
const onPullDownRefresh: typeof import('@dcloudio/uni-app')['onPullDownRefresh']
const onReachBottom: typeof import('@dcloudio/uni-app')['onReachBottom']
const onReady: typeof import('@dcloudio/uni-app')['onReady']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onResize: typeof import('@dcloudio/uni-app')['onResize']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onShareAppMessage: typeof import('@dcloudio/uni-app')['onShareAppMessage']
const onShareTimeline: typeof import('@dcloudio/uni-app')['onShareTimeline']
const onShow: typeof import('@dcloudio/uni-app')['onShow']
const onTabItemTap: typeof import('@dcloudio/uni-app')['onTabItemTap']
const onThemeChange: typeof import('@dcloudio/uni-app')['onThemeChange']
const onUnhandledRejection: typeof import('@dcloudio/uni-app')['onUnhandledRejection']
const onUnload: typeof import('@dcloudio/uni-app')['onUnload']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const onWatcherCleanup: typeof import('vue')['onWatcherCleanup']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const toValue: typeof import('vue')['toValue']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useId: typeof import('vue')['useId']
const useModel: typeof import('vue')['useModel']
const useNavbarWeixin: (typeof import('../hooks/useNavbarWeixin'))['default']
const useRequest: typeof import('../hooks/useRequest')['default']
const useSlots: typeof import('vue')['useSlots']
const useTemplateRef: typeof import('vue')['useTemplateRef']
const useUpload: typeof import('../hooks/useUpload')['default']
const useUpload2: typeof import('../hooks/useUpload2')['default']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}
// for type re-export
declare global {
// @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue'
import('vue')
}
// for vue template auto import
import { UnwrapRef } from 'vue'
declare module 'vue' {
interface GlobalComponents {}
interface ComponentCustomProperties {
readonly EffectScope: UnwrapRef<typeof import('vue')['EffectScope']>
readonly computed: UnwrapRef<typeof import('vue')['computed']>
readonly createApp: UnwrapRef<typeof import('vue')['createApp']>
readonly customRef: UnwrapRef<typeof import('vue')['customRef']>
readonly defineAsyncComponent: UnwrapRef<typeof import('vue')['defineAsyncComponent']>
readonly defineComponent: UnwrapRef<typeof import('vue')['defineComponent']>
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
readonly h: UnwrapRef<typeof import('vue')['h']>
readonly inject: UnwrapRef<typeof import('vue')['inject']>
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
readonly markRaw: UnwrapRef<typeof import('vue')['markRaw']>
readonly nextTick: UnwrapRef<typeof import('vue')['nextTick']>
readonly onActivated: UnwrapRef<typeof import('vue')['onActivated']>
readonly onAddToFavorites: UnwrapRef<typeof import('@dcloudio/uni-app')['onAddToFavorites']>
readonly onBackPress: UnwrapRef<typeof import('@dcloudio/uni-app')['onBackPress']>
readonly onBeforeMount: UnwrapRef<typeof import('vue')['onBeforeMount']>
readonly onBeforeUnmount: UnwrapRef<typeof import('vue')['onBeforeUnmount']>
readonly onBeforeUpdate: UnwrapRef<typeof import('vue')['onBeforeUpdate']>
readonly onDeactivated: UnwrapRef<typeof import('vue')['onDeactivated']>
readonly onError: UnwrapRef<typeof import('@dcloudio/uni-app')['onError']>
readonly onErrorCaptured: UnwrapRef<typeof import('vue')['onErrorCaptured']>
readonly onHide: UnwrapRef<typeof import('@dcloudio/uni-app')['onHide']>
readonly onLaunch: UnwrapRef<typeof import('@dcloudio/uni-app')['onLaunch']>
readonly onLoad: UnwrapRef<typeof import('@dcloudio/uni-app')['onLoad']>
readonly onMounted: UnwrapRef<typeof import('vue')['onMounted']>
readonly onNavigationBarButtonTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarButtonTap']>
readonly onNavigationBarSearchInputChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputChanged']>
readonly onNavigationBarSearchInputClicked: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputClicked']>
readonly onNavigationBarSearchInputConfirmed: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputConfirmed']>
readonly onNavigationBarSearchInputFocusChanged: UnwrapRef<typeof import('@dcloudio/uni-app')['onNavigationBarSearchInputFocusChanged']>
readonly onPageNotFound: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageNotFound']>
readonly onPageScroll: UnwrapRef<typeof import('@dcloudio/uni-app')['onPageScroll']>
readonly onPullDownRefresh: UnwrapRef<typeof import('@dcloudio/uni-app')['onPullDownRefresh']>
readonly onReachBottom: UnwrapRef<typeof import('@dcloudio/uni-app')['onReachBottom']>
readonly onReady: UnwrapRef<typeof import('@dcloudio/uni-app')['onReady']>
readonly onRenderTracked: UnwrapRef<typeof import('vue')['onRenderTracked']>
readonly onRenderTriggered: UnwrapRef<typeof import('vue')['onRenderTriggered']>
readonly onResize: UnwrapRef<typeof import('@dcloudio/uni-app')['onResize']>
readonly onScopeDispose: UnwrapRef<typeof import('vue')['onScopeDispose']>
readonly onServerPrefetch: UnwrapRef<typeof import('vue')['onServerPrefetch']>
readonly onShareAppMessage: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareAppMessage']>
readonly onShareTimeline: UnwrapRef<typeof import('@dcloudio/uni-app')['onShareTimeline']>
readonly onShow: UnwrapRef<typeof import('@dcloudio/uni-app')['onShow']>
readonly onTabItemTap: UnwrapRef<typeof import('@dcloudio/uni-app')['onTabItemTap']>
readonly onThemeChange: UnwrapRef<typeof import('@dcloudio/uni-app')['onThemeChange']>
readonly onUnhandledRejection: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnhandledRejection']>
readonly onUnload: UnwrapRef<typeof import('@dcloudio/uni-app')['onUnload']>
readonly onUnmounted: UnwrapRef<typeof import('vue')['onUnmounted']>
readonly onUpdated: UnwrapRef<typeof import('vue')['onUpdated']>
readonly onWatcherCleanup: UnwrapRef<typeof import('vue')['onWatcherCleanup']>
readonly provide: UnwrapRef<typeof import('vue')['provide']>
readonly reactive: UnwrapRef<typeof import('vue')['reactive']>
readonly readonly: UnwrapRef<typeof import('vue')['readonly']>
readonly ref: UnwrapRef<typeof import('vue')['ref']>
readonly resolveComponent: UnwrapRef<typeof import('vue')['resolveComponent']>
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
readonly shallowReadonly: UnwrapRef<typeof import('vue')['shallowReadonly']>
readonly shallowRef: UnwrapRef<typeof import('vue')['shallowRef']>
readonly toRaw: UnwrapRef<typeof import('vue')['toRaw']>
readonly toRef: UnwrapRef<typeof import('vue')['toRef']>
readonly toRefs: UnwrapRef<typeof import('vue')['toRefs']>
readonly toValue: UnwrapRef<typeof import('vue')['toValue']>
readonly triggerRef: UnwrapRef<typeof import('vue')['triggerRef']>
readonly unref: UnwrapRef<typeof import('vue')['unref']>
readonly useAttrs: UnwrapRef<typeof import('vue')['useAttrs']>
readonly useCssModule: UnwrapRef<typeof import('vue')['useCssModule']>
readonly useCssVars: UnwrapRef<typeof import('vue')['useCssVars']>
readonly useId: UnwrapRef<typeof import('vue')['useId']>
readonly useModel: UnwrapRef<typeof import('vue')['useModel']>
readonly useRequest: UnwrapRef<typeof import('../hooks/useRequest')['default']>
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
readonly useTemplateRef: UnwrapRef<typeof import('vue')['useTemplateRef']>
readonly useUpload: UnwrapRef<typeof import('../hooks/useUpload')['default']>
readonly watch: UnwrapRef<typeof import('vue')['watch']>
readonly watchEffect: UnwrapRef<typeof import('vue')['watchEffect']>
readonly watchPostEffect: UnwrapRef<typeof import('vue')['watchPostEffect']>
readonly watchSyncEffect: UnwrapRef<typeof import('vue')['watchSyncEffect']>
}
}
+23
View File
@@ -0,0 +1,23 @@
declare const __UNI_PLATFORM__:
| 'h5'
| 'app'
| 'mp-alipay'
| 'mp-baidu'
| 'mp-jd'
| 'mp-kuaishou'
| 'mp-lark'
| 'mp-qq'
| 'mp-toutiao'
| 'mp-weixin'
| 'quickapp-webview'
| 'quickapp-webview-huawei'
| 'quickapp-webview-union'
declare const __VITE_APP_PROXY__: 'true' | 'false'
declare namespace JSX {
interface IntrinsicElements {
template: any
block: any
}
}
+8
View File
@@ -0,0 +1,8 @@
export {}
declare module 'vue' {
interface ComponentCustomProperties {
$t: (key: string, opt?: Record<string, any>) => string
$tm: (key: string, opt?: Record<string, any>) => [] | { [p: string]: any }
}
}
+8
View File
@@ -0,0 +1,8 @@
/// <reference types='@dcloudio/types' />
import 'vue'
declare module '@vue/runtime-core' {
type Hooks = App.AppInstance & Page.PageInstance
interface ComponentCustomOptions extends Hooks {}
}
+25
View File
@@ -0,0 +1,25 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by vite-plugin-uni-pages
interface NavigateToOptions {
url: "/pages/index/index" |
"/pages/about/about" |
"/pages/about/i18n" |
"/pages-sub/demo/index";
}
interface RedirectToOptions extends NavigateToOptions {}
interface SwitchTabOptions {
url: "/pages/index/index" | "/pages/about/about"
}
type ReLaunchOptions = NavigateToOptions | SwitchTabOptions;
declare interface Uni {
navigateTo(options: UniNamespace.NavigateToOptions & NavigateToOptions): void;
redirectTo(options: UniNamespace.RedirectToOptions & RedirectToOptions): void;
switchTab(options: UniNamespace.SwitchTabOptions & SwitchTabOptions): void;
reLaunch(options: UniNamespace.ReLaunchOptions & ReLaunchOptions): void;
}
+29
View File
@@ -0,0 +1,29 @@
// 全局要用的类型放到这里
type IResData<T> = {
code: number
msg: string
data: T
}
// uni.uploadFile文件上传参数
type IUniUploadFileOptions = {
file?: File
files?: UniApp.UploadFileOptionFiles[]
filePath?: string
name?: string
formData?: any
}
type IUserInfo = {
nickname?: string
avatar?: string
/** 微信的 openid,非微信没有这个字段 */
openid?: string
token?: string
}
enum TestEnum {
A = 'a',
B = 'b',
}
+77
View File
@@ -0,0 +1,77 @@
/* stylelint-disable comment-empty-line-before */
/**
* 这里是uni-app内置的常用样式变量
*
* uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量
* 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App
*
*/
/**
* 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能
*
* 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件
*/
/* 颜色变量 */
/* 行为相关颜色 */
$uni-color-primary: #007aff;
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
/* 文字基本颜色 */
$uni-text-color: #333; // 基本色
$uni-text-color-inverse: #fff; // 反色
$uni-text-color-grey: #999; // 辅助灰色,如加载更多的提示信息
$uni-text-color-placeholder: #808080;
$uni-text-color-disable: #c0c0c0;
/* 背景颜色 */
$uni-bg-color: #fff;
$uni-bg-color-grey: #f8f8f8;
$uni-bg-color-hover: #f1f1f1; // 点击状态颜色
$uni-bg-color-mask: rgb(0 0 0 / 40%); // 遮罩颜色
/* 边框颜色 */
$uni-border-color: #c8c7cc;
/* 尺寸变量 */
/* 文字尺寸 */
$uni-font-size-sm: 12px;
$uni-font-size-base: 14px;
$uni-font-size-lg: 16;
/* 图片尺寸 */
$uni-img-size-sm: 20px;
$uni-img-size-base: 26px;
$uni-img-size-lg: 40px;
/* Border Radius */
$uni-border-radius-sm: 2px;
$uni-border-radius-base: 3px;
$uni-border-radius-lg: 6px;
$uni-border-radius-circle: 50%;
/* 水平间距 */
$uni-spacing-row-sm: 5px;
$uni-spacing-row-base: 10px;
$uni-spacing-row-lg: 15px;
/* 垂直间距 */
$uni-spacing-col-sm: 4px;
$uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
/* 透明度 */
$uni-opacity-disabled: 0.3; // 组件禁用态的透明度
/* 文章场景相关 */
$uni-color-title: #2c405a; // 文章标题颜色
$uni-font-size-title: 20px;
$uni-color-subtitle: #555; // 二级标题颜色
$uni-font-size-subtitle: 18px;
$uni-color-paragraph: #3f536e; // 文章段落颜色
$uni-font-size-paragraph: 15px;
View File
+80
View File
@@ -0,0 +1,80 @@
import { CustomRequestOptions } from '@/interceptors/request'
export const http = <T>(options: CustomRequestOptions) => {
// 1. 返回 Promise 对象
return new Promise<IResData<T>>((resolve, reject) => {
uni.request({
...options,
dataType: 'json',
// #ifndef MP-WEIXIN
responseType: 'json',
// #endif
// 响应成功
success(res) {
// 状态码 2xx,参考 axios 的设计
if (res.statusCode >= 200 && res.statusCode < 300) {
// 2.1 提取核心数据 res.data
resolve(res.data as IResData<T>)
} else if (res.statusCode === 401) {
// 401错误 -> 清理用户信息,跳转到登录页
// userStore.clearUserInfo()
// uni.navigateTo({ url: '/pages/login/login' })
reject(res)
} else {
// 其他错误 -> 根据后端错误信息轻提示
!options.hideErrorToast &&
uni.showToast({
icon: 'none',
title: (res.data as IResData<T>).msg || '请求错误',
})
reject(res)
}
},
// 响应失败
fail(err) {
uni.showToast({
icon: 'none',
title: '网络错误,换个网络试试',
})
reject(err)
},
})
})
}
/**
* GET 请求
* @param url 后台地址
* @param query 请求query参数
* @returns
*/
export const httpGet = <T>(url: string, query?: Record<string, any>) => {
return http<T>({
url,
query,
method: 'GET',
})
}
/**
* POST 请求
* @param url 后台地址
* @param data 请求body参数
* @param query 请求query参数,post请求也支持query,很多微信接口都需要
* @returns
*/
export const httpPost = <T>(
url: string,
data?: Record<string, any>,
query?: Record<string, any>,
) => {
return http<T>({
url,
query,
data,
method: 'POST',
})
}
http.get = httpGet
http.post = httpPost
+10
View File
@@ -0,0 +1,10 @@
import { t } from '@/locale/index'
/** 非vue 文件使用 i18n */
export const testI18n = () => {
// 下面同样生效
uni.showModal({
title: 'i18n 测试',
content: t('app.name'),
})
}
+178
View File
@@ -0,0 +1,178 @@
import { pages, subPackages, tabBar } from '@/pages.json'
import { isMp } from './platform'
const getLastPage = () => {
// getCurrentPages() 至少有1个元素,所以不再额外判断
// const lastPage = getCurrentPages().at(-1)
// 上面那个在低版本安卓中打包回报错,所以改用下面这个【虽然我加了src/interceptions/prototype.ts,但依然报错】
const pages = getCurrentPages()
return pages[pages.length - 1]
}
/** 判断当前页面是否是tabbar页 */
export const getIsTabbar = () => {
if (!tabBar) {
return false
}
if (!tabBar.list.length) {
// 通常有tabBar的话,list不能有空,且至少有2个元素,这里其实不用处理
return false
}
const lastPage = getLastPage()
const currPath = lastPage.route
return !!tabBar.list.find((e) => e.pagePath === currPath)
}
/**
* 获取当前页面路由的 path 路径和 redirectPath 路径
* path 如 /pages/login/index
* redirectPath 如 /pages/demo/base/route-interceptor
*/
export const currRoute = () => {
const lastPage = getLastPage()
const currRoute = (lastPage as any).$page
// console.log('lastPage.$page:', currRoute)
// console.log('lastPage.$page.fullpath:', currRoute.fullPath)
// console.log('lastPage.$page.options:', currRoute.options)
// console.log('lastPage.options:', (lastPage as any).options)
// 经过多端测试,只有 fullPath 靠谱,其他都不靠谱
const { fullPath } = currRoute as { fullPath: string }
// console.log(fullPath)
// eg: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor (小程序)
// eg: /pages/login/index?redirect=%2Fpages%2Froute-interceptor%2Findex%3Fname%3Dfeige%26age%3D30(h5)
return getUrlObj(fullPath)
}
const ensureDecodeURIComponent = (url: string) => {
if (url.startsWith('%')) {
return ensureDecodeURIComponent(decodeURIComponent(url))
}
return url
}
/**
* 解析 url 得到 path 和 query
* 比如输入url: /pages/login/index?redirect=%2Fpages%2Fdemo%2Fbase%2Froute-interceptor
* 输出: {path: /pages/login/index, query: {redirect: /pages/demo/base/route-interceptor}}
*/
export const getUrlObj = (url: string) => {
const [path, queryStr] = url.split('?')
// console.log(path, queryStr)
if (!queryStr) {
return {
path,
query: {},
}
}
const query: Record<string, string> = {}
queryStr.split('&').forEach((item) => {
const [key, value] = item.split('=')
// console.log(key, value)
query[key] = ensureDecodeURIComponent(value) // 这里需要统一 decodeURIComponent 一下,可以兼容h5和微信y
})
return { path, query }
}
/**
* 得到所有的需要登录的pages,包括主包和分包的
* 这里设计得通用一点,可以传递key作为判断依据,默认是 needLogin, 与 route-block 配对使用
* 如果没有传 key,则表示所有的pages,如果传递了 key, 则表示通过 key 过滤
*/
export const getAllPages = (key = 'needLogin') => {
// 这里处理主包
const mainPages = [
...pages
.filter((page) => !key || page[key])
.map((page) => ({
...page,
path: `/${page.path}`,
})),
]
// 这里处理分包
const subPages: any[] = []
subPackages.forEach((subPageObj) => {
// console.log(subPageObj)
const { root } = subPageObj
subPageObj.pages
.filter((page) => !key || page[key])
.forEach((page: { path: string } & Record<string, any>) => {
subPages.push({
...page,
path: `/${root}/${page.path}`,
})
})
})
const result = [...mainPages, ...subPages]
// console.log(`getAllPages by ${key} result: `, result)
return result
}
/**
* 得到所有的需要登录的pages,包括主包和分包的
* 只得到 path 数组
*/
export const getNeedLoginPages = (): string[] => getAllPages('needLogin').map((page) => page.path)
/**
* 得到所有的需要登录的pages,包括主包和分包的
* 只得到 path 数组
*/
export const needLoginPages: string[] = getAllPages('needLogin').map((page) => page.path)
/**
* 根据微信小程序当前环境,判断应该获取的BaseUrl
*/
export const getEnvBaseUrl = () => {
// 请求基准地址
let baseUrl = import.meta.env.VITE_SERVER_BASEURL
// 小程序端环境区分
if (isMp) {
const {
miniProgram: { envVersion },
} = uni.getAccountInfoSync()
switch (envVersion) {
case 'develop':
baseUrl = 'https://ukw0y1.laf.run'
break
case 'trial':
baseUrl = 'https://ukw0y1.laf.run'
break
case 'release':
baseUrl = 'https://ukw0y1.laf.run'
break
}
}
return baseUrl
}
/**
* 根据微信小程序当前环境,判断应该获取的UPLOAD_BASEURL
*/
export const getEnvBaseUploadUrl = () => {
// 请求基准地址
let baseUploadUrl = import.meta.env.VITE_UPLOAD_BASEURL
// 小程序端环境区分
if (isMp) {
const {
miniProgram: { envVersion },
} = uni.getAccountInfoSync()
switch (envVersion) {
case 'develop':
baseUploadUrl = 'https://ukw0y1.laf.run/upload'
break
case 'trial':
baseUploadUrl = 'https://ukw0y1.laf.run/upload'
break
case 'release':
baseUploadUrl = 'https://ukw0y1.laf.run/upload'
break
}
}
return baseUploadUrl
}
+17
View File
@@ -0,0 +1,17 @@
/*
* @Author: 菲鸽
* @Date: 2024-03-28 19:13:55
* @Last Modified by: 菲鸽
* @Last Modified time: 2024-03-28 19:24:55
*/
export const platform = __UNI_PLATFORM__
export const isH5 = __UNI_PLATFORM__ === 'h5'
export const isApp = __UNI_PLATFORM__ === 'app'
export const isMp = __UNI_PLATFORM__.startsWith('mp-')
const PLATFORM = {
platform,
isH5,
isApp,
isMp,
}
export default PLATFORM
+38
View File
@@ -0,0 +1,38 @@
{
"compilerOptions": {
"composite": true,
"skipLibCheck": true,
"module": "ESNext",
"moduleResolution": "Node",
"resolveJsonModule": true,
"noImplicitThis": true,
"allowSyntheticDefaultImports": true,
"allowJs": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"outDir": "dist",
"lib": ["esnext", "dom"],
"types": [
"@dcloudio/types",
"@uni-helper/uni-types",
"@types/wechat-miniprogram",
"wot-design-uni/global.d.ts"
]
},
"vueCompilerOptions": {
"plugins": ["@uni-helper/uni-types/volar-plugin"]
},
"exclude": ["node_modules"],
"include": [
"src/**/*.ts",
"src/**/*.js",
"src/**/*.d.ts",
"src/**/*.tsx",
"src/**/*.jsx",
"src/**/*.vue",
"src/**/*.json"
]
}
+91
View File
@@ -0,0 +1,91 @@
// uno.config.ts
import {
type Preset,
defineConfig,
presetUno,
presetAttributify,
presetIcons,
transformerDirectives,
transformerVariantGroup,
} from 'unocss'
import { presetApplet, presetRemRpx, transformerAttributify } from 'unocss-applet'
import presetRemToPx from '@unocss/preset-rem-to-px'
// @see https://unocss.dev/presets/legacy-compat
// import { presetLegacyCompat } from '@unocss/preset-legacy-compat'
const isMp = process.env?.UNI_PLATFORM?.startsWith('mp') ?? false
const presets: Preset[] = []
if (isMp) {
// 使用小程序预设
presets.push(presetApplet(), presetRemRpx())
} else {
presets.push(
// 非小程序用官方预设
presetUno(),
// 支持css class属性化
presetAttributify(),
presetRemToPx({ baseFontSize: 4 }),
)
}
export default defineConfig({
presets: [
...presets,
// 支持图标,需要搭配图标库,eg: @iconify-json/carbon, 使用 `<button class="i-carbon-sun dark:i-carbon-moon" />`
presetIcons({
scale: 1.2,
warn: true,
extraProperties: {
display: 'inline-block',
'vertical-align': 'middle',
},
}),
// 将颜色函数 (rgb()和hsl()) 从空格分隔转换为逗号分隔,更好的兼容性app端,example:
// `rgb(255 0 0)` -> `rgb(255, 0, 0)`
// `rgba(255 0 0 / 0.5)` -> `rgba(255, 0, 0, 0.5)`
// 与群友的正常写法冲突,先去掉!(2024-05-25)
// presetLegacyCompat({
// commaStyleColorFunction: true,
// }) as Preset,
],
/**
* 自定义快捷语句
* @see https://github.com/unocss/unocss#shortcuts
*/
shortcuts: [['center', 'flex justify-center items-center']],
transformers: [
// 启用 @apply 功能
transformerDirectives(),
// 启用 () 分组功能
// 支持css class组合,eg: `<div class="hover:(bg-gray-400 font-medium) font-(light mono)">测试 unocss</div>`
transformerVariantGroup(),
// Don't change the following order
transformerAttributify({
// 解决与第三方框架样式冲突问题
prefixedOnly: true,
prefix: 'fg',
}),
],
rules: [
[
'p-safe',
{
padding:
'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',
},
],
['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],
['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],
],
})
/**
* 最终这一套组合下来会得到:
* mp 里面:mt-4 => margin-top: 32rpx == 16px
* h5 里面:mt-4 => margin-top: 1rem == 16px
*
* 如果是传统方式写样式,则推荐设计稿设置为 750,这样设计稿1px,代码写1rpx。
* rpx是响应式的,可以让不同设备的屏幕显示效果保持一致。
*/

Some files were not shown because too many files have changed in this diff Show More