|
@ -0,0 +1,17 @@
|
|||
# .editorconfig 文件
|
||||
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 # 关闭末尾空格修剪
|
||||
|
||||
[*.json]
|
||||
insert_final_newline = false
|
|
@ -0,0 +1 @@
|
|||
uni_modules/
|
|
@ -0,0 +1,91 @@
|
|||
{
|
||||
"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,
|
||||
"useSlots": true,
|
||||
"watch": true,
|
||||
"watchEffect": true,
|
||||
"watchPostEffect": true,
|
||||
"watchSyncEffect": true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
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-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,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
# 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
|
||||
|
||||
# unplugin-auto-import 生成的类型文件
|
||||
# auto-import.d.ts
|
||||
# vite-plugin-uni-pages 生成的类型文件
|
||||
uni-pages.d.ts
|
||||
# 插件生成的文件
|
||||
pages.json
|
||||
manifest.json
|
||||
|
||||
# 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 . ## 针对所有文件
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install commitlint --edit
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install -- lint-staged
|
|
@ -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
|
|
@ -0,0 +1,2 @@
|
|||
# unplugin-auto-import 生成的类型文件,每次提交都改变,所以加入这里吧,与 .gitignore 配合使用
|
||||
auto-import.d.ts
|
|
@ -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',
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
uni_modules/
|
|
@ -0,0 +1,57 @@
|
|||
// .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'],
|
||||
},
|
||||
],
|
||||
'unit-no-unknown': [
|
||||
true,
|
||||
{
|
||||
ignoreUnits: ['rpx'],
|
||||
},
|
||||
],
|
||||
// 处理小程序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,
|
||||
},
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"vue.volar",
|
||||
"stylelint.vscode-stylelint",
|
||||
"esbenp.prettier-vscode",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"antfu.unocss",
|
||||
"antfu.iconify",
|
||||
"evils.uniapp-vscode",
|
||||
"mrmaoddxxaa.create-uniapp-view",
|
||||
"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",
|
||||
"uni-helper.uni-cloud-snippets-vscode"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
// 默认格式化工具选择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",
|
||||
"feige",
|
||||
"qrcode",
|
||||
"refresherrefresh",
|
||||
"safeareainsets",
|
||||
"scrolltolower",
|
||||
"tabbar",
|
||||
"unibest",
|
||||
"uvui",
|
||||
"WechatMiniprogram"
|
||||
],
|
||||
"typescript.tsdk": "node_modules\\typescript\\lib",
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
// 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": [
|
||||
"<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"],
|
||||
},
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<script>
|
||||
export default {
|
||||
onLaunch() {
|
||||
console.log('App Launch')
|
||||
},
|
||||
onShow() {
|
||||
console.log('App Show')
|
||||
},
|
||||
onHide() {
|
||||
console.log('App Hide')
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* 每个页面公共css */
|
||||
</style>
|
|
@ -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.
|
|
@ -0,0 +1,115 @@
|
|||
<p align="center">
|
||||
<a href="https://github.com/codercup/unibest">
|
||||
<img width="160" src="./static/logo.svg">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<h1 align="center">
|
||||
<a href="https://github.com/codercup/unibest" target="_blank">unibest - 最好的 uniapp 跨端解决方案</a>
|
||||
</h1>
|
||||
|
||||
<div align="center">
|
||||
|
||||
[![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)
|
||||
[![star](https://gitee.com/codercup/unibest/badge/star.svg?theme=dark)](https://gitee.com/codercup/unibest/stargazers)
|
||||
[![fork](https://gitee.com/codercup/unibest/badge/fork.svg?theme=dark)](https://gitee.com/codercup/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/codercup/unibest)
|
||||
![GitHub License](https://img.shields.io/github/license/codercup/unibest)
|
||||
|
||||
</div>
|
||||
|
||||
`unibest` 是一个 uniapp 跨端解决方案,由 `uniapp` + `Vue3` + `Ts` + `Vite4` + `UnoCss` + `VSCode`(可选 `webstorm`) 实现。它使用了最新的前端技术栈,可以通过命令行方式运行 `web`、`小程序` 和 `App`,同时也支持 `HBuilderX` 运行,当前版本为 `HBuilderX` 运行版本。如需通过命令行运行,请安装命令行版(<a href="https://github.com/codercup/unibest" target="_blank">unibest</a>)。
|
||||
|
||||
`unibest` 内置了 `约定式路由`、`layout布局`、`请求封装`、`请求拦截`、`登录拦截`、`UnoCSS`、`i18n多语言` 等基础功能,提供了 `代码提示`、`自动格式化`、`统一配置`、`代码片段` 等辅助功能,让你编写 `uniapp` 拥有 `best` 体验 ( `unibest 的由来`)。
|
||||
|
||||
![](https://raw.githubusercontent.com/andreasbm/readme/master/assets/lines/rainbow.png)
|
||||
|
||||
<p align="center">
|
||||
<a href="https://codercup.github.io/unibest/" target="_blank">📱 在线预览</a>
|
||||
<span style="margin:0 10px;">|</span>
|
||||
<a href="https://codercup.github.io/unibest-docs/base/13-hbx" target="_blank">📖 阅读文档</a>
|
||||
</p>
|
||||
|
||||
## ✨ 特性
|
||||
|
||||
- ⚡️ [Vue 3](https://github.com/vuejs/core), [Vite](https://github.com/vitejs/vite), [pnpm](https://pnpm.io/), [esbuild](https://github.com/evanw/esbuild) - 就是快!
|
||||
- 🔥 最新语法 - `<script lang="ts" setup>` 语法
|
||||
- 🎨 [UnoCSS](https://unocss.dev/) - 高性能且极具灵活性的即时原子化 CSS 引擎
|
||||
- 😃 [UnoCSS Icons](https://unocss.dev/presets/icons) & [icones](https://icones.js.org/) - 海量图标供你选择
|
||||
- 🍍 [pinia](https://pinia.vuejs.org/) & [pinia-plugin-persistedstate](https://prazdevs.github.io/pinia-plugin-persistedstate/zh/guide/) - 全端适配的全局数据管理
|
||||
- 🗂 `uni.request` 请求封装 - 一键引入,快捷使用
|
||||
- 📦 [组件自动化加载](./src/components) - 可配置化的组件加载方式,轻松加载组件
|
||||
- 📥 [API 自动加载](https://github.com/antfu/unplugin-auto-import) - 直接使用 Composition API 无需引入
|
||||
- 🎉 `v3` Code Snippets 加快你的页面生成
|
||||
- 🦾 [TypeScript](https://www.typescriptlang.org/) & [ESLint](https://eslint.org/) & [stylelint](https://stylelint.io/) - 保证代码质量
|
||||
- 🌈 [husky](https://typicode.github.io/husky/) & [lint-staged](https://github.com/lint-staged/lint-staged) + [commitlint](https://commitlint.js.org/) - 保证代码提交质量
|
||||
- 💡 `ES6 import` 自动排序,`css 属性` 自动排序,增强编码一致性
|
||||
|
||||
- 🖥 `多环境` 配置分开,想则怎么配置就怎么配置
|
||||
|
||||
<p align="center">
|
||||
<a href="https://cn.vuejs.org/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/-Vue3-34495e?logo=vue.js" />
|
||||
</a>
|
||||
<a href="https://cn.vitejs.dev/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/-Vite4-646cff?logo=vite&logoColor=white" />
|
||||
</a>
|
||||
<a href="https://www.typescriptlang.org/zh/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/-TypeScript5-blue?logo=typescript&logoColor=white" />
|
||||
</a>
|
||||
<a href="https://eslint.org/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/-ESLint8-4b32c3?logo=eslint&logoColor=white" />
|
||||
</a>
|
||||
<a href="https://stylelint.io/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/-StyleLint16-4b32c3?logo=stylelint&logoColor=white" />
|
||||
</a>
|
||||
<a href="https://pnpm.io/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/-pnpm8-F69220?logo=pnpm&logoColor=white" />
|
||||
</a>
|
||||
<a href="https://unocss.dev/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/-UnoCSS-4d4d4d?logo=unocss" />
|
||||
</a>
|
||||
<a href="https://icones.js.org/" target="_blank">
|
||||
<img src="https://img.shields.io/badge/-icones-1769aa?logo=Iconify" />
|
||||
</a>
|
||||
<a href="https://ext.dcloud.net.cn/plugin?id=8559" target="_blank">
|
||||
<img src="https://svg.hamm.cn/badge.svg?key=Platform&value=uni-app"/>
|
||||
</a>
|
||||
</p>
|
||||
|
||||
## ⚙️ 环境
|
||||
|
||||
- node>=18
|
||||
- pnpm>=7.30
|
||||
|
||||
## 📂 安装
|
||||
|
||||
```bash
|
||||
# HBuilderX 模板,方便使用 uniCloud 云开发 (未来可以对接 uni-app x)
|
||||
pnpm create unibest my-project -t hbx-base # hbx的base模板
|
||||
pnpm create unibest my-project -t hbx-demo # hbx的demo模板,包含所有的demo
|
||||
|
||||
# 执行 `pnpm i` 安装依赖
|
||||
pnpm i
|
||||
```
|
||||
|
||||
## 📦 使用 `HBuilderX` 打开
|
||||
|
||||
在 `HBuilderX` 运行(支持热更新)或 编译
|
||||
|
||||
## 📄 License
|
||||
|
||||
[MIT](https://opensource.org/license/mit/)
|
||||
|
||||
Copyright (c) 2024 菲鸽
|
||||
|
||||
## 贡献者
|
||||
|
||||
由仓库直接生成:
|
||||
|
||||
<a href="https://github.com/codercup/unibest-hbx/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=codercup/unibest-hbx" />
|
||||
</a>
|
|
@ -0,0 +1,51 @@
|
|||
// commitlint.config.cjs
|
||||
|
||||
module.exports = {
|
||||
extends: ['@commitlint/config-conventional'],
|
||||
rules: {
|
||||
// 'body-leading-blank': [2, 'always'], // 主体前有空行,默认就是 always
|
||||
// 'footer-leading-blank': [2, 'always'], // 末行前有空行,默认就是 always
|
||||
// 'header-max-length': [2, 'always', 108], // 首行最大长度,默认就是 always,72
|
||||
// 'subject-empty': [2, 'never'], // 标题不可为空,默认就是 never
|
||||
// 'type-empty': [2, 'never'], // 类型不可为空,默认就是 never
|
||||
|
||||
// 允许的类型
|
||||
'type-enum': [
|
||||
2,
|
||||
'always',
|
||||
[
|
||||
'build', // 构造工具、外部依赖(webpack、npm)
|
||||
'chore', // 不涉及 src、test 的其他修改(构建过程或辅助工具的变更)
|
||||
'ci', // 修改项目继续集成流程(Travis,Jenkins,GitLab CI,Circle等)
|
||||
'docs', // 文档
|
||||
'feat', // 新增功能
|
||||
'fix', // bug 修复
|
||||
'perf', // 性能优化
|
||||
'refactor', // 重构
|
||||
'revert', // 回退
|
||||
'style', // 代码风格(不影响代码含义)
|
||||
'test', // 测试
|
||||
|
||||
// 下面几个是自定义新增的
|
||||
'wip', // 开发中
|
||||
'refine', // 小优化,没有到 refactor 的程度
|
||||
],
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// @see https://commitlint.js.org/#/reference-rules?id=type-enum
|
||||
// 默认值为:
|
||||
// [
|
||||
// 'build',
|
||||
// 'chore',
|
||||
// 'ci',
|
||||
// 'docs',
|
||||
// 'feat',
|
||||
// 'fix',
|
||||
// 'perf',
|
||||
// 'refactor',
|
||||
// 'revert',
|
||||
// 'style',
|
||||
// 'test',
|
||||
// ];
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<view class="fly-content">
|
||||
<view v-for="n in line" :key="n" class="h-10 leading-10 text-center">
|
||||
很多内容,这里是第{{ n }}行
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
withDefaults(defineProps<{ line?: number }>(), { line: 10 })
|
||||
</script>
|
|
@ -0,0 +1,3 @@
|
|||
<template>
|
||||
<view class="text-green-500"> header </view>
|
||||
</template>
|
|
@ -0,0 +1,7 @@
|
|||
# fly-login
|
||||
|
||||
点击“点击显示微信头像”按钮后,出现的半屏登录弹窗,可以在任意页面引入。
|
||||
|
||||
仿“掘金小册”小程序。
|
||||
|
||||
![掘金小册登录](screenshot.png)
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,120 @@
|
|||
<template>
|
||||
<view class="fly-login" v-if="modelValue">
|
||||
<view class="fly-login-mask" />
|
||||
<view class="fly-login-content px-4">
|
||||
<view class="font-bold h-16 leading-16">获取您的昵称、头像</view>
|
||||
<view
|
||||
class="rounded-full bg-light-600 w-6 h-6 text-center absolute top-4 right-4"
|
||||
@click="onClose"
|
||||
>
|
||||
<view class="i-carbon-close text-gray-700" />
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex items-center h-16 leading-16 border-b-gray-400 border-b-solid border-[1rpx]"
|
||||
>
|
||||
<text class="mr-4 flex-shrink-0">头像</text>
|
||||
<button
|
||||
class="bg-transparent flex items-center after:b-none w-full h-12 leading-12"
|
||||
open-type="chooseAvatar"
|
||||
@chooseavatar="onChooseAvatar"
|
||||
>
|
||||
<image class="w-8 h-8 rounded-full" :src="avatarUrl"></image>
|
||||
<text class="ml-auto i-carbon-chevron-right"></text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<view
|
||||
class="flex items-center h-16 leading-16 border-b-gray-400 border-b-solid border-1 mt-4"
|
||||
>
|
||||
<text class="mr-4 flex-shrink-0">昵称</text>
|
||||
<input type="nickname" placeholder="请输入昵称" @change="onChange" @blur="onChange" />
|
||||
</view>
|
||||
|
||||
<button
|
||||
size="default"
|
||||
type="default"
|
||||
style="color: #fff; background-color: #1aad19; border-color: #1aad19"
|
||||
class="text-center leading-12 w-40 my-4"
|
||||
@click="onSubmit"
|
||||
>
|
||||
确定
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { useUserStore } from '@/store'
|
||||
import defaultAvatarUrl from './defaultAvatar.png'
|
||||
|
||||
const emit = defineEmits(['update:modelValue'])
|
||||
defineProps<{ modelValue: boolean }>()
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const avatarUrl = ref(defaultAvatarUrl)
|
||||
const nickname = ref('')
|
||||
|
||||
const onClose = () => {
|
||||
emit('update:modelValue', false)
|
||||
}
|
||||
|
||||
const onChooseAvatar = (e) => {
|
||||
const { avatarUrl: url } = e.detail
|
||||
avatarUrl.value = url
|
||||
// 这里就要上传,加快速度,提升体验(用户多次选择头像就多次上传吧,总有取舍)
|
||||
console.log(url)
|
||||
}
|
||||
|
||||
const onChange = (e) => {
|
||||
const { value } = e.detail
|
||||
nickname.value = value
|
||||
console.log(value)
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
// 1、上传刚刚的图片,并返回网络地址
|
||||
// 2、把用户信息存起来
|
||||
if (avatarUrl.value === defaultAvatarUrl) {
|
||||
uni.showToast({
|
||||
title: '请选择头像',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
if (!nickname.value) {
|
||||
uni.showToast({
|
||||
title: '请填写昵称',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
emit('update:modelValue', false)
|
||||
console.log('保存用户信息')
|
||||
userStore.setUserInfo({ nickname: nickname.value, avatar: avatarUrl.value })
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fly-login {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
|
||||
.fly-login-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background-color: rgb(0 0 0 / 30%);
|
||||
}
|
||||
|
||||
.fly-login-content {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: var(--window-bottom);
|
||||
left: 0;
|
||||
background-color: #fff;
|
||||
border-top-left-radius: 16px;
|
||||
border-top-right-radius: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
After Width: | Height: | Size: 275 KiB |
|
@ -0,0 +1,3 @@
|
|||
# fly-navbar
|
||||
|
||||
建议本导航栏组件在设置 `"navigationStyle": "custom"` 的页面使用,目前支持微信小程序的页面滚动动画。
|
|
@ -0,0 +1,71 @@
|
|||
<template>
|
||||
<!-- 自定义导航栏: 默认透明不可见, scroll-view 滚动到 50 时展示 -->
|
||||
<view class="fly-navbar" :style="{ paddingTop: safeAreaInsets?.top + 'px' }">
|
||||
<!-- 1/3,多于1个页面,用返回图标 -->
|
||||
<navigator v-if="pages.length > 1" open-type="navigateBack" class="left-icon">
|
||||
<view class="bg-gray-500/80 rounded-full w-8 h-8 flex items-center justify-center">
|
||||
<button class="i-carbon-chevron-left text-white w-7 h-7"></button>
|
||||
</view>
|
||||
</navigator>
|
||||
<!-- 2/3,只有1个页面,如果不是tabbar,需要首页图标 -->
|
||||
<!-- 这种情况一般出现在用户直接打开分享出去的详情页面,或者使用redirectTo等API -->
|
||||
<navigator
|
||||
v-else-if="!isTabbar"
|
||||
open-type="switchTab"
|
||||
url="/pages/index/index"
|
||||
class="left-icon"
|
||||
>
|
||||
<view class="bg-gray-500/80 rounded-full w-8 h-8 flex items-center justify-center">
|
||||
<button class="i-carbon-home text-white w-6 h-6"></button>
|
||||
</view>
|
||||
</navigator>
|
||||
<!-- 3/3,如果当前页就是tabbar页,不用去首页,也就是什么图标都不需要 -->
|
||||
<view class="title">{{ title || '' }}</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { getIsTabbar } from '@/utils/index'
|
||||
|
||||
defineProps<{ title?: string }>()
|
||||
// 获取页面栈
|
||||
const pages = getCurrentPages()
|
||||
const isTabbar = getIsTabbar()
|
||||
console.log({ isTabbar, pagesLen: pages.length })
|
||||
|
||||
// 获取屏幕边界到安全区域距离
|
||||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fly-navbar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 9;
|
||||
width: 750rpx;
|
||||
color: #000;
|
||||
background-color: transparent;
|
||||
|
||||
.left-icon {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
font-size: 44rpx;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 44px;
|
||||
font-size: 32rpx;
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,21 @@
|
|||
/// <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
|
||||
readonly VITE_DELETE_CONSOLE: string
|
||||
// 更多环境变量...
|
||||
}
|
||||
|
||||
interface ImportMeta {
|
||||
readonly env: ImportMetaEnv
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
VITE_APP_TITLE = 'unibest'
|
||||
VITE_APP_PORT = 9000
|
||||
|
||||
# github actions 部署地址根路径,用在 manifest.config.ts 里面的 h5.router.base
|
||||
VITE_APP_PUBLIC_BASE=/
|
||||
|
||||
# TODO: 记得修改
|
||||
VITE_UNI_APPID = 'H5871D791'
|
||||
VITE_WX_APPID = 'wxa2abb91f64032a2b'
|
||||
|
||||
# fallback lacale:en, zh-Hans, zh-Hant 等
|
||||
# 必须要在 lacale 文件夹中配置对应的 json 文件!!!
|
||||
# 参考文档如下
|
||||
# https://uniapp.dcloud.net.cn/tutorial/i18n.html
|
||||
# https://uniapp.dcloud.net.cn/api/ui/locale.html#onlocalechange 打开页面后最下面的注意事项
|
||||
VITE_FALLBACK_LOCALE = 'zh-Hans'
|
||||
|
||||
VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
|
|
@ -0,0 +1,6 @@
|
|||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = false
|
||||
|
||||
# VITE_SERVER_BASEURL = 'https://ukw0y1.laf.run'
|
|
@ -0,0 +1,6 @@
|
|||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = true
|
||||
|
||||
# VITE_SERVER_BASEURL = 'https://xxx.com'
|
|
@ -0,0 +1,6 @@
|
|||
# 变量必须以 VITE_ 为前缀才能暴露给外部读取
|
||||
NODE_ENV = 'development'
|
||||
# 是否去除console 和 debugger
|
||||
VITE_DELETE_CONSOLE = false
|
||||
|
||||
# VITE_SERVER_BASEURL = 'https://xxx.com'
|
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,62 @@
|
|||
import { onReady } from '@dcloudio/uni-app'
|
||||
import { getIsTabbar, getArrElementByIdx } from '@/utils/index'
|
||||
|
||||
export default () => {
|
||||
// 获取页面栈
|
||||
const pages = getCurrentPages()
|
||||
const isTabbar = getIsTabbar()
|
||||
|
||||
// 页面滚动到底部时的操作,通常用于加载更多数据
|
||||
const onScrollToLower = () => {}
|
||||
// 获取屏幕边界到安全区域距离
|
||||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
||||
|
||||
// #ifdef MP-WEIXIN
|
||||
// 基于小程序的 Page 类型扩展 uni-app 的 Page
|
||||
type PageInstance = Page.PageInstance & WechatMiniprogram.Page.InstanceMethods<any>
|
||||
// 获取当前页面实例,数组最后一项
|
||||
const pageInstance = getArrElementByIdx(getCurrentPages(), -1) as PageInstance
|
||||
|
||||
// 页面渲染完毕,绑定动画效果
|
||||
onReady(() => {
|
||||
// 动画效果,导航栏背景色
|
||||
pageInstance.animate(
|
||||
'.fly-navbar',
|
||||
[{ backgroundColor: 'transparent' }, { backgroundColor: '#f8f8f8' }],
|
||||
1000,
|
||||
{
|
||||
scrollSource: '#scroller',
|
||||
timeRange: 1000,
|
||||
startScrollOffset: 0,
|
||||
endScrollOffset: 50,
|
||||
},
|
||||
)
|
||||
// 动画效果,导航栏标题
|
||||
pageInstance.animate(
|
||||
'.fly-navbar .title',
|
||||
[{ color: 'transparent' }, { color: '#000' }],
|
||||
1000,
|
||||
{
|
||||
scrollSource: '#scroller',
|
||||
timeRange: 1000,
|
||||
startScrollOffset: 0,
|
||||
endScrollOffset: 50,
|
||||
},
|
||||
)
|
||||
// 动画效果,导航栏返回按钮
|
||||
pageInstance.animate('.fly-navbar .left-icon', [{ color: '#fff' }, { color: '#000' }], 1000, {
|
||||
scrollSource: '#scroller',
|
||||
timeRange: 1000,
|
||||
startScrollOffset: 0,
|
||||
endScrollOffset: 50,
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
|
||||
return {
|
||||
pages,
|
||||
isTabbar,
|
||||
onScrollToLower,
|
||||
safeAreaInsets,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<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></title>
|
||||
<!--preload-links-->
|
||||
<!--app-context-->
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"><!--app-html--></div>
|
||||
<script type="module" src="/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,2 @@
|
|||
export { routeInterceptor } from './route'
|
||||
export { requestInterceptor } from './request'
|
|
@ -0,0 +1,53 @@
|
|||
/* eslint-disable no-param-reassign */
|
||||
import qs from 'qs'
|
||||
import { useUserStore } from '@/store'
|
||||
|
||||
export type CustomRequestOptions = UniApp.RequestOptions & {
|
||||
query?: Record<string, any>
|
||||
} & IUniUploadFileOptions // 添加uni.uploadFile参数类型
|
||||
|
||||
// 请求基地址
|
||||
const baseURL = import.meta.env.VITE_SERVER_BASEURL
|
||||
|
||||
// 拦截器配置
|
||||
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}`
|
||||
}
|
||||
}
|
||||
|
||||
// 1. 非 http 开头需拼接地址
|
||||
if (!options.url.startsWith('http')) {
|
||||
options.url = baseURL + options.url
|
||||
}
|
||||
// 2. 请求超时
|
||||
options.timeout = 10000 // 10s
|
||||
// 3. 添加小程序端请求头标识
|
||||
options.header = {
|
||||
platform: 'mp-weixin', // 可选值与 uniapp 定义的平台一致,告诉后台来源
|
||||
...options.header,
|
||||
}
|
||||
// 4. 添加 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)
|
||||
},
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
/**
|
||||
* 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
|
||||
}
|
||||
console.log(needLoginPages.includes(path))
|
||||
|
||||
if (needLoginPages.includes(path)) {
|
||||
const isLogin = isLogined()
|
||||
if (isLogin) {
|
||||
return true
|
||||
}
|
||||
const redirectRoute = `${loginRoute}?redirect=${encodeURIComponent(url)}`
|
||||
uni.navigateTo({ url: redirectRoute })
|
||||
return false
|
||||
}
|
||||
return true
|
||||
},
|
||||
}
|
||||
|
||||
export const routeInterceptor = {
|
||||
install() {
|
||||
uni.addInterceptor('navigateTo', navigateToInterceptor)
|
||||
uni.addInterceptor('reLaunch', navigateToInterceptor)
|
||||
uni.addInterceptor('redirectTo', navigateToInterceptor)
|
||||
},
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<template>
|
||||
<view class="default-layout">
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style lang="scss">
|
||||
.default-layout {
|
||||
height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,5 @@
|
|||
<template>
|
||||
<view class="text-center p-4">
|
||||
<slot />
|
||||
</view>
|
||||
</template>
|
|
@ -0,0 +1,15 @@
|
|||
import { createSSRApp } from 'vue'
|
||||
import App from './App.vue'
|
||||
import store from './store'
|
||||
import { routeInterceptor, requestInterceptor } from './interceptors'
|
||||
import 'virtual:uno.css'
|
||||
|
||||
export function createApp() {
|
||||
const app = createSSRApp(App)
|
||||
app.use(store)
|
||||
app.use(routeInterceptor)
|
||||
app.use(requestInterceptor)
|
||||
return {
|
||||
app,
|
||||
}
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
// 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.env.VITE_ROOT_DIR, 'env'))
|
||||
// console.log(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,
|
||||
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: {},
|
||||
ios: {},
|
||||
},
|
||||
},
|
||||
},
|
||||
/* 快应用特有相关 */
|
||||
quickapp: {},
|
||||
/* 小程序特有相关 */
|
||||
'mp-weixin': {
|
||||
appid: VITE_WX_APPID,
|
||||
setting: {
|
||||
urlCheck: false,
|
||||
},
|
||||
usingComponents: true,
|
||||
// __usePrivacyCheck__: true,
|
||||
},
|
||||
'mp-alipay': {
|
||||
usingComponents: true,
|
||||
},
|
||||
'mp-baidu': {
|
||||
usingComponents: true,
|
||||
},
|
||||
'mp-toutiao': {
|
||||
usingComponents: true,
|
||||
},
|
||||
uniStatistics: {
|
||||
enable: false,
|
||||
},
|
||||
vueVersion: '3',
|
||||
})
|
|
@ -0,0 +1,97 @@
|
|||
{
|
||||
"name": "my-project",
|
||||
"version": "0.2.0",
|
||||
"description": "unibest - 最好的 uniapp 开发模板, HBX 版本",
|
||||
"main": "main.js",
|
||||
"author": {
|
||||
"name": "codercup",
|
||||
"zhName": "菲鸽",
|
||||
"email": "1020103647@qq.com",
|
||||
"github": "https://github.com/codercup",
|
||||
"gitee": "https://gitee.com/codercup"
|
||||
},
|
||||
"license": "MIT",
|
||||
"repository": "https://github.com/codercup/unibest-hbx",
|
||||
"repository-gitee": "https://gitee.com/codercup/unibest-hbx",
|
||||
"bugs": {
|
||||
"url": "https://github.com/codercup/unibest-hbx/issues"
|
||||
},
|
||||
"homepage": "https://codercup.github.io/unibest-hbx/",
|
||||
"engines": {
|
||||
"node": ">=18",
|
||||
"pnpm": ">=7.30"
|
||||
},
|
||||
"scripts": {
|
||||
"prepare": "node ./shell/postinstall.js & git init && husky install"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{html,vue,ts,cjs,json,md}": [
|
||||
"prettier --write"
|
||||
],
|
||||
"**/*.{vue,js,ts,jsx,tsx}": [
|
||||
"eslint --fix"
|
||||
],
|
||||
"**/*.{vue,css,scss,html}": [
|
||||
"stylelint --fix"
|
||||
]
|
||||
},
|
||||
"resolutions": {
|
||||
"bin-wrapper": "npm:bin-wrapper-china"
|
||||
},
|
||||
"dependencies": {
|
||||
"dayjs": "^1.11.11",
|
||||
"pinia": "2.0.36",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"qs": "6.5.3",
|
||||
"vue": "3.4.21",
|
||||
"wot-design-uni": "^1.2.20"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@commitlint/cli": "^19.3.0",
|
||||
"@commitlint/config-conventional": "^19.2.2",
|
||||
"@dcloudio/types": "^3.4.8",
|
||||
"@dcloudio/vite-plugin-uni": "3.0.0-4010520240507001",
|
||||
"@iconify-json/carbon": "^1.1.33",
|
||||
"@types/node": "^20.12.12",
|
||||
"@types/wechat-miniprogram": "^3.4.7",
|
||||
"@typescript-eslint/eslint-plugin": "^7.9.0",
|
||||
"@typescript-eslint/parser": "^7.9.0",
|
||||
"@uni-helper/uni-app-types": "^0.5.13",
|
||||
"@uni-helper/uni-ui-types": "^0.5.14",
|
||||
"@uni-helper/vite-plugin-uni-layouts": "^0.1.8",
|
||||
"@uni-helper/vite-plugin-uni-manifest": "^0.2.6",
|
||||
"@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",
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"autoprefixer": "^10.4.19",
|
||||
"commitlint": "^19.3.0",
|
||||
"esbuild": "0.20.1",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.6.1",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-vue": "^9.26.0",
|
||||
"husky": "^9.0.11",
|
||||
"lint-staged": "^15.2.2",
|
||||
"postcss": "^8.4.38",
|
||||
"postcss-html": "^1.7.0",
|
||||
"postcss-scss": "^4.0.9",
|
||||
"sass": "^1.77.2",
|
||||
"stylelint": "^16.5.0",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-recess-order": "^5.0.1",
|
||||
"stylelint-config-recommended": "^14.0.0",
|
||||
"stylelint-config-recommended-scss": "^14.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.5.0",
|
||||
"stylelint-prettier": "^5.0.0",
|
||||
"terser": "^5.31.0",
|
||||
"unocss": "^0.58.9",
|
||||
"unocss-applet": "^0.7.8",
|
||||
"unplugin-auto-import": "^0.17.6",
|
||||
"vite": "5.2.8",
|
||||
"vite-plugin-restart": "^0.4.0"
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -0,0 +1,45 @@
|
|||
import { defineUniPages } from '@uni-helper/vite-plugin-uni-pages'
|
||||
|
||||
export default defineUniPages({
|
||||
globalStyle: {
|
||||
navigationStyle: 'default',
|
||||
navigationBarTitleText: 'unibest',
|
||||
navigationBarBackgroundColor: '#f8f8f8',
|
||||
navigationBarTextStyle: 'black',
|
||||
backgroundColor: '#FFFFFF',
|
||||
h5: {
|
||||
navigationStyle: 'custom',
|
||||
},
|
||||
},
|
||||
easycom: {
|
||||
autoscan: true,
|
||||
custom: {
|
||||
'^wd-(.*)': 'wot-design-uni/components/wd-$1/wd-$1.vue',
|
||||
'^layout-(.*)-uni': '@/layouts/$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: '首页',
|
||||
},
|
||||
{
|
||||
iconPath: 'static/tabbar/example.png',
|
||||
selectedIconPath: 'static/tabbar/exampleHL.png',
|
||||
pagePath: 'pages/index/about',
|
||||
text: '关于',
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
|
@ -0,0 +1,37 @@
|
|||
<route lang="json5">
|
||||
{
|
||||
layout: 'default',
|
||||
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-4">关于页面</view>
|
||||
<view class="text-center text-3xl mt-8">
|
||||
鸽友们好,我是
|
||||
<text class="text-red-500">菲鸽</text>
|
||||
</view>
|
||||
<view class="text-center mt-8">
|
||||
<wd-button type="primary">wot 组件库测试</wd-button>
|
||||
</view>
|
||||
<view class="desc">测试设计稿样式</view>
|
||||
<view class="desc">设计稿是750px,css里面全部写rpx 即可</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// 获取屏幕边界到安全区域距离
|
||||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.desc {
|
||||
width: 700rpx;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,75 @@
|
|||
<!-- 使用 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-center text-2xl mt-2 mb-8 text-green-400">hbx版(HBuilderX)</view>
|
||||
<view class="text-justify max-w-100 m-auto text-4 indent mb-2">{{ description }} </view>
|
||||
<view class="mt-8 text-center">
|
||||
<text class="text-green-400">在线文档:</text>
|
||||
<!-- #ifndef H5 -->
|
||||
https://codercup.github.io/unibest-docs/
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef H5 -->
|
||||
<a href="https://codercup.github.io/unibest-docs/" target="_blank">
|
||||
https://codercup.github.io/unibest-docs/
|
||||
</a>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
<view class="mt-4 text-center">
|
||||
<text class="text-green-400">在线预览:</text>
|
||||
<!-- #ifndef H5 -->
|
||||
https://codercup.github.io/unibest/
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef H5 -->
|
||||
<a href="https://codercup.github.io/unibest/" target="_blank">
|
||||
https://codercup.github.io/unibest/
|
||||
</a>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
// 获取屏幕边界到安全区域距离
|
||||
const { safeAreaInsets } = uni.getSystemInfoSync()
|
||||
|
||||
const author = ref('菲鸽')
|
||||
const description = ref(
|
||||
'unibest 是一个集成了多种工具和技术的 uniapp 开发模板,由 uniapp + Vue3 + Ts + Vite4 + UnoCss + UniUI + VSCode 构建,模板具有代码提示、自动格式化、统一配置、代码片段等功能,并内置了许多常用的基本组件和基本功能,让你编写 uniapp 拥有 best 体验。',
|
||||
)
|
||||
|
||||
onLoad(() => {
|
||||
console.log(author)
|
||||
})
|
||||
|
||||
/** 激活“分享给好友” */
|
||||
onShareAppMessage((options: Page.ShareAppMessageOption): Page.CustomShareContent => {
|
||||
console.log('options:', options)
|
||||
return {
|
||||
title: 'unibest',
|
||||
desc: 'unibest 演示示例',
|
||||
path: '/pages/index/index?id=xxx',
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.main-title-color {
|
||||
color: #d14328;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,4 @@
|
|||
export interface IFooItem {
|
||||
id: string
|
||||
name: string
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
import { http, uniFileUpload } from '@/utils/http'
|
||||
import type { IFooItem } from './foo.d'
|
||||
|
||||
export { IFooItem }
|
||||
|
||||
/** get 请求 */
|
||||
export const getFooAPI = (name: string) => {
|
||||
return http<IFooItem>({
|
||||
url: `/foo`,
|
||||
method: 'GET',
|
||||
query: { name },
|
||||
})
|
||||
}
|
||||
|
||||
/** get 请求 */
|
||||
export const postFooAPI = (name: string) => {
|
||||
return http<IFooItem>({
|
||||
url: `/foo`,
|
||||
method: 'POST',
|
||||
query: { name }, // post 请求也支持 query
|
||||
data: { name },
|
||||
})
|
||||
}
|
||||
|
||||
// 文件上传
|
||||
export const fileUpload = (data: IUniUploadFileOptions) => {
|
||||
return uniFileUpload({
|
||||
url: `/foo/upload`,
|
||||
method: 'POST',
|
||||
...data,
|
||||
})
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* 本文件会在依赖包安装时执行,用以生成 `src/manifest.json`
|
||||
* 如果不存在 `src/manifest.json` 会运行报错,提示找不到 `src/manifest.json`
|
||||
* 如果中途自己删除了 'src/manifest.json' 文件,记得手动执行本文件,可以右键 `Run Code` 快速执行
|
||||
*
|
||||
* 本文件是为了兼容 window 系统才生成的
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
const fs = require('fs')
|
||||
|
||||
const filePath = './manifest.json'
|
||||
|
||||
if (fs.existsSync(filePath)) {
|
||||
// console.log(`${filePath}存在`)
|
||||
} else {
|
||||
// console.log(`${filePath}不存在,需要创建`)
|
||||
fs.writeFile(filePath, '{"vueVersion": "3"}\n', {}, () => {
|
||||
// console.log(`${filePath}已经成功创建,并写入{}`)
|
||||
})
|
||||
}
|
||||
|
||||
const filePath2 = './pages.json'
|
||||
if (fs.existsSync(filePath2)) {
|
||||
// console.log(`${filePath}存在`)
|
||||
} else {
|
||||
// console.log(`${filePath}不存在,需要创建`)
|
||||
fs.writeFile(
|
||||
filePath2,
|
||||
`{
|
||||
"pages": [
|
||||
{
|
||||
"path": "pages/index/index",
|
||||
"type": "home",
|
||||
"style": {
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "首页"
|
||||
}
|
||||
}
|
||||
]
|
||||
}\n`,
|
||||
{},
|
||||
() => {
|
||||
// console.log(`${filePath}已经成功创建,并写入{}`)
|
||||
},
|
||||
)
|
||||
}
|
After Width: | Height: | Size: 3.9 KiB |
|
@ -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 |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.5 KiB |
|
@ -0,0 +1,28 @@
|
|||
// src/store/useCountStore.ts
|
||||
import { defineStore } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export const useCountStore = defineStore(
|
||||
'count',
|
||||
() => {
|
||||
const count = ref(0)
|
||||
const increment = () => {
|
||||
count.value++
|
||||
}
|
||||
const decrement = () => {
|
||||
count.value--
|
||||
}
|
||||
const reset = () => {
|
||||
count.value = 0
|
||||
}
|
||||
return {
|
||||
count,
|
||||
decrement,
|
||||
increment,
|
||||
reset,
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
)
|
|
@ -0,0 +1,18 @@
|
|||
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'
|
||||
export * from './count'
|
|
@ -0,0 +1,34 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { ref, computed } 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 }
|
||||
}
|
||||
const reset = () => {
|
||||
userInfo.value = { ...initState }
|
||||
}
|
||||
const isLogined = computed(() => !!userInfo.value.token)
|
||||
|
||||
return {
|
||||
userInfo,
|
||||
setUserInfo,
|
||||
clearUserInfo,
|
||||
isLogined,
|
||||
reset,
|
||||
}
|
||||
},
|
||||
{
|
||||
persist: true,
|
||||
},
|
||||
)
|
|
@ -0,0 +1,41 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"composite": true,
|
||||
"skipLibCheck": true,
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"resolveJsonModule": true,
|
||||
"noImplicitThis": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"allowJs": true,
|
||||
"sourceMap": true,
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": ["./*"]
|
||||
},
|
||||
"outDir": "dist",
|
||||
"lib": ["esnext", "dom"],
|
||||
"types": [
|
||||
"@dcloudio/types",
|
||||
"@types/wechat-miniprogram",
|
||||
"@uni-helper/uni-app-types",
|
||||
"@uni-helper/uni-ui-types",
|
||||
"wot-design-uni/global.d.ts"
|
||||
]
|
||||
},
|
||||
"vueCompilerOptions": {
|
||||
"target": 3,
|
||||
"nativeTags": ["block", "template", "component", "slot"]
|
||||
},
|
||||
"exclude": ["node_modules"],
|
||||
"include": [
|
||||
"./**/*.ts",
|
||||
"./**/*.js",
|
||||
"./**/*.d.ts",
|
||||
"./**/*.tsx",
|
||||
"./**/*.jsx",
|
||||
"./**/*.vue",
|
||||
"./**/*.json",
|
||||
"main.js"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,257 @@
|
|||
/* 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 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 useNavbarWeixin: typeof import('../hooks/useNavbarWeixin')['default']
|
||||
const useSlots: typeof import('vue')['useSlots']
|
||||
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, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, 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 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 useNavbarWeixin: UnwrapRef<typeof import('../hooks/useNavbarWeixin')['default']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
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']>
|
||||
}
|
||||
}
|
||||
declare module '@vue/runtime-core' {
|
||||
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 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 useNavbarWeixin: UnwrapRef<typeof import('../hooks/useNavbarWeixin')['default']>
|
||||
readonly useSlots: UnwrapRef<typeof import('vue')['useSlots']>
|
||||
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']>
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||
// 全局要用的类型放到这里
|
||||
|
||||
type IResData<T> = {
|
||||
code: number
|
||||
msg: string
|
||||
result: 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',
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* 这里是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: #ffffff;
|
||||
$uni-bg-color-grey: #f8f8f8;
|
||||
$uni-bg-color-hover: #f1f1f1; //点击状态颜色
|
||||
$uni-bg-color-mask: rgba(0, 0, 0, 0.4); //遮罩颜色
|
||||
|
||||
/* 边框颜色 */
|
||||
$uni-border-color: #c8c7cc;
|
||||
|
||||
/* 尺寸变量 */
|
||||
|
||||
/* 文字尺寸 */
|
||||
$uni-font-size-sm: 12px;
|
||||
$uni-font-size-base: 14px;
|
||||
$uni-font-size-lg: 16px;
|
||||
|
||||
/* 图片尺寸 */
|
||||
$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: #555555; // 二级标题颜色
|
||||
$uni-font-size-subtitle: 26px;
|
||||
$uni-color-paragraph: #3f536e; // 文章段落颜色
|
||||
$uni-font-size-paragraph: 15px;
|
|
@ -0,0 +1,96 @@
|
|||
// uno.config.ts
|
||||
import {
|
||||
type Preset,
|
||||
defineConfig,
|
||||
presetUno,
|
||||
presetAttributify,
|
||||
presetIcons,
|
||||
transformerDirectives,
|
||||
transformerVariantGroup,
|
||||
} from 'unocss'
|
||||
|
||||
import { presetApplet, presetRemRpx, transformerAttributify } from 'unocss-applet'
|
||||
|
||||
// @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(),
|
||||
)
|
||||
}
|
||||
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',
|
||||
},
|
||||
// 可以指定需要的图标集合
|
||||
collections: {
|
||||
carbon: () => import('@iconify-json/carbon').then((i) => i.icons as any),
|
||||
},
|
||||
}),
|
||||
// 将颜色函数 (rgb()和hsl()) 从空格分隔转换为逗号分隔,更好的兼容性app端,example:
|
||||
// `rgb(255 0 0)` -> `rgb(255, 0, 0)`
|
||||
// `rgba(255 0 0 / 0.5)` -> `rgba(255, 0, 0, 0.5)`
|
||||
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
|
||||
*
|
||||
* 另外,我们还可以推算出 UnoCSS 单位与设计稿差别4倍。
|
||||
* 375 * 4 = 1500,把设计稿设置为1500,那么设计稿里多少px,unocss就写多少述职。
|
||||
* 举个例子,设计稿显示某元素宽度100px,就写w-100即可。
|
||||
*
|
||||
* 如果是传统方式写样式,则推荐设计稿设置为 750,这样设计稿1px,代码写1rpx。
|
||||
* rpx是响应式的,可以让不同设备的屏幕显示效果保持一致。
|
||||
*/
|
|
@ -0,0 +1,45 @@
|
|||
/* layer: preflights */
|
||||
page,::before,::after{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: ;}::backdrop{--un-rotate:0;--un-rotate-x:0;--un-rotate-y:0;--un-rotate-z:0;--un-scale-x:1;--un-scale-y:1;--un-scale-z:1;--un-skew-x:0;--un-skew-y:0;--un-translate-x:0;--un-translate-y:0;--un-translate-z:0;--un-pan-x: ;--un-pan-y: ;--un-pinch-zoom: ;--un-scroll-snap-strictness:proximity;--un-ordinal: ;--un-slashed-zero: ;--un-numeric-figure: ;--un-numeric-spacing: ;--un-numeric-fraction: ;--un-border-spacing-x:0;--un-border-spacing-y:0;--un-ring-offset-shadow:0 0 rgb(0 0 0 / 0);--un-ring-shadow:0 0 rgb(0 0 0 / 0);--un-shadow-inset: ;--un-shadow:0 0 rgb(0 0 0 / 0);--un-ring-inset: ;--un-ring-offset-width:0px;--un-ring-offset-color:#fff;--un-ring-width:0px;--un-ring-color:rgb(147 197 253 / 0.5);--un-blur: ;--un-brightness: ;--un-contrast: ;--un-drop-shadow: ;--un-grayscale: ;--un-hue-rotate: ;--un-invert: ;--un-saturate: ;--un-sepia: ;--un-backdrop-blur: ;--un-backdrop-brightness: ;--un-backdrop-contrast: ;--un-backdrop-grayscale: ;--un-backdrop-hue-rotate: ;--un-backdrop-invert: ;--un-backdrop-opacity: ;--un-backdrop-saturate: ;--un-backdrop-sepia: ;}
|
||||
/* layer: icons */
|
||||
.i-carbon-car{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 32 32' display='inline-block' vertical-align='middle' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='m29.338 15.934l-7.732-2.779l-3.232-4.058A2.99 2.99 0 0 0 16.054 8H8.058a2.998 2.998 0 0 0-2.48 1.312l-2.712 3.983A4.988 4.988 0 0 0 2 16.107V24a1 1 0 0 0 1 1h2.142a3.98 3.98 0 0 0 7.716 0h6.284a3.98 3.98 0 0 0 7.716 0H29a1 1 0 0 0 1-1v-7.125a1 1 0 0 0-.662-.941M9 26a2 2 0 1 1 2-2a2.003 2.003 0 0 1-2 2m14 0a2 2 0 1 1 2-2a2.003 2.003 0 0 1-2 2m5-3h-1.142a3.98 3.98 0 0 0-7.716 0h-6.284a3.98 3.98 0 0 0-7.716 0H4v-6.893a2.998 2.998 0 0 1 .52-1.688l2.711-3.981A1 1 0 0 1 8.058 10h7.996a.993.993 0 0 1 .764.355l3.4 4.268a1 1 0 0 0 .444.318L28 17.578Z'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;vertical-align:middle;width:1.2em;height:1.2em;}
|
||||
.i-carbon-sun{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 32 32' display='inline-block' vertical-align='middle' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M16 12.005a4 4 0 1 1-4 4a4.005 4.005 0 0 1 4-4m0-2a6 6 0 1 0 6 6a6 6 0 0 0-6-6M5.394 6.813L6.81 5.399l3.505 3.506L8.9 10.319zM2 15.005h5v2H2zm3.394 10.193L8.9 21.692l1.414 1.414l-3.505 3.506zM15 25.005h2v5h-2zm6.687-1.9l1.414-1.414l3.506 3.506l-1.414 1.414zm3.313-8.1h5v2h-5zm-3.313-6.101l3.506-3.506l1.414 1.414l-3.506 3.506zM15 2.005h2v5h-2z'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;vertical-align:middle;width:1.2em;height:1.2em;}
|
||||
.i-carbon-user-avatar{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 32 32' display='inline-block' vertical-align='middle' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M16 8a5 5 0 1 0 5 5a5 5 0 0 0-5-5m0 8a3 3 0 1 1 3-3a3.003 3.003 0 0 1-3 3'/%3E%3Cpath fill='currentColor' d='M16 2a14 14 0 1 0 14 14A14.016 14.016 0 0 0 16 2m-6 24.377V25a3.003 3.003 0 0 1 3-3h6a3.003 3.003 0 0 1 3 3v1.377a11.899 11.899 0 0 1-12 0m13.993-1.451A5.002 5.002 0 0 0 19 20h-6a5.002 5.002 0 0 0-4.992 4.926a12 12 0 1 1 15.985 0'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;vertical-align:middle;width:1.2em;height:1.2em;}
|
||||
/* layer: applet_shortcuts */
|
||||
.dark .dark_a_i-carbon-moon{--un-icon:url("data:image/svg+xml;utf8,%3Csvg viewBox='0 0 32 32' display='inline-block' vertical-align='middle' width='1.2em' height='1.2em' xmlns='http://www.w3.org/2000/svg' %3E%3Cpath fill='currentColor' d='M13.503 5.414a15.076 15.076 0 0 0 11.593 18.194a11.113 11.113 0 0 1-7.975 3.39c-.138 0-.278.005-.418 0a11.094 11.094 0 0 1-3.2-21.584M14.98 3a1.002 1.002 0 0 0-.175.016a13.096 13.096 0 0 0 1.825 25.981c.164.006.328 0 .49 0a13.072 13.072 0 0 0 10.703-5.555a1.01 1.01 0 0 0-.783-1.565A13.08 13.08 0 0 1 15.89 4.38A1.015 1.015 0 0 0 14.98 3'/%3E%3C/svg%3E");-webkit-mask:var(--un-icon) no-repeat;mask:var(--un-icon) no-repeat;-webkit-mask-size:100% 100%;mask-size:100% 100%;background-color:currentColor;color:inherit;display:inline-block;vertical-align:middle;width:1.2em;height:1.2em;}
|
||||
/* layer: default */
|
||||
.m-8{margin:64rpx;}
|
||||
.m-auto{margin:auto;}
|
||||
.mx-auto{margin-left:auto;margin-right:auto;}
|
||||
.mb-2{margin-bottom:16rpx;}
|
||||
.mb-4{margin-bottom:32rpx;}
|
||||
.mb-8{margin-bottom:64rpx;}
|
||||
.ml-2{margin-left:16rpx;}
|
||||
.mr-2{margin-right:16rpx;}
|
||||
.mt-12{margin-top:96rpx;}
|
||||
.mt-2{margin-top:16rpx;}
|
||||
.mt-4{margin-top:32rpx;}
|
||||
.mt-8{margin-top:64rpx;}
|
||||
.block{display:block;}
|
||||
.h-28{height:224rpx;}
|
||||
.max-w-100{max-width:800rpx;}
|
||||
.w-20{width:160rpx;}
|
||||
.w-28{width:224rpx;}
|
||||
.flex{display:flex;}
|
||||
.items-center{align-items:center;}
|
||||
.justify-center{justify-content:center;}
|
||||
.overflow-hidden{overflow:hidden;}
|
||||
.bg-white{--un-bg-opacity:1;background-color:rgb(255 255 255 / var(--un-bg-opacity));}
|
||||
.p-4{padding:32rpx;}
|
||||
.px,
|
||||
.px-4{padding-left:32rpx;padding-right:32rpx;}
|
||||
.pt-2{padding-top:16rpx;}
|
||||
.text-center{text-align:center;}
|
||||
.text-justify{text-align:justify;}
|
||||
.indent{text-indent:48rpx;}
|
||||
.text-2xl{font-size:48rpx;line-height:64rpx;}
|
||||
.text-4{font-size:32rpx;}
|
||||
.text-4xl{font-size:72rpx;line-height:80rpx;}
|
||||
.text-blue-500{--un-text-opacity:1;color:rgb(59 130 246 / var(--un-text-opacity));}
|
||||
.text-green-400{--un-text-opacity:1;color:rgb(74 222 128 / var(--un-text-opacity));}
|
||||
.text-red{--un-text-opacity:1;color:rgb(248 113 113 / var(--un-text-opacity));}
|
||||
.leading-8{line-height:64rpx;}
|
|
@ -0,0 +1,81 @@
|
|||
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 {
|
||||
// 其他错误 -> 根据后端错误信息轻提示
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: (res.data as IResData<T>).msg || '请求错误',
|
||||
})
|
||||
reject(res)
|
||||
}
|
||||
},
|
||||
// 响应失败
|
||||
fail(err) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '网络错误,换个网络试试',
|
||||
})
|
||||
reject(err)
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// uni.uploadFile封装
|
||||
export const uniFileUpload = <T>(options: CustomRequestOptions) => {
|
||||
// 1. 返回 Promise 对象
|
||||
return new Promise<IResData<T>>((resolve, reject) => {
|
||||
uni.uploadFile({
|
||||
...options,
|
||||
// 响应成功
|
||||
success(res) {
|
||||
// 状态码 2xx,参考 axios 的设计
|
||||
if (res.statusCode >= 200 && res.statusCode < 300) {
|
||||
// 文件上传接口的rea.data的类型为string,这里转一下
|
||||
const resData = JSON.parse(res.data) as IResData<T>
|
||||
resolve(resData)
|
||||
} else if (res.statusCode === 401) {
|
||||
// 401错误 -> 清理用户信息,跳转到登录页
|
||||
// userStore.clearUserInfo()
|
||||
// uni.navigateTo({ url: '/pages/login/login' })
|
||||
reject(res)
|
||||
} else {
|
||||
// 其他错误 -> 根据后端错误信息轻提示
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '文件上传错误',
|
||||
})
|
||||
reject(res)
|
||||
}
|
||||
},
|
||||
// 响应失败
|
||||
fail(err) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: '网络错误,换个网络试试',
|
||||
})
|
||||
reject(err)
|
||||
},
|
||||
})
|
||||
})
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
import pagesJson from '@/pages.json'
|
||||
|
||||
console.log(pagesJson)
|
||||
|
||||
/** 判断当前页面是否是tabbar页 */
|
||||
export const getIsTabbar = () => {
|
||||
if (!Object.keys(pagesJson).includes('tabBar')) {
|
||||
return false
|
||||
}
|
||||
const pages = getCurrentPages()
|
||||
const currPath = pages.at(-1).route
|
||||
return !!pagesJson.tabBar.list.find((e) => e.pagePath === currPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前页面路由的 path 路劲和 redirectPath 路径
|
||||
* path 如 ‘/pages/login/index’
|
||||
* redirectPath 如 ‘/pages/demo/base/route-interceptor’
|
||||
*/
|
||||
export const currRoute = () => {
|
||||
const pages = getCurrentPages()
|
||||
console.log('pages:', pages)
|
||||
const currRoute = (pages.at(-1) 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:', (pages.at(-1) 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)
|
||||
|
||||
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 pages = [
|
||||
...pagesJson.pages
|
||||
.filter((page) => !key || page[key])
|
||||
.map((page) => ({
|
||||
...page,
|
||||
path: `/${page.path}`,
|
||||
})),
|
||||
]
|
||||
// 这里处理分包
|
||||
const subPages: any[] = []
|
||||
pagesJson.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 = [...pages, ...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)
|
||||
|
||||
export const getArrElementByIdx = (arr: any[], index: number) => {
|
||||
if (index < 0) return arr[arr.length + index]
|
||||
if (index >= arr.length) return undefined
|
||||
return arr[index]
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
import path from 'node:path'
|
||||
import dayjs from 'dayjs'
|
||||
import { defineConfig, loadEnv } from 'vite'
|
||||
import Uni from '@dcloudio/vite-plugin-uni'
|
||||
// @see https://uni-helper.js.org/vite-plugin-uni-pages
|
||||
import UniPages from '@uni-helper/vite-plugin-uni-pages'
|
||||
// @see https://uni-helper.js.org/vite-plugin-uni-layouts
|
||||
import UniLayouts from '@uni-helper/vite-plugin-uni-layouts'
|
||||
// @see https://github.com/uni-helper/vite-plugin-uni-platform
|
||||
// 需要与 @uni-helper/vite-plugin-uni-pages 插件一起使用
|
||||
// import UniPlatform from '@uni-helper/vite-plugin-uni-platform'
|
||||
// @see https://github.com/uni-helper/vite-plugin-uni-manifest
|
||||
import UniManifest from '@uni-helper/vite-plugin-uni-manifest'
|
||||
// @see https://unocss.dev/
|
||||
import UnoCSS from 'unocss/vite'
|
||||
// import autoprefixer from 'autoprefixer'
|
||||
import AutoImport from 'unplugin-auto-import/vite'
|
||||
// import viteCompression from 'vite-plugin-compression'
|
||||
import ViteRestart from 'vite-plugin-restart'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default ({ command, mode }) => {
|
||||
// console.log(mode === process.env.NODE_ENV) // true
|
||||
|
||||
// mode: 区分生产环境还是开发环境
|
||||
console.log('command, mode -> ', command, mode)
|
||||
// dev 和 build 命令可以分别使用 .env.development 和 .env.production 的环境变量
|
||||
|
||||
// process.env.VITE_ROOT_DIR: 获取当前文件的目录跟地址
|
||||
// loadEnv(): 返回当前环境env文件中额外定义的变量
|
||||
const env = loadEnv(mode, path.resolve(process.env.VITE_ROOT_DIR, 'env'))
|
||||
const { VITE_ROOT_DIR, VITE_APP_PORT, VITE_DELETE_CONSOLE, VITE_SHOW_SOURCEMAP } = env
|
||||
console.log('环境变量 env -> ', env)
|
||||
const { UNI_PLATFORM } = process.env
|
||||
console.log('UNI_PLATFORM -> ', UNI_PLATFORM) // 得到 mp-weixin, h5, app 等
|
||||
|
||||
return defineConfig({
|
||||
// root: VITE_ROOT_DIR,
|
||||
envDir: './env', // 自定义env目录
|
||||
plugins: [
|
||||
UniPages({
|
||||
dts: path.join(VITE_ROOT_DIR, 'types/uni-pages.d.ts'),
|
||||
homePage: 'pages/index/index', // 设置默认路由入口
|
||||
dir: 'pages', // 主目录
|
||||
subPackages: ['pages-sub'], // 子目录,是个数组,可以配置多个,但是不能为pages里面的目录
|
||||
outDir: './',
|
||||
exclude: ['**/components/**/**.*'],
|
||||
routeBlockLang: 'json5', // 虽然设了默认值,但是vue文件还是要加上 lang="json5", 这样才能很好地格式化
|
||||
}),
|
||||
UniLayouts({
|
||||
layoutDir: 'layouts',
|
||||
layout: '',
|
||||
cwd: VITE_ROOT_DIR,
|
||||
}),
|
||||
// UniPlatform(),
|
||||
UniManifest(),
|
||||
// UniXXX 需要在 Uni 之前引入
|
||||
Uni(),
|
||||
UnoCSS(),
|
||||
AutoImport({
|
||||
imports: ['vue', 'uni-app'],
|
||||
dts: 'types/auto-import.d.ts',
|
||||
dirs: ['hooks'], // 自动导入 hooks
|
||||
eslintrc: { enabled: true },
|
||||
vueTemplate: true, // default false
|
||||
}),
|
||||
|
||||
// viteCompression(),
|
||||
ViteRestart({
|
||||
// 通过这个插件,在修改vite.config.js文件则不需要重新运行也生效配置
|
||||
restart: ['vite.config.js'],
|
||||
}),
|
||||
// h5环境增加编译时间
|
||||
UNI_PLATFORM === 'h5' && {
|
||||
name: 'html-transform',
|
||||
transformIndexHtml(html) {
|
||||
return html.replace('%BUILD_DATE%', dayjs().format('YYYY-MM-DD HH:mm:ss'))
|
||||
},
|
||||
},
|
||||
],
|
||||
define: {
|
||||
__UNI_PLATFORM__: JSON.stringify(UNI_PLATFORM),
|
||||
},
|
||||
css: {
|
||||
postcss: {
|
||||
plugins: [
|
||||
// autoprefixer({
|
||||
// // 指定目标浏览器
|
||||
// overrideBrowserslist: ['> 1%', 'last 2 versions'],
|
||||
// }),
|
||||
],
|
||||
},
|
||||
},
|
||||
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': path.join(VITE_ROOT_DIR, ''),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
hmr: true,
|
||||
port: Number.parseInt(VITE_APP_PORT, 10),
|
||||
},
|
||||
build: {
|
||||
// 方便非h5端调试
|
||||
sourcemap: VITE_SHOW_SOURCEMAP === 'true', // 默认是false
|
||||
target: 'es6',
|
||||
minify: 'terser',
|
||||
terserOptions: {
|
||||
compress: {
|
||||
drop_console: VITE_DELETE_CONSOLE === 'true',
|
||||
drop_debugger: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|