# uniapp-vue2-hbx-starter
**Repository Path**: wufujie2008/uniapp-vue2-hbx-starter
## Basic Information
- **Project Name**: uniapp-vue2-hbx-starter
- **Description**: 🚀 一个开箱即用的uniapp脚手架
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: main
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 6
- **Created**: 2022-12-01
- **Last Updated**: 2022-12-01
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
uniapp-vue2-hbx-starter
🚀 一个开箱即用的uniapp脚手架





小何同学 / [github@MyHdg0601](https://github.com/MyHdg0601) / [gitee@MyHdg](https://gitee.com/MyHdg)
### 🎉 特性
- 🍔 代码高质量, 严谨规范
- 🍚 支持多端兼容 (Android、iOS、Web、小程序...)
- 🍖 支持多主题切换, 深色模式自动跟随系统 (CssVar实现)
- 🍙 支持全局配置、环境变量
- 🍟 完全自定义的Navbar、Tabbar, 多端表现高度一致
- 🧀 内置丰富的优秀工具方法封装 (网络请求、权限申请、字符串操作...)
- 🍳 内置组件提供VueDoc, 内置方法提供JsDoc (IDE自动代码提示)
- 🍨 集成uview-ui 2.x、z-paging等开源组件库
- 🌭 支持免费商用
- 🥗 更多特性等你发掘...
### 📇 目录结构
```
uniapp-vue2-hbx-starter
├─.settings // 【HBuilderX推荐配置】
| ├─jsbeautifyrc.js // == 代码格式化风格
| └-settings.json // == 编辑器设置 [按需使用]
|
├─components // 【组件】
| ├─AppContainer.vue // == App页面容器 (用于容纳页面内的所有元素, 并向子元素提供Css变量, 实现主题切换等功能。注意:本组件应为页面的根组件。)
| ├─AppNavbar.vue // == App导航栏 (用于自定义导航栏 [即navbar]。)
| ├─AppTabbar.vue // == App底部导航栏 (用于自定义底部导航栏 [即tabbar]。)
| ├─AppSafearea.vue // == App安全区域 (用于安全区域占位, 且可提供额外垫高。)
| └-AppVideo.vue // == App视频组件 (用于视频播放, APP-PLUS端使用iframe实现同层渲染。)
|
├─enums // 【枚举】
| └-user.js // == 示例
|
├─extensions // 【扩展】(Vue prototype 扩展 [即this.$...], 以及uni扩展)
| └-index.js // (内置 => $date: dayjs; $device: 设备相关工具方法; $string: 字符串相关工具方法; uni.simulateSwitchTab: 模拟uni.switchTab, 详细请查看下方(#自定义tabbar)部分)
|
├─filters // 【Vue filter】 (简单用法: {{ someValue | defaults }}, 更多用法请查阅Vue 2官方文档 [https://v2.cn.vuejs.org/v2/guide/filters.html])
| └-index.js // == 全局filter (内置 => defaults: 默认值; sources: 处理相对路径)
|
├─mixins // 【Vue mixin】
| ├─index.js // == 全局mixin (内置 => preventDefault: 只是一个空方法, 可自行扩展; destroyToastTimer: 销毁toastTimer定时器)
| └-lifecycle.js // == 生命周期mixin (在pages.json中配置的页面使用, 用于向页面内组件传递当前页面所处的生命周期, 一般是app-container组件使用, 详细请查看下方(#页面结构)部分)
|
├─pages // 【页面】
| ├─main // == 主页面 (默认是tabbar容器页面, 详细请查看下方(#自定义tabbar)部分)
| | ├─index.vue // ==== tabbar容器页面
| | ├─components // ==== 主页面组件 (一般用于存放tab页面 [使用组件模拟页面])
| | | ├─TheHome.vue // ====== tab页面: 主页
| | | └-TheMine.vue // ====== tab页面: 我的
|
├─service // 【接口定义】 详细请查看下方(#网络请求)部分
|
├─static // 【静态资源】
| ├─icons // == 图标
| └-iconfont // == iconfont字体图标
|
├─store // 【VueX store】
| ├─index.js // == store总入口
| ├─modules // == store模块
| | ├─system.js // ==== 设备/系统信息 (数据来自uni.getSystemInfoSync)
| | ├─theme.js // ==== 主题信息
| | └-user.js // ==== 用户信息
|
├─styles // 【样式表】
| ├─reset.scss // == 样式重置
| ├─common.scss // == 公共样式
| ├─theme.scss // == 主题样式
| └-app.scss // == app样式
|
├─utils // 【配置/工具方法】
| ├─config.js // == 配置文件
| ├─dayjs.js // == dayjs配置
| ├─device.js // == 设备相关工具方法
| ├─network.js // == 网络请求相关工具方法
| ├─permission.js // == 权限相关工具方法
| ├─promisify.js // == Promise化uni的异步方法
| ├─route.js // == 路由相关工具方法
| └-string.js // == 字符串相关工具方法
|
├─.editorconfig // editorconfig
├─.env // 通用环境变量 (注意与Vue cli中环境变量的差异, 详细请查看下方(#环境变量)部分)
├─.env.development // 开发环境变量
├─.env.production // 生产环境变量
├─.gitignore // git忽略文件
├─App.vue // App.vue
├─index.html // index.html
├─main.js // 应用入口
├─manifest.json // uniapp配置
├─package.json // package.json
├─pages.json // 页面配置
├─theme.json // 主题配置 [由于项目中navbar、tabbar等组件已完全自定义, 故无需使用, 按默认内容保留即可]
├─uni.scss // uniapp scss变量
├─vue.config.js // vue.config.js
├─yarn.lock // 推荐使用Yarn管理依赖
```
### 📱 页面结构
```vue
```
> 上方示例是一个页面的基本框架
1. 将 `lifecycleMixin` 引入 (`import`) 并混入 (`mixin`) 当前页面;
1. `AppContainer` 组件作为页面的根元素(已全局导入, 无需 `import`), 并将 `lifecycleMixin` 中的 `thePercept` 传给 `AppContainer` 的 `percept` 参数;
1. 若页面需要导航栏, 则添加 `AppNavbar` 组件至页面中(已全局导入, 无需 `import`), 组件 `fixed` 参数默认为 `true`, 导航栏会自动固定在页面最上方, 并且在文档流中添加一个导航栏同等高度的 `placeholder` 来填充导航栏的位置, 详细请查看下方[自定义导航栏](#自定义导航栏)部分;
1. 根据需要可以在页面中可滚动到屏幕底部的元素末尾添加 `AppSafearea` 组件(安全区域)来垫高滚动内容, 详细请查看下方[安全区域](#安全区域)部分。
### 🍕 内置组件
#### AppContainer
> 本组件用于容纳页面内的所有元素, 并向子元素提供Css变量, 实现主题切换等功能。注意:本组件应为页面的根组件。
##### Props
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|percept|所在页面是否被展示 (传入 `lifecycleMixin` 中的 `thePercept`)|Boolean|-|`false`|
|lifecycle|所在页面生命周期 (传入 `lifecycleMixin` 中的 `theLifecycle`)|String|-|`null`|
|background-color|背景颜色|String|-|var(--color-bg-normal)|
|status-front-color|状态栏前景色 (若为 `auto` 则会根据当前主题的 `brightness` 自动变化颜色, 详细请查看下方[主题](#主题)部分)|String|auto / #ffffff / #000000|auto|
|status-background-color|状态栏背景色 (`auto` 同上)|String|auto / 16进制颜色值|auto|
##### Slots
|名称|说明|
|---|---|
|default|页面内容|
##### CssVars (样式定制)
|名称|说明|默认值|
|---|---|---|
|--app-main__space_vertical|纵向空隙大小|20rpx|
|--app-main__space_horizontal|横向空隙大小|22rpx|
|--app-main__txt_size|文字大小|28rpx|
|--app-main__txt_weight|文字字重|400|
|--app-main__txt_color|文字颜色|var(--color-txt-primary)|
##### CssVars (组件提供)
|名称|说明|
|---|---|
|--app-navbar__status_height|状态栏高度|
|--app-navbar__title_height|标题栏高度|
|--app-navbar__body_height|导航栏高度 (= 状态栏高度 + 标题栏高度)|
|--app-navbar__status_color|状态栏前景色 (对应 `status-front-color`)|
|--app-navbar__status_background|状态栏背景色 (对应 `status-background-color`)|
|--app-safearea__body_top|上方安全区域大小|
|--app-safearea__body_right|右侧安全区域大小|
|--app-safearea__body_bottom|下方安全区域大小|
|--app-safearea__body_left|左侧安全区域大小|
|--app-scroller__cushion_height|`app-scroller` 垫高高度, 详细请查看下方[z-paging](#z-paging)部分|
##### 相关说明
- 现已支持使用页面滚动相关事件
- ~~由于 `AppContainer` 的高度为 `100%`, 通过 `overflow-y: auto;` 实现页面滚动, 这将导致页面滚动相关事件(`onReachBottom`、`onPageScroll` 等)无法触发, 若需监听页面滚动事件, 可通过 `scroll-view` 等方式实现 (相关注意事项请查看下方[自定义导航栏](#自定义导航栏)部分)~~。
#### AppNavbar
> 本组件用于自定义导航栏 (即navbar)。
##### Props
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|show|是否展示导航栏|Boolean|-|`true`|
|fixed|是否固定在屏幕顶部展示|Boolean|-|`true`|
|placeholder|固定在屏幕顶部展示时是否在文档流中填充等高view|Boolean|-|`true`|
|border|是否展示下边框|Boolean|-|`true`|
|use-custom-slot|是否使用custom插槽 (小程序端不支持判断是否提供对应slot, 故添加属性手动指定是否使用)|Boolean|-|`false`|
|show-left|是否展示左侧内容|Boolean|-|`true`|
|use-left-slot|是否使用left插槽|Boolean|-|`false`|
|left-icon|左侧图标 (u-icon中的图标)|String|-|arrow-left|
|left-text|左侧文字|String|-|返回|
|show-right|是否展示右侧内容|Boolean|-|`true`|
|use-right-slot|是否使用right插槽|Boolean|-|`false`|
|right-icon|右侧图标|String|-|-|
|right-text|右侧文字|String|-|-|
|use-center-slot|是否使用center插槽|Boolean|-|`false`|
|title|标题文字|String|-|-|
|title-width|标题宽度|String|-|var(--app-navbar__title_width)|
|text-color|文字颜色|String|-|var(--app-navbar__txt_color)|
|background-color|背景颜色|String|-|var(--color-bg-primary)|
|icon-size|左右图标大小|String|-|var(--app-navbar__icon_size)|
|icon-color|左右图标颜色|String|-|inherit|
|z-index|css中的z-index|Number|-|`50`|
|auto-back|点击navbar左侧是否触发navigateBack|Boolean|-|`true`|
##### Events
|事件|说明|回调参数|
|---|---|---|
|left-tap|点击navbar左侧|-|
|right-tap|点击navbar右侧|-|
##### Slots
|名称|说明|
|---|---|
|custom|标题栏内容 (`use-custom-slot` 为 `true` 时有效)|
|left|标题栏内容 (`use-left-slot` 为 `true` 且无 `custom` 时有效)|
|center|标题栏内容 (`use-center-slot` 为 `true` 且无 `custom` 时有效)|
|right|标题栏内容 (`use-right-slot` 为 `true` 且无 `custom` 时有效)|
##### CssVars (样式定制)
|名称|说明|默认值|
|---|---|---|
|--app-navbar__body_border|下边框样式|2rpx solid rgba(0, 0, 0, 0.05)|
|--app-navbar__title_width|标题宽度|300rpx|
|--app-navbar__txt_size|字体大小|32rpx|
|--app-navbar__txt_color|文字颜色|var(--color-txt-primary)|
|--app-navbar__icon_mright|左右侧图标 `margin-right`|8rpx|
|--app-navbar__icon_size|左右侧图标大小|32rpx|
##### 自定义导航栏
- 项目中 `globalStyle.navigationStyle` 已设置为 `custom`, 页面中若需导航栏可以使用 `AppNavbar` 组件或其他自定义方式实现;
- 自定义导航栏后页面级的下拉刷新时, 自定义导航栏也会被一并下拉, 若不满足需求可以使用 `z-paging` 或 `scroll-view` 等其他方式实现自定义下拉刷新, 详细请查看下方[z-paging](#z-paging)部分;
- 注意:在有 `AppNavbar` 的页面中, 若需要某个元素占满屏幕, 需要考虑 `AppNavbar` 的`placeholder` 所占用的高度, 一般来说有如下几种做法:
1. 使用 `css` 中的 `calc` 计算出页面中除去 `AppNavbar` 后剩余空间的高度
- 使用 `VueX` 的 `getters`
```vue
```
- 使用 `Css` 变量
```vue
```
1. 将 `AppNavbar` 的 `placeholder` 属性设置为 `false`, 然后将元素高度设置为 `100%`, 将与 `AppNavbar` 等高的元素放置在该元素的第一项
- 使用 `VueX` 的 `getters`
```vue
```
- 使用 `Css` 变量
```vue
```
#### AppTabbar
> 本组件用于自定义底部导航栏 (即tabbar)。
##### Props
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|* value / v-model|当前选中组件名称 (对应 `items[].component`)|String|-|-|
|* items|tabs配置 (类型: `AppTabbarItem[]`, 具体见下方说明)|Array|-|-|
|show|是否展示tabbar|Boolean|-|`true`|
|round|是否展示圆角 (圆角大小: `--app-tabbar__body_radius`)|Boolean|-|`false`|
|border|是否展示上边框 (边框样式: `--app-tabbar__body_border`)|Boolean|-|`true`|
|z-index|css中的z-index|Number|-|`50`|
##### AppTabbarItem
|属性|说明|类型|可选值|默认值|
|---|---|---|---|---|
|name|名称 (展示文字)|string|-|-|
|component|组件名称|string|-|-|
|icon|图标 (图片绝对路径)|string|-|-|
|iconSelected|选中状态图标 (图片绝对路径)|string|-|-|
|badgeKey|badge取值 (需提供Vuex中的getters)|string|-|-|
##### Events
|事件|说明|回调参数|
|---|---|---|
|input|当前选中组件名称变化|value: 当前选中组件名称|
##### CssVars (样式定制)
|名称|说明|默认值|
|---|---|---|
|--app-tabbar__body_height|高度|120rpx|
|--app-tabbar__body_radius|上圆角大小|0|
|--app-tabbar__body_border|上边框样式|2rpx solid rgba(0, 0, 0, 0.04)|
|--app-tabbar__body_background|背景颜色|var(--color-bg-primary)|
|--app-tabbar__item_width|tab item宽度|120rpx|
|--app-tabbar__icon_size|图标大小|56rpx|
|--app-tabbar__txt_mtop|文字的 `margin-top`|6rpx|
|--app-tabbar__txt_size|文字大小|24rpx|
|--app-tabbar__txt_weight|文字字重|500|
|--app-tabbar__txt_color|文字颜色|#bbbdd4|
|--app-tabbar__txt_color--selected|文字颜色 (选中状态)|var(--color-accent)|
|--app-tabbar__badge_size--dot|圆点型badge大小|10rpx|
|--app-tabbar__badge_size--text|文字型badge文字大小|20rpx|
|--app-tabbar__badge_radius--text|文字型badge圆角大小|20rpx|
|--app-tabbar__badge_color|文字型badge文字颜色|#ffffff|
|--app-tabbar__badge_background|badge背景颜色|#ff6d2c|
##### 自定义tabbar
```vue
```
> 上方示例是一个tabbar容器页面的基本框架 `/pages/main/index.vue`
```vue
```
> 上方示例是一个tab页面的基本框架 `/pages/main/components/TheHome.vue`
- 为了实现tabbar的灵活控制(包括样式、层级、显示/隐藏等), 并且能够多端统一, 项目中采用了以组件模拟页面的方案, 并使用 `v-if` 和 `v-show` 模拟tab页面的功能 (即 第一次展示tab页面的时候, 页面才开始挂载, 离开页面后不销毁页面实例), 同时实现了 `uni.simulateSwitchTab` 方法用于模拟 `uni.switchTab` 便于tab页面的跳转 (由于是单页实现方案, 若tab页面功能复杂则会影响应用性能, 若对性能要求严苛可自行改造其他方案);
- tabbar容器页面作为tab页面的入口, 也就承担了为各个tab页面分发数据的职责, 例如 `lifecycleMixin` 中的数据以及其他额外的需要传递给某个tab页面的参数, 通过父子组件传参的形式实现; tab页面若需要传参给tabbar容器页面, 可通过 `$emit` 或者 `VueX` 等方式实现;
- 若需要启动tabbar容器页面时指定展示某个tab页面, 可通过路由跳转的 `query.component` 指定, 也可以调用 `uni.simulateSwitchTab({ url: "某个tab页面的组件名称" })` 来实现跳转, 示例如下
```javascript
// tabbar容器页面启动时指定
uni.reLaunch({
url: "/pages/main/index?component=TheMine"
});
// 任何时候跳转
// 注意: 使用该方法需要在 /utils/config.js 中配置 route.simulateTabbarPage 为tabbar容器页面路径 (即: /pages/main/index)
uni.simulateSwitchTab({
url: "TheMine"
});
// 扫码进入启动时同理, 可根据需要自行修改tabbar容器页面的onLoad方法
```
- 由于采用组件模拟tab页面, 所以也就失去了页面所拥有的生命周期钩子, 若需要使用生命周期钩子, 则使用组件的生命周期代替, 其中 `onShow` 则通过 `watch` 组件的 `percept` 属性实现;
- 不采用微信小程序 `custom-tab-bar` 的方案是因为该方案仅微信小程序端支持;
- 另外某些通过使用 `reLaunch` 实现的多页方案并不能达到tab页面的功能要求, 因为每次切换tab页面均会重新生成页面实例, 而隐藏的页面则会被销毁;
#### AppSafearea
> 本组件用于安全区域占位, 且可提供额外垫高。
##### Props
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|direction|安全区域方向|String|top / right / bottom / left|bottom|
|cushion|是否垫高|Boolean|-|`true`|
|cushion-height|垫高高度 (单位: rpx)|Number|-|`60`|
##### 安全区域
- 一般情况, 直接将 `AppSafearea` 添加至 `AppContainer` 末尾
```vue
```
- 页面内包含可滚动到屏幕底部的元素, 则将 `AppSafearea` 添加至滚动元素末尾
```vue
```
- 若页面包含 `fixed` 到屏幕底部的元素, 则将 `AppSafearea` 添加至 `fixed` 元素底部并将 `cushion` 属性设置为 `false`, 同时页面中可滚动元素末尾也需要添加 `AppSafearea` 并将 `cushion-height` 设置为 `fixed` 元素的高度 (单位: `rpx`)
```vue
```
#### AppVideo
> 本组件用于视频播放, APP-PLUS端使用iframe实现同层渲染。
##### Props
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|* vid|播放器id|String|-|-|
|* src|要播放视频的资源地址|String|-|-|
|width|组件宽度|String|-|100%|
|height|组件高度|String|-|100%|
|border-radius|组件圆角|String|-|0|
|duration|指定视频时长|Number|-|-|
|autoplay|是否自动播放|Boolean|-|`false`|
|loop|是否循环播放|Boolean|-|`false`|
|muted|是否静音播放|Boolean|-|`false`|
|controls|是否显示默认播放控件|Boolean|-|`true`|
|poster|视频封面的图片网络资源地址|String|-|-|
|---|以下属性仅 `非APP-PLUS` 端支持|---|---|---|
|title|视频标题|String|-|-|
|direction|设置全屏时视频的方向, 不指定则根据宽高比自动判断|Number|`0` / `90` / `-90`|-|
|initial-time|指定视频初始播放位置|Number|-|-|
|object-fit|当视频大小与video容器大小不一致时, 视频的表现形式|String|contain / fill / cover|contain|
|vslide-gesture|在非全屏模式下, 是否开启亮度与音量调节手势|Boolean|-|`false`|
|vslide-gesture-in-fullscreen|在全屏模式下, 是否开启亮度与音量调节手势|Boolean|-|`true`|
|danmu-list|弹幕列表|Array|-|-|
|danmu-btn|是否显示弹幕按钮|Boolean|-|`false`|
|play-btn-position|播放按钮的位置|String|bottom / center|bottom|
|ad-unit-id|视频前贴广告单元ID|String|-|-|
|poster-for-crawler|用于给搜索等场景作为视频封面展示|String|-|-|
|auto-pause-if-navigate|当跳转到本小程序的其他页面时, 是否自动暂停本页面的视频播放|Boolean|-|`true`|
|auto-pause-if-open-native|当跳转到其它微信原生页面时, 是否自动暂停本页面的视频|Boolean|-|`true`|
|picture-in-picture-mode|设置小窗模式|String / Array|push / pop / `[]`|-|
|picture-in-picture-show-progress|是否在小窗模式下显示播放进度|Boolean|-|`false`|
|background-poster|进入后台音频播放后的通知栏图标 (Android独有)|String|-|-|
|referrer-policy|referrer策略|String|origin / no-referrer|no-referrer|
|enable-danmu|是否展示弹幕|Boolean|-|`false`|
|enable-play-gesture|是否开启播放手势, 即双击切换播放/暂停|Boolean|-|`true`|
|enable-progress-gesture|是否开启控制进度的手势|Boolean|-|`true`|
|enable-auto-rotation|是否开启手机横屏时自动全屏, 当系统设置开启自动旋转时生效|Boolean|-|`false`|
|show-mute-btn|是否显示静音按钮|Boolean|-|`false`|
|show-progress|是否显示进度条|Boolean|-|`true`|
|show-fullscreen-btn|是否显示全屏按钮|Boolean|-|`true`|
|show-bottom-progress|是否展示底部进度条|Boolean|-|`true`|
|show-play-btn|是否显示视频底部控制栏的播放按钮|Boolean|-|`true`|
|show-center-play-btn|是否显示视频中间的播放按钮|Boolean|-|`true`|
|show-casting-button|是否显示投屏按钮|Boolean|-|`false`|
|show-snapshot-button|是否显示截屏按钮|Boolean|-|`true`|
|show-screen-lock-button|是否显示锁屏按钮|Boolean|-|`true`|
|show-background-playback-button|是否展示后台音频播放按钮|Boolean|-|`false`|
##### Events
> 注意: 由于 `APP-PLUS` 端使用 `iframe` 实现同层渲染, 故 `events` 与 `非APP-PLUS` 端有所差异, 且均无回调参数, 下表中回调参数仅 `非APP-PLUS` 端有效, `APP-PLUS` 端可通过组件 `ref` 的 `instance` 属性访问 `video` 元素实例以及其他 `video` 组件相关操作, 若与 `非APP-PLUS` 端有差异请注意条件编译 。
|事件|说明|回调参数|
|---|---|---|
|play|开始/继续播放|-|
|pause|暂停播放|-|
|ended|播放到末尾|-|
|timeupdate|播放进度变化|event.detail = {currentTime, duration}|
|waiting|视频出现缓冲|-|
|progress|加载进度变化|event.detail = {buffered}|
|loadedmetadata|视频元数据加载完成|event.detail = {width, height, duration}|
|---|以下事件仅 `非APP-PLUS` 端支持|---|
|error|视频播放出错|-|
|controlstoggle|切换controls显示隐藏|event.detail = {show}|
|seekcomplete|seek完成|event.detail = {position}|
|fullscreenchange|视频进入和退出全屏|event.detail = {fullScreen, direction}|
|enterpictureinpicture|播放器进入小窗|-|
|leavepictureinpicture|播放器退出小窗|-|
|---|以下事件仅 `APP-PLUS` 端支持|---|
|canplay|浏览器可以播放媒体文件了, 但估计没有足够的数据来支撑播放到结束, 不必停下来进一步缓冲内容|-|
|canplaythrough|浏览器估计它可以在不停止内容缓冲的情况下播放媒体直到结束|-|
|complete|OfflineAudioContext渲染完成|-|
|durationchange|duration属性的值改变时触发|-|
|emptied|视频停止播放, 因为media已经到达结束点|-|
|loadeddata|media中的首帧已经完成加载|-|
|playing|由于缺乏数据而暂停或延迟后, 播放准备开始|-|
|ratechange|播放速率发生变化|-|
|seeked|seek操作完成|-|
|seeking|seek操作开始|-|
|stalled|用户代理(UserAgent)正在尝试获取媒体数据, 但数据意外未出现|-|
|suspend|媒体数据加载已暂停|-|
|volumechange|音量发生变化|-|
##### 相关说明
- `video` 实例的使用 (`APP-PLUS` 端为 `HTMLVideoElement`, `非APP-PLUS` 端为 `VideoContext`):
```vue
```
### 🌏 网络请求
> 项目中对uniapp的网络请求方法做了许多实用的封装, 让网络请求不再繁琐, 仅需1行代码即可发起请求。(`/utils/network.js`)
##### 请求方式
- `network.js` 中对不同的请求方式导出了相应的方法, 所有的方法都基于统一的 `_request` 方法实现, 除请求方式不同以外, 其余用法均一致。
- GET: `_get(url, data, config)`
- DELETE: `_delete(url, data, config)`
- HEAD: `_head(url, data, config)`
- OPTIONS: `_options(url, data, config)`
- POST: `_post(url, data, config)`
- PUT: `_put(url, data, config)`
- TRACE: `_trace(url, data, config)`
- CONNECT: `_connect(url, data, config)`
- `url` 参数传递全局配置(`/utils/config.js`)中请求基地址(`http.requestBaseUrl`)之后的路径即可, 若需要请求其他基地址的接口, 则直接传递全路径
- 其中 `_get` 方法比较特殊, 其 `data` 参数会拼接至url末尾作为query部分, 其他方法则会将其添加至请求体中
- 另外, 除以上请求方式外, 还有两个特殊的请求, 分别是上传和下载, 也提供了一致的使用方式
- 上传: `_upload(url, path, progress, config)`
- `path` 参数为uniapp文件临时路径, 可通过 `uni.chooseMedia` 等API获取
- `progress` 参数为上传进度回调函数
- 下载: `_download(url, progress, config)`
- `progress` 参数为下载进度回调函数
- 以上方法中的 `config` 参数详细请查看下方[请求配置](#请求配置)部分
- 接口定义推荐写在 `service` 目录下, 按不同的模块或接口类型区分不同的js文件定义, 并且在 `/service/index.js` 中统一导出, 示例如下
```javascript
// service/user.js
import { _post } from "@/utils/network.js";
/**
* 简单示例 (微信code登录)
*/
export const apiSignInByWxCode = (data) => _post("/sys/sign/in/wx-code", data);
/**
* 若要覆盖全局配置或默认配置, 则在config中传递对应的参数, 例如该请求在业务异常时不会弹出错误提示
*/
export const apiRequestNoToast = () => _post("/test", {}, {
toastError: false
});
```
```javascript
// service/index.js
export * from "./user.js";
```
```javascript
import { apiSignInByWxCode } from "@/service";
const { code } = await uni.pro.login();
// 在async方法中可直接使用await, 若业务无异常则直接解构出data即可
// 若需处理异常情况, 请查看下方(#异常处理)部分
const { data } = await apiSignInByWxCode({
"code": code
});
console.log(data);
// 也可以使用Promise形式
apiSignInByWxCode({
"code": code
}).then(({ data }) => {
console.log(data);
});
```
- **注意:** 上方示例中解构出的 `data` 是后端服务统一响应体中的数据字段, 若需要获取原始数据, 可解构出 `raw` 字段
- 针对不同后端服务统一响应体的字段不同的情况, 可在全局配置(`/utils/config.js`)中配置 `http.fieldCode`、`http.fieldMessage`、`http.fieldData` 字段, 详细请查看下方[请求配置](#请求配置)部分
##### 异常处理
- 我们通常认为的请求异常即为接口响应的code字段值与定义的成功值不同, 一般的业务异常直接弹出错误提示并且逻辑停止执行即可, token异常则退出登录, 这些情况项目中已自动处理, 若有其他的业务需要可手动catch请求方法, 在请求异常的情况下做自己的业务, 示例如下
```javascript
import { apiSignInByWxCode } from "@/service";
const { code } = await uni.pro.login();
try {
const { data } = await apiSignInByWxCode({
"code": code
});
// 业务正常情况
} catch(err) {
// 这里可以处理异常情况
// err = { ...response, state, data, raw }
// 其中state的取值可在/utils/config.js中配置, 详细请查看下方[请求配置](#请求配置)部分
}
apiSignInByWxCode({
"code": code
}).then(({ data }) => {
// 业务正常情况
}).catch((err) => {
// Promise形式同理
});
```
- 此处涉及的相关配置项, 如 `code` 字段的请求成功值、token失效值等, 亦查看下方[请求配置](#请求配置)部分
##### 加载动画
- 当请求长时间未收到响应时, 会自动展示加载中的弹窗 (即 `uni.showLoading`), 加载动画的延迟时间可在全局配置中的 `http.requestLoadingDelay` 配置, 若请求在此时间内响应, 则不会显示加载动画
- 加载动画提示文字等其他配置, 详细请查看下方[请求配置](#请求配置)部分
##### token处理
- 当 `storage` 中存在 `token` 时, 请求会自动携带至 `header` 中, `token` 存储位置可在全局配置的 `storage.token` 中配置
- 存储 `token` 方式如下
```javascript
import Config from "@/utils/config.js";
// 模拟登录
const { data } = await apiSignIn();
uni.setStorageSync(Config.storage.token, data.token);
```
##### 中断请求
- 在某些场景下, 当请求还未响应时, 可以手动中断请求, 此时可在 `config` 中传入 `signal` 属性, 请求发起时, 请求 `task` 会回传至`signal.task` 属性, 示例如下
```javascript
// 接口定义, 传入signal属性至config参数中
export const apiRequestTest = (data, signal) => _post("", data, {signal});
// 定义信号量, 用于接收请求task
const signal = {};
// 发起请求, 传入signal即可
apiRequestTest({
"some": "value"
}, signal);
// 在发起请求后, 在请求响应前的某个时机可以通过操作task中断请求
signal.task.abort();
// 亦可使用空安全运算符
signal.task?.abort();
```
##### 请求配置
> 请求配置分为全局配置和自定义配置
- **全局配置**
> `/utils/config.js` 中的 `http` 部分
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|requestBaseUrl|请求基地址, 即请求url中相同的前缀部分 (例如域名、项目前缀等)|string|-|`process.env.UNI_APP_REQUEST_BASE_URL`|
|sourceBaseUrl|资源基地址, 用于图片、视频等网络地址相对路径拼接统一的前缀|string|-|`process.env.UNI_APP_SOURCE_BASE_URL`|
|requestTimeout|请求超时 (单位: ms)|number|-|`10 * 1000`|
|requestLoadingDelay|加载动画延迟时间 (单位: ms)(若请求在此时间内响应, 则不会显示加载动画)|number|-|`600`|
|requestAcceptType|请求接受数据类型|string|-|application/json|
|requestContentType|😀 请求发送数据类型|string|application/json, application/x-www-form-urlencoded, 其他|application/json|
|enableHttp2|启用HTTP2|boolean|-|`false`|
|enableQuic|启用QUIC|boolean|-|`false`|
|enableCache|启用Cache|boolean|-|`false`|
|tokenKey|😀 请求Header中Token的key|string|-|Authorization|
|tokenPrefix|😀 请求Header中Token的前缀|string|-|-|
|fieldCode|😀 响应内容字段 - code|string|-|code|
|fieldMessage|😀 响应内容字段 - message|string|-|message|
|fieldData|😀 响应内容字段 - data|string|-|data|
|codeSuccess|😀 业务状态 - 操作成功|number|-|`200`|
|codeAuthError|😀 业务状态 - 登录失效|number|-|`401`|
|stateSuccess|state - 业务正常|number|-|`100`|
|stateBusinessError|state - 业务异常 (即[响应内容code字段]的值与[codeSuccess]不相等)|number|-|`0`|
|stateRequestError|state - 请求异常|number|-|`-1`|
|stateHttpError|state - HTTP状态异常|number|-|`-2`|
|stateUnknownError|state - 未知异常|number|-|`-3`|
|stateRequestAbort|state - 请求取消|number|-|`-4`|
|redirectAuthPage|😀 登录失效重定向页面地址|string|-|-|
|redirectAuthAction|😀 登录失效页重定向方式|string / null|`null` / reLaunch / switchTab / navigateTo / simulateSwitchTab|`null`|
- **自定义配置**
> 接口定义中的 `config` 参数
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|url|请求URL|string|请求地址的相对路径或全路径|-|
|method|请求方式|string|GET / DELETE / HEAD / OPTIONS / POST / PUT / TRACE / CONNECT|-|
|data|请求参数|any|-|-|
|timeout|请求超时|number|-|`Config.http.requestTimeout`|
|header|请求header|Record|-|-|
|dataType|返回数据格式|string|json / 其他|json|
|responseType|响应数据类型|string|text / arraybuffer|text|
|toastError|是否提示错误信息|boolean|-|`true`|
|showLoading|是否显示加载动画|boolean|-|`true`|
|loadingText|加载文字|string|-|请稍候|
|ignoreLoadingDelay|是否忽略等待弹窗延时 (若为 `true`, 等待弹窗会在请求开始时立即显示)|boolean|-|`false`|
|ignoreToken|是否跳过自动添加token|boolean|-|`false`|
|signal|信号量 (若提供则会将网络请求task回传至 `task` 属性, 可用于中断请求等)|any|-|-|
|third|是否为第三方请求 (若为 `true`, 则直接返回响应内容, 不会进行进一步处理)|boolean|-|`false`|
|authNotRedirect|是否禁用登录失效重定向|boolean|-|`false`|
|authRedirectPage|登录失效重定向页面地址|string|-|`Config.http.redirectAuthPage`|
|authRedirectAction|登录失效重定向方式|string|`null` / reLaunch / switchTab / navigateTo / simulateSwitchTab|`Config.http.redirectAuthAction`|
|key|(仅 `_upload`) FormData上传时文件的key|string|-|file|
|extra|(仅 `_upload`) FormData上传时的附加信息 (会在上传时携带在FormData中)|Record|-|-|
- 请求配置相关说明
- 上方全局配置中带😀的参数是第一次接口对接时需要特别注意的地方, 应当根据后端接口文档定义中的规定字段配置对应的值, 部分示例如下
```json
{
"code": 200,
"message": "请求成功",
"data": {
"some": "value"
}
}
```
```javascript
// 假设请求响应体如上方所示, 那么对应的字段配置如下
{
// ...
http: {
fieldCode: "code",
fieldMessage: "message",
fieldData: "data",
codeSuccess: 200
}
// ...
}
```
> 再举一个例子
```json
{
"ret": 0,
"msg": "请求成功",
"res": {
"some": "value"
}
}
```
```javascript
// 假设请求响应体如上方所示, 那么对应的字段配置如下
{
// ...
http: {
fieldCode: "ret",
fieldMessage: "msg",
fieldData: "res",
codeSuccess: 0
}
// ...
}
```
- `requestBaseUrl` 和 `sourceBaseUrl` 的默认取值是对应的环境变量, 关于环境变量的使用详细请查看下方[环境变量](#环境变量)部分
- 内置 `filter` 中的 `sources` 可用于快速拼接 `sourceBaseUrl` 前缀, 用法如下
```vue
```
- 由于小程序中不支持 `FormData`, 若接口要求传递 `FormData` 类型数据, 分为如下几种情况:
- 参数中不包含文件, 将 `requestContentType` 修改为 `application/x-www-form-urlencoded`, 或将单个请求的自定义配置 `header` 中的 `content-type` 修改为 `application/x-www-form-urlencoded`, 单个请求的示例如下
```javascript
export const apiRequestByFormData = (data) => _post("接口地址", data, {
header: {
"content-type": "application/x-www-form-urlencoded"
}
});
```
- 参数中包含一个文件, 则使用 `_upload` 方法定义接口, 将文件临时路径传至 `path` 参数, 除文件外的其他参数传至自定义配置中的 `extra` 属性即可, 示例如下
```javascript
export const apiRequestByFormData = (path, data) => _upload("接口地址", path, null, {
key: "file",
extra: data
});
```
- 参数中包含多个文件, 小程序中不支持多文件同时上传, 无法实现 (此时需要后端修改逻辑, 例如提供单文件上传接口, 前端通过 `Promise.all` 等方式实现多文件上传)
### 🚀 全局配置
> `/utils/config.js` 为项目的全局配置文件 (注意: 请勿运行时修改配置文件数据, 以免造成意料之外的错误)
除了上方[网络请求](#网络请求)部分提到的配置以外, 还有一些其他可配置项如下所示
> 默认值 `Config.defaults`
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|currentTheme|默认主题|AppTheme (具体见下方说明)|-|`{appTheme: "light", brightness: "light"}`|
|systemTheme|默认系统主题 (仅不支持获取系统主题时使用, 程序获取到系统主题时将会被更新)|string|light / dark|light|
|themeWithSystem|默认主题是否跟随系统|boolean|-|`true`|
|statusBarHeight|默认状态栏高度 (单位: px) (初始状态下使用, 程序获取到状态栏高度时将会被更新)|number|-|`20`|
|titleBarHeight|默认标题栏高度 (单位: px) (在App或H5等不能获取标题栏高度的环境中使用)|number|-|`40`|
> 主题配置 AppTheme
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|appTheme|App主题 (可自行扩展, 详细请查看下方[主题](#主题)部分)|string|light / dark|-|
|brightness|主题亮度 (一般用于控制状态栏颜色等)|string|light / dark|-|
> 路由 `Config.route`
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|simulateTabbarPage|tabbar容器页面地址 (详细请查看上方[自定义tabbar](#自定义tabbar)部分)|string|-|/pages/main/index|
> 持久化存储 `Config.storage`
|参数|说明|默认值|
|---|---|---|
|currentTheme|当前主题|CURRENT_THEME|
|themeWithSystem|主题是否跟随系统|THEME_WITH_SYSTEM|
|token|token|TOKEN|
- `Config.storage` 说明: 建议在使用 `Storage` 相关API时, 其中的 `key` 使用全局的常量以方便维护, 示例如下
```javascript
import Config from "@/utils/config.js";
uni.setStorageSync(Config.storage.token, "这是一个token");
const token = uni.getStorageSync(Config.storage.token);
```
### 🛸 环境变量
- 由于项目采用的是HBuilderX创建方式, 默认不支持类似于Vue cli创建方式的env环境变量, 故项目中模仿Vue cli使用 `dotenv` 实现了env环境变量, 具体实现可查看 `vue.config.js`, 也可根据实际需要做对应修改
- 之所以不采用Vue cli创建项目, 是因为HBuilderX对cli项目的支持并不友好, 例如不能自动跳转文件对应页面, App在线打包时基础库更新麻烦等
- 同时因为HBuilderX创建的项目使用的编译器是集成在HBuilderX中的, 所以当HBuilderX有新版本时请及时更新, 以免造成一些未知的异常
- 虽然是模仿Vue cli的环境变量, 但是项目中对其做出了一些优化以及差异化, 例如支持了除字符串以外的其他数据类型、修改了变量前缀等, 具体使用规范如下
- 命名规范
1. key: 全大写下划线命名, 并且以`UNI_APP_`开头[`ENVIRONMENT`除外] (例: UNI_APP_EXAMPLE)
- 取值规范
1. value与key之间使用(`=`)符号连接, 中间不添加`空格`, 并且值不加引号(`""`、`''`、``` `), 同时禁止换行 (例: UNI_APP_EXAMPLE=this is example)
- 注释规范
1. 以(`#`)符号开头并且与注释内容之间以一个`空格`隔开 (例: # 这里是注释内容)
- 值的类型
1. 默认情况下, 会自动解析值的类型[包括`number`、`boolean`、`null`、`undefined`以及`Json`类型], 若无法解析则会以字符串处理 (例: UNI_APP_EXAMPLE=true)
1. 若需要强制为字符串类型, 则以(`` `)符号开头即可 (例: UNI_APP_EXAMPLE=`true)
- `ENVIRONMENT` 可指定当前环境 (影响配置文件读取位置)
- default: 运行时 -> .env.development, 发行时 -> .env.production
- 其他值: .env.[ENVIRONMENT]
```env
# .env 所有环境均会读取该文件
# 指定当前环境
ENVIRONMENT=default
# 一些其他的环境变量, 所有环境都能访问到
UNI_APP_TEST=test
```
```env
# .env.development 开发环境会读取该文件, 可在.env中手动指定ENVIRONMENT为development
# 请求基地址
UNI_APP_REQUEST_BASE_URL=http://192.168.0.1:8888
# 资源基地址
UNI_APP_SOURCE_BASE_URL=http://192.168.0.1:8888
```
```env
# .env.local 可在.env中手动指定ENVIRONMENT为local, 则会读取该文件
# 该文件已在.gitignore中忽略, 可用于本地调试时使用 (若需使用请手动创建该文件)
# 请求基地址
UNI_APP_REQUEST_BASE_URL=http://192.168.0.1:8888
# 资源基地址
UNI_APP_SOURCE_BASE_URL=http://192.168.0.1:8888
```
### 🕹️ 主题
> 项目中采用了主流高效的CssVar方式实现了主题切换, 并且提供了方便易用的配置 [CssVar](https://developer.mozilla.org/zh-CN/docs/Web/CSS/var)
1. 首先需要定义主题相关的css变量 (`/styles/theme.scss`)
- 项目中css变量的命名规范
- 基础css变量: `--属性-名称: 变量值;` (例如: `--color-divider: #dadbde;` 指的是分割线颜色)
- 模块或组件中的css变量: `--模块或组件名称__内部的某个元素_元素的属性: 变量值;` (例如: `--app-tabbar__body_height: 120rpx;` 指的是AppTabbar组件主体内容的高度)
- 其中 `--color-accent` 为主题色, 可根据产品实际情况修改
- 字体颜色有4种定义, 即 `--color-txt-primary`、`--color-txt-secondary`、`--color-txt-tertiary`、`--color-txt-other`, 分别指的是UI设计规范中的由深至浅的4级字体颜色, 可根据UI中的实际情况配置相应的颜色, 或者增加/删除字体颜色等级
- 背景颜色有2种定义, 即 `--color-bg-primary` 和 `--color-bg-normal` 分别是指主要背景颜色和普通背景颜色, 亦可根据实际情况修改
- `theme.scss` 中包含了3个模块, 其中 `.app-container` 下定义的是所有主题均有效的css变量, `.app-container.app-theme-light` 和 `.app-container.app-theme-dark` 下定义的是默认的 `light` 和 `dark` 两个主题下分别有效的css变量
1. 编写页面样式时, 若需要目标颜色跟随主题变化, 则不再使用固定的颜色值, 而是使用 `theme.scss` 种定义的css变量, 当主题切换时颜色即可自动变化, 例如: `color: var(--color-txt-primary);`
1. 若需要手动修改主题, 可调用 `/store/modules/theme.js` 中的 `setCurrentTheme`, 例如: `this.$store.commit("theme/setCurrentTheme", {appTheme: "dark", brightness: "dark"});`
1. 若需要获取当前的主题信息 `/store/modules/theme.js` 中亦提供了相关的 `getters` 用于查询当前状态, 也可根据实际情况扩展其他的 `getter` 用于适配主题变化
### 🚦 权限
> 项目中基于[权限插件](https://ext.dcloud.net.cn/plugin?id=594)二次封装了权限申请方法 (`/utils/permission.js`), 用于确保用户已授予权限后才会继续执行逻辑, 并且支持同时申请微信小程序、Android、iOS多端权限 (可根据需要自行扩展其他端), 也兼容了用户拒绝授权的场景
- 确保权限已授予 `ensurePermissionAuthorized(scope: EnsurePermissionAuthorizedScope, config: EnsurePermissionAuthorizedConfig)`
- 需要使用 `await` 或 `Promise` 方式调用
*EnsurePermissionAuthorizedScope*
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|mp|小程序权限的scope|string|参考微信小程序官方文档|-|
|android|Android权限的permissionId|string|参考Android官方文档|-|
|ios|iOS权限的permissionId|string|参考iOS官方文档|-|
*iOS部分权限清单 (详细请查阅iOS官方文档)*
|参数|说明|
|---|---|
|location|位置|
|push|推送|
|camera|摄像头|
|photoLibrary|相册|
|record|麦克风|
|contact|通讯录|
|calendar|日历|
|memo|备忘录|
*Android部分权限清单 (详细请查阅Android官方文档)*
|参数|说明|
|---|---|
|android.permission.ACCESS_FINE_LOCATION|位置权限|
|android.permission.ACCESS_COARSE_LOCATION|模糊位置权限|
|android.permission.CAMERA|摄像头权限|
|android.permission.READ_EXTERNAL_STORAGE|外部存储(含相册)读取权限|
|android.permission.WRITE_EXTERNAL_STORAGE|外部存储(含相册)写入权限|
|android.permission.RECORD_AUDIO|麦克风权限|
|android.permission.READ_CONTACTS|通讯录读取权限|
|android.permission.WRITE_CONTACTS|通讯录写入权限|
|android.permission.READ_CALENDAR|日历读取权限|
|android.permission.WRITE_CALENDAR|日历写入权限|
|android.permission.READ_SMS|短信读取权限|
|android.permission.SEND_SMS|短信发送权限|
|android.permission.RECEIVE_SMS|接收新短信权限|
|android.permission.READ_PHONE_STATE|获取手机识别码等信息的权限|
|android.permission.CALL_PHONE|拨打电话权限|
|android.permission.READ_CALL_LOG|获取通话记录权限|
*EnsurePermissionAuthorizedConfig*
|参数|说明|类型|可选值|默认值|
|---|---|---|---|---|
|settings|拒绝授权时是否跳转至设置页|boolean|-|`true`|
|modalTitle|申请权限弹窗标题|string|-|权限申请|
|modalContent|申请权限弹窗内容|string|-|-|
- 使用示例:
```javascript
// 申请定位权限
await ensurePermissionAuthorized({
mp: "scope.userLocation",
android: "android.permission.ACCESS_FINE_LOCATION",
ios: "location"
}, {
modalContent: "您已拒绝授予定位权限,需要手动开启定位权限,是否立即跳转至设置页面?"
});
// 权限申请成功后, 继续执行逻辑
const { longitude, latitude } = await uni.pro.getLocation({
type: "gcj02",
isHighAccuracy: true
});
console.log("经纬度", longitude, latitude);
```
### 🏍️ 其他
- [uview-ui](https://www.uviewui.com)
- 由于 `uview-ui` 的组件都将 `virtualHost` 属性设置为 `true`, 其目的是将 `微信小程序` 中的自定义节点设置成虚拟的, 使其更加接近Vue组件的表现, 能更好的使用flex属性, 但是这同时也导致了一些问题, 比如微信小程序端不直接给 `uview-ui` 的组件添加 `class` (其他端不受影响)。所以为了能够更好的多端兼容, 若需要给 `uview-ui` 的组件添加 `class` 时, 在其外层使用 `view` 包裹, 并将 `class` 添加至外层 `view` 上, 然后样式中在外层 `class` 下选择 `uview-ui` 组件的 `class` 进而去修改样式, 示例如下
```vue
```
- `uview-ui` 组件库目前尚存在一些缺陷或参数缺失, 已发现部分如下所示
- 项目中已修复缺陷或添加参数, 并且已向 `uview-ui` 官方仓库提交PR, 等官方合入PR并更新版本后, 会及时更新当前项目中的 `uview-ui` 版本, 若自行更新组件版本时, 请注意当前修改项
- `u-input` (`/uni_modules/uview-ui/components/u-input/u-input.vue`)
- 强制input处于同层状态: ` $emit('focus', e)"`
- `u-picker` (`/uni_modules/uview-ui/components/u-picker/u-picker.vue`)
- 小程序端深色模式支持: `
```
- [z-paging](https://z-paging.zxlee.cn)
- `z-paging` 组件目前尚存在一些缺陷, 已发现部分如下所示
- 项目中已修复缺陷, 并且已向 `z-paging` 官方仓库提交PR, 目前PR已被合入主分支, 等官方更新版本后, 会及时更新当前项目中的 `z-paging` 版本, 若自行更新组件版本时, 请注意当前修改项
- `empty` (`/uni_modules/z-paging/components/z-paging/js/modules/empty.js`)
- 修复 `auto-hide-empty-view-when-loading` 为 `false` 且列表不为空时, 加载中时列表底部仍然会出现空数据视图
- `115行`: `this.totalData.length` -> `this.realTotalData.length`
- 项目中针对 `z-paging` 基于CssVar做了一些安全区域和主题切换方面的适配, 使用方式及配置项如下
```vue
```
*CssVars (样式定制)*
|名称|说明|默认值|
|---|---|---|
|--app-scroller__cushion_height|底部安全区域高度|`safeAreaInsets.bottom + uni.upx2px(60)`|
|--app-scroller__bktop_size|返回顶部按钮大小|76rpx|
|--app-scroller__bktop_right|返回顶部按钮右侧间距|var(--app-main__space_horizontal)|
|--app-scroller__bktop_bottom|返回顶部按钮底部间距 (会自动添加底部安全区域高度)|40rpx|
|--app-scroller__bktop_zindex|返回顶部按钮z-index|10|
- 项目中默认添加的依赖如下
- [dayjs](https://dayjs.fenxianglu.cn) Day.js是一个极简的JavaScript库,可以为现代浏览器解析、验证、操作和显示日期和时间
- `/utils/dayjs.js` 中有相关配置 (可自行修改), 并且已添加至Vue实例中(`$date`), 可参考官方文档使用, 若不需要可直接移除依赖及相关配置文件
- [lodash](https://www.lodashjs.com) Lodash 是一个一致性、模块化、高性能的 JavaScript 实用工具库
- 若不需要可自行移除, 并改造项目中依赖该库的相关方法, 涉及的方法如下
- /utils/route.js
- simulateSwitchTab: `_.findLastIndex`
### 🐶讨论交流
- ❓: 若有疑问或BUG反馈, 可提交issues
- 📫: HeDianGeng0601@outlook.com
- 🐧: 暂未开通
### 🏆 开源协议
- MIT [LICENSE](./LICENSE)