liujiaxin 1 개월 전
부모
커밋
d45b4b0236
100개의 변경된 파일6445개의 추가작업 그리고 1024개의 파일을 삭제
  1. 26 8
      api/live.js
  2. 4 4
      api/order.js
  3. 56 24
      main.js
  4. 275 0
      node_modules/mescroll-uni/README.md
  5. 55 0
      node_modules/mescroll-uni/components/mescroll-down.css
  6. 47 0
      node_modules/mescroll-uni/components/mescroll-down.vue
  7. 118 0
      node_modules/mescroll-uni/components/mescroll-empty.vue
  8. 83 0
      node_modules/mescroll-uni/components/mescroll-top.vue
  9. 47 0
      node_modules/mescroll-uni/components/mescroll-up.css
  10. 39 0
      node_modules/mescroll-uni/components/mescroll-up.vue
  11. 19 0
      node_modules/mescroll-uni/mescroll-body.css
  12. 403 0
      node_modules/mescroll-uni/mescroll-body.vue
  13. 15 0
      node_modules/mescroll-uni/mescroll-i18n.js
  14. 57 0
      node_modules/mescroll-uni/mescroll-mixins.js
  15. 64 0
      node_modules/mescroll-uni/mescroll-uni-option.js
  16. 36 0
      node_modules/mescroll-uni/mescroll-uni.css
  17. 799 0
      node_modules/mescroll-uni/mescroll-uni.js
  18. 480 0
      node_modules/mescroll-uni/mescroll-uni.vue
  19. 47 0
      node_modules/mescroll-uni/mixins/mescroll-comp.js
  20. 66 0
      node_modules/mescroll-uni/mixins/mescroll-more-item.js
  21. 74 0
      node_modules/mescroll-uni/mixins/mescroll-more.js
  22. 41 0
      node_modules/mescroll-uni/package.json
  23. 109 0
      node_modules/mescroll-uni/wxs/mixins.js
  24. 92 0
      node_modules/mescroll-uni/wxs/renderjs.js
  25. 268 0
      node_modules/mescroll-uni/wxs/wxs.wxs
  26. 51 0
      node_modules/uview-plus/changelog.md
  27. 0 85
      node_modules/uview-plus/components/u--form/u--form.vue
  28. 0 50
      node_modules/uview-plus/components/u--image/u--image.vue
  29. 0 74
      node_modules/uview-plus/components/u--input/u--input.vue
  30. 0 45
      node_modules/uview-plus/components/u--text/u--text.vue
  31. 0 47
      node_modules/uview-plus/components/u--textarea/u--textarea.vue
  32. 4 4
      node_modules/uview-plus/components/u-action-sheet/actionSheet.js
  33. 8 0
      node_modules/uview-plus/components/u-action-sheet/props.js
  34. 13 4
      node_modules/uview-plus/components/u-action-sheet/u-action-sheet.vue
  35. 3 3
      node_modules/uview-plus/components/u-album/album.js
  36. 9 0
      node_modules/uview-plus/components/u-album/props.js
  37. 56 11
      node_modules/uview-plus/components/u-album/u-album.vue
  38. 9 5
      node_modules/uview-plus/components/u-alert/alert.js
  39. 29 0
      node_modules/uview-plus/components/u-alert/props.js
  40. 59 16
      node_modules/uview-plus/components/u-alert/u-alert.vue
  41. 2 2
      node_modules/uview-plus/components/u-avatar/u-avatar.vue
  42. 2 2
      node_modules/uview-plus/components/u-back-top/u-back-top.vue
  43. 0 49
      node_modules/uview-plus/components/u-barcode/u-barcode.vue
  44. 5 5
      node_modules/uview-plus/components/u-button/u-button.vue
  45. 1 1
      node_modules/uview-plus/components/u-calendar/month.vue
  46. 1 1
      node_modules/uview-plus/components/u-calendar/u-calendar.vue
  47. 1 1
      node_modules/uview-plus/components/u-calendar/util.js
  48. 2 2
      node_modules/uview-plus/components/u-car-keyboard/u-car-keyboard.vue
  49. 51 21
      node_modules/uview-plus/components/u-cate-tab/u-cate-tab.vue
  50. 4 4
      node_modules/uview-plus/components/u-cell/u-cell.vue
  51. 1 1
      node_modules/uview-plus/components/u-checkbox/u-checkbox.vue
  52. 2 2
      node_modules/uview-plus/components/u-collapse-item/u-collapse-item.vue
  53. 6 6
      node_modules/uview-plus/components/u-column-notice/u-column-notice.vue
  54. 6 0
      node_modules/uview-plus/components/u-datetime-picker/dayjs.esm.min.js
  55. 1 1
      node_modules/uview-plus/components/u-datetime-picker/u-datetime-picker.vue
  56. 1 1
      node_modules/uview-plus/components/u-dropdown/u-dropdown.vue
  57. 2 2
      node_modules/uview-plus/components/u-empty/u-empty.vue
  58. 2 2
      node_modules/uview-plus/components/u-form-item/u-form-item.vue
  59. 4 4
      node_modules/uview-plus/components/u-image/u-image.vue
  60. 6 6
      node_modules/uview-plus/components/u-input/u-input.vue
  61. 6 0
      node_modules/uview-plus/components/u-markdown/marked.esm.js
  62. 300 0
      node_modules/uview-plus/components/u-markdown/u-markdown.vue
  63. 4 4
      node_modules/uview-plus/components/u-navbar/u-navbar.vue
  64. 2 2
      node_modules/uview-plus/components/u-no-network/u-no-network.vue
  65. 2 2
      node_modules/uview-plus/components/u-notify/u-notify.vue
  66. 4 4
      node_modules/uview-plus/components/u-number-box/u-number-box.vue
  67. 2 2
      node_modules/uview-plus/components/u-number-keyboard/u-number-keyboard.vue
  68. 2 1
      node_modules/uview-plus/components/u-parse/node/node.vue
  69. 2 2
      node_modules/uview-plus/components/u-popup/u-popup.vue
  70. 1 1
      node_modules/uview-plus/components/u-radio/u-radio.vue
  71. 5 5
      node_modules/uview-plus/components/u-rate/u-rate.vue
  72. 2 2
      node_modules/uview-plus/components/u-read-more/u-read-more.vue
  73. 6 6
      node_modules/uview-plus/components/u-row-notice/u-row-notice.vue
  74. 4 4
      node_modules/uview-plus/components/u-search/u-search.vue
  75. 1 1
      node_modules/uview-plus/components/u-select/u-select.vue
  76. 0 14
      node_modules/uview-plus/components/u-signature/u-signature.vue
  77. 4 4
      node_modules/uview-plus/components/u-steps-item/u-steps-item.vue
  78. 2 2
      node_modules/uview-plus/components/u-swipe-action-item/u-swipe-action-item.vue
  79. 2 2
      node_modules/uview-plus/components/u-tabbar-item/u-tabbar-item.vue
  80. 4 4
      node_modules/uview-plus/components/u-tag/u-tag.vue
  81. 4 4
      node_modules/uview-plus/components/u-text/u-text.vue
  82. 3 3
      node_modules/uview-plus/components/u-toast/u-toast.vue
  83. 13 3
      node_modules/uview-plus/components/u-tooltip/props.js
  84. 3 1
      node_modules/uview-plus/components/u-tooltip/tooltip.js
  85. 139 93
      node_modules/uview-plus/components/u-tooltip/u-tooltip.vue
  86. 11 11
      node_modules/uview-plus/components/u-upload/u-upload.vue
  87. 12 12
      node_modules/uview-plus/package.json
  88. 8 3
      package-lock.json
  89. 2 1
      package.json
  90. 34 8
      pages.json
  91. 431 287
      pages/home/living.vue
  92. 13 8
      pages_shop/goods.vue
  93. 112 31
      pages_shop/store.vue
  94. 146 0
      pages_shop/storeOrderRefundAddDelivery.vue
  95. 603 0
      pages_shop/storeOrderRefundDetails.vue
  96. 387 0
      pages_shop/storeOrderRefundList.vue
  97. 474 0
      pages_shop/storeOrderRefundSubmit.vue
  98. 7 4
      pages_user/integralGoodsList.vue
  99. 8 0
      uni_modules/mescroll-uni/changelog.md
  100. 19 0
      uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.css

+ 26 - 8
api/live.js

@@ -4,9 +4,15 @@ const api = {
 	liveDataLike: (liveId) => `/app/live/liveData/like/${liveId}`, // 点赞
 	collectStore:(sotreId)=> `/app/live/liveData/collectStore?storeId=${sotreId}`, // 店铺收藏/取消收藏
 	collectGoods:(goodId)=> `/app/live/liveData/collectGoods?goodId=${goodId}`, // 商品收藏/取消收藏
-	store:(storeld,key)=> `/app/live/store/${storeld}?key=${key}`, // 查询店铺
+	store:(storeld,key)=> `/app/live/store/${storeld}?key=${key}`, // 小黄车查询店铺
+	
+	queryStore:(storeld,pageSize,pageNum,keywords)=> `/app/live/store?storeId=${storeld}&pageSize=${pageSize}&pageNum=${pageNum}&keywords=${keywords}`, // 查询店铺
 	follow: (liveId) => `/app/live/liveData/follow/${liveId}`, // 关注/取消关注
-	getRecentLiveViewers: (liveId) => `/app/live/liveData/getRecentLiveViewers/${liveId}`, // 获取直播间用户(展示在线用户)
+	// getRecentLiveViewers: (liveId) => `/app/live/liveData/getRecentLiveViewers/${liveId}`, // 获取直播间用户(展示在线用户)
+	watchUserList: (liveId,pageSize,pageNum,reasonable) => `/app/live/liveWatchUser/watchUserList/?liveId=${liveId}&pageSize=${pageSize}&pageNum=${pageNum}&reasonable=${reasonable}`, // 获取直播间用户(展示在线用户)
+	liveMsg: (liveId,pageSize,pageNum) => `/app/live/liveMsg/list/?liveId=${liveId}&pageSize=${pageSize}&pageNum=${pageNum}`, //获取最近聊天记录
+
+	
 	// checkSms: 'api/sms/checkSms',
 	getLiveInfo: (liveId) => `/app/live/${liveId}`, // 获取直播间信息接口
 	getLiveViewData: (liveId) => `/app/live/liveData/getLiveViewData/${liveId}`, // 直播间点赞、关注、在线人数数据
@@ -32,17 +38,22 @@ export function collectStore(sotreId,data= {}) {
 export function collectGoods(goodId,data= {}) {
 	return request(api.collectGoods(goodId), data, 'POST', 'application/json;charset=UTF-8')
 }
-// 查询店铺
+// 小黄车查询店铺
 export function store(storeld,key,data= {}) {
 	return request(api.store(storeld,key), data, 'GET', 'application/json;charset=UTF-8')
 }
+// 查询店铺
+export function queryStore(storeld,pageSize,pageNum,keywords,data= {}) {
+	return request(api.queryStore(storeld,pageSize,pageNum,keywords), data, 'GET', 'application/json;charset=UTF-8')
+}
+
 // 分享(还没有)
 // export function userLoginH5(data) {
 // 	return request.get(api.userLoginH5, data)
 // }
 // 点击领红包
-export function liveRed(data) {
-	return liveRed(api.follow, data, 'POST', 'application/json;charset=UTF-8')
+export function liveRed (data) {
+	return request(api.liveRed, data, 'POST', 'application/json;charset=UTF-8')
 }
 
 // 关注/取消关注
@@ -58,10 +69,17 @@ export function follow(liveId, data = {}) {
 
 // 观众
 // 获取直播间用户(展示在线用户)
-export function getRecentLiveViewers(liveId, data = {}) {
-	return request(api.getRecentLiveViewers(liveId), data, 'GET', 'application/json;charset=UTF-8')
+// export function getRecentLiveViewers(liveId, data = {}) {
+// 	return request(api.getRecentLiveViewers(liveId), data, 'GET', 'application/json;charset=UTF-8')
+// }
+// 获取直播间用户(展示在线用户)
+export function watchUserList(liveId,pageSize,pageNum,reasonable, data = {}) {
+	return request(api.watchUserList(liveId,pageSize,pageNum,reasonable), data, 'GET', 'application/json;charset=UTF-8')
+}
+// 获取最近聊天记录
+export function liveMsg(liveId,pageSize,pageNum, data = {}) {
+	return request(api.liveMsg(liveId,pageSize,pageNum), data, 'GET', 'application/json;charset=UTF-8')
 }
-
 
 
 // 小黄车

+ 4 - 4
api/order.js

@@ -29,7 +29,7 @@ const api = {
 	getStoreOrderItems: '/app/live/storeAfterSales/getStoreOrderItems', // 获取订单项列表
 	applyAfterSales: '/app/live/storeAfterSales/applyAfterSales', // 申请售后
 	revoke: '/app/live/storeAfterSales/revoke', // 撤销售后
-	getStoreAfterSalesList: '/app/live/storeAfterSales/getStoreAfterSalesList', // 获取售后列表
+	getStoreAfterSalesList:  (liveId,pageSize,pageNum) =>`/app/live/storeAfterSales/getStoreAfterSalesList?liveId=${liveId}&pageSize=${pageSize}&pageNum=${pageNum}`, // 获取售后列表
 	getStoreAfterSalesById: '/app/live/storeAfterSales/getStoreAfterSalesById', // 获取售后详情
 
 
@@ -165,8 +165,8 @@ export function revoke(data) {
 }
 
 //获取售后列表
-export function getStoreAfterSalesList(data) {
-	return request(api.getStoreAfterSalesList, data, 'GET', 'application/json;charset=UTF-8')
+export function getStoreAfterSalesList(liveId,pageSize,pageNum,data={}) {
+	return request(api.getStoreAfterSalesList(liveId,pageSize,pageNum), data, 'GET', 'application/json;charset=UTF-8')
 }
 
 // 获取售后详情
@@ -176,5 +176,5 @@ export function getStoreAfterSalesById(data) {
 
 // 申请原因
 export function Dicts(data) {
- 	 return request('/app/common/getDictByKey',data,'GET');
+ 	 return request('/app/common/getDictByKey',data,'GET', 'application/json;charset=UTF-8');
 } 

+ 56 - 24
main.js

@@ -1,10 +1,20 @@
 import App from './App'
+
 // vuex
 import store from '@/store/index.js'
-// uviewPlus导入
+
+// uviewPlus导入 - 注意 Vue3 的特殊导入方式
+// #ifdef VUE3
+import uviewPlus from 'uview-plus'
+// #endif
+
+// #ifndef VUE3
 import uviewPlus from '@/uni_modules/uview-plus'
+// #endif
+
 import bootstrap from './core/bootstrap'
-import {isLogin,isEmpty,navTo,getRegistrationID,parsePhone} from '@/utils/util.js'
+import { isLogin, isEmpty, navTo, getRegistrationID, parsePhone } from '@/utils/util.js'
+
 // 全局 mixin
 import share from './core/mixins/share.js'
 import mixin from './core/mixins/app'
@@ -12,24 +22,40 @@ import mixin from './core/mixins/app'
 // #ifndef VUE3
 import Vue from 'vue'
 import './uni.promisify.adaptor'
+
+// 配置 uView Plus
+Vue.use(uviewPlus)
+
+// 配置 Vuex
+Vue.use(store)
+
 Vue.config.productionTip = false
+
+// 全局属性
 Vue.prototype.$img = {
-  img: '/static/images/img.png', // 默认图片
-  logo: '/static/images/logo.png', // 其他公共图片
+  img: '/static/images/img.png',
+  logo: '/static/images/logo.png',
 }
-Vue.prototype.$isLogin = isLogin;
-Vue.prototype.$isEmpty = isEmpty;
-Vue.prototype.$navTo = navTo;
-Vue.prototype.$getRegistrationID = getRegistrationID;
-Vue.prototype.$parsePhone = parsePhone;
+Vue.prototype.$isLogin = isLogin
+Vue.prototype.$isEmpty = isEmpty
+Vue.prototype.$navTo = navTo
+Vue.prototype.$getRegistrationID = getRegistrationID
+Vue.prototype.$parsePhone = parsePhone
 
+// 全局 mixin
+Vue.mixin(share)
+Vue.mixin(mixin)
 
 App.mpType = 'app'
 
-const app = new Vue({
-  ...App
+// 初始化
+bootstrap().then(() => {
+  const app = new Vue({
+    store, // 注入 store
+    ...App
+  })
+  app.$mount()
 })
-app.$mount()
 // #endif
 
 // #ifdef VUE3
@@ -38,24 +64,30 @@ import { createSSRApp } from 'vue'
 export function createApp() {
   const app = createSSRApp(App)
   
-  // 注册组件库和vuex
-  app.use(uviewPlus).use(store)
+  // 注册 uView Plus (Vue3 方式)
+  app.use(uviewPlus)
+  
+  // 注册 Vuex
+  app.use(store)
+  
   app.config.productionTip = false
   
-  // 定义全局变量 $img(Vue3 方式)
+  // 定义全局属性
   app.config.globalProperties.$img = {
-    img: '/static/images/img.png', // 默认图片
-    logo: '/static/images/logo.png', // 其他公共图片
+    img: '/static/images/img.png',
+    logo: '/static/images/logo.png',
   }
- app.config.globalProperties.$isLogin = isLogin
- app.config.globalProperties.$isEmpty = isEmpty
- app.config.globalProperties.$navTo = navTo
- app.config.globalProperties.$getRegistrationID = getRegistrationID
- app.config.globalProperties.$parsePhone = parsePhone
- 
+  app.config.globalProperties.$isLogin = isLogin
+  app.config.globalProperties.$isEmpty = isEmpty
+  app.config.globalProperties.$navTo = navTo
+  app.config.globalProperties.$getRegistrationID = getRegistrationID
+  app.config.globalProperties.$parsePhone = parsePhone
+  
   // 全局 mixin
-  app.mixin(share).mixin(mixin)
+  app.mixin(share)
+  app.mixin(mixin)
   
+  // 初始化
   return {
     app,
     created: bootstrap

+ 275 - 0
node_modules/mescroll-uni/README.md

@@ -0,0 +1,275 @@
+## mescroll --【wxs+renderjs实现】高性能的下拉刷新上拉加载组件
+1. mescroll的uni版本 是专门用在uni-app的下拉刷新和上拉加载的组件, 支持一套代码编译到iOS、Android、H5、小程序等多个平台
+
+2. mescroll的uni版本 继承了mescroll.js的实用功能: 自动处理分页, 自动控制无数据, 空布局提示, 回到顶部按钮 ..
+
+3. mescroll的uni版本 丰富的案例, 自由灵活的api, 超详细的注释, 可让您快速自定义真正属于自己的下拉上拉组件
+
+<br/>
+
+
+## 最新文档(1.3.7版本): <a href="http://www.mescroll.com/uni.html">http://www.mescroll.com/uni.html</a>
+2021-04-13 by mescroll (文档可能会有缓存,建议打开时刷新一下)
+
+
+## 近期已更新优化的内容:
+1. 微信小程序, app, h5使用高性能wxs和renderjs, 下拉刷新更流畅丝滑, 尤其能明显解决Android小程序下拉卡顿的问题
+2. 新增国际化`mescroll-i18n.vue`示例, 新增轮播吸顶菜单`mescroll-swiper-sticky.vue`示例,    
+3. 新增 "局部区域滚动" 的案例: mescroll-body-part.vue 和 mescroll-uni-part.vue  
+4. 新增 me-video 视频组件, 解决APP端视频下拉悬浮错位的问题, 参考 mescroll-options.vue 示例  
+5. 新增 me-tabs 组件,tabs支持水平滑动; 优化mescroll-more和mescroll-swiper的案例, 顶部tab支持水平滑动
+6. 吸顶悬浮提供了原生sticky和监听滚动条实现的示例: sticky.vue 和 sticky-scroll.vue (推荐使用sticky样式实现)
+7. mescroll.scrollTo(y)的y支持css选择器, 包括跨自定义组件的后代选择器, 支持滚动到子组件的view (参考 mescroll-options.vue)  
+8. topbar 顶部是否预留状态栏的高度, 默认false; 还可支持设置状态栏背景: 如 '#ffff00', 'url(xxx) 0 0/100% 100%', 'linear-gradient(xx)'
+9. down.bgColor 和 up.bgColor 加载区域的背景,不仅支持色值, 而且还是支持背景图和渐变: 如 'url(xxx) 0 0/100% 100%', 'linear-gradient(xx)'
+10. topbar,bgColor支持一行代码定义background: [https://www.runoob.com/cssref/css3-pr-background.html](https://www.runoob.com/cssref/css3-pr-background.html)
+<br/>
+<br/>
+<a href="https://ext.dcloud.net.cn/plugin?id=343&update_log">查看更多 ... </a>
+
+
+### 更新记录:
+
+---
+#### 1.3.7版本 (2021/04/13)  
+1. 新增`mescroll-swiper-sticky.vue`的示例, 轮播吸顶菜单导航  
+2. 新增`mescroll-empty.vue`的示例, 单独使用空布局组件  
+3. 简化tabs在具体项目中的使用,并简化对应的示例  
+4. mescroll-uni 支持动态禁止滚动的属性 disableScroll (注: mescroll-body不支持)  
+-by 小瑾同学
+
+---
+#### 1.3.5版本 (2021/04/10)  
+1. 新增 `mescroll-i18n.vue` 的示例, 支持国际化的配置  
+2. down的`beforeEndDelay`不再默认配置 // (显示加载成功/失败的时长, android小程序设置此项结束下拉会卡顿, 配置后请注意测试)  
+3. mescroll-body-part.vue 和 mescroll-uni-part.vue 的示例新增参考代码: `异步加载左侧菜单(超简单)`  
+4. mescroll的极简示例新增参考代码: `先请求其他接口,再触发upCallback,无需配置auto为false(超简单)`  
+5. 修复mescroll-swiper的tabs在app或h5错位的问题  
+-by 小瑾同学
+
+---
+#### 1.3.3版本 (2020/09/15)  
+1. 新增下拉刷新成功和失败的文本配置, 可在 mescroll-uni-option.js 配置修改  
+1. 新增 me-video 视频组件, 解决APP端视频下拉悬浮错位的问题, 参考最新的 mescroll-options.vue 示例  
+2. 更新 mescroll-comp.vue 的示例, 支持 mescroll-body 子子..子组件 , 包括 mescroll-more.vue 也支持写在子子..子组件  
+3. mescroll-uni 的 down.offset 由原来的 80 调整为 150 , 避免超快速滑动列表到底部,偶尔出现无法再翻页的问题 (mescroll-body无此问题)  
+4. 修复 mescroll-more.vue 和 mescroll-swiper.vue 的示例在字节跳动小程序2.0.0以上新版编辑器无法正常运行的问题  
+5. 修复淘宝和美团的示例下拉卡顿的问题 ( 案例需重新下载 )  
+-by mescroll  
+
+---
+#### 1.3.2版本 (2020/08/05)
+1. mescroll-body新增sticky属性, 简化吸顶悬浮sticky.vue的示例  
+2. QQ小程序支持wxs,解决QQ小程序卡顿和无法隐藏加载状态的问题  
+3. mescroll.scrollTo(y)的y支持css选择器, 包括跨自定义组件的后代选择器, 支持滚动到子组件的view (参考 mescroll-options.vue)  
+-by 小瑾同学
+
+---
+#### 1.3.1版本 (2020/07/27)
+1. 修复Android小程序下拉刷新时, image 和 swiper 脱离文档流的问题  
+2. 修复H5端, 当配置down.use为false时, 返回其他页面无法滚动的问题  
+3. mescroll-comp.js支持mescroll-body写在子子子...组件中 (以前版本仅支持写在一级子组件) 
+4. 吸顶悬浮提供了sticky样式和监听滚动条实现的示例: sticky.vue 和 sticky-scroll.vue (推荐使用sticky样式实现)  
+-by 小瑾同学
+
+---
+#### 1.3.0版本 (2020/07/10)
+1. 微信小程序, app, h5使用高性能wxs和renderjs, 下拉刷新更流畅丝滑, 尤其能明显解决Android小程序下拉卡顿的问题
+2. 使用wxs和renderjs优化所有案例, 尤其是中高级案例, 建议大家重新下载最新的案例
+3. 废弃down的isBounce配置, 已通过renderjs自动判断, 无需配置mescroll-touch
+4. 废弃down的fps配置, 已通过wxs提高性能, 无需手动节流
+5. 新增 "局部区域滚动" 的案例: mescroll-body-part.vue 和 mescroll-uni-part.vue
+6. 解决swiper切换时,有时会触发下拉刷新的问题, 已避免swiper和下拉刷新相互冲突
+7. 解决钉钉小程序mescroll-uni下拉刷新有时无法触发的问题
+8. 解决上拉加载进度在部分Android手机显示不全的问题
+9. 提高 me-tabs 组件在部分Android手机的兼容性  
+-by 小瑾同学
+
+---
+#### 1.2.8版本 (2020/06/28)
+1. 解决 mescroll-uni 再某些情况下列表数据渲染不完全的问题 ( mescroll-body无此问题 )
+2. 优化 me-tabs 组件, 使用支付宝小程序可隐藏滚动条, 同时修复字节跳动小程序tab切换时渲染延迟的问题
+-by 小瑾同学
+
+---
+#### 1.2.7版本 (2020/06/24)
+1. 上拉加载结束隐藏底部加载区域,避免加载区域占位
+2. h5端的tab页默认偏移TabBar的高度,避免h5端列表被TabBar遮住 (如不想偏移,可通过配置 :bottombar="false" 取消)
+3. 新增 me-tabs 组件,tabs支持水平滑动; 优化mescroll-more和mescroll-swiper的案例, 顶部tab支持水平滑动
+
+---
+#### 1.2.6版本 (2020/06/16)
+1. mescroll-uni 和 mescroll-body 的 scrollTo 正式支持 scroll-into-view (传入的 y 为view的id即可生效)
+2. topbar 顶部是否预留状态栏的高度, 默认false; 这个版本还可支持设置状态栏背景: 如 '#ffff00', 'url(xxx)', 'linear-gradient(xx)'
+3. down.bgColor 和 up.bgColor 加载区域的背景,不仅支持色值, 而且还是支持背景图和渐变: 如 'url(xxx)', 'linear-gradient(xx)'
+4. 通过css方式适配iPhoneX, 比之前通过style方式具有更好的兼容性, 也同时消除了edge浏览器重复设置相同属性的警告
+5. 移除非必须的标签选择器,避免微信小程序提示组件内不可使用标签选择器的警告
+6. 修复当配置up的use为false时,默认的下拉刷新有时候无法自动隐藏的问题
+7. 修复当配置down的native为true时,auto失效的问题
+8. 修复空布局在某些情况下图片和文本错位的问题
+
+---
+#### 1.2.5版本 (2020/03/15)
+1. mescroll-body 的 props 支持 safearea 的配置 (需要适配iPhoneX时,配置为 true 即可, 默认 false)
+2. mescroll-uni 的 scrollTo 支持 scroll-into-view (当传入的 y 为view的id时, 即可生效)
+3. 新增 下拉加载聊天记录的案例 list-msg.vue, 类似微信QQ的聊天记录
+
+---
+#### 1.2.4版本 (2020/03/11)
+1. down和up分别新增 bgColor 的配置: 下拉区域背景颜色,默认"transparent"
+2. down和up分别新增 textColor 的配置: 下拉文本的颜色,默认"gray" (当bgColor配置了颜色,而textColor未配置时,则自动默认为白色)
+3. 调整mescroll-more-item.js, 使mescroll-more的案例支持初始化tabIndex大于0的tab页
+4. mescroll-body支持isBounce的配置, 解决H5下拉刷新失效的问题
+5. 解决mescroll-body在Android真机小程序下拉卡顿的问题 (mescroll-uni无此问题)
+
+---
+#### 1.2.3版本 (2020/02/18)
+新增3个mescroll的mixins, 极大简化了mescroll-comp, mescroll-more, mescroll-swiper的案例
+
+
+#### 1.2.2版本 (2020/02/16)
+1. 调整mescroll-more和mescroll-swiper的案例,确保各小程序平台可正确获取到mescroll对象  
+2. 修复字节跳动小程序初始化时的异常警告: <a href="http://www.mescroll.com/qa.html?v=20200216#q26">详情</a>
+
+
+#### 1.2.1版本 (2020/02/08)
+1. 新增 &lt;mescroll-body&gt; 组件, 用来填补 &lt;mescroll-uni&gt; 的不足.
+2. mescroll-body基于原生页的滚动,支持写入原生组件和fixed元素,不必固定高度,不必配置pages.json,简单性能好.
+3. mescroll-body可配置down的native:true, 可直接代理系统自带的下拉组件, 参考 mescroll-native 示例
+4. 新增mescroll-mixins.js,简化代码,兼容更多小程序平台
+5. 修复字节跳动小程序和支付宝小程序的部分异常警告
+
+#### 1.2.0版本 (2020/01/06)
+1. mescroll-uni.vue的props新增height. // 简单快捷设置mescroll的高度, 此项有值,则不使用fixed. 
+	使用场景: 当在弹窗或浮层中使用fixed固定mescroll高度比较麻烦时, 配置此项就很方便了
+	支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight
+2. mescroll-uni.vue的props新增safearea. // bottom的偏移量是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用,此项值对回到顶部按钮生效)
+3. mescroll-uni.vue的props中,top和bottom不仅仅支持数字, 还支持"20rpx", "20px", "20%"格式的值
+4. 补充锁定上拉加载mescroll.lockUpScroll的方法
+5. down.fps默认值提高至80
+6. 独立出空布局的组件&lt;mescroll-empty&gt;, 以便在不使用mescroll的界面也能统一管理空布局
+```
+<template>
+		<view>
+			// 基本用法
+			<mescroll-empty v-if="list.length==0"></mescroll-empty>
+			// 所有配置项 (option同up.empty的配置一致)
+			<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
+		</view>
+	</template>
+	
+	<script>
+		import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
+		export default {
+			components: {
+				MescrollEmpty
+			},
+			...
+		}
+```
+7. 为了更快速自定义回到顶部按钮, up.toTop新增以下配置项:
+```
+	toTop: {
+		zIndex: 9990, // fixed定位z-index值
+		right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false. 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取mescroll-uni.vue的safearea值)
+		width: 72, // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		radius: "50%", // 圆角, 默认"50%" (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
+		src: null, // (已有配置)
+		offset: 1000 // (已有配置)
+	}
+```
+
+
+---
+#### 1.1.9版本 (2019/12/16)
+1. 解决左右滑动屏幕,某些情况下会触发上拉回调的bug
+2. mescroll-uni.vue的props新增topbar: top的偏移量是否加上状态栏高度 (当fixed为false时不生效; 使用场景:取消原生导航栏时,配置此项可自动加上状态栏高度的偏移量)
+
+---
+#### 1.1.8版本 (2019/11/01)
+1. 解决Android小程序快速上拉可能会导致不断翻页直到没有数据为止的bug
+2. 过滤某些情况下touch的警告输出
+
+---
+#### 1.1.7版本 (2019/10/15)
+1. 重点解决下拉刷新卡顿,特别是Android小程序卡顿的问题
+2. 补充之前版本遗漏的mescroll.lockUpScroll方法
+3. 支持数据不满屏时,仍可触发翻页的回调
+
+---
+#### 1.1.5版本 (2019/07/25)
+1. up新增isBounce的配置, 默认值为false. 目的是解决H5在ios下拉出现灰色背景和android端无法正常下拉的问题. 详见: <a href="http://www.mescroll.com/qa.html?v=190725#q25">http://www.mescroll.com/qa.html?v=190725#q25</a>
+2. 修复setClientHeight某些情况下会提示undefined的问题
+3. 修复在mescroll-uni-option.js配置page会导致分页异常的问题
+
+---
+#### 1.1.2版本 (2019/07/19)
+一. 修复和优化的内容:
+1. 修复列表不满屏时,无法上拉的问题
+2. 修复列表滚动到底部,有时无法上拉的问题
+3. 修复快速滚动到顶部时,滚动条的位置有时不为0的问题
+4. 修复配置page.num不生效的问题
+5. 修复小程序android端下拉卡顿的问题 (可配置down的supply,复杂的列表可适当调大值)
+6. 修复H5多mescroll的情况下回到顶部按钮错位的问题
+7. 优化和简化逻辑,去除冗余代码
+
+二. 完善获取节点信息的方式:
+1. 获取mescroll的唯一元素id: mescroll.viewId (可通过uni.createSelectorQuery进一步获取更多信息)
+2. 获取滚动内容的高度: mescroll.getScrollHeight()
+3. 获取mescroll的高度: mescroll.getClientHeight()
+4. 获取滚动条位置: mescroll.getScrollTop()
+5. 获取到底部的距离: mescroll.getScrollBottom()
+6. 获取滚动的方向: mescroll.isScrollUp // true向上滑; false向下滑
+7. 更多信息详见mescroll-options的示例
+
+---
+#### 1.1.1版本 (2019/07/16)
+1. 修复滚动到顶部,有时无法下拉的问题
+2. 修复禁止上拉时,列表底部有空白区域的问题
+
+---
+#### 1.1.0版本 (重要版本) (2019/07/01)
+一. 简化使用:
+1. **无需**在page.json中配置onReachBottomDistance<br/>
+2. **无需**在页面中注册onReachBottom 和 onPageScroll<br/>
+3. 初始化时@init**不再**是必须配置项<br/>
+4. 极大的**简化** list-mescroll-more.vue 的案例<br/>
+5. 全面**支持swiper**,详见 mescroll-swiper.vue 的案例<br/>
+6. 所有基础案例都有所简化, **建议**重新下载参考. (以前版本向下兼容)<br/> 
+7. 所有中高级案例自定义的部分,改动比较大, **必须**重新下载参考. (不兼容以前版本)
+
+二. 优化性能:
+1. 支持fixed定位,可实现局部**区域滚动**<br/>
+2. 支持swiper,scrollview**嵌套**使用<br/>
+3. 优化下拉逻辑,修复下拉刷新卡顿和抖动的问题<br/>
+4. 优化默认样式,避免某些情况出现双滚动条的问题<br/>
+5. up废弃errDistance配置, 内部已优化处理相关问题
+
+三. 新增配置:
+1. down和up新增fps节流配置.(默认40fps, 值越大每秒刷新频率越高)<br/>
+2. up新增offset配置: 距底部多远时,触发upCallback (默认80, 代替page.json的onReachBottomDistance)<br/>
+3. up新增onScroll配置: 是否监听滚动事件,默认false<br/>
+4. < mescroll-uni 新增 @scroll="scroll" 获取滚动条的位置和滚动方向, 需配置up的onScroll为true, 详见 mescroll-options.vue 的示例<br/>
+5. < mescroll-uni 新增 :fixed="true/false" 是否支持fixed定位, 默认 true. 
+<br/> 当:fixed="true", 此时 :top 和 :bottom 为 fixed 的 top 和 bottom
+<br/> 当:fixed="false", 此时 :top 和 :bottom 为 padding-top 和 padding-bottom
+
+
+---
+#### 1.0.3版本 (2019/06/13)
+1. 默认设置page高度100%,使列表不满屏的时候,仍可下拉刷新<br/>
+2. 加入-webkit-overflow-scrolling: touch, 编译到H5和APP,使iOS列表滚动更流畅<br/>
+3. mescroll-empty加入box-sizing: border-box, 修复图标和文本不居中的问题<br/>
+4. 上拉配置新增errDistance,默认110 // mescroll.endErr()的时候需往上滑动一段距离,使其能再次触发上拉加载 (已在1.1.0版本废弃)<br/>
+5. mescroll.endErr(errDistance) 新增的参数 errDistance; 可单独配置异常往上滑动的距离 (已在1.1.0版本废弃)<br/>
+6. 修改了list-mescroll-more的160行为mescroll.endErr(0);(已在1.1.0版本优化)<br/>
+7. 新增mescroll.scrollTo(y,t) 滚动到指定的位置; 本质调用的是uni.pageScrollTo
+
+---
+#### 1.0.2版本 (2019/05/28)  
+1. 组件根元素加入mescroll-uni的样式,empty新增fixed等配置项; <br/>
+2. 修复list-mescroll-more案例切换tabs,某些情况的page.num会错乱的问题

+ 55 - 0
node_modules/mescroll-uni/components/mescroll-down.css

@@ -0,0 +1,55 @@
+/* 下拉刷新区域 */
+.mescroll-downwarp {
+	position: absolute;
+	top: -100%;
+	left: 0;
+	width: 100%;
+	height: 100%;
+	text-align: center;
+}
+
+/* 下拉刷新--内容区,定位于区域底部 */
+.mescroll-downwarp .downwarp-content {
+	position: absolute;
+	left: 0;
+	bottom: 0;
+	width: 100%;
+	min-height: 60rpx;
+	padding: 20rpx 0;
+	text-align: center;
+}
+
+/* 下拉刷新--提示文本 */
+.mescroll-downwarp .downwarp-tip {
+	display: inline-block;
+	font-size: 28rpx;
+	vertical-align: middle;
+	margin-left: 16rpx;
+	/* color: gray; 已在style设置color,此处删去*/
+}
+
+/* 下拉刷新--旋转进度条 */
+.mescroll-downwarp .downwarp-progress {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid gray;
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+	vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-downwarp .mescroll-rotate {
+	animation: mescrollDownRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollDownRotate {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}

+ 47 - 0
node_modules/mescroll-uni/components/mescroll-down.vue

@@ -0,0 +1,47 @@
+<!-- 下拉刷新区域 -->
+<template>
+	<view v-if="mOption.use" class="mescroll-downwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
+		<view class="downwarp-content">
+			<view class="downwarp-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mOption.textColor, 'transform':downRotate}"></view>
+			<view class="downwarp-tip">{{downText}}</view>
+		</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object , // down的配置项
+		type: Number, // 下拉状态(inOffset:1, outOffset:2, showLoading:3, endDownScroll:4)
+		rate: Number // 下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 是否在加载中
+		isDownLoading(){
+			return this.type === 3
+		},
+		// 旋转的角度
+		downRotate(){
+			return 'rotate(' + 360 * this.rate + 'deg)'
+		},
+		// 文本提示
+		downText(){
+			switch (this.type){
+				case 1: return this.mOption.textInOffset;
+				case 2: return this.mOption.textOutOffset;
+				case 3: return this.mOption.textLoading;
+				case 4: return this.mOption.textLoading;
+				default: return this.mOption.textInOffset;
+			}
+		}
+	}
+};
+</script>
+
+<style>
+@import "./mescroll-down.css";
+</style>

+ 118 - 0
node_modules/mescroll-uni/components/mescroll-empty.vue

@@ -0,0 +1,118 @@
+<!--空布局:
+
+可作为独立的组件, 不使用mescroll的页面也能单独引入, 以便APP全局统一管理:
+import MescrollEmpty from '@/components/mescroll-uni/components/mescroll-empty.vue';
+<mescroll-empty v-if="isShowEmpty" :option="optEmpty" @emptyclick="emptyClick"></mescroll-empty>
+-->
+<template>
+	<view class="mescroll-empty" :class="{ 'empty-fixed': option.fixed }" :style="{ 'z-index': option.zIndex, top: option.top }">
+		<view> <image v-if="icon" class="empty-icon" :src="icon" mode="widthFix" /> </view>
+		<view v-if="tip" class="empty-tip">{{ tip }}</view>
+		<view v-if="btnText" class="empty-btn" @click="emptyClick">{{ btnText }}</view>
+	</view>
+</template>
+
+<script>
+// 引入全局配置
+import GlobalOption from './../mescroll-uni-option.js';
+// 引入国际化工具类
+import mescrollI18n from './../mescroll-i18n.js';
+export default {
+	props: {
+		// empty的配置项: 默认为GlobalOption.up.empty
+		option: {
+			type: Object,
+			default() {
+				return {};
+			}
+		}
+	},
+	// 使用computed获取配置,用于支持option的动态配置
+	computed: {
+		// 图标
+		icon() {
+			if (this.option.icon != null) { // 此处不使用短路求值, 用于支持传空串不显示图标
+				return this.option.icon
+			} else{
+				let i18nType = mescrollI18n.getType() // 国际化配置
+				if (this.option.i18n) {
+					return this.option.i18n[i18nType].icon
+				} else{
+					return GlobalOption.i18n[i18nType].up.empty.icon || GlobalOption.up.empty.icon
+				}
+			}
+		},
+		// 文本提示
+		tip() {
+			if (this.option.tip != null) { // 支持传空串不显示文本提示
+				return this.option.tip
+			} else{
+				let i18nType = mescrollI18n.getType() // 国际化配置
+				if (this.option.i18n) {
+					return this.option.i18n[i18nType].tip
+				} else{
+					return GlobalOption.i18n[i18nType].up.empty.tip || GlobalOption.up.empty.tip
+				}
+			}
+		},
+		// 按钮文本
+		btnText() {
+			if (this.option.i18n) {
+				let i18nType = mescrollI18n.getType() // 国际化配置
+				return this.option.i18n[i18nType].btnText
+			} else{
+				return this.option.btnText
+			}
+		}
+	},
+	methods: {
+		// 点击按钮
+		emptyClick() {
+			this.$emit('emptyclick');
+		}
+	}
+};
+</script>
+
+<style>
+/* 无任何数据的空布局 */
+.mescroll-empty {
+	box-sizing: border-box;
+	width: 100%;
+	padding: 100rpx 50rpx;
+	text-align: center;
+}
+
+.mescroll-empty.empty-fixed {
+	z-index: 99;
+	position: absolute; /*transform会使fixed失效,最终会降级为absolute */
+	top: 100rpx;
+	left: 0;
+}
+
+.mescroll-empty .empty-icon {
+	width: 280rpx;
+	height: 280rpx;
+}
+
+.mescroll-empty .empty-tip {
+	margin-top: 20rpx;
+	font-size: 24rpx;
+	color: gray;
+}
+
+.mescroll-empty .empty-btn {
+	display: inline-block;
+	margin-top: 40rpx;
+	min-width: 200rpx;
+	padding: 18rpx;
+	font-size: 28rpx;
+	border: 1rpx solid #e04b28;
+	border-radius: 60rpx;
+	color: #e04b28;
+}
+
+.mescroll-empty .empty-btn:active {
+	opacity: 0.75;
+}
+</style>

+ 83 - 0
node_modules/mescroll-uni/components/mescroll-top.vue

@@ -0,0 +1,83 @@
+<!-- 回到顶部的按钮 -->
+<template>
+	<image
+		v-if="mOption.src"
+		class="mescroll-totop"
+		:class="[value ? 'mescroll-totop-in' : 'mescroll-totop-out', {'mescroll-totop-safearea': mOption.safearea}]"
+		:style="{'z-index':mOption.zIndex, 'left': left, 'right': right, 'bottom':addUnit(mOption.bottom), 'width':addUnit(mOption.width), 'border-radius':addUnit(mOption.radius)}"
+		:src="mOption.src"
+		mode="widthFix"
+		@click="toTopClick"
+	/>
+</template>
+
+<script>
+export default {
+	props: {
+		// up.toTop的配置项
+		option: Object,
+		// 是否显示
+		value: false
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption(){
+			return this.option || {}
+		},
+		// 优先显示左边
+		left(){
+			return this.mOption.left ? this.addUnit(this.mOption.left) : 'auto';
+		},
+		// 右边距离 (优先显示左边)
+		right() {
+			return this.mOption.left ? 'auto' : this.addUnit(this.mOption.right);
+		}
+	},
+	methods: {
+		addUnit(num){
+			if(!num) return 0;
+			if(typeof num === 'number') return num + 'rpx';
+			return num
+		},
+		toTopClick() {
+			this.$emit('input', false); // 使v-model生效
+			this.$emit('click'); // 派发点击事件
+		}
+	}
+};
+</script>
+
+<style>
+/* 回到顶部的按钮 */
+.mescroll-totop {
+	z-index: 9990;
+	position: fixed !important; /* 加上important避免编译到H5,在多mescroll中定位失效 */
+	right: 20rpx;
+	bottom: 120rpx;
+	width: 72rpx;
+	height: auto;
+	border-radius: 50%;
+	opacity: 0;
+	transition: opacity 0.5s; /* 过渡 */
+	margin-bottom: var(--window-bottom); /* css变量 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-totop-safearea {
+		margin-bottom: calc(var(--window-bottom) + constant(safe-area-inset-bottom)); /* window-bottom + 适配 iPhoneX */
+		margin-bottom: calc(var(--window-bottom) + env(safe-area-inset-bottom));
+	}
+}
+
+/* 显示 -- 淡入 */
+.mescroll-totop-in {
+	opacity: 1;
+}
+
+/* 隐藏 -- 淡出且不接收事件*/
+.mescroll-totop-out {
+	opacity: 0;
+	pointer-events: none;
+}
+</style>

+ 47 - 0
node_modules/mescroll-uni/components/mescroll-up.css

@@ -0,0 +1,47 @@
+/* 上拉加载区域 */
+.mescroll-upwarp {
+	box-sizing: border-box;
+	min-height: 110rpx;
+	padding: 30rpx 0;
+	text-align: center;
+	clear: both;
+}
+
+/*提示文本 */
+.mescroll-upwarp .upwarp-tip,
+.mescroll-upwarp .upwarp-nodata {
+	display: inline-block;
+	font-size: 28rpx;
+	vertical-align: middle;
+	/* color: gray; 已在style设置color,此处删去*/
+}
+
+.mescroll-upwarp .upwarp-tip {
+	margin-left: 16rpx;
+}
+
+/*旋转进度条 */
+.mescroll-upwarp .upwarp-progress {
+	display: inline-block;
+	width: 32rpx;
+	height: 32rpx;
+	border-radius: 50%;
+	border: 2rpx solid gray;
+	border-bottom-color: transparent !important; /*已在style设置border-color,此处需加 !important*/
+	vertical-align: middle;
+}
+
+/* 旋转动画 */
+.mescroll-upwarp .mescroll-rotate {
+	animation: mescrollUpRotate 0.6s linear infinite;
+}
+
+@keyframes mescrollUpRotate {
+	0% {
+		transform: rotate(0deg);
+	}
+
+	100% {
+		transform: rotate(360deg);
+	}
+}

+ 39 - 0
node_modules/mescroll-uni/components/mescroll-up.vue

@@ -0,0 +1,39 @@
+<!-- 上拉加载区域 -->
+<template>
+	<view class="mescroll-upwarp" :style="{'background-color':mOption.bgColor,'color':mOption.textColor}">
+		<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+		<view v-show="isUpLoading">
+			<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mOption.textColor}"></view>
+			<view class="upwarp-tip">{{ mOption.textLoading }}</view>
+		</view>
+		<!-- 无数据 -->
+		<view v-if="isUpNoMore" class="upwarp-nodata">{{ mOption.textNoMore }}</view>
+	</view>
+</template>
+
+<script>
+export default {
+	props: {
+		option: Object, // up的配置项
+		type: Number // 上拉加载的状态:0(loading前),1(loading中),2(没有更多了)
+	},
+	computed: {
+		// 支付宝小程序需写成计算属性,prop定义default仍报错
+		mOption() {
+			return this.option || {};
+		},
+		// 加载中
+		isUpLoading() {
+			return this.type === 1;
+		},
+		// 没有更多了
+		isUpNoMore() {
+			return this.type === 2;
+		}
+	}
+};
+</script>
+
+<style>
+@import './mescroll-up.css';
+</style>

+ 19 - 0
node_modules/mescroll-uni/mescroll-body.css

@@ -0,0 +1,19 @@
+.mescroll-body {
+	position: relative; /* 下拉刷新区域相对自身定位 */
+	height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
+	overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
+.mescroll-body.mescorll-sticky{
+	overflow: unset !important
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 403 - 0
node_modules/mescroll-uni/mescroll-body.vue

@@ -0,0 +1,403 @@
+<template>
+	<view 
+	class="mescroll-body mescroll-render-touch" 
+	:class="{'mescorll-sticky': sticky}"
+	:style="{'minHeight':minHeight, 'padding-top': padTop, 'padding-bottom': padBottom}" 
+	@touchstart="wxsBiz.touchstartEvent" 
+	@touchmove="wxsBiz.touchmoveEvent" 
+	@touchend="wxsBiz.touchendEvent" 
+	@touchcancel="wxsBiz.touchendEvent"
+	:change:prop="wxsBiz.propObserver"
+	:prop="wxsProp"
+	>
+		<!-- 状态栏 -->
+		<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+		<view class="mescroll-body-content mescroll-wxs-content" :style="{ transform: translateY, transition: transition }" :change:prop="wxsBiz.callObserver" :prop="callProp">
+			<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+			<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+			<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+				<view class="downwarp-content">
+					<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+					<view class="downwarp-tip">{{downText}}</view>
+				</view>
+			</view>
+	
+			<!-- 列表内容 -->
+			<slot></slot>
+
+			<!-- 空布局 -->
+			<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+			<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+			<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+			<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+				<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+				<view v-show="upLoadType===1">
+					<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+					<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+				</view>
+				<!-- 无数据 -->
+				<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+			</view>
+		</view>
+		
+		<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+		<!-- #ifdef H5 -->
+		<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+		<!-- #endif -->
+		
+		<!-- 适配iPhoneX -->
+		<view v-if="safearea" class="mescroll-safearea"></view>
+		
+		<!-- 回到顶部按钮 (fixed元素需写在transform外面,防止降级为absolute)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from './wxs/renderjs.js';
+	export default {
+		mixins: [renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from './mescroll-uni.js';
+	// 引入全局配置
+	import GlobalOption from './mescroll-uni-option.js';
+	// 引入空布局组件
+	import MescrollEmpty from './components/mescroll-empty.vue';
+	// 引入国际化工具类
+	import mescrollI18n from './mescroll-i18n.js';
+	// 引入回到顶部组件
+	import MescrollTop from './components/mescroll-top.vue';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	/**
+	 * mescroll-body 基于page滚动的下拉刷新和上拉加载组件, 支持嵌套原生组件, 性能好
+	 * @property {Object} down 下拉刷新的参数配置
+	 * @property {Object} up 上拉加载的参数配置
+	 * @property {Object} i18n 国际化的参数配置
+	 * @property {String, Number} top 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean, String} topbar 偏移量top是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+	 * @property {String, Number} bottom 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean} safearea 偏移量bottom是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+	 * @property {Boolean} fixed 是否通过fixed固定mescroll的高度, 默认true
+	 * @property {String, Number} height 指定mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+	 * @property {Boolean} bottombar 底部是否偏移TabBar的高度 (仅在H5端的tab页生效)
+	 * @property {Boolean} sticky 是否支持sticky,默认false; 当值配置true时,需避免在mescroll-body标签前面加非定位的元素,否则下拉区域无法隐藏
+	 * @event {Function} init 初始化完成的回调 
+	 * @event {Function} down 下拉刷新的回调
+	 * @event {Function} up 上拉加载的回调 
+	 * @event {Function} emptyclick 点击empty配置的btnText按钮回调
+	 * @event {Function} topclick 点击回到顶部的按钮回调
+	 * @event {Function} scroll 滚动监听 (需在 up 配置 onScroll:true 才生效)
+	 * @example <mescroll-body ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"> ... </mescroll-body>
+	 */
+	export default {
+		name: 'mescroll-body',
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		props: {
+			down: Object,
+			up: Object,
+			i18n: Object,
+			top: [String, Number],
+			topbar: [Boolean, String],
+			bottom: [String, Number],
+			safearea: Boolean,
+			height: [String, Number],
+			bottombar:{
+				type: Boolean,
+				default: true
+			},
+			sticky: Boolean
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态:0(loading前),1(loading中),2(没有更多了,显示END文本提示),3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				windowHeight: 0, // 可使用窗口的高度
+				windowBottom: 0, // 可使用窗口的底部位置
+				statusBarHeight: 0 // 状态栏高度
+			};
+		},
+		computed: {
+			// mescroll最小高度,默认windowHeight,使列表不满屏仍可下拉
+			minHeight(){
+				return this.toPx(this.height || '100%') + 'px'
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			padTop() {
+				return this.numTop + 'px';
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom);
+			},
+			padBottom() {
+				return this.numBottom + 'px';
+			},
+			// 是否为重置下拉的状态
+			isDownReset() {
+				return this.downLoadType === 3 || this.downLoadType === 4;
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : '';
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num) {
+				if (typeof num === 'string') {
+					if (num.indexOf('px') !== -1) {
+						if (num.indexOf('rpx') !== -1) {
+							// "10rpx"
+							num = num.replace('rpx', '');
+						} else if (num.indexOf('upx') !== -1) {
+							// "10upx"
+							num = num.replace('upx', '');
+						} else {
+							// "10px"
+							return Number(num.replace('px', ''));
+						}
+					} else if (num.indexOf('%') !== -1) {
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace('%', '')) / 100;
+						return this.windowHeight * rate;
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0;
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll);
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						if(vm.downResetTimer) {clearTimeout(vm.downResetTimer); vm.downResetTimer = null} // 移除重置倒计时
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,避免下次inOffset不及时显示textInOffset
+							if(vm.downLoadType === 4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll);
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) {
+							// 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+					}
+				}
+			};
+			
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({down: vm.down,up: vm.up})); // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption, true); // 传入true,标记body为滚动区域
+			// 挂载语言包
+			vm.mescroll.i18n = i18nOption;
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if (sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if (sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if (sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是page的scroll,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				if(typeof y === 'string'){
+					// 滚动到指定view (y为css选择器)
+					setTimeout(()=>{ // 延时确保view已渲染; 不使用$nextTick
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let top = rect.top
+								top += vm.mescroll.getScrollTop()
+								uni.pageScrollTo({
+									scrollTop: top,
+									duration: t
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					},30)
+				} else{
+					// 滚动到指定位置 (y必须为数字)
+					uni.pageScrollTo({
+						scrollTop: y,
+						duration: t
+					})
+				}
+			});
+
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	};
+</script>
+
+<style>
+	@import "./mescroll-body.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 15 - 0
node_modules/mescroll-uni/mescroll-i18n.js

@@ -0,0 +1,15 @@
+// 国际化工具类
+const mescrollI18n = {
+	// 默认语言
+	def: "zh",
+	// 获取当前语言类型
+	getType(){
+		return uni.getStorageSync("mescroll-i18n") || this.def
+	},
+	// 设置当前语言类型
+	setType(type){
+		uni.setStorageSync("mescroll-i18n", type)
+	}
+}
+
+export default mescrollI18n

+ 57 - 0
node_modules/mescroll-uni/mescroll-mixins.js

@@ -0,0 +1,57 @@
+// mescroll-body 和 mescroll-uni 通用
+const MescrollMixin = {
+	data() {
+		return {
+			mescroll: null //mescroll实例对象
+		}
+	},
+	// 注册系统自带的下拉刷新 (配置down.native为true时生效, 还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	onPullDownRefresh(){
+		this.mescroll && this.mescroll.onPullDownRefresh();
+	},
+	// 注册列表滚动事件,用于判定在顶部可下拉刷新,在指定位置可显示隐藏回到顶部按钮 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+	onPageScroll(e) {
+		this.mescroll && this.mescroll.onPageScroll(e);
+	},
+	// 注册滚动到底部的事件,用于上拉加载 (此方法为页面生命周期,无法在子组件中触发, 仅在mescroll-body生效)
+	onReachBottom() {
+		this.mescroll && this.mescroll.onReachBottom();
+	},
+	methods: {
+		// mescroll组件初始化的回调,可获取到mescroll对象
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef(); // 兼容字节跳动小程序
+		},
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
+		mescrollInitByRef() {
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
+				let mescrollRef = this.$refs.mescrollRef;
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
+			}
+		},
+		// 下拉刷新的回调 (mixin默认resetUpScroll)
+		downCallback() {
+			if(this.mescroll.optUp.use){
+				this.mescroll.resetUpScroll()
+			}else{
+				setTimeout(()=>{
+					this.mescroll.endSuccess();
+				}, 500)
+			}
+		},
+		// 上拉加载的回调
+		upCallback() {
+			// mixin默认延时500自动结束加载
+			setTimeout(()=>{
+				this.mescroll.endErr();
+			}, 500)
+		}
+	},
+	mounted() {
+		this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况
+	}
+	
+}
+
+export default MescrollMixin;

+ 64 - 0
node_modules/mescroll-uni/mescroll-uni-option.js

@@ -0,0 +1,64 @@
+// 全局配置
+// mescroll-body 和 mescroll-uni 通用
+const GlobalOption = {
+	down: {
+		// 其他down的配置参数也可以写,这里只展示了常用的配置:
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+	},
+	up: {
+		// 其他up的配置参数也可以写,这里只展示了常用的配置:
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
+		toTop: {
+			// 回到顶部按钮,需配置src才显示
+			src: "https://www.mescroll.com/img/mescroll-totop.png", // 图片路径 (建议放入static目录, 如 /static/img/mescroll-totop.png )
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000px
+			right: 20, // 到右边的距离, 默认20 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+			bottom: 120, // 到底部的距离, 默认120 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+			width: 72 // 回到顶部图标的宽度, 默认72 (支持"20rpx", "20px", "20%"格式的值, 纯数字则默认单位rpx)
+		},
+		empty: {
+			use: true, // 是否显示空布局
+			icon: "https://www.mescroll.com/img/mescroll-empty.png" // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png )
+		}
+	},
+	// 国际化配置
+	i18n: {
+		// 中文
+		zh: {
+			down: {
+				textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+				textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+				textLoading: '加载中 ...', // 加载中的提示文本
+				textSuccess: '加载成功', // 加载成功的文本
+				textErr: '加载失败', // 加载失败的文本
+			},
+			up: {
+				textLoading: '加载中 ...', // 加载中的提示文本
+				textNoMore: '-- END --', // 没有更多数据的提示文本
+				empty: {
+					tip: '~ 空空如也 ~' // 空提示
+				}
+			}
+		},
+		// 英文
+		en: {
+			down: {
+				textInOffset: 'drop down refresh',
+				textOutOffset: 'release updates',
+				textLoading: 'loading ...',
+				textSuccess: 'loaded successfully',
+				textErr: 'loading failed'
+			},
+			up: {
+				textLoading: 'loading ...',
+				textNoMore: '-- END --',
+				empty: {
+					tip: '~ absolutely empty ~'
+				}
+			}
+		}
+	}
+}
+
+export default GlobalOption

+ 36 - 0
node_modules/mescroll-uni/mescroll-uni.css

@@ -0,0 +1,36 @@
+.mescroll-uni-warp{
+	height: 100%;
+}
+
+.mescroll-uni-content{
+	height: 100%;
+}
+
+.mescroll-uni {
+	position: relative;
+	width: 100%;
+	height: 100%;
+	min-height: 200rpx;
+	overflow-y: auto;
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 定位的方式固定高度 */
+.mescroll-uni-fixed{
+	z-index: 1;
+	position: fixed;
+	top: 0;
+	left: 0;
+	right: 0;
+	bottom: 0;
+	width: auto; /* 使right生效 */
+	height: auto; /* 使bottom生效 */
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

+ 799 - 0
node_modules/mescroll-uni/mescroll-uni.js

@@ -0,0 +1,799 @@
+/* mescroll
+ * version 1.3.7
+ * 2021-04-13 wenju
+ * https://www.mescroll.com
+ */
+
+export default function MeScroll(options, isScrollBody) {
+	let me = this;
+	me.version = '1.3.7'; // mescroll版本号
+	me.options = options || {}; // 配置
+	me.isScrollBody = isScrollBody || false; // 滚动区域是否为原生页面滚动; 默认为scroll-view
+
+	me.isDownScrolling = false; // 是否在执行下拉刷新的回调
+	me.isUpScrolling = false; // 是否在执行上拉加载的回调
+	let hasDownCallback = me.options.down && me.options.down.callback; // 是否配置了down的callback
+
+	// 初始化下拉刷新
+	me.initDownScroll();
+	// 初始化上拉加载,则初始化
+	me.initUpScroll();
+
+	// 自动加载
+	setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+		// 自动触发下拉刷新 (只有配置了down的callback才自动触发下拉刷新)
+		if ((me.optDown.use || me.optDown.native) && me.optDown.auto && hasDownCallback) {
+			if (me.optDown.autoShowLoading) {
+				me.triggerDownScroll(); // 显示下拉进度,执行下拉回调
+			} else {
+				me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调
+			}
+		}
+		// 自动触发上拉加载
+		if(!me.isUpAutoLoad){ // 部分小程序(头条小程序)emit是异步, 会导致isUpAutoLoad判断有误, 先延时确保先执行down的callback,再执行up的callback
+			setTimeout(function(){
+				me.optUp.use && me.optUp.auto && !me.isUpAutoLoad && me.triggerUpScroll();
+			},100)
+		}
+	}, 30); // 需让me.optDown.inited和me.optUp.inited先执行
+}
+
+/* 配置参数:下拉刷新 */
+MeScroll.prototype.extendDownScroll = function(optDown) {
+	// 下拉刷新的配置
+	MeScroll.extend(optDown, {
+		use: true, // 是否启用下拉刷新; 默认true
+		auto: true, // 是否在初始化完毕之后自动执行下拉刷新的回调; 默认true
+		native: false, // 是否使用系统自带的下拉刷新; 默认false; 仅mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例)
+		autoShowLoading: false, // 如果设置auto=true(在初始化完毕之后自动执行下拉刷新的回调),那么是否显示下拉刷新的进度; 默认false
+		isLock: false, // 是否锁定下拉刷新,默认false;
+		offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调
+		startTop: 100, // scroll-view快速滚动到顶部时,此时的scroll-top可能大于0, 此值用于控制最大的误差
+		inOffsetRate: 1, // 在列表顶部,下拉的距离小于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+		outOffsetRate: 0.2, // 在列表顶部,下拉的距离大于offset时,改变下拉区域高度比例;值小于1且越接近0,高度变化越小,表现为越往下越难拉
+		bottomOffset: 20, // 当手指touchmove位置在距离body底部20px范围内的时候结束上拉刷新,避免Webview嵌套导致touchend事件不执行
+		minAngle: 45, // 向下滑动最少偏移的角度,取值区间  [0,90];默认45度,即向下滑动的角度大于45度则触发下拉;而小于45度,将不触发下拉,避免与左右滑动的轮播等组件冲突;
+		textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本
+		textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textSuccess: '加载成功', // 加载成功的文本
+		textErr: '加载失败', // 加载失败的文本
+		beforeEndDelay: 0, // 延时结束的时长 (显示加载成功/失败的时长, android小程序设置此项结束下拉会卡顿, 配置后请注意测试)
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorTop)
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+		inited: null, // 下拉刷新初始化完毕的回调
+		inOffset: null, // 下拉的距离进入offset范围内那一刻的回调
+		outOffset: null, // 下拉的距离大于offset那一刻的回调
+		onMoving: null, // 下拉过程中的回调,滑动过程一直在执行; rate下拉区域当前高度与指定距离的比值(inOffset: rate<1; outOffset: rate>=1); downHight当前下拉区域的高度
+		beforeLoading: null, // 准备触发下拉刷新的回调: 如果return true,将不触发showLoading和callback回调; 常用来完全自定义下拉刷新, 参考案例【淘宝 v6.8.0】
+		showLoading: null, // 显示下拉刷新进度的回调
+		afterLoading: null, // 显示下拉刷新进度的回调之后,马上要执行的代码 (如: 在wxs中使用)
+		beforeEndDownScroll: null, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】
+		endDownScroll: null, // 结束下拉刷新的回调
+		afterEndDownScroll: null, // 结束下拉刷新的回调,马上要执行的代码 (如: 在wxs中使用)
+		callback: function(mescroll) {
+			// 下拉刷新的回调;默认重置上拉加载列表为第一页
+			mescroll.resetUpScroll();
+		}
+	})
+}
+
+/* 配置参数:上拉加载 */
+MeScroll.prototype.extendUpScroll = function(optUp) {
+	// 上拉加载的配置
+	MeScroll.extend(optUp, {
+		use: true, // 是否启用上拉加载; 默认true
+		auto: true, // 是否在初始化完毕之后自动执行上拉加载的回调; 默认true
+		isLock: false, // 是否锁定上拉加载,默认false;
+		isBoth: true, // 上拉加载时,如果滑动到列表顶部是否可以同时触发下拉刷新;默认true,两者可同时触发;
+		callback: null, // 上拉加载的回调;function(page,mescroll){ }
+		page: {
+			num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
+			size: 10, // 每页数据的数量
+			time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复;
+		},
+		noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看
+		offset: 150, // 距底部多远时,触发upCallback,仅mescroll-uni生效 ( mescroll-body配置的是pages.json的 onReachBottomDistance )
+		textLoading: '加载中 ...', // 加载中的提示文本
+		textNoMore: '-- END --', // 没有更多数据的提示文本
+		bgColor: "transparent", // 背景颜色 (建议在pages.json中再设置一下backgroundColorBottom)
+		textColor: "gray", // 文本颜色 (当bgColor配置了颜色,而textColor未配置时,则textColor会默认为白色)
+		inited: null, // 初始化完毕的回调
+		showLoading: null, // 显示加载中的回调
+		showNoMore: null, // 显示无更多数据的回调
+		hideUpScroll: null, // 隐藏上拉加载的回调
+		errDistance: 60, // endErr的时候需往上滑动一段距离,使其往下滑动时再次触发onReachBottom,仅mescroll-body生效
+		toTop: {
+			// 回到顶部按钮,需配置src才显示
+			src: null, // 图片路径,默认null (绝对路径或网络图)
+			offset: 1000, // 列表滚动多少距离才显示回到顶部按钮,默认1000
+			duration: 300, // 回到顶部的动画时长,默认300ms (当值为0或300则使用系统自带回到顶部,更流畅; 其他值则通过step模拟,部分机型可能不够流畅,所以非特殊情况不建议修改此项)
+			btnClick: null, // 点击按钮的回调
+			onShow: null, // 是否显示的回调
+			zIndex: 9990, // fixed定位z-index值
+			left: null, // 到左边的距离, 默认null. 此项有值时,right不生效. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			right: 20, // 到右边的距离, 默认20 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			bottom: 120, // 到底部的距离, 默认120 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			safearea: false, // bottom的偏移量是否加上底部安全区的距离, 默认false, 需要适配iPhoneX时使用 (具体的界面如果不配置此项,则取本vue的safearea值)
+			width: 72, // 回到顶部图标的宽度, 默认72 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+			radius: "50%" // 圆角, 默认"50%" (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx)
+		},
+		empty: {
+			use: true, // 是否显示空布局
+			icon: null, // 图标路径
+			tip: '~ 暂无相关数据 ~', // 提示
+			btnText: '', // 按钮
+			btnClick: null, // 点击按钮的回调
+			onShow: null, // 是否显示的回调
+			fixed: false, // 是否使用fixed定位,默认false; 配置fixed为true,以下的top和zIndex才生效 (transform会使fixed失效,最终会降级为absolute)
+			top: "100rpx", // fixed定位的top值 (完整的单位值,如 "10%"; "100rpx")
+			zIndex: 99 // fixed定位z-index值
+		},
+		onScroll: false // 是否监听滚动事件
+	})
+}
+
+/* 配置参数 */
+MeScroll.extend = function(userOption, defaultOption) {
+	if (!userOption) return defaultOption;
+	for (let key in defaultOption) {
+		if (userOption[key] == null) {
+			let def = defaultOption[key];
+			if (def != null && typeof def === 'object') {
+				userOption[key] = MeScroll.extend({}, def); // 深度匹配
+			} else {
+				userOption[key] = def;
+			}
+		} else if (typeof userOption[key] === 'object') {
+			MeScroll.extend(userOption[key], defaultOption[key]); // 深度匹配
+		}
+	}
+	return userOption;
+}
+
+/* 简单判断是否配置了颜色 (非透明,非白色) */
+MeScroll.prototype.hasColor = function(color) {
+	if(!color) return false;
+	let c = color.toLowerCase();
+	return c != "#fff" && c != "#ffffff" && c != "transparent" && c != "white"
+}
+
+/* -------初始化下拉刷新------- */
+MeScroll.prototype.initDownScroll = function() {
+	let me = this;
+	// 配置参数
+	me.optDown = me.options.down || {};
+	if(!me.optDown.textColor && me.hasColor(me.optDown.bgColor)) me.optDown.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+	me.extendDownScroll(me.optDown);
+	
+	// 如果是mescroll-body且配置了native,则禁止自定义的下拉刷新
+	if(me.isScrollBody && me.optDown.native){
+		me.optDown.use = false
+	}else{
+		me.optDown.native = false // 仅mescroll-body支持,mescroll-uni不支持
+	}
+	
+	me.downHight = 0; // 下拉区域的高度
+
+	// 在页面中加入下拉布局
+	if (me.optDown.use && me.optDown.inited) {
+		// 初始化完毕的回调
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+			me.optDown.inited(me);
+		}, 0)
+	}
+}
+
+/* 列表touchstart事件 */
+MeScroll.prototype.touchstartEvent = function(e) {
+	if (!this.optDown.use) return;
+
+	this.startPoint = this.getPoint(e); // 记录起点
+	this.startTop = this.getScrollTop(); // 记录此时的滚动条位置
+	this.startAngle = 0; // 初始角度
+	this.lastPoint = this.startPoint; // 重置上次move的点
+	this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+	this.inTouchend = false; // 标记不是touchend
+}
+
+/* 列表touchmove事件 */
+MeScroll.prototype.touchmoveEvent = function(e) {
+	if (!this.optDown.use) return;
+	let me = this;
+
+	let scrollTop = me.getScrollTop(); // 当前滚动条的距离
+	let curPoint = me.getPoint(e); // 当前点
+
+	let moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+
+	// 向下拉 && 在顶部
+	// mescroll-body,直接判定在顶部即可
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+	if (moveY > 0 && (
+			(me.isScrollBody && scrollTop <= 0)
+			||
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+		)) {
+		// 可下拉的条件
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+				me.optUp.isBoth))) {
+
+			// 下拉的初始角度是否在配置的范围内
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (me.startAngle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新
+
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				me.touchendEvent(); // 提前触发touchend
+				return;
+			}
+			
+			me.preventDefault(e); // 阻止默认事件
+
+			let diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+
+			// 下拉距离  < 指定距离
+			if (me.downHight < me.optDown.offset) {
+				if (me.movetype !== 1) {
+					me.movetype = 1; // 加入标记,保证只执行一次
+					me.isDownEndSuccess = null; // 重置是否加载成功的状态 (wxs执行的是wxs.wxs)
+					me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+
+				// 指定距离  <= 下拉距离
+			} else {
+				if (me.movetype !== 2) {
+					me.movetype = 2; // 加入标记,保证只执行一次
+					me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				if (diff > 0) { // 向下拉
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+			
+			me.downHight = Math.round(me.downHight) // 取整
+			let rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+			me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+		}
+	}
+
+	me.lastPoint = curPoint; // 记录本次移动的点
+}
+
+/* 列表touchend事件 */
+MeScroll.prototype.touchendEvent = function(e) {
+	if (!this.optDown.use) return;
+	// 如果下拉区域高度已改变,则需重置回来
+	if (this.isMoveDown) {
+		if (this.downHight >= this.optDown.offset) {
+			// 符合触发刷新的条件
+			this.triggerDownScroll();
+		} else {
+			// 不符合的话 则重置
+			this.downHight = 0;
+			this.endDownScrollCall(this);
+		}
+		this.movetype = 0;
+		this.isMoveDown = false;
+	} else if (!this.isScrollBody && this.getScrollTop() === this.startTop) { // scroll-view到顶/左/右/底的滑动事件
+		let isScrollUp = this.getPoint(e).y - this.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 上滑
+		if (isScrollUp) {
+			// 需检查滑动的角度
+			let angle = this.getAngle(this.getPoint(e), this.startPoint); // 两点之间的角度,区间 [0,90]
+			if (angle > 80) {
+				// 检查并触发上拉
+				this.triggerUpScroll(true);
+			}
+		}
+	}
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+MeScroll.prototype.getPoint = function(e) {
+	if (!e) {
+		return {
+			x: 0,
+			y: 0
+		}
+	}
+	if (e.touches && e.touches[0]) {
+		return {
+			x: e.touches[0].pageX,
+			y: e.touches[0].pageY
+		}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {
+			x: e.changedTouches[0].pageX,
+			y: e.changedTouches[0].pageY
+		}
+	} else {
+		return {
+			x: e.clientX,
+			y: e.clientY
+		}
+	}
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+MeScroll.prototype.getAngle = function(p1, p2) {
+	let x = Math.abs(p1.x - p2.x);
+	let y = Math.abs(p1.y - p2.y);
+	let z = Math.sqrt(x * x + y * y);
+	let angle = 0;
+	if (z !== 0) {
+		angle = Math.asin(y / z) / Math.PI * 180;
+	}
+	return angle
+}
+
+/* 触发下拉刷新 */
+MeScroll.prototype.triggerDownScroll = function() {
+	if (this.optDown.beforeLoading && this.optDown.beforeLoading(this)) {
+		//return true则处于完全自定义状态
+	} else {
+		this.showDownScroll(); // 下拉刷新中...
+		!this.optDown.native && this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示下拉进度布局 */
+MeScroll.prototype.showDownScroll = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	if (this.optDown.native) {
+		uni.startPullDownRefresh(); // 系统自带的下拉刷新
+		this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+	} else{
+		this.downHight = this.optDown.offset; // 更新下拉区域高度
+		this.showDownLoadingCall(this.downHight); // 下拉刷新中...
+	}
+}
+
+MeScroll.prototype.showDownLoadingCall = function(downHight) {
+	this.optDown.showLoading && this.optDown.showLoading(this, downHight); // 下拉刷新中...
+	this.optDown.afterLoading && this.optDown.afterLoading(this, downHight); // 下拉刷新中...触发之后马上要执行的代码
+}
+
+/* 显示系统自带的下拉刷新时需要处理的业务 */
+MeScroll.prototype.onPullDownRefresh = function() {
+	this.isDownScrolling = true; // 标记下拉中
+	this.showDownLoadingCall(0); // 仍触发showLoading,因为上拉加载用到
+	this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据
+}
+
+/* 结束下拉刷新 */
+MeScroll.prototype.endDownScroll = function() {
+	if (this.optDown.native) { // 结束原生下拉刷新
+		this.isDownScrolling = false;
+		this.endDownScrollCall(this);
+		uni.stopPullDownRefresh();
+		return
+	}
+	let me = this;
+	// 结束下拉刷新的方法
+	let endScroll = function() {
+		me.downHight = 0;
+		me.isDownScrolling = false;
+		me.endDownScrollCall(me);
+		if(!me.isScrollBody){
+			me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页
+			me.scrollTo(0,0) // scroll-view需重置滚动条到顶部,避免startTop大于0时,对下拉刷新的影响
+		}
+	}
+	// 结束下拉刷新时的回调
+	let delay = 0;
+	if (me.optDown.beforeEndDownScroll) {
+		delay = me.optDown.beforeEndDownScroll(me); // 结束下拉刷新的延时,单位ms
+		if(me.isDownEndSuccess == null) delay = 0; // 没有执行加载中,则不延时
+	}
+	if (typeof delay === 'number' && delay > 0) {
+		setTimeout(endScroll, delay);
+	} else {
+		endScroll();
+	}
+}
+
+MeScroll.prototype.endDownScrollCall = function() {
+	this.optDown.endDownScroll && this.optDown.endDownScroll(this);
+	this.optDown.afterEndDownScroll && this.optDown.afterEndDownScroll(this);
+}
+
+/* 锁定下拉刷新:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockDownScroll = function(isLock) {
+	if (isLock == null) isLock = true;
+	this.optDown.isLock = isLock;
+}
+
+/* 锁定上拉加载:isLock=ture,null锁定;isLock=false解锁 */
+MeScroll.prototype.lockUpScroll = function(isLock) {
+	if (isLock == null) isLock = true;
+	this.optUp.isLock = isLock;
+}
+
+/* -------初始化上拉加载------- */
+MeScroll.prototype.initUpScroll = function() {
+	let me = this;
+	// 配置参数
+	me.optUp = me.options.up || {use: false}
+	if(!me.optUp.textColor && me.hasColor(me.optUp.bgColor)) me.optUp.textColor = "#fff"; // 当bgColor有值且textColor未设置,则textColor默认白色
+	me.extendUpScroll(me.optUp);
+
+	if (me.optUp.use === false) return; // 配置不使用上拉加载时,则不初始化上拉布局
+	me.optUp.hasNext = true; // 如果使用上拉,则默认有下一页
+	me.startNum = me.optUp.page.num + 1; // 记录page开始的页码
+
+	// 初始化完毕的回调
+	if (me.optUp.inited) {
+		setTimeout(function() { // 待主线程执行完毕再执行,避免new MeScroll未初始化,在回调获取不到mescroll的实例
+			me.optUp.inited(me);
+		}, 0)
+	}
+}
+
+/*滚动到底部的事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onReachBottom = function() {
+	if (this.isScrollBody && !this.isUpScrolling) { // 只能支持下拉刷新的时候同时可以触发上拉加载,否则滚动到底部就需要上滑一点才能触发onReachBottom
+		if (!this.optUp.isLock && this.optUp.hasNext) {
+			this.triggerUpScroll();
+		}
+	}
+}
+
+/*列表滚动事件 (仅mescroll-body生效)*/
+MeScroll.prototype.onPageScroll = function(e) {
+	if (!this.isScrollBody) return;
+	
+	// 更新滚动条的位置 (主要用于判断下拉刷新时,滚动条是否在顶部)
+	this.setScrollTop(e.scrollTop);
+
+	// 顶部按钮的显示隐藏
+	if (e.scrollTop >= this.optUp.toTop.offset) {
+		this.showTopBtn();
+	} else {
+		this.hideTopBtn();
+	}
+}
+
+/*列表滚动事件*/
+MeScroll.prototype.scroll = function(e, onScroll) {
+	// 更新滚动条的位置
+	this.setScrollTop(e.scrollTop);
+	// 更新滚动内容高度
+	this.setScrollHeight(e.scrollHeight);
+
+	// 向上滑还是向下滑动
+	if (this.preScrollY == null) this.preScrollY = 0;
+	this.isScrollUp = e.scrollTop - this.preScrollY > 0;
+	this.preScrollY = e.scrollTop;
+
+	// 上滑 && 检查并触发上拉
+	this.isScrollUp && this.triggerUpScroll(true);
+
+	// 顶部按钮的显示隐藏
+	if (e.scrollTop >= this.optUp.toTop.offset) {
+		this.showTopBtn();
+	} else {
+		this.hideTopBtn();
+	}
+
+	// 滑动监听
+	this.optUp.onScroll && onScroll && onScroll()
+}
+
+/* 触发上拉加载 */
+MeScroll.prototype.triggerUpScroll = function(isCheck) {
+	if (!this.isUpScrolling && this.optUp.use && this.optUp.callback) {
+		// 是否校验在底部; 默认不校验
+		if (isCheck === true) {
+			let canUp = false;
+			// 还有下一页 && 没有锁定 && 不在下拉中
+			if (this.optUp.hasNext && !this.optUp.isLock && !this.isDownScrolling) {
+				if (this.getScrollBottom() <= this.optUp.offset) { // 到底部
+					canUp = true; // 标记可上拉
+				}
+			}
+			if (canUp === false) return;
+		}
+		this.showUpScroll(); // 上拉加载中...
+		this.optUp.page.num++; // 预先加一页,如果失败则减回
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+		this.num = this.optUp.page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+		this.size = this.optUp.page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.time = this.optUp.page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.optUp.callback(this); // 执行回调,联网加载数据
+	}
+}
+
+/* 显示上拉加载中 */
+MeScroll.prototype.showUpScroll = function() {
+	this.isUpScrolling = true; // 标记上拉加载中
+	this.optUp.showLoading && this.optUp.showLoading(this); // 回调
+}
+
+/* 显示上拉无更多数据 */
+MeScroll.prototype.showNoMore = function() {
+	this.optUp.hasNext = false; // 标记无更多数据
+	this.optUp.showNoMore && this.optUp.showNoMore(this); // 回调
+}
+
+/* 隐藏上拉区域**/
+MeScroll.prototype.hideUpScroll = function() {
+	this.optUp.hideUpScroll && this.optUp.hideUpScroll(this); // 回调
+}
+
+/* 结束上拉加载 */
+MeScroll.prototype.endUpScroll = function(isShowNoMore) {
+	if (isShowNoMore != null) { // isShowNoMore=null,不处理下拉状态,下拉刷新的时候调用
+		if (isShowNoMore) {
+			this.showNoMore(); // isShowNoMore=true,显示无更多数据
+		} else {
+			this.hideUpScroll(); // isShowNoMore=false,隐藏上拉加载
+		}
+	}
+	this.isUpScrolling = false; // 标记结束上拉加载
+}
+
+/* 重置上拉加载列表为第一页
+ *isShowLoading 是否显示进度布局;
+ * 1.默认null,不传参,则显示上拉加载的进度布局
+ * 2.传参true, 则显示下拉刷新的进度布局
+ * 3.传参false,则不显示上拉和下拉的进度 (常用于静默更新列表数据)
+ */
+MeScroll.prototype.resetUpScroll = function(isShowLoading) {
+	if (this.optUp && this.optUp.use) {
+		let page = this.optUp.page;
+		this.prePageNum = page.num; // 缓存重置前的页码,加载失败可退回
+		this.prePageTime = page.time; // 缓存重置前的时间,加载失败可退回
+		page.num = this.startNum; // 重置为第一页
+		page.time = null; // 重置时间为空
+		if (!this.isDownScrolling && isShowLoading !== false) { // 如果不是下拉刷新触发的resetUpScroll并且不配置列表静默更新,则显示进度;
+			if (isShowLoading == null) {
+				this.removeEmpty(); // 移除空布局
+				this.showUpScroll(); // 不传参,默认显示上拉加载的进度布局
+			} else {
+				this.showDownScroll(); // 传true,显示下拉刷新的进度布局,不清空列表
+			}
+		}
+		this.isUpAutoLoad = true; // 标记上拉已经自动执行过,避免初始化时多次触发上拉回调
+		this.num = page.num; // 把最新的页数赋值在mescroll上,避免对page的影响
+		this.size = page.size; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.time = page.time; // 把最新的页码赋值在mescroll上,避免对page的影响
+		this.optUp.callback && this.optUp.callback(this); // 执行上拉回调
+	}
+}
+
+/* 设置page.num的值 */
+MeScroll.prototype.setPageNum = function(num) {
+	this.optUp.page.num = num - 1;
+}
+
+/* 设置page.size的值 */
+MeScroll.prototype.setPageSize = function(size) {
+	this.optUp.page.size = size;
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalPage: 总页数(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endByPage = function(dataSize, totalPage, systime) {
+	let hasNext;
+	if (this.optUp.use && totalPage != null) hasNext = this.optUp.page.num < totalPage; // 是否还有下一页
+	this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据量(必传)
+ * totalSize: 列表所有数据总数量(必传)
+ * systime: 服务器时间 (可空)
+ */
+MeScroll.prototype.endBySize = function(dataSize, totalSize, systime) {
+	let hasNext;
+	if (this.optUp.use && totalSize != null) {
+		let loadSize = (this.optUp.page.num - 1) * this.optUp.page.size + dataSize; // 已加载的数据总数
+		hasNext = loadSize < totalSize; // 是否还有下一页
+	}
+	this.endSuccess(dataSize, hasNext, systime);
+}
+
+/* 联网回调成功,结束下拉刷新和上拉加载
+ * dataSize: 当前页的数据个数(不是所有页的数据总和),用于上拉加载判断是否还有下一页.如果不传,则会判断还有下一页
+ * hasNext: 是否还有下一页,布尔类型;用来解决这个小问题:比如列表共有20条数据,每页加载10条,共2页.如果只根据dataSize判断,则需翻到第三页才会知道无更多数据,如果传了hasNext,则翻到第二页即可显示无更多数据.
+ * systime: 服务器时间(可空);用来解决这个小问题:当准备翻下一页时,数据库新增了几条记录,此时翻下一页,前面的几条数据会和上一页的重复;这里传入了systime,那么upCallback的page.time就会有值,把page.time传给服务器,让后台过滤新加入的那几条记录
+ */
+MeScroll.prototype.endSuccess = function(dataSize, hasNext, systime) {
+	let me = this;
+	// 结束下拉刷新
+	if (me.isDownScrolling) {
+		me.isDownEndSuccess = true
+		me.endDownScroll();
+	}
+
+	// 结束上拉加载
+	if (me.optUp.use) {
+		let isShowNoMore; // 是否已无更多数据
+		if (dataSize != null) {
+			let pageNum = me.optUp.page.num; // 当前页码
+			let pageSize = me.optUp.page.size; // 每页长度
+			// 如果是第一页
+			if (pageNum === 1) {
+				if (systime) me.optUp.page.time = systime; // 设置加载列表数据第一页的时间
+			}
+			if (dataSize < pageSize || hasNext === false) {
+				// 返回的数据不满一页时,则说明已无更多数据
+				me.optUp.hasNext = false;
+				if (dataSize === 0 && pageNum === 1) {
+					// 如果第一页无任何数据且配置了空布局
+					isShowNoMore = false;
+					me.showEmpty();
+				} else {
+					// 总列表数少于配置的数量,则不显示无更多数据
+					let allDataSize = (pageNum - 1) * pageSize + dataSize;
+					if (allDataSize < me.optUp.noMoreSize) {
+						isShowNoMore = false;
+					} else {
+						isShowNoMore = true;
+					}
+					me.removeEmpty(); // 移除空布局
+				}
+			} else {
+				// 还有下一页
+				isShowNoMore = false;
+				me.optUp.hasNext = true;
+				me.removeEmpty(); // 移除空布局
+			}
+		}
+
+		// 隐藏上拉
+		me.endUpScroll(isShowNoMore);
+	}
+}
+
+/* 回调失败,结束下拉刷新和上拉加载 */
+MeScroll.prototype.endErr = function(errDistance) {
+	// 结束下拉,回调失败重置回原来的页码和时间
+	if (this.isDownScrolling) {
+		this.isDownEndSuccess = false
+		let page = this.optUp.page;
+		if (page && this.prePageNum) {
+			page.num = this.prePageNum;
+			page.time = this.prePageTime;
+		}
+		this.endDownScroll();
+	}
+	// 结束上拉,回调失败重置回原来的页码
+	if (this.isUpScrolling) {
+		this.optUp.page.num--;
+		this.endUpScroll(false);
+		// 如果是mescroll-body,则需往回滚一定距离
+		if(this.isScrollBody && errDistance !== 0){ // 不处理0
+			if(!errDistance) errDistance = this.optUp.errDistance; // 不传,则取默认
+			this.scrollTo(this.getScrollTop() - errDistance, 0) // 往上回滚的距离
+		}
+	}
+}
+
+/* 显示空布局 */
+MeScroll.prototype.showEmpty = function() {
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(true)
+}
+
+/* 移除空布局 */
+MeScroll.prototype.removeEmpty = function() {
+	this.optUp.empty.use && this.optUp.empty.onShow && this.optUp.empty.onShow(false)
+}
+
+/* 显示回到顶部的按钮 */
+MeScroll.prototype.showTopBtn = function() {
+	if (!this.topBtnShow) {
+		this.topBtnShow = true;
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(true);
+	}
+}
+
+/* 隐藏回到顶部的按钮 */
+MeScroll.prototype.hideTopBtn = function() {
+	if (this.topBtnShow) {
+		this.topBtnShow = false;
+		this.optUp.toTop.onShow && this.optUp.toTop.onShow(false);
+	}
+}
+
+/* 获取滚动条的位置 */
+MeScroll.prototype.getScrollTop = function() {
+	return this.scrollTop || 0
+}
+
+/* 记录滚动条的位置 */
+MeScroll.prototype.setScrollTop = function(y) {
+	this.scrollTop = y;
+}
+
+/* 滚动到指定位置 */
+MeScroll.prototype.scrollTo = function(y, t) {
+	this.myScrollTo && this.myScrollTo(y, t) // scrollview需自定义回到顶部方法
+}
+
+/* 自定义scrollTo */
+MeScroll.prototype.resetScrollTo = function(myScrollTo) {
+	this.myScrollTo = myScrollTo
+}
+
+/* 滚动条到底部的距离 */
+MeScroll.prototype.getScrollBottom = function() {
+	return this.getScrollHeight() - this.getClientHeight() - this.getScrollTop()
+}
+
+/* 计步器
+ star: 开始值
+ end: 结束值
+ callback(step,timer): 回调step值,计步器timer,可自行通过window.clearInterval(timer)结束计步器;
+ t: 计步时长,传0则直接回调end值;不传则默认300ms
+ rate: 周期;不传则默认30ms计步一次
+ * */
+MeScroll.prototype.getStep = function(star, end, callback, t, rate) {
+	let diff = end - star; // 差值
+	if (t === 0 || diff === 0) {
+		callback && callback(end);
+		return;
+	}
+	t = t || 300; // 时长 300ms
+	rate = rate || 30; // 周期 30ms
+	let count = t / rate; // 次数
+	let step = diff / count; // 步长
+	let i = 0; // 计数
+	let timer = setInterval(function() {
+		if (i < count - 1) {
+			star += step;
+			callback && callback(star, timer);
+			i++;
+		} else {
+			callback && callback(end, timer); // 最后一次直接设置end,避免计算误差
+			clearInterval(timer);
+		}
+	}, rate);
+}
+
+/* 滚动容器的高度 */
+MeScroll.prototype.getClientHeight = function(isReal) {
+	let h = this.clientHeight || 0
+	if (h === 0 && isReal !== true) { // 未获取到容器的高度,可临时取body的高度 (可能会有误差)
+		h = this.getBodyHeight()
+	}
+	return h
+}
+MeScroll.prototype.setClientHeight = function(h) {
+	this.clientHeight = h;
+}
+
+/* 滚动内容的高度 */
+MeScroll.prototype.getScrollHeight = function() {
+	return this.scrollHeight || 0;
+}
+MeScroll.prototype.setScrollHeight = function(h) {
+	this.scrollHeight = h;
+}
+
+/* body的高度 */
+MeScroll.prototype.getBodyHeight = function() {
+	return this.bodyHeight || 0;
+}
+MeScroll.prototype.setBodyHeight = function(h) {
+	this.bodyHeight = h;
+}
+
+/* 阻止浏览器默认滚动事件 */
+MeScroll.prototype.preventDefault = function(e) {
+	// 小程序不支持e.preventDefault, 已在wxs中禁止
+	// app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止, 或使用renderjs禁止
+	// cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用
+	if (e && e.cancelable && !e.defaultPrevented) e.preventDefault()
+}

+ 480 - 0
node_modules/mescroll-uni/mescroll-uni.vue

@@ -0,0 +1,480 @@
+<template>
+	<view class="mescroll-uni-warp">
+		<scroll-view :id="viewId" class="mescroll-uni" :class="{'mescroll-uni-fixed':isFixed}" :style="{'height':scrollHeight,'padding-top':padTop,'padding-bottom':padBottom,'top':fixedTop,'bottom':fixedBottom}" :scroll-top="scrollTop" :scroll-with-animation="scrollAnim" @scroll="scroll" :scroll-y='scrollable' :enable-back-to-top="true" :throttle="false">
+			<view class="mescroll-uni-content mescroll-render-touch"
+			@touchstart="wxsBiz.touchstartEvent" 
+			@touchmove="wxsBiz.touchmoveEvent" 
+			@touchend="wxsBiz.touchendEvent" 
+			@touchcancel="wxsBiz.touchendEvent"
+			:change:prop="wxsBiz.propObserver"
+			:prop="wxsProp">
+				<!-- 状态栏 -->
+				<view v-if="topbar&&statusBarHeight" class="mescroll-topbar" :style="{height: statusBarHeight+'px', background: topbar}"></view>
+		
+				<view class="mescroll-wxs-content" :style="{'transform': translateY, 'transition': transition}" :change:prop="wxsBiz.callObserver" :prop="callProp">
+					<!-- 下拉加载区域 (支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-down组件实现)-->
+					<!-- <mescroll-down :option="mescroll.optDown" :type="downLoadType" :rate="downRate"></mescroll-down> -->
+					<view v-if="mescroll.optDown.use" class="mescroll-downwarp" :style="{'background':mescroll.optDown.bgColor,'color':mescroll.optDown.textColor}">
+						<view class="downwarp-content">
+							<view class="downwarp-progress mescroll-wxs-progress" :class="{'mescroll-rotate': isDownLoading}" :style="{'border-color':mescroll.optDown.textColor, 'transform': downRotate}"></view>
+							<view class="downwarp-tip">{{downText}}</view>
+						</view>
+					</view>
+
+					<!-- 列表内容 -->
+					<slot></slot>
+
+					<!-- 空布局 -->
+					<mescroll-empty v-if="isShowEmpty" :option="mescroll.optUp.empty" @emptyclick="emptyClick"></mescroll-empty>
+
+					<!-- 上拉加载区域 (下拉刷新时不显示, 支付宝小程序子组件传参给子子组件仍报单项数据流的异常,暂时不通过mescroll-up组件实现)-->
+					<!-- <mescroll-up v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" :option="mescroll.optUp" :type="upLoadType"></mescroll-up> -->
+					<view v-if="mescroll.optUp.use && !isDownLoading && upLoadType!==3" class="mescroll-upwarp" :style="{'background':mescroll.optUp.bgColor,'color':mescroll.optUp.textColor}">
+						<!-- 加载中 (此处不能用v-if,否则android小程序快速上拉可能会不断触发上拉回调) -->
+						<view v-show="upLoadType===1">
+							<view class="upwarp-progress mescroll-rotate" :style="{'border-color':mescroll.optUp.textColor}"></view>
+							<view class="upwarp-tip">{{ mescroll.optUp.textLoading }}</view>
+						</view>
+						<!-- 无数据 -->
+						<view v-if="upLoadType===2" class="upwarp-nodata">{{ mescroll.optUp.textNoMore }}</view>
+					</view>
+				</view>
+			
+				<!-- 底部是否偏移TabBar的高度(默认仅在H5端的tab页生效) -->
+				<!-- #ifdef H5 -->
+				<view v-if="bottombar && windowBottom>0" class="mescroll-bottombar" :style="{height: windowBottom+'px'}"></view>
+				<!-- #endif -->
+				
+				<!-- 适配iPhoneX -->
+				<view v-if="safearea" class="mescroll-safearea"></view>
+			</view>
+		</scroll-view>
+
+		<!-- 回到顶部按钮 (fixed元素,需写在scroll-view外面,防止滚动的时候抖动)-->
+		<mescroll-top v-model="isShowToTop" :option="mescroll.optUp.toTop" @click="toTopClick"></mescroll-top>
+		
+		<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+		<!-- renderjs的数据载体,不可写在mescroll-downwarp内部,避免use为false时,载体丢失,无法更新数据 -->
+		<view :change:prop="renderBiz.propObserver" :prop="wxsProp"></view>
+		<!-- #endif -->
+	</view>
+</template>
+
+<!-- 微信小程序, QQ小程序, app, h5使用wxs -->
+<!-- #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5 -->
+<script src="./wxs/wxs.wxs" module="wxsBiz" lang="wxs"></script>
+<!-- #endif -->
+
+<!-- app, h5使用renderjs -->
+<!-- #ifdef APP-PLUS || H5 -->
+<script module="renderBiz" lang="renderjs">
+	import renderBiz from './wxs/renderjs.js';
+	export default {
+		mixins:[renderBiz]
+	}
+</script>
+<!-- #endif -->
+
+<script>
+	// 引入mescroll-uni.js,处理核心逻辑
+	import MeScroll from './mescroll-uni.js';
+	// 引入全局配置
+	import GlobalOption from './mescroll-uni-option.js';
+	// 引入空布局组件
+	import MescrollEmpty from './components/mescroll-empty.vue';
+	// 引入国际化工具类
+	import mescrollI18n from './mescroll-i18n.js';
+	// 引入回到顶部组件
+	import MescrollTop from './components/mescroll-top.vue';
+	// 引入兼容wxs(含renderjs)写法的mixins
+	import WxsMixin from './wxs/mixins.js';
+	
+	/**
+	 * mescroll-uni 嵌在页面某个区域的下拉刷新和上拉加载组件, 如嵌在弹窗,浮层,swiper中...
+	 * @property {Object} down 下拉刷新的参数配置
+	 * @property {Object} up 上拉加载的参数配置
+	 * @property {Object} i18n 国际化的参数配置
+	 * @property {String, Number} top 下拉布局往下的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean, String} topbar 偏移量top是否加上状态栏高度, 默认false (使用场景:取消原生导航栏时,配置此项可留出状态栏的占位, 支持传入字符串背景,如色值,背景图,渐变)
+	 * @property {String, Number} bottom 上拉布局往上的偏移量 (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean} safearea 偏移量bottom是否加上底部安全区的距离, 默认false (需要适配iPhoneX时使用)
+	 * @property {Boolean} fixed 是否通过fixed固定mescroll的高度, 默认true
+	 * @property {String, Number} height 指定mescroll的高度, 此项有值,则不使用fixed. (支持20, "20rpx", "20px", "20%"格式的值, 其中纯数字则默认单位rpx, 百分比则相对于windowHeight)
+	 * @property {Boolean} bottombar 底部是否偏移TabBar的高度 (仅在H5端的tab页生效)
+	 * @property {Boolean} disableScroll 是否禁止滚动, 默认false
+	 * @event {Function} init 初始化完成的回调 
+	 * @event {Function} down 下拉刷新的回调
+	 * @event {Function} up 上拉加载的回调 
+	 * @event {Function} emptyclick 点击empty配置的btnText按钮回调
+	 * @event {Function} topclick 点击回到顶部的按钮回调
+	 * @event {Function} scroll 滚动监听 (需在 up 配置 onScroll:true 才生效)
+	 * @example <mescroll-uni ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback"> ... </mescroll-uni>
+	 */
+	export default {
+		name: 'mescroll-uni',
+		mixins: [WxsMixin],
+		components: {
+			MescrollEmpty,
+			MescrollTop
+		},
+		props: {
+			down: Object,
+			up: Object,
+			i18n: Object,
+			top: [String, Number],
+			topbar: [Boolean, String],
+			bottom: [String, Number],
+			safearea: Boolean,
+			fixed: {
+				type: Boolean,
+				default: true
+			},
+			height: [String, Number],
+			bottombar:{
+				type: Boolean,
+				default: true
+			},
+			disableScroll: Boolean
+		},
+		data() {
+			return {
+				mescroll: {optDown:{},optUp:{}}, // mescroll实例
+				viewId: 'id_' + Math.random().toString(36).substr(2,16), // 随机生成mescroll的id(不能数字开头,否则找不到元素)
+				downHight: 0, //下拉刷新: 容器高度
+				downRate: 0, // 下拉比率(inOffset: rate<1; outOffset: rate>=1)
+				downLoadType: 0, // 下拉刷新状态: 0(loading前), 1(inOffset), 2(outOffset), 3(showLoading), 4(endDownScroll)
+				upLoadType: 0, // 上拉加载状态: 0(loading前), 1loading中, 2没有更多了,显示END文本提示, 3(没有更多了,不显示END文本提示)
+				isShowEmpty: false, // 是否显示空布局
+				isShowToTop: false, // 是否显示回到顶部按钮
+				scrollTop: 0, // 滚动条的位置
+				scrollAnim: false, // 是否开启滚动动画
+				windowTop: 0, // 可使用窗口的顶部位置
+				windowBottom: 0, // 可使用窗口的底部位置
+				windowHeight: 0, // 可使用窗口的高度
+				statusBarHeight: 0 // 状态栏高度
+			}
+		},
+		computed: {
+			// 是否使用fixed定位 (当height有值,则不使用)
+			isFixed(){
+				return !this.height && this.fixed
+			},
+			// mescroll的高度
+			scrollHeight(){
+				if (this.isFixed) {
+					return "auto"
+				} else if(this.height){
+					return this.toPx(this.height) + 'px'
+				}else{
+					return "100%"
+				}
+			},
+			// 下拉布局往下偏移的距离 (px)
+			numTop() {
+				return this.toPx(this.top)
+			},
+			fixedTop() {
+				return this.isFixed ? (this.numTop + this.windowTop) + 'px' : 0
+			},
+			padTop() {
+				return !this.isFixed ? this.numTop + 'px' : 0
+			},
+			// 上拉布局往上偏移 (px)
+			numBottom() {
+				return this.toPx(this.bottom)
+			},
+			fixedBottom() {
+				return this.isFixed ? (this.numBottom + this.windowBottom) + 'px' : 0
+			},
+			padBottom() {
+				return !this.isFixed ? this.numBottom + 'px' : 0
+			},
+			// 是否为重置下拉的状态
+			isDownReset(){
+				return this.downLoadType===3 || this.downLoadType===4
+			},
+			// 过渡
+			transition() {
+				return this.isDownReset ? 'transform 300ms' : '';
+			},
+			translateY() {
+				return this.downHight > 0 ? 'translateY(' + this.downHight + 'px)' : ''; // transform会使fixed失效,需注意把fixed元素写在mescroll之外
+			},
+			// 列表是否可滑动
+			scrollable(){
+				if(this.disableScroll) return false
+				return this.downLoadType===0 || this.isDownReset
+			},
+			// 是否在加载中
+			isDownLoading(){
+				return this.downLoadType === 3
+			},
+			// 旋转的角度
+			downRotate(){
+				return 'rotate(' + 360 * this.downRate + 'deg)'
+			},
+			// 文本提示
+			downText(){
+				if(!this.mescroll) return ""; // 避免头条小程序初始化时报错
+				switch (this.downLoadType){
+					case 1: return this.mescroll.optDown.textInOffset;
+					case 2: return this.mescroll.optDown.textOutOffset;
+					case 3: return this.mescroll.optDown.textLoading;
+					case 4: return this.mescroll.isDownEndSuccess ? this.mescroll.optDown.textSuccess : this.mescroll.isDownEndSuccess==false ? this.mescroll.optDown.textErr : this.mescroll.optDown.textInOffset;
+					default: return this.mescroll.optDown.textInOffset;
+				}
+			}
+		},
+		methods: {
+			//number,rpx,upx,px,% --> px的数值
+			toPx(num){
+				if(typeof num === "string"){
+					if (num.indexOf('px') !== -1) {
+						if(num.indexOf('rpx') !== -1) { // "10rpx"
+							num = num.replace('rpx', '');
+						} else if(num.indexOf('upx') !== -1) { // "10upx"
+							num = num.replace('upx', '');
+						} else { // "10px"
+							return Number(num.replace('px', ''))
+						}
+					}else if (num.indexOf('%') !== -1){
+						// 传百分比,则相对于windowHeight,传"10%"则等于windowHeight的10%
+						let rate = Number(num.replace("%","")) / 100
+						return this.windowHeight * rate
+					}
+				}
+				return num ? uni.upx2px(Number(num)) : 0
+			},
+			//注册列表滚动事件,用于下拉刷新和上拉加载
+			scroll(e) {
+				this.mescroll.scroll(e.detail, () => {
+					this.$emit('scroll', this.mescroll) // 此时可直接通过 this.mescroll.scrollTop获取滚动条位置; this.mescroll.isScrollUp获取是否向上滑动
+				})
+			},
+			// 点击空布局的按钮回调
+			emptyClick() {
+				this.$emit('emptyclick', this.mescroll)
+			},
+			// 点击回到顶部的按钮回调
+			toTopClick() {
+				this.mescroll.scrollTo(0, this.mescroll.optUp.toTop.duration); // 执行回到顶部
+				this.$emit('topclick', this.mescroll); // 派发点击回到顶部按钮的回调
+			},
+			// 更新滚动区域的高度 (使内容不满屏和到底,都可继续翻页)
+			setClientHeight() {
+				if (this.mescroll.getClientHeight(true) === 0 && !this.isExec) {
+					this.isExec = true; // 避免多次获取
+					this.$nextTick(() => { // 确保dom已渲染
+						this.getClientInfo(data=>{
+							this.isExec = false;
+							if (data) {
+								this.mescroll.setClientHeight(data.height);
+							} else if (this.clientNum != 3) { // 极少部分情况,可能dom还未渲染完毕,递归获取,最多重试3次
+								this.clientNum = this.clientNum == null ? 1 : this.clientNum + 1;
+								setTimeout(() => {
+									this.setClientHeight()
+								}, this.clientNum * 100)
+							}
+						})
+					})
+				}
+			},
+			// 获取滚动区域的信息
+			getClientInfo(success){
+				let query = uni.createSelectorQuery();
+				// #ifndef MP-ALIPAY || MP-DINGTALK
+				query = query.in(this) // 支付宝小程序不支持in(this),而字节跳动小程序必须写in(this), 否则都取不到值
+				// #endif
+				let view = query.select('#' + this.viewId);
+				view.boundingClientRect(data => {
+					success(data)
+				}).exec();
+			}
+		},
+		// 使用created初始化mescroll对象; 如果用mounted部分css样式编译到H5会失效
+		created() {
+			let vm = this;
+
+			let diyOption = {
+				// 下拉刷新的配置
+				down: {
+					inOffset() {
+						vm.downLoadType = 1; // 下拉的距离进入offset范围内那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					outOffset() {
+						vm.downLoadType = 2; // 下拉的距离大于offset那一刻的回调 (自定义mescroll组件时,此行不可删)
+					},
+					onMoving(mescroll, rate, downHight) {
+						// 下拉过程中的回调,滑动过程一直在执行;
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downRate = rate; //下拉比率 (inOffset: rate<1; outOffset: rate>=1)
+					},
+					showLoading(mescroll, downHight) {
+						vm.downLoadType = 3; // 显示下拉刷新进度的回调 (自定义mescroll组件时,此行不可删)
+						vm.downHight = downHight; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+					},
+					beforeEndDownScroll(mescroll){
+						vm.downLoadType = 4; 
+						return mescroll.optDown.beforeEndDelay // 延时结束的时长
+					},
+					endDownScroll() {
+						vm.downLoadType = 4; // 结束下拉 (自定义mescroll组件时,此行不可删)
+						vm.downHight = 0; // 设置下拉区域的高度 (自定义mescroll组件时,此行不可删)
+						vm.downResetTimer && clearTimeout(vm.downResetTimer)
+						vm.downResetTimer = setTimeout(()=>{ // 过渡动画执行完毕后,需重置为0的状态,以便置空this.transition,避免iOS小程序列表渲染不完整
+							if(vm.downLoadType===4) vm.downLoadType = 0
+						},300)
+					},
+					// 派发下拉刷新的回调
+					callback: function(mescroll) {
+						vm.$emit('down', mescroll)
+					}
+				},
+				// 上拉加载的配置
+				up: {
+					// 显示加载中的回调
+					showLoading() {
+						vm.upLoadType = 1;
+					},
+					// 显示无更多数据的回调
+					showNoMore() {
+						vm.upLoadType = 2;
+					},
+					// 隐藏上拉加载的回调
+					hideUpScroll(mescroll) {
+						vm.upLoadType = mescroll.optUp.hasNext ? 0 : 3;
+					},
+					// 空布局
+					empty: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowEmpty = isShow;
+						}
+					},
+					// 回到顶部
+					toTop: {
+						onShow(isShow) { // 显示隐藏的回调
+							vm.isShowToTop = isShow;
+						}
+					},
+					// 派发上拉加载的回调
+					callback: function(mescroll) {
+						vm.$emit('up', mescroll);
+						// 更新容器的高度 (多mescroll的情况)
+						vm.setClientHeight()
+					}
+				}
+			}
+
+			let i18nType = mescrollI18n.getType() // 当前语言类型
+			let i18nOption = {type: i18nType} // 国际化配置
+			MeScroll.extend(i18nOption, vm.i18n) // 具体页面的国际化配置
+			MeScroll.extend(i18nOption, GlobalOption.i18n) // 全局的国际化配置
+			MeScroll.extend(diyOption, i18nOption[i18nType]); // 混入国际化配置
+			MeScroll.extend(diyOption, {down:GlobalOption.down, up:GlobalOption.up}); // 混入全局的配置
+			let myOption = JSON.parse(JSON.stringify({'down': vm.down,'up': vm.up})) // 深拷贝,避免对props的影响
+			MeScroll.extend(myOption, diyOption); // 混入具体界面的配置
+
+			// 初始化MeScroll对象
+			vm.mescroll = new MeScroll(myOption);
+			vm.mescroll.viewId = vm.viewId; // 附带id
+			vm.mescroll.i18n = i18nOption; // 挂载语言包
+			// init回调mescroll对象
+			vm.$emit('init', vm.mescroll);
+			
+			// 设置高度
+			const sys = uni.getSystemInfoSync();
+			if(sys.windowTop) vm.windowTop = sys.windowTop;
+			if(sys.windowBottom) vm.windowBottom = sys.windowBottom;
+			if(sys.windowHeight) vm.windowHeight = sys.windowHeight;
+			if(sys.statusBarHeight) vm.statusBarHeight = sys.statusBarHeight;
+			// 使down的bottomOffset生效
+			vm.mescroll.setBodyHeight(sys.windowHeight);
+
+			// 因为使用的是scrollview,这里需自定义scrollTo
+			vm.mescroll.resetScrollTo((y, t) => {
+				vm.scrollAnim = (t !== 0); // t为0,则不使用动画过渡
+				if(typeof y === 'string'){
+					// 小程序不支持slot里面的scroll-into-view, 统一使用计算的方式实现
+					vm.getClientInfo(function(rect){
+						let mescrollTop = rect.top // mescroll到顶部的距离
+						let selector;
+						if(y.indexOf('#')==-1 && y.indexOf('.')==-1){
+							selector = '#'+y // 不带#和. 则默认为id选择器
+						}else{
+							selector = y
+							// #ifdef APP-PLUS || H5 || MP-ALIPAY || MP-DINGTALK
+							if(y.indexOf('>>>')!=-1){ // 不支持跨自定义组件的后代选择器 (转为普通的选择器即可跨组件查询)
+								selector = y.split('>>>')[1].trim()
+							}
+							// #endif
+						}
+						uni.createSelectorQuery().select(selector).boundingClientRect(function(rect){
+							if (rect) {
+								let curY = vm.mescroll.getScrollTop()
+								let top = rect.top - mescrollTop
+								top += curY
+								if(!vm.isFixed) top -= vm.numTop
+								vm.scrollTop = curY;
+								vm.$nextTick(function() {
+									vm.scrollTop = top
+								})
+							} else{
+								console.error(selector + ' does not exist');
+							}
+						}).exec()
+					})
+					return;
+				}
+				let curY = vm.mescroll.getScrollTop()
+				if (t === 0 || t === 300) { // 当t使用默认配置的300时,则使用系统自带的动画过渡
+					vm.scrollTop = curY;
+					vm.$nextTick(function() {
+						vm.scrollTop = y
+					})
+				} else {
+					vm.mescroll.getStep(curY, y, step => { // 此写法可支持配置t
+						vm.scrollTop = step
+					}, t)
+				}
+			})
+			
+			// 具体的界面如果不配置up.toTop.safearea,则取本vue的safearea值
+			if (vm.up && vm.up.toTop && vm.up.toTop.safearea != null) {} else {
+				vm.mescroll.optUp.toTop.safearea = vm.safearea;
+			}
+			
+			// 全局配置监听
+			uni.$on("setMescrollGlobalOption", options=>{
+				if(!options) return;
+				let i18nType = options.i18n ? options.i18n.type : null
+				if(i18nType && vm.mescroll.i18n.type != i18nType){
+					vm.mescroll.i18n.type = i18nType
+					mescrollI18n.setType(i18nType)
+					MeScroll.extend(options, vm.mescroll.i18n[i18nType])
+				}
+				if(options.down){
+					let down = MeScroll.extend({}, options.down)
+					vm.mescroll.optDown = MeScroll.extend(down, vm.mescroll.optDown)
+				}
+				if(options.up){
+					let up = MeScroll.extend({}, options.up)
+					vm.mescroll.optUp = MeScroll.extend(up, vm.mescroll.optUp)
+				}
+			})
+		},
+		mounted() {
+			// 设置容器的高度
+			this.setClientHeight()
+		},
+		destroyed() {
+			// 注销全局配置监听
+			uni.$off("setMescrollGlobalOption")
+		}
+	}
+</script>
+
+<style>
+	@import "./mescroll-uni.css";
+	@import "./components/mescroll-down.css";
+	@import './components/mescroll-up.css';
+</style>

+ 47 - 0
node_modules/mescroll-uni/mixins/mescroll-comp.js

@@ -0,0 +1,47 @@
+/**
+ * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期
+ */
+const MescrollCompMixin = {
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 (一级)
+	onPageScroll(e) {
+		this.handlePageScroll(e)
+	},
+	onReachBottom() {
+		this.handleReachBottom()
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		this.handlePullDownRefresh()
+	},
+	data() {
+		return {
+			mescroll: { // mescroll-body写在子子子...组件的情况 (多级)
+				onPageScroll: e=>{
+					this.handlePageScroll(e)
+				},
+				onReachBottom: ()=>{
+					this.handleReachBottom()
+				},
+				onPullDownRefresh: ()=>{
+					this.handlePullDownRefresh()
+				}
+			}
+		}
+	},
+	methods:{
+		handlePageScroll(e){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onPageScroll(e);
+		},
+		handleReachBottom(){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onReachBottom();
+		},
+		handlePullDownRefresh(){
+			let item = this.$refs["mescrollItem"];
+			if(item && item.mescroll) item.mescroll.onPullDownRefresh();
+		}
+	}
+}
+
+export default MescrollCompMixin;

+ 66 - 0
node_modules/mescroll-uni/mixins/mescroll-more-item.js

@@ -0,0 +1,66 @@
+/**
+ * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例)
+ */
+const MescrollMoreItemMixin = {
+	// 支付宝小程序不支持props的mixin,需写在具体的页面中
+	// #ifndef MP-ALIPAY || MP-DINGTALK
+	props:{
+		i: Number, // 每个tab页的专属下标
+		index: { // 当前tab的下标
+			type: Number,
+			default(){
+				return 0
+			}
+		}
+	},
+	// #endif
+	data() {
+		return {
+			downOption:{
+				auto:false // 不自动加载
+			},
+			upOption:{
+				auto:false // 不自动加载
+			},
+			isInit: false // 当前tab是否已初始化
+		}
+	},
+	watch:{
+		// 监听下标的变化
+		index(val){
+			if (this.i === val && !this.isInit) this.mescrollTrigger()
+		}
+	},
+	methods: {
+		// 以ref的方式初始化mescroll对象 (兼容字节跳动小程序)
+		mescrollInitByRef() {
+			if(!this.mescroll || !this.mescroll.resetUpScroll){
+				// 字节跳动小程序编辑器不支持一个页面存在相同的ref, 多mescroll的ref需动态生成, 格式为'mescrollRef下标'
+				let mescrollRef = this.$refs.mescrollRef || this.$refs['mescrollRef'+this.i];
+				if(mescrollRef) this.mescroll = mescrollRef.mescroll
+			}
+		},
+		// mescroll组件初始化的回调,可获取到mescroll对象 (覆盖mescroll-mixins.js的mescrollInit, 为了标记isInit)
+		mescrollInit(mescroll) {
+			this.mescroll = mescroll;
+			this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序
+			// 自动加载当前tab的数据
+			if(this.i === this.index){
+				this.mescrollTrigger()
+			}
+		},
+		// 主动触发加载
+		mescrollTrigger(){
+			this.isInit = true; // 标记为true
+			if (this.mescroll) {
+				if (this.mescroll.optDown.use) {
+					this.mescroll.triggerDownScroll();
+				} else{
+					this.mescroll.triggerUpScroll();
+				}
+			}
+		}
+	}
+}
+
+export default MescrollMoreItemMixin;

+ 74 - 0
node_modules/mescroll-uni/mixins/mescroll-more.js

@@ -0,0 +1,74 @@
+/**
+ * mescroll-body写在子组件时, 需通过mescroll的mixins补充子组件缺少的生命周期
+ */
+const MescrollMoreMixin = {
+	data() {
+		return {
+			tabIndex: 0, // 当前tab下标
+			mescroll: { // mescroll-body写在子子子...组件的情况 (多级)
+				onPageScroll: e=>{
+					this.handlePageScroll(e)
+				},
+				onReachBottom: ()=>{
+					this.handleReachBottom()
+				},
+				onPullDownRefresh: ()=>{
+					this.handlePullDownRefresh()
+				}
+			}
+		}
+	},
+	// 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件
+	onPageScroll(e) {
+		this.handlePageScroll(e)
+	},
+	onReachBottom() {
+		this.handleReachBottom()
+	},
+	// 当down的native: true时, 还需传递此方法进到子组件
+	onPullDownRefresh(){
+		this.handlePullDownRefresh()
+	},
+	methods:{
+		handlePageScroll(e){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onPageScroll(e);
+		},
+		handleReachBottom(){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onReachBottom();
+		},
+		handlePullDownRefresh(){
+			let mescroll = this.getMescroll(this.tabIndex);
+			mescroll && mescroll.onPullDownRefresh();
+		},
+		// 根据下标获取对应子组件的mescroll
+		getMescroll(i){
+			if(!this.mescrollItems) this.mescrollItems = [];
+			if(!this.mescrollItems[i]) {
+				// v-for中的refs
+				let vForItem = this.$refs["mescrollItem"];
+				if(vForItem){
+					this.mescrollItems[i] = vForItem[i]
+				}else{
+					// 普通的refs,不可重复
+					this.mescrollItems[i] = this.$refs["mescrollItem"+i];
+				}
+			}
+			let item = this.mescrollItems[i]
+			return item ? item.mescroll : null
+		},
+		// 切换tab,恢复滚动条位置
+		tabChange(i){
+			let mescroll = this.getMescroll(i);
+			if(mescroll){
+				// 延时(比$nextTick靠谱一些),确保元素已渲染
+				setTimeout(()=>{
+					mescroll.scrollTo(mescroll.getScrollTop(),0)
+				},30)
+			}
+		}
+	}
+}
+
+export default MescrollMoreMixin;

+ 41 - 0
node_modules/mescroll-uni/package.json

@@ -0,0 +1,41 @@
+{
+  "_args": [
+    [
+      "mescroll-uni@1.3.7",
+      "C:\\Users\\Administrator\\Desktop\\项目\\直播\\liveH5-v3"
+    ]
+  ],
+  "_from": "mescroll-uni@1.3.7",
+  "_id": "mescroll-uni@1.3.7",
+  "_inBundle": false,
+  "_integrity": "sha512-1pQMtGA+iVRKhfJZZNXdBx05NnthIk6zm3hRbumswSA54eaKOMgpUDb9AQ2+rRdXmS6kLkEYSbW/fkb7/IyoAg==",
+  "_location": "/mescroll-uni",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "version",
+    "registry": true,
+    "raw": "mescroll-uni@1.3.7",
+    "name": "mescroll-uni",
+    "escapedName": "mescroll-uni",
+    "rawSpec": "1.3.7",
+    "saveSpec": null,
+    "fetchSpec": "1.3.7"
+  },
+  "_requiredBy": [
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/mescroll-uni/-/mescroll-uni-1.3.7.tgz",
+  "_spec": "1.3.7",
+  "_where": "C:\\Users\\Administrator\\Desktop\\项目\\直播\\liveH5-v3",
+  "author": {
+    "name": "wenju"
+  },
+  "description": "mescroll的uni-app版本:【wxs+renderjs实现】高性能的下拉刷新上拉加载组件",
+  "license": "MIT",
+  "main": "mescroll-uni.vue",
+  "name": "mescroll-uni",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "version": "1.3.7"
+}

+ 109 - 0
node_modules/mescroll-uni/wxs/mixins.js

@@ -0,0 +1,109 @@
+// 定义在wxs (含renderjs) 逻辑层的数据和方法, 与视图层相互通信
+const WxsMixin = {
+	data() {
+		return {
+			// 传入wxs视图层的数据 (响应式)
+			wxsProp: {
+				optDown:{}, // 下拉刷新的配置
+				scrollTop:0, // 滚动条的距离
+				bodyHeight:0, // body的高度
+				isDownScrolling:false, // 是否正在下拉刷新中
+				isUpScrolling:false, // 是否正在上拉加载中
+				isScrollBody:true, // 是否为mescroll-body滚动
+				isUpBoth:true, // 上拉加载时,是否同时可以下拉刷新
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+			},
+			
+			// 标记调用wxs视图层的方法
+			callProp: {
+				callType: '', // 方法名
+				t: 0 // 数据更新的标记 (只有数据更新了,才会触发wxs的Observer)
+			},
+			
+			// 不用wxs的平台使用此处的wxsBiz对象,抹平wxs的写法 (微信小程序和APP使用的wxsBiz对象是./wxs/wxs.wxs)
+			// #ifndef MP-WEIXIN || MP-QQ || APP-PLUS || H5
+			wxsBiz: {
+				//注册列表touchstart事件,用于下拉刷新
+				touchstartEvent: e=> {
+					this.mescroll.touchstartEvent(e);
+				},
+				//注册列表touchmove事件,用于下拉刷新
+				touchmoveEvent: e=> {
+					this.mescroll.touchmoveEvent(e);
+				},
+				//注册列表touchend事件,用于下拉刷新
+				touchendEvent: e=> {
+					this.mescroll.touchendEvent(e);
+				},
+				propObserver(){}, // 抹平wxs的写法
+				callObserver(){} // 抹平wxs的写法
+			},
+			// #endif
+			
+			// 不用renderjs的平台使用此处的renderBiz对象,抹平renderjs的写法 (app 和 h5 使用的renderBiz对象是./wxs/renderjs.js)
+			// #ifndef APP-PLUS || H5
+			renderBiz: {
+				propObserver(){} // 抹平renderjs的写法
+			}
+			// #endif
+		}
+	},
+	methods: {
+		// wxs视图层调用逻辑层的回调
+		wxsCall(msg){
+			if(msg.type === 'setWxsProp'){
+				// 更新wxsProp数据 (值改变才触发更新)
+				this.wxsProp = {
+					optDown: this.mescroll.optDown,
+					scrollTop: this.mescroll.getScrollTop(),
+					bodyHeight: this.mescroll.getBodyHeight(),
+					isDownScrolling: this.mescroll.isDownScrolling,
+					isUpScrolling: this.mescroll.isUpScrolling,
+					isUpBoth: this.mescroll.optUp.isBoth,
+					isScrollBody:this.mescroll.isScrollBody,
+					t: Date.now()
+				}
+			}else if(msg.type === 'setLoadType'){
+				// 设置inOffset,outOffset的状态
+				this.downLoadType = msg.downLoadType
+				// 状态挂载到mescroll对象, 以便在其他组件中使用, 比如<me-video>中
+				this.$set(this.mescroll, 'downLoadType', this.downLoadType)
+				// 重置是否加载成功的状态
+				this.$set(this.mescroll, 'isDownEndSuccess', null)
+			}else if(msg.type === 'triggerDownScroll'){
+				// 主动触发下拉刷新
+				this.mescroll.triggerDownScroll();
+			}else if(msg.type === 'endDownScroll'){
+				// 结束下拉刷新
+				this.mescroll.endDownScroll();
+			}else if(msg.type === 'triggerUpScroll'){
+				// 主动触发上拉加载
+				this.mescroll.triggerUpScroll(true);
+			}
+		}
+	},
+	mounted() {
+		// #ifdef MP-WEIXIN || MP-QQ || APP-PLUS || H5
+		// 配置主动触发wxs显示加载进度的回调
+		this.mescroll.optDown.afterLoading = ()=>{
+			this.callProp = {callType: "showLoading", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+		}
+		// 配置主动触发wxs隐藏加载进度的回调
+		this.mescroll.optDown.afterEndDownScroll = ()=>{
+			this.callProp = {callType: "endDownScroll", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+			let delay = 300 + (this.mescroll.optDown.beforeEndDelay || 0)
+			setTimeout(()=>{
+				if(this.downLoadType === 4 || this.downLoadType === 0){
+					this.callProp = {callType: "clearTransform", t: Date.now()} // 触发wxs的方法 (值改变才触发更新)
+				}
+				// 状态挂载到mescroll对象, 以便在其他组件中使用, 比如<me-video>中
+				this.$set(this.mescroll, 'downLoadType', this.downLoadType)
+			}, delay)
+		}
+		// 初始化wxs的数据
+		this.wxsCall({type: 'setWxsProp'})
+		// #endif
+	}
+}
+
+export default WxsMixin;

+ 92 - 0
node_modules/mescroll-uni/wxs/renderjs.js

@@ -0,0 +1,92 @@
+// 使用renderjs直接操作window对象,实现动态控制app和h5的bounce
+// bounce: iOS橡皮筋,Android半月弧,h5浏览器下拉背景等效果 (下拉刷新时禁止)
+// https://uniapp.dcloud.io/frame?id=renderjs
+
+// 与wxs的me实例一致
+var me = {}
+
+// 初始化window对象的touch事件 (仅初始化一次)
+if(window && !window.$mescrollRenderInit){
+	window.$mescrollRenderInit = true
+	
+	
+	window.addEventListener('touchstart', function(e){
+		if (me.disabled()) return;
+		me.startPoint = me.getPoint(e); // 记录起点
+	}, {passive: true})
+	
+	
+	window.addEventListener('touchmove', function(e){
+		if (me.disabled()) return;
+		if (me.getScrollTop() > 0) return; // 需在顶部下拉,才禁止bounce
+		
+		var curPoint = me.getPoint(e); // 当前点
+		var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 向下拉
+		if (moveY > 0) {
+			// 可下拉的条件
+			if (!me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling && me.isUpBoth))) {
+				
+				// 只有touch在mescroll的view上面,才禁止bounce
+				var el = e.target;
+				var isMescrollTouch = false;
+				while (el && el.tagName && el.tagName !== 'UNI-PAGE-BODY' && el.tagName != "BODY") {
+					var cls = el.classList;
+					if (cls && cls.contains('mescroll-render-touch')) {
+						isMescrollTouch = true
+						break;
+					}
+					el = el.parentNode; // 继续检查其父元素
+				}
+				// 禁止bounce (不会对swiper和iOS侧滑返回造成影响)
+				if (isMescrollTouch && e.cancelable && !e.defaultPrevented) e.preventDefault();
+			}
+		}
+	}, {passive: false})
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+	return me.scrollTop || 0
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+	return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.getPoint = function(e) {
+	if (!e) {
+		return {x: 0,y: 0}
+	}
+	if (e.touches && e.touches[0]) {
+		return {x: e.touches[0].pageX,y: e.touches[0].pageY}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
+	} else {
+		return {x: e.clientX,y: e.clientY}
+	}
+}
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+	me.optDown = wxsProp.optDown
+	me.scrollTop = wxsProp.scrollTop
+	me.isDownScrolling = wxsProp.isDownScrolling
+	me.isUpScrolling = wxsProp.isUpScrolling
+	me.isUpBoth = wxsProp.isUpBoth
+}
+
+/* 导出模块 */
+const renderBiz = {
+	data() {
+		return {
+			propObserver: propObserver,
+		}
+	}
+}
+
+export default renderBiz;

+ 268 - 0
node_modules/mescroll-uni/wxs/wxs.wxs

@@ -0,0 +1,268 @@
+// 使用wxs处理交互动画, 提高性能, 同时避免小程序bounce对下拉刷新的影响
+// https://uniapp.dcloud.io/frame?id=wxs
+// https://developers.weixin.qq.com/miniprogram/dev/framework/view/interactive-animation.html 
+
+// 模拟mescroll实例, 与mescroll.js的写法尽量保持一致
+var me = {}
+
+// ------ 自定义下拉刷新动画 start ------
+
+/* 下拉过程中的回调,滑动过程一直在执行 (rate<1为inOffset; rate>1为outOffset) */
+me.onMoving = function (ins, rate, downHight){
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'transform', // 可解决下拉过程中, image和swiper脱离文档流的问题
+			'transform': 'translateY(' + downHight + 'px)',
+			'transition': ''
+		})
+		// 环形进度条
+		var progress = ins.selectComponent('.mescroll-wxs-progress')
+		progress && progress.setStyle({transform: 'rotate(' + 360 * rate + 'deg)'})
+	})
+}
+
+/* 显示下拉刷新进度 */
+me.showLoading = function (ins){
+	me.downHight = me.optDown.offset
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'auto',
+			'transform': 'translateY(' + me.downHight + 'px)',
+			'transition': 'transform 300ms'
+		})
+	})
+}
+
+/* 结束下拉 */
+me.endDownScroll = function (ins){
+	me.downHight = 0;
+	me.isDownScrolling = false;
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': 'auto',
+			'transform': 'translateY(0)', // 不可以写空串,否则scroll-view渲染不完整 (延时350ms会调clearTransform置空)
+			'transition': 'transform 300ms'
+		})
+	})
+}
+
+/* 结束下拉动画执行完毕后, 清除transform和transition, 避免对列表内容样式造成影响, 如: h5的list-msg示例下拉进度条漏出来等 */
+me.clearTransform = function (ins){
+	ins.requestAnimationFrame(function () {
+		ins.selectComponent('.mescroll-wxs-content').setStyle({
+			'will-change': '',
+			'transform': '',
+			'transition': ''
+		})
+	})
+}
+
+// ------ 自定义下拉刷新动画 end ------
+
+/**
+ * 监听逻辑层数据的变化 (实时更新数据)
+ */
+function propObserver(wxsProp) {
+	me.optDown = wxsProp.optDown
+	me.scrollTop = wxsProp.scrollTop
+	me.bodyHeight = wxsProp.bodyHeight
+	me.isDownScrolling = wxsProp.isDownScrolling
+	me.isUpScrolling = wxsProp.isUpScrolling
+	me.isUpBoth = wxsProp.isUpBoth
+	me.isScrollBody = wxsProp.isScrollBody
+	me.startTop = wxsProp.scrollTop // 及时更新touchstart触发的startTop, 避免scroll-view快速惯性滚动到顶部取值不准确
+}
+
+/**
+ * 监听逻辑层数据的变化 (调用wxs的方法)
+ */
+function callObserver(callProp, oldValue, ins) {
+	if (me.disabled()) return;
+	if(callProp.callType){
+		// 逻辑层(App Service)的style已失效,需在视图层(Webview)设置style
+		if(callProp.callType === 'showLoading'){
+			me.showLoading(ins)
+		}else if(callProp.callType === 'endDownScroll'){
+			me.endDownScroll(ins)
+		}else if(callProp.callType === 'clearTransform'){
+			me.clearTransform(ins)
+		}
+	}
+}
+
+/**
+ * touch事件
+ */
+function touchstartEvent(e, ins) {
+	me.downHight = 0; // 下拉的距离
+	me.startPoint = me.getPoint(e); // 记录起点
+	me.startTop = me.getScrollTop(); // 记录此时的滚动条位置
+	me.startAngle = 0; // 初始角度
+	me.lastPoint = me.startPoint; // 重置上次move的点
+	me.maxTouchmoveY = me.getBodyHeight() - me.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况)
+	me.inTouchend = false; // 标记不是touchend
+	
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+function touchmoveEvent(e, ins) {
+	var isPrevent = true // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+	
+	if (me.disabled()) return isPrevent;
+	
+	var scrollTop = me.getScrollTop(); // 当前滚动条的距离
+	var curPoint = me.getPoint(e); // 当前点
+	
+	var moveY = curPoint.y - me.startPoint.y; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+	
+	// 向下拉 && 在顶部
+	// mescroll-body,直接判定在顶部即可
+	// scroll-view在滚动时不会触发touchmove,当触顶/底/左/右时,才会触发touchmove
+	// scroll-view滚动到顶部时,scrollTop不一定为0,也有可能大于0; 在iOS的APP中scrollTop可能为负数,不一定和startTop相等
+	if (moveY > 0 && (
+			(me.isScrollBody && scrollTop <= 0)
+			||
+			(!me.isScrollBody && (scrollTop <= 0 || (scrollTop <= me.optDown.startTop && scrollTop === me.startTop)) )
+		)) {
+		// 可下拉的条件
+		if (!me.inTouchend && !me.isDownScrolling && !me.optDown.isLock && (!me.isUpScrolling || (me.isUpScrolling &&
+				me.isUpBoth))) {
+	
+			// 下拉的角度是否在配置的范围内
+			if(!me.startAngle) me.startAngle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90]
+			if (me.startAngle < me.optDown.minAngle) return isPrevent; // 如果小于配置的角度,则不往下执行下拉刷新
+	
+			// 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发
+			if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) {
+				me.inTouchend = true; // 标记执行touchend
+				touchendEvent(e, ins); // 提前触发touchend
+				return isPrevent;
+			}
+			
+			isPrevent = false // 小程序是return false
+	
+			var diff = curPoint.y - me.lastPoint.y; // 和上次比,移动的距离 (大于0向下,小于0向上)
+	
+			// 下拉距离  < 指定距离
+			if (me.downHight < me.optDown.offset) {
+				if (me.movetype !== 1) {
+					me.movetype = 1; // 加入标记,保证只执行一次
+					// me.optDown.inOffset && me.optDown.inOffset(me); // 进入指定距离范围内那一刻的回调,只执行一次
+					me.callMethod(ins, {type: 'setLoadType', downLoadType: 1})
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				me.downHight += diff * me.optDown.inOffsetRate; // 越往下,高度变化越小
+	
+				// 指定距离  <= 下拉距离
+			} else {
+				if (me.movetype !== 2) {
+					me.movetype = 2; // 加入标记,保证只执行一次
+					// me.optDown.outOffset && me.optDown.outOffset(me); // 下拉超过指定距离那一刻的回调,只执行一次
+					me.callMethod(ins, {type: 'setLoadType', downLoadType: 2})
+					me.isMoveDown = true; // 标记下拉区域高度改变,在touchend重置回来
+				}
+				if (diff > 0) { // 向下拉
+					me.downHight += diff * me.optDown.outOffsetRate; // 越往下,高度变化越小
+				} else { // 向上收
+					me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度
+				}
+			}
+			
+			me.downHight = Math.round(me.downHight) // 取整
+			var rate = me.downHight / me.optDown.offset; // 下拉区域当前高度与指定距离的比值
+			// me.optDown.onMoving && me.optDown.onMoving(me, rate, me.downHight); // 下拉过程中的回调,一直在执行
+			me.onMoving(ins, rate, me.downHight)
+		}
+	}
+	
+	me.lastPoint = curPoint; // 记录本次移动的点
+	
+	return isPrevent // false表示不往上冒泡,相当于调用了同时调用了stopPropagation和preventDefault (对小程序生效, h5和app无效)
+}
+
+function touchendEvent(e, ins) {
+	// 如果下拉区域高度已改变,则需重置回来
+	if (me.isMoveDown) {
+		if (me.downHight >= me.optDown.offset) {
+			// 符合触发刷新的条件
+			me.downHight = me.optDown.offset; // 更新下拉区域高度
+			// me.triggerDownScroll();
+			me.callMethod(ins, {type: 'triggerDownScroll'})
+		} else {
+			// 不符合的话 则重置
+			me.downHight = 0;
+			// me.optDown.endDownScroll && me.optDown.endDownScroll(me);
+			me.callMethod(ins, {type: 'endDownScroll'})
+		}
+		me.movetype = 0;
+		me.isMoveDown = false;
+	} else if (!me.isScrollBody && me.getScrollTop() === me.startTop) { // scroll-view到顶/左/右/底的滑动事件
+		var isScrollUp = me.getPoint(e).y - me.startPoint.y < 0; // 和起点比,移动的距离,大于0向下拉,小于0向上拉
+		// 上滑
+		if (isScrollUp) {
+			// 需检查滑动的角度
+			var angle = me.getAngle(me.getPoint(e), me.startPoint); // 两点之间的角度,区间 [0,90]
+			if (angle > 80) {
+				// 检查并触发上拉
+				// me.triggerUpScroll(true);
+				me.callMethod(ins, {type: 'triggerUpScroll'})
+			}
+		}
+	}
+	me.callMethod(ins, {type: 'setWxsProp'}) // 同步更新wxsProp的数据 (小程序是异步的,可能touchmove先执行,才到propObserver; h5和app是同步)
+}
+
+/* 是否禁用下拉刷新 */
+me.disabled = function(){
+	return !me.optDown || !me.optDown.use || me.optDown.native
+}
+
+/* 根据点击滑动事件获取第一个手指的坐标 */
+me.getPoint = function(e) {
+	if (!e) {
+		return {x: 0,y: 0}
+	}
+	if (e.touches && e.touches[0]) {
+		return {x: e.touches[0].pageX,y: e.touches[0].pageY}
+	} else if (e.changedTouches && e.changedTouches[0]) {
+		return {x: e.changedTouches[0].pageX,y: e.changedTouches[0].pageY}
+	} else {
+		return {x: e.clientX,y: e.clientY}
+	}
+}
+
+/* 计算两点之间的角度: 区间 [0,90]*/
+me.getAngle = function (p1, p2) {
+	var x = Math.abs(p1.x - p2.x);
+	var y = Math.abs(p1.y - p2.y);
+	var z = Math.sqrt(x * x + y * y);
+	var angle = 0;
+	if (z !== 0) {
+		angle = Math.asin(y / z) / Math.PI * 180;
+	}
+	return angle
+}
+
+/* 获取滚动条的位置 */
+me.getScrollTop = function() {
+	return me.scrollTop || 0
+}
+
+/* 获取body的高度 */
+me.getBodyHeight = function() {
+	return me.bodyHeight || 0;
+}
+
+/* 调用逻辑层的方法 */
+me.callMethod = function(ins, param) {
+	if(ins) ins.callMethod('wxsCall', param)
+}
+
+/* 导出模块 */
+module.exports = {
+	propObserver: propObserver,
+	callObserver: callObserver,
+	touchstartEvent: touchstartEvent,
+	touchmoveEvent: touchmoveEvent,
+	touchendEvent: touchendEvent
+}

+ 51 - 0
node_modules/uview-plus/changelog.md

@@ -1,3 +1,54 @@
+## 3.4.106(2025-08-20)
+feat: tooltip组件支持左右方向弹窗
+
+## 3.4.105(2025-08-19)
+feat: 增加markdown解析器组件AI对话流式响应示例
+
+## 3.4.104(2025-08-19)
+feat: tooltip支持自定义触发器
+
+feat: tooltip支持插槽自定义内容
+
+feat: tooltip支持单击触发
+
+feat: tooltip支持设置弹窗背景色
+
+## 3.4.103(2025-08-19)
+feat: 内置一份dayjs库
+
+fix: 删除个别组件多余重复方法
+
+fix: 修复marked库App打包时报错
+
+## 3.4.102(2025-08-19)
+fix: 小程序下cate-tab动态加载数据后尺寸获取及联动异常
+
+## 3.4.101(2025-08-18)
+improvment: 增加一些涉及触摸组件PC端需要仿真模式提示
+
+## 3.4.100(2025-08-18)
+feat: 新增markdown解析器组件
+
+## 3.4.99(2025-08-18)
+fix: 修复parse富文本组件可能导致无限循环的问题
+
+## 3.4.98(2025-08-17)
+feat: alert组件新增transitionMode/icon/duration/modelValue等参数
+
+improvment:  完善alert组件注释
+
+## 3.4.97(2025-08-16)
+improvment: 完善album组件注释
+
+## 3.4.96(2025-08-16)
+improvment: 完善action-sheet注释
+
+## 3.4.95(2025-08-16)
+del: 去除不再需要的重复组件
+
+## 3.4.94(2025-08-15)
+improvment: 组件库内部icon前缀统一
+
 ## 3.4.93(2025-08-15)
 fix: 修复dropdown组件在打开下拉菜单时content高度为NANpx的问题
 

+ 0 - 85
node_modules/uview-plus/components/u--form/u--form.vue

@@ -1,85 +0,0 @@
-<template>
-	<uvForm
-		ref="uForm"
-		:model="model"
-		:rules="rules"
-		:errorType="errorType"
-		:borderBottom="borderBottom"
-		:labelPosition="labelPosition"
-		:labelWidth="labelWidth"
-		:labelAlign="labelAlign"
-		:labelStyle="labelStyle"
-		:customStyle="customStyle"
-	>
-		<slot />
-	</uvForm>
-</template>
-
-<script>
-	/**
-	 * 此组件存在的理由是,在nvue下,u-form被uni-app官方占用了,u-form在nvue中相当于form组件
-	 * 所以在nvue下,取名为u--form,内部其实还是u-form.vue,只不过做一层中转
-	 */
-	import uvForm from '../u-form/u-form.vue';
-	import { props } from '../u-form/props.js';
-	import { mpMixin } from '../../libs/mixin/mpMixin';
-	import { mixin } from '../../libs/mixin/mixin';
-	export default {
-		// #ifdef MP-WEIXIN
-		name: 'u-form',
-		// #endif
-		// #ifndef MP-WEIXIN
-		name: 'u--form',
-		// #endif
-		mixins: [mpMixin, props, mixin],
-		components: {
-			uvForm
-		},
-		created() {
-			this.children = []
-		},
-		methods: {
-			// 手动设置校验的规则,如果规则中有函数的话,微信小程序中会过滤掉,所以只能手动调用设置规则
-			setRules(rules) {
-				this.$refs.uForm.setRules(rules)
-			},
-			/**
-			 * 校验全部数据
-			 * @param {Object} options
-			 * @param {Boolean} options.showErrorMsg -是否显示校验信息,
-			 */
-			validate(options) {
-				/**
-				 * 在微信小程序中,通过this.$parent拿到的父组件是u--form,而不是其内嵌的u-form
-				 * 导致在u-form组件中,拿不到对应的children数组,从而校验无效,所以这里每次调用u-form组件中的
-				 * 对应方法的时候,在小程序中都先将u--form的children赋值给u-form中的children
-				 */
-				// #ifdef MP-WEIXIN
-				this.setMpData()
-				// #endif
-				return this.$refs.uForm.validate(options)
-			},
-			validateField(value, callback) {
-				// #ifdef MP-WEIXIN
-				this.setMpData()
-				// #endif
-				return this.$refs.uForm.validateField(value, callback)
-			},
-			resetFields() {
-				// #ifdef MP-WEIXIN
-				this.setMpData()
-				// #endif
-				return this.$refs.uForm.resetFields()
-			},
-			clearValidate(props) {
-				// #ifdef MP-WEIXIN
-				this.setMpData()
-				// #endif
-				return this.$refs.uForm.clearValidate(props)
-			},
-			setMpData() {
-				this.$refs.uForm.children = this.children
-			}
-		},
-	}
-</script>

+ 0 - 50
node_modules/uview-plus/components/u--image/u--image.vue

@@ -1,50 +0,0 @@
-<template>
-	<uvImage 
-		:src="src"
-		:mode="mode"
-		:width="width"
-		:height="height"
-		:shape="shape"
-		:radius="radius"
-		:lazyLoad="lazyLoad"
-		:showMenuByLongpress="showMenuByLongpress"
-		:loadingIcon="loadingIcon"
-		:errorIcon="errorIcon"
-		:showLoading="showLoading"
-		:showError="showError"
-		:fade="fade"
-		:webp="webp"
-		:duration="duration"
-		:bgColor="bgColor"
-		:customStyle="customStyle"
-		@click="$emit('click')"
-		@error="$emit('error')"
-		@load="$emit('load')"
-	>
-		<template v-slot:loading>
-			<slot name="loading"></slot>
-		</template>
-		<template v-slot:error>
-			<slot name="error"></slot>
-		</template>
-	</uvImage>
-</template>
-
-<script>
-	/**
-	 * 此组件存在的理由是,在nvue下,u-image被uni-app官方占用了,u-image在nvue中相当于image组件
-	 * 所以在nvue下,取名为u--image,内部其实还是u-iamge.vue,只不过做一层中转
-	 */
-	import uvImage from '../u-image/u-image.vue';
-	import { props } from '../u-image/props.js';
-	import { mpMixin } from '../../libs/mixin/mpMixin';
-	import { mixin } from '../../libs/mixin/mixin';
-	export default {
-		name: 'u--image',
-		mixins: [mpMixin, props, mixin],
-		components: {
-			uvImage
-		},
-		emits: ['click', 'error', 'load']
-	}
-</script>

+ 0 - 74
node_modules/uview-plus/components/u--input/u--input.vue

@@ -1,74 +0,0 @@
-<template>
-	<uvInput 
-		<!-- #ifdef VUE2 -->
-		:value="value"
-		@input="e => $emit('input', e)"
-		<!-- #endif -->
-		<!-- #ifdef VUE3 -->
-		:modelValue="modelValue"
-		@update:modelValue="e => $emit('update:modelValue', e)"
-		<!-- #endif -->
-		:type="type"
-		:fixed="fixed"
-		:disabled="disabled"
-		:disabledColor="disabledColor"
-		:clearable="clearable"
-		:password="password"
-		:maxlength="maxlength"
-		:placeholder="placeholder"
-		:placeholderClass="placeholderClass"
-		:placeholderStyle="placeholderStyle"
-		:showWordLimit="showWordLimit"
-		:confirmType="confirmType"
-		:confirmHold="confirmHold"
-		:holdKeyboard="holdKeyboard"
-		:focus="focus"
-		:autoBlur="autoBlur"
-		:disableDefaultPadding="disableDefaultPadding"
-		:cursor="cursor"
-		:cursorSpacing="cursorSpacing"
-		:selectionStart="selectionStart"
-		:selectionEnd="selectionEnd"
-		:adjustPosition="adjustPosition"
-		:inputAlign="inputAlign"
-		:fontSize="fontSize"
-		:color="color"
-		:prefixIcon="prefixIcon"
-		:suffixIcon="suffixIcon"
-		:suffixIconStyle="suffixIconStyle"
-		:prefixIconStyle="prefixIconStyle"
-		:border="border"
-		:readonly="readonly"
-		:shape="shape"
-		:customStyle="customStyle"
-		:formatter="formatter"
-		:ignoreCompositionEvent="ignoreCompositionEvent"
-	>
-		<!-- #ifdef MP -->
-		<slot name="prefix"></slot>
-		<slot name="suffix"></slot>
-		<!-- #endif -->
-		<!-- #ifndef MP -->
-		<slot name="prefix" slot="prefix"></slot>
-		<slot name="suffix" slot="suffix"></slot>
-		<!-- #endif -->
-	</uvInput>
-</template>
-
-<script>
-	/**
-	 * 此组件存在的理由是,在nvue下,u-input被uni-app官方占用了,u-input在nvue中相当于input组件
-	 * 所以在nvue下,取名为u--input,内部其实还是u-input.vue,只不过做一层中转
-	 */
-	import uvInput from '../u-input/u-input.vue';
-	import { props } from '../u-input/props.js';
-	import { mpMixin } from '../../libs/mixin/mpMixin';
-	import { mixin } from '../../libs/mixin/mixin';
-	export default {
-		name: 'u--input',
-		mixins: [mpMixin, props, mixin],
-		components: {
-			uvInput
-		},
-	}
-</script>

+ 0 - 45
node_modules/uview-plus/components/u--text/u--text.vue

@@ -1,45 +0,0 @@
-<template>
-    <uvText
-        :type="type"
-        :show="show"
-        :text="text"
-        :prefixIcon="prefixIcon"
-        :suffixIcon="suffixIcon"
-        :mode="mode"
-        :href="href"
-        :format="format"
-        :call="call"
-        :openType="openType"
-        :bold="bold"
-        :block="block"
-        :lines="lines"
-        :color="color"
-		:decoration="decoration"
-        :size="size"
-        :iconStyle="iconStyle"
-        :margin="margin"
-        :lineHeight="lineHeight"
-        :align="align"
-        :wordWrap="wordWrap"
-        :customStyle="customStyle"
-    ></uvText>
-</template>
-
-<script>
-/**
- * 此组件存在的理由是,在nvue下,u-text被uni-app官方占用了,u-text在nvue中相当于input组件
- * 所以在nvue下,取名为u--input,内部其实还是u-text.vue,只不过做一层中转
- * 不使用v-bind="$attrs",而是分开独立写传参,是因为微信小程序不支持此写法
- */
-import uvText from "../u-text/u-text.vue";
-import { props } from "../u-text/props.js";
-import { mpMixin } from '../../libs/mixin/mpMixin.js'
-import { mixin } from '../../libs/mixin/mixin.js'
-export default {
-    name: "u--text",
-    mixins: [mpMixin, mixin, props,],
-    components: {
-        uvText,
-    },
-};
-</script>

+ 0 - 47
node_modules/uview-plus/components/u--textarea/u--textarea.vue

@@ -1,47 +0,0 @@
-<template>
-	<uvTextarea
-		:value="value"
-		:modelValue="modelValue"
-		:placeholder="placeholder"
-		:height="height"
-		:confirmType="confirmType"
-		:disabled="disabled"
-		:count="count"
-		:focus="focus"
-		:autoHeight="autoHeight"
-		:fixed="fixed"
-		:cursorSpacing="cursorSpacing"
-		:cursor="cursor"
-		:showConfirmBar="showConfirmBar"
-		:selectionStart="selectionStart"
-		:selectionEnd="selectionEnd"
-		:adjustPosition="adjustPosition"
-		:disableDefaultPadding="disableDefaultPadding"
-		:holdKeyboard="holdKeyboard"
-		:maxlength="maxlength"
-		:border="border"
-		:customStyle="customStyle"
-		:formatter="formatter"
-		:ignoreCompositionEvent="ignoreCompositionEvent"
-		@input="e => $emit('input', e)"
-		@update:modelValue="e => $emit('update:modelValue', e)"
-	></uvTextarea>
-</template>
-
-<script>
-	/**
-	 * 此组件存在的理由是,在nvue下,u--textarea被uni-app官方占用了,u-textarea在nvue中相当于textarea组件
-	 * 所以在nvue下,取名为u--textarea,内部其实还是u-textarea.vue,只不过做一层中转
-	 */
-	import uvTextarea from '../u-textarea/u-textarea.vue';
-	import { props } from '../u-textarea/props.js';
-	import { mpMixin } from '../../libs/mixin/mpMixin';
-	import { mixin } from '../../libs/mixin/mixin';
-	export default {
-		name: 'u--textarea',
-		mixins: [mpMixin, props, mixin],
-		components: {
-			uvTextarea
-		},
-	}
-</script>

+ 4 - 4
node_modules/uview-plus/components/u-action-sheet/actionSheet.js

@@ -1,11 +1,11 @@
 /*
  * @Author       : LQ
  * @Description  :
- * @version      : 1.0
+ * @version      : 3.0
  * @Date         : 2021-08-20 16:44:21
- * @LastAuthor   : LQ
- * @lastTime     : 2021-08-20 16:44:35
- * @FilePath     : /u-view2.0/uview-ui/libs/config/props/actionSheet.js
+ * @LastAuthor   : jry
+ * @lastTime     : 2025-08-16 10:52:35
+ * @FilePath     : /uview-plus/libs/config/props/actionSheet.js
  */
 export default {
     // action-sheet组件

+ 8 - 0
node_modules/uview-plus/components/u-action-sheet/props.js

@@ -1,3 +1,11 @@
+/*
+ * @Author       : LQ
+ * @Description  :
+ * @version      : 3.0
+ * @LastAuthor   : jry
+ * @lastTime     : 2025-08-16 10:52:35
+ * @FilePath     : /uview-plus/libs/config/props/props.js
+ */
 import { defineMixin } from '../../libs/vue'
 import defProps from '../../libs/config/props.js'
 

+ 13 - 4
node_modules/uview-plus/components/u-action-sheet/u-action-sheet.vue

@@ -1,4 +1,3 @@
-
 <template>
 	<u-popup
 	    :show="show"
@@ -8,6 +7,7 @@
 	    :round="round"
 	>
 		<view class="u-action-sheet">
+			<!-- 顶部标题区域 -->
 			<view
 			    class="u-action-sheet__header"
 			    v-if="title"
@@ -17,14 +17,15 @@
 				    class="u-action-sheet__header__icon-wrap"
 				    @tap.stop="cancel"
 				>
-					<u-icon
+					<up-icon
 					    name="close"
 					    size="17"
 					    color="#c8c9cc"
 					    bold
-					></u-icon>
+					></up-icon>
 				</view>
 			</view>
+			<!-- 描述信息 -->
 			<text
 			    class="u-action-sheet__description"
 				:style="[{
@@ -33,7 +34,9 @@
 			    v-if="description"
 			>{{description}}</text>
 			<slot>
+				<!-- 分割线 -->
 				<u-line v-if="description"></u-line>
+				<!-- 操作项列表 -->
 				<scroll-view scroll-y class="u-action-sheet__item-wrap" :style="{maxHeight: wrapMaxHeight}">
 					<view :key="index" v-for="(item, index) in actions">
 						<!-- #ifdef MP -->
@@ -73,6 +76,7 @@
 									    class="u-action-sheet__item-wrap__item__subname"
 									>{{ item.subname }}</text>
 								</template>
+								<!-- 加载状态图标 -->
 								<u-loading-icon
 								    v-else
 								    custom-class="van-action-sheet__loading"
@@ -83,15 +87,18 @@
 							<!-- #ifdef MP -->
 						</button>
 						<!-- #endif -->
+						<!-- 选项间分割线 -->
 						<u-line v-if="index !== actions.length - 1"></u-line>
 					</view>
 				</scroll-view>
 			</slot>
+			<!-- 取消按钮前的分割区域 -->
 			<u-gap
 			    bgColor="#eaeaec"
 			    height="6"
 			    v-if="cancelText"
 			></u-gap>
+			<!-- 取消按钮 -->
 			<view class="u-action-sheet__item-wrap__item u-action-sheet__cancel"
 				hover-class="u-action-sheet--hover" @tap="cancel" v-if="cancelText">
 				<text
@@ -168,6 +175,7 @@
 		},
 		emits: ["close", "select", "update:show"],
 		methods: {
+			// 关闭操作菜单事件处理
 			closeHandler() {
 				// 允许点击遮罩关闭时,才发出close事件
 				if(this.closeOnClickOverlay) {
@@ -180,6 +188,7 @@
 				this.$emit('update:show', false)
 				this.$emit('close')
 			},
+			// 选择操作项处理
 			selectHandler(index) {
 				const item = this.actions[index]
 				if (item && !item.disabled && !item.loading) {
@@ -279,4 +288,4 @@
 			background-color: $u-action-sheet-cancel-text-hover-background-color;
 		}
 	}
-</style>
+</style>

+ 3 - 3
node_modules/uview-plus/components/u-album/album.js

@@ -3,9 +3,9 @@
  * @Description  :
  * @version      : 1.0
  * @Date         : 2021-08-20 16:44:21
- * @LastAuthor   : LQ
- * @lastTime     : 2021-08-20 16:47:24
- * @FilePath     : /u-view2.0/uview-ui/libs/config/props/album.js
+ * @LastAuthor   : jry
+ * @lastTime     : 2025-08-16 16:32:24
+ * @FilePath     : /uview-plus/libs/config/props/album.js
  */
 export default {
     // album 组件

+ 9 - 0
node_modules/uview-plus/components/u-album/props.js

@@ -1,5 +1,14 @@
+/*
+ * @Author       : jry
+ * @Description  :
+ * @version      : 3.0
+ * @LastAuthor   : jry
+ * @lastTime     : 2025-08-16 16:35:24
+ * @FilePath     : /uview-plus/components/u-album/props.js
+ */
 import { defineMixin } from '../../libs/vue'
 import defProps from '../../libs/config/props.js'
+
 export const props = defineMixin({
     props: {
         // 图片地址,Array<String>|Array<Object>形式

+ 56 - 11
node_modules/uview-plus/components/u-album/u-album.vue

@@ -1,5 +1,6 @@
 <template>
     <view class="u-album">
+        <!-- 相册行容器,每行显示 rowCount 个图片 -->
         <view
             class="u-album__row"
             ref="u-album__row"
@@ -8,6 +9,7 @@
             :key="index"
             :style="{flexWrap: autoWrap ? 'wrap' : 'nowrap'}"
         >
+            <!-- 图片包装容器 -->
             <view
                 class="u-album__row__wrapper"
                 v-for="(item, index1) in arr"
@@ -15,6 +17,7 @@
                 :style="[imageStyle(index + 1, index1 + 1)]"
                 @tap="onPreviewTap($event, getSrc(item))"
             >
+                <!-- 图片显示 -->
                 <image
                     :src="getSrc(item)"
                     :mode="
@@ -32,6 +35,7 @@
                         }
                     ]"
                 ></image>
+                <!-- 超出最大显示数量时的更多提示 -->
                 <view
                     v-if="
                         showMore &&
@@ -64,7 +68,7 @@ import { mixin } from '../../libs/mixin/mixin';
 import { addUnit, sleep } from '../../libs/function/index';
 import test from '../../libs/function/test';
 // #ifdef APP-NVUE
-// 由于weex为阿里的KPI业绩考核的产物,所以不支持百分比单位,这里需要通过dom查询组件的宽度
+// 不支持百分比单位,这里需要通过dom查询组件的宽度
 const dom = uni.requireNativePlugin('dom')
 // #endif
 
@@ -108,6 +112,7 @@ export default {
         urls: {
             immediate: true,
             handler(newVal) {
+                // 当只有一张图片时,获取图片尺寸信息
                 if (newVal.length === 1) {
                     this.getImageRect()
                 }
@@ -115,6 +120,12 @@ export default {
         }
     },
     computed: {
+        /**
+         * 计算图片样式
+         * @param {Number} index1 - 行索引
+         * @param {Number} index2 - 列索引
+         * @returns {Object} 图片样式对象
+         */
         imageStyle() {
             return (index1, index2) => {
                 const { space, rowCount, multipleSize, urls } = this,
@@ -138,11 +149,16 @@ export default {
                 return style
             }
         },
-        // 将数组划分为二维数组
+        /**
+         * 将图片地址数组划分为二维数组,用于按行显示
+         * @returns {Array} 二维数组,每个子数组代表一行图片
+         */
         showUrls() {
             if (this.autoWrap) {
+                // 自动换行模式下,所有图片放在一行中显示
                 return [ this.urls.slice(0, this.maxCount) ];
             } else {
+                // 固定行数模式下,按 rowCount 分割图片
                 const arr = []
                 this.urls.map((item, index) => {
                     // 限制最大展示数量
@@ -159,18 +175,29 @@ export default {
                 return arr
             }
         },
+        /**
+         * 计算图片宽度
+         * @returns {String} 图片宽度样式值
+         */
         imageWidth() {
             return addUnit(
                 this.urls.length === 1 ? this.singleWidth : this.multipleSize, this.unit
             )
         },
+        /**
+         * 计算图片高度
+         * @returns {String} 图片高度样式值
+         */
         imageHeight() {
             return addUnit(
                 this.urls.length === 1 ? this.singleHeight : this.multipleSize, this.unit
             )
         },
-        // 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
-        // 因为用户在某些特殊的情况下,需要让文字与相册的宽度相等,所以这里事件的形式对外发送
+        /**
+         * 计算相册总宽度,用于外部组件对齐
+         * 此变量无实际用途,仅仅是为了利用computed特性,让其在urls长度等变化时,重新计算图片的宽度
+         * @returns {Number} 相册宽度
+         */
         albumWidth() {
             let width = 0
             if (this.urls.length === 1) {
@@ -187,12 +214,18 @@ export default {
     emits: ['preview', 'albumWidth'],
     methods: {
         addUnit,
-        // 预览图片
+        /**
+         * 点击图片预览
+         * @param {Event} e - 点击事件对象
+         * @param {String} url - 当前点击图片的地址
+         */
         onPreviewTap(e, url) {
+            // 获取所有图片地址
             const urls = this.urls.map((item) => {
                 return this.getSrc(item)
             })
             if (this.previewFullImage) {
+                // 使用系统默认预览图片功能
                 uni.previewImage({
                     current: url,
                     urls
@@ -200,21 +233,28 @@ export default {
                 // 是否阻止事件传播
                 this.stop && this.preventEvent(e)
             } else {
+                // 发送自定义预览事件
                 this.$emit('preview', {
                     urls,
                     currentIndex: urls.indexOf(url)
                 })
             }
         },
-        // 获取图片的路径
+        /**
+         * 获取图片地址
+         * @param {String|Object} item - 图片项,可以是字符串或对象
+         * @returns {String} 图片地址
+         */
         getSrc(item) {
             return test.object(item)
                 ? (this.keyName && item[this.keyName]) || item.src
                 : item
         },
-        // 单图时,获取图片的尺寸
-        // 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
-        // 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
+        /**
+         * 单图时,获取图片的尺寸
+         * 在小程序中,需要将网络图片的的域名添加到小程序的download域名才可能获取尺寸
+         * 在没有添加的情况下,让单图宽度默认为盒子的一定宽度(singlePercent)
+         */
         getImageRect() {
             const src = this.getSrc(this.urls[0])
             uni.getImageInfo({
@@ -245,21 +285,26 @@ export default {
                     }
                 },
                 fail: () => {
+                    // 获取图片信息失败时,通过组件宽度计算
                     this.getComponentWidth()
                 }
             })
         },
-        // 获取组件的宽度
+        /**
+         * 获取组件的宽度,用于计算单图显示尺寸
+         */
         async getComponentWidth() {
             // 延时一定时间,以获取dom尺寸
             await sleep(30)
             // #ifndef APP-NVUE
+            // H5、小程序等平台通过 $uGetRect 获取组件宽度
             this.$uGetRect('.u-album__row').then((size) => {
                 this.singleWidth = size.width * this.singlePercent
             })
             // #endif
 
             // #ifdef APP-NVUE
+            // NVUE 平台通过 dom 插件获取组件宽度
             // 这里ref="u-album__row"所在的标签为通过for循环出来,导致this.$refs['u-album__row']是一个数组
             const ref = this.$refs['u-album__row'][0]
             ref &&
@@ -296,4 +341,4 @@ export default {
         }
     }
 }
-</style>
+</style>

+ 9 - 5
node_modules/uview-plus/components/u-alert/alert.js

@@ -1,11 +1,11 @@
 /*
  * @Author       : LQ
  * @Description  :
- * @version      : 1.0
+ * @version      : 3.0
  * @Date         : 2021-08-20 16:44:21
- * @LastAuthor   : LQ
- * @lastTime     : 2021-08-20 16:48:53
- * @FilePath     : /u-view2.0/uview-ui/libs/config/props/alert.js
+ * @LastAuthor   : jry
+ * @lastTime     : 2025-08-17 17:23:53
+ * @FilePath     : /uview-plus/libs/config/props/alert.js
  */
 export default {
     // alert警告组件
@@ -17,6 +17,10 @@ export default {
         showIcon: false,
         effect: 'light',
         center: false,
-        fontSize: 14
+        fontSize: 14,
+        transitionMode: 'fade',
+        duration: 0,
+        icon: '',
+        value: true
     }
 }

+ 29 - 0
node_modules/uview-plus/components/u-alert/props.js

@@ -1,5 +1,14 @@
+/*
+ * @Author       : jry
+ * @Description  :
+ * @version      : 3.0
+ * @LastAuthor   : jry
+ * @lastTime     : 2025-08-17 17:23:53
+ * @FilePath     : /uview-plus/libs/config/props/props.js
+ */
 import { defineMixin } from '../../libs/vue'
 import defProps from '../../libs/config/props.js'
+
 export const props = defineMixin({
     props: {
         // 显示文字
@@ -41,6 +50,26 @@ export const props = defineMixin({
         fontSize: {
             type: [String, Number],
             default: () => defProps.alert.fontSize
+        },
+        // 动画类型
+        transitionMode: {
+            type: [String],
+            default: () => defProps.alert.transitionMode
+        },
+        // 自动定时关闭毫秒
+        duration: {
+            type: [Number],
+            default: () => defProps.alert.duration
+        },
+        // 自定义图标
+        icon: {
+            type: [String],
+            default: () => defProps.alert.icon
+        },
+        // 是否显示
+        modelValue: {
+            type: [String],
+            default: () => defProps.alert.value
         }
     }
 })

+ 59 - 16
node_modules/uview-plus/components/u-alert/u-alert.vue

@@ -1,6 +1,6 @@
 <template>
-	<u-transition
-	    mode="fade"
+	<up-transition
+	    :mode="transitionMode"
 	    :show="show"
 	>
 		<view
@@ -9,22 +9,25 @@
 		    @tap.stop="clickHandler"
 		    :style="[addStyle(customStyle)]"
 		>
+			<!-- 左侧图标 -->
 			<view
 			    class="u-alert__icon"
 			    v-if="showIcon"
 			>
-				<u-icon
+				<up-icon
 				    :name="iconName"
 				    size="18"
 				    :color="iconColor"
-				></u-icon>
+				></up-icon>
 			</view>
+			<!-- 内容区域 -->
 			<view
 			    class="u-alert__content"
 			    :style="[{
 					paddingRight: closable ? '20px' : 0
 				}]"
 			>
+				<!-- 标题 -->
 				<text
 				    class="u-alert__content__title"
 				    v-if="title"
@@ -34,6 +37,7 @@
 					}]"
 				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
 				>{{ title }}</text>
+				<!-- 描述信息 -->
 				<text
 				    class="u-alert__content__desc"
 					v-if="description"
@@ -44,19 +48,22 @@
 				    :class="[effect === 'dark' ? 'u-alert__text--dark' : `u-alert__text--${type}--light`]"
 				>{{ description }}</text>
 			</view>
+			<!-- 关闭按钮 -->
 			<view
 			    class="u-alert__close"
 			    v-if="closable"
 			    @tap.stop="closeHandler"
 			>
-				<u-icon
-				    name="close"
-				    :color="iconColor"
-				    size="15"
-				></u-icon>
+				<slot name="close">
+					<up-icon
+					    name="close"
+					    :color="iconColor"
+					    size="15"
+					></up-icon>
+				</slot>
 			</view>
 		</view>
-	</u-transition>
+	</up-transition>
 </template>
 
 <script>
@@ -70,7 +77,7 @@
 	 * @tutorial https://ijry.github.io/uview-plus/components/alertTips.html
 	 * 
 	 * @property {String}			title       显示的文字 
-	 * @property {String}			type        使用预设的颜色  (默认 'warning' )
+	 * @property {String}			type        使用预设的颜色 (默认 'warning' )
 	 * @property {String}			description 辅助性文字,颜色比title浅一点,字号也小一点,可选  
 	 * @property {Boolean}			closable    关闭按钮(默认为叉号icon图标)  (默认 false )
 	 * @property {Boolean}			showIcon    是否显示左边的辅助图标   ( 默认 false )
@@ -78,24 +85,34 @@
 	 * @property {Boolean}			center		文字是否居中  (默认 false )
 	 * @property {String | Number}	fontSize    字体大小  (默认 14 )
 	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @property {String}			transitionMode 过渡动画模式 (默认 'fade' )
+	 * @property {String | Number}	duration	自动关闭延时(毫秒),设置为0或负数则不自动关闭 (默认 0 )
+	 * @property {String}			icon		自定义图标名称,优先级高于type默认图标
+	 * @property {Boolean}			modelValue/v-model	绑定值,控制是否显示 (默认 true )
 	 * @event    {Function}        click       点击组件时触发
 	 * @event    {Function}        close       点击关闭按钮时触发
-	 * @example  <u-alert :title="title"  type = "warning" :closable="closable" :description = "description"></u-alert>
+	 * @event    {Function}        closed      关闭动画结束时触发
+	 * @example  <up-alert :title="title"  type = "warning" :closable="closable" :description = "description"></up-alert>
 	 */
 	export default {
 		name: 'u-alert',
 		mixins: [mpMixin, mixin, props],
 		data() {
 			return {
+				// 控制组件显示隐藏
 				show: true
 			}
 		},
 		computed: {
+			// 根据不同的主题类型返回对应的图标颜色
 			iconColor() {
 				return this.effect === 'light' ? this.type : '#fff'
 			},
 			// 不同主题对应不同的图标
 			iconName() {
+				// 如果用户自定义了图标,则优先使用自定义图标
+				if (this.icon) return this.icon;
+				
 				switch (this.type) {
 					case 'success':
 						return 'checkmark-circle-fill';
@@ -117,18 +134,44 @@
 				}
 			}
 		},
-		emits: ["click","close"],
+		emits: ["click","close", "closed", "update:modelValue"],
+		watch: {
+			modelValue: {
+				handler(newVal) {
+					this.show = newVal;
+				},
+				immediate: true
+			},
+			show: {
+				handler(newVal) {
+					this.$emit('update:modelValue', newVal);
+					
+					// 如果是从显示到隐藏,且启用了自动关闭功能
+					if (!newVal && this.duration > 0) {
+						this.$emit('closed');
+					}
+				}
+			}
+		},
+		mounted() {
+			// 如果设置了自动关闭时间,则在指定时间后自动关闭
+			if (this.duration > 0) {
+				setTimeout(() => {
+					this.closeHandler();
+				}, this.duration);
+			}
+		},
 		methods: {
 			addUnit,
 			addStyle,
-			// 点击内容
+			// 点击内容区域触发click事件
 			clickHandler() {
 				this.$emit('click')
 			},
-			// 点击关闭按钮
+			// 点击关闭按钮触发close事件并隐藏组件
 			closeHandler() {
 				this.show = false   
-				this.$emit('close')
+				this.$emit('close');
 			}
 		}
 	}

+ 2 - 2
node_modules/uview-plus/components/u-avatar/u-avatar.vue

@@ -23,12 +23,12 @@
 			<!-- #ifndef MP-WEIXIN && MP-QQ && MP-BAIDU  -->
 			<template v-if="mpAvatar && allowMp"></template>
 			<!-- #endif -->
-			<u-icon
+			<up-icon
 				v-else-if="icon"
 				:name="icon"
 				:size="fontSize"
 				:color="color"
-			></u-icon>
+			></up-icon>
 			<up-text
 				v-else-if="text"
 				:text="text"

+ 2 - 2
node_modules/uview-plus/components/u-back-top/u-back-top.vue

@@ -10,10 +10,10 @@
 		    v-if="!$slots.default && !$slots.$default"
 			@click="backToTop"
 		>
-			<u-icon
+			<up-icon
 			    :name="icon"
 			    :custom-style="iconStyle"
-			></u-icon>
+			></up-icon>
 			<text
 			    v-if="text"
 			    class="u-back-top__text"

+ 0 - 49
node_modules/uview-plus/components/u-barcode/u-barcode.vue

@@ -418,55 +418,6 @@ export default {
         throw error
       }
     },
-    
-    /**
-     * CODE128编码实现
-     * @author jry <ijry@qq.com>
-     * @param {String} data - 要编码的数据
-     * @returns {String|null} 编码后的条形码数据
-     */
-    encodeCode128(data) {
-      const CODE128_START_CODE_B = 104
-      const CODE128_STOP = 106
-      
-      // CODE128 Code B 字符集
-      const CODE128_CODE_B_CHARS = ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~'
-      
-      // 条形码模式 (B 模式)
-      const codes = []
-      let checksum = CODE128_START_CODE_B
-      
-      // 添加起始字符
-      codes.push(CODE128_START_CODE_B)
-      
-      // 编码每个字符
-      for (let i = 0; i < data.length; i++) {
-        const char = data[i]
-        const code = CODE128_CODE_B_CHARS.indexOf(char)
-        
-        if (code === -1) {
-          throw new Error('Invalid character in CODE128: ' + char)
-        }
-        
-        codes.push(code)
-        checksum += code * (i + 1)
-      }
-      
-      // 添加校验字符
-      codes.push(checksum % 103)
-      
-      // 添加结束字符
-      codes.push(CODE128_STOP)
-      
-      // 转换为条形码模式 (1 = 黑条, 0 = 白条)
-      let barcode = ''
-      for (let i = 0; i < codes.length; i++) {
-        const code = codes[i]
-        barcode += this.getCode128Pattern(code)
-      }
-      
-      return barcode
-    },
 
     /**
      * 添加右侧安静区(至少2个模块宽度的空白)

+ 5 - 5
node_modules/uview-plus/components/u-button/u-button.vue

@@ -39,13 +39,13 @@
             >
         </template>
         <template v-else>
-            <u-icon
+            <up-icon
                 v-if="icon"
                 :name="icon"
                 :color="iconColorCom"
                 :size="textSize * 1.35"
                 :customStyle="{ marginRight: '2px' }"
-            ></u-icon>
+            ></up-icon>
             <slot>
                 <text
                     class="u-button__text"
@@ -87,12 +87,12 @@
             >
         </template>
         <template v-else>
-            <u-icon
+            <up-icon
                 v-if="icon"
                 :name="icon"
                 :color="iconColorCom"
                 :size="textSize * 1.35"
-            ></u-icon>
+            ></up-icon>
             <text
                 class="u-button__text"
                 :style="[
@@ -206,7 +206,7 @@ export default {
         },
         iconColorCom() {
             // 如果是镂空状态,设置了color就用color值,否则使用主题颜色,
-            // u-icon的color能接受一个主题颜色的值
+            // up-icon的color能接受一个主题颜色的值
 			if (this.iconColor) return this.iconColor;
 			if (this.plain) {
                 return this.color ? this.color : this.type;

+ 1 - 1
node_modules/uview-plus/components/u-calendar/month.vue

@@ -37,7 +37,7 @@
 	import { colorGradient } from '../../libs/function/colorGradient';
 	import test from '../../libs/function/test';
 	import defProps from '../../libs/config/props';
-	import dayjs from 'dayjs/esm/index'
+	import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
 	export default {
 		name: 'u-calendar-month',
 		mixins: [mpMixin, mixin],

+ 1 - 1
node_modules/uview-plus/components/u-calendar/u-calendar.vue

@@ -71,7 +71,7 @@ import uHeader from './header.vue'
 import uMonth from './month.vue'
 import { props } from './props.js'
 import util from './util.js'
-import dayjs from 'dayjs/esm/index'
+import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
 import Calendar from '../../libs/util/calendar.js'
 import { mpMixin } from '../../libs/mixin/mpMixin.js'
 import { mixin } from '../../libs/mixin/mixin.js'

+ 1 - 1
node_modules/uview-plus/components/u-calendar/util.js

@@ -1,4 +1,4 @@
-import dayjs from 'dayjs/esm/index'
+import dayjs from '../u-datetime-picker/dayjs.esm.min.js';
 export default {
     methods: {
         // 设置月份数据

+ 2 - 2
node_modules/uview-plus/components/u-car-keyboard/u-car-keyboard.vue

@@ -56,11 +56,11 @@
 					hover-class="u-hover-class"
 					:hover-stay-time="200"
 				>
-					<u-icon
+					<up-icon
 						size="28"
 						name="backspace"
 						color="#303133"
-					></u-icon>
+					></up-icon>
 				</view>
 			</view>
 		</view>

+ 51 - 21
node_modules/uview-plus/components/u-cate-tab/u-cate-tab.vue

@@ -12,10 +12,12 @@
                     <text v-if="!$slots['tabItem']" class="u-line-1">{{item[tabKeyName]}}</text>
 				</view>
 			</scroll-view>
-			<scroll-view :scroll-top="scrollRightTop" scroll-with-animation
+			<scroll-view :scroll-top="scrollRightTop" scroll-with-animation :scroll-into-view="scrollIntoView"
 				scroll-y class="u-cate-tab__right-box" @scroll="rightScroll">
-				<slot name="rightTop" :tabList="tabList">
-                </slot>
+				<view class="u-cate-tab__right-top">
+					<slot name="rightTop" :tabList="tabList">
+                	</slot>
+				</view>
 				<view class="u-cate-tab__page-view">
 					<view class="u-cate-tab__page-item" :id="'item' + index"
 						v-for="(item , index) in tabList" :key="index">
@@ -43,7 +45,7 @@
 	</view>
 </template>
 <script>
-	import { addUnit } from '../../libs/function/index';
+	import { addUnit, sleep } from '../../libs/function/index';
 	export default {
 		name: 'up-cate-tab',
         props: {
@@ -71,18 +73,30 @@
             }
         },
         watch: {
-            tabList() {
-                this.getMenuItemTop()
-            },
+			tabList: {
+				deep: true,
+				handler(newVal, oldVal) {
+					// this.observer();
+					sleep(30);
+					this.getMenuItemTop();
+					this.leftMenuStatus(this.innerCurrent);
+				}
+			},
 			current(nval) {
 				this.innerCurrent = nval;
 				this.leftMenuStatus(this.innerCurrent);
+			},
+			height() {
+				// console.log('height change');
+				this.getMenuItemTop();
+				this.leftMenuStatus(this.innerCurrent);
 			}
         },
 		emits: ['update:current'],
 		data() {
 			return {
 				scrollTop: 0, //tab标题的滚动条位置
+				scrollIntoView: '', // 滚动至哪个元素
 				oldScrollTop: 0,
 				innerCurrent: 0, // 预设当前项的值
 				menuHeight: 0, // 左边菜单的高度
@@ -95,7 +109,8 @@
 				timer: null, // 定时器
 			}
 		},
-		onMounted() {
+		mounted() {
+			// this.observer();
 			this.innerCurrent = this.current;
 			this.leftMenuStatus(this.innerCurrent);
 			this.getMenuItemTop()
@@ -107,18 +122,16 @@
 				if(this.arr.length == 0) {
 					await this.getMenuItemTop();
 				}
+				this.scrollIntoView = 'item' + index;
 				if (index == this.innerCurrent) return;
-				this.scrollRightTop = this.oldScrollTop;
 				this.$nextTick(function(){
-					this.scrollRightTop = this.arr[index];
 					this.innerCurrent = index;
-					this.leftMenuStatus(index);
 					this.$emit('update:current', index);
 				})
 			},
 			// 获取一个目标元素的高度
 			getElRect(elClass, dataVal) {
-				new Promise((resolve, reject) => {
+				return new Promise((resolve, reject) => {
 					const query = uni.createSelectorQuery().in(this);
 					query.select('.' + elClass).fields({
 						size: true
@@ -137,16 +150,27 @@
 			},
 			// 观测元素相交状态
 			async observer() {
+				await this.$nextTick();
+				// 清除之前的观察器
+				if (this._observerList) {
+					this._observerList.forEach(observer => {
+						observer.disconnect();
+					});
+				}
+				this._observerList = [];
+				
 				this.tabList.map((val, index) => {
 					let observer = uni.createIntersectionObserver(this);
-					// 检测右边scroll-view的id为itemxx的元素与u-cate-tab__right-box的相交状态
-					// 如果跟.u-cate-tab__right-box底部相交,就动态设置左边栏目的活动状态
+					this._observerList.push(observer);
+					// 检测相交状态
 					observer.relativeTo('.u-cate-tab__right-box', {
-						top: 0
-					}).observe('#item' + index, res => {
+						top: 10
+					}).observe('#item' + index, (res) => {
 						if (res.intersectionRatio > 0) {
-							let id = res.id.substring(4);
-							this.leftMenuStatus(id);
+							console.log('res', res);
+							// 修复:确保正确获取索引
+							let id = res.id ? res.id.substring(4) : index;
+							this.leftMenuStatus(parseInt(id));
 						}
 					})
 				})
@@ -160,11 +184,14 @@
 					await this.getElRect('u-cate-tab__menu-scroll-view', 'menuHeight');
 					await this.getElRect('u-cate-tab__item', 'menuItemHeight');
 				}
+				// console.log(this.menuHeight, this.menuItemHeight)
 				// 将菜单活动item垂直居中
 				this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
 			},
 			// 获取右边菜单每个item到顶部的距离
-			getMenuItemTop() {
+			async getMenuItemTop() {
+				// await this.$nextTick();
+				// console.log('getMenuItemTop')
 				return new Promise(resolve => {
 					let selectorQuery = uni.createSelectorQuery().in(this);
 					selectorQuery.selectAll('.u-cate-tab__page-item').boundingClientRect((rects) => {
@@ -172,14 +199,17 @@
 						if(!rects.length) {
 							setTimeout(() => {
 								this.getMenuItemTop();
-							}, 10);
+							}, 100);
 							return ;
 						}
+						// console.log(rects)
                         this.rects = rects;
+						this.arr = [];
 						rects.forEach((rect) => {
 							// 这里减去rects[0].top,是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
 							this.arr.push(rect.top - rects[0].top);
 						})
+						// console.log(this.arr)
                         resolve();
 					}).exec()
 				})
@@ -216,7 +246,7 @@
 							return ;
 						}
 					}
-				}, 10)
+				}, 100)
 			}
 		}
 	}

+ 4 - 4
node_modules/uview-plus/components/u-cell/u-cell.vue

@@ -7,9 +7,9 @@
 				<view class="u-cell__left-icon-wrap" v-if="$slots.icon || icon">
 					<slot name="icon" v-if="$slots.icon">
 					</slot>
-					<u-icon v-else :name="icon"
+					<up-icon v-else :name="icon"
 						:custom-style="iconStyle"
-						:size="size === 'large' ? 22 : 18"></u-icon>
+						:size="size === 'large' ? 22 : 18"></up-icon>
 				</view>
 				<view class="u-cell__title">
                     <!-- 将slot与默认内容用if/else分开主要是因为微信小程序不支持slot嵌套传递,这样才能解决collapse组件的slot不失效问题,label暂时未用到。 -->
@@ -30,9 +30,9 @@
 			</slot>
 			<view class="u-cell__right-icon-wrap" v-if="$slots['right-icon'] || isLink"
 				:class="[`u-cell__right-icon-wrap--${arrowDirection}`]">
-				<u-icon v-if="rightIcon && !$slots['right-icon']" :name="rightIcon"
+				<up-icon v-if="rightIcon && !$slots['right-icon']" :name="rightIcon"
 					:custom-style="rightIconStyle" :color="disabled ? '#c8c9cc' : 'info'"
-					:size="size === 'large' ? 18 : 16"></u-icon>
+					:size="size === 'large' ? 18 : 16"></up-icon>
 				<slot v-else name="right-icon">
 				</slot>
 			</view>

+ 1 - 1
node_modules/uview-plus/components/u-checkbox/u-checkbox.vue

@@ -12,7 +12,7 @@
 		    :style="[iconWrapStyle]"
 		>
 			<slot name="icon" :elIconSize="elIconSize" :elIconColor="elIconColor">
-				<u-icon
+				<up-icon
 				    class="u-checkbox__icon-wrap__icon"
 				    name="checkbox-mark"
 				    :size="elIconSize"

+ 2 - 2
node_modules/uview-plus/components/u-collapse-item/u-collapse-item.vue

@@ -24,7 +24,7 @@
 			</template>
 			<template #icon>
 				<slot name="icon">
-					<u-icon v-if="!$slots.icon && icon" :size="22" :name="icon"></u-icon>
+					<up-icon v-if="!$slots.icon && icon" :size="22" :name="icon"></up-icon>
 				</slot>
 			</template>
 			<template #value>
@@ -36,7 +36,7 @@
 			</template>
 			<template #right-icon>
 				<template v-if="showRight">
-					<u-icon v-if="!$slots['right-icon']" :size="16" name="arrow-right"></u-icon>
+					<up-icon v-if="!$slots['right-icon']" :size="16" name="arrow-right"></up-icon>
 					<slot name="right-icon">
 					</slot>
 				</template>

+ 6 - 6
node_modules/uview-plus/components/u-column-notice/u-column-notice.vue

@@ -8,11 +8,11 @@
 				class="u-notice__left-icon"
 				v-if="icon"
 			>
-				<u-icon
+				<up-icon
 					:name="icon"
 					:color="color"
 					size="19"
-				></u-icon>
+				></up-icon>
 			</view>
 		</slot>
 		<swiper
@@ -40,19 +40,19 @@
 			class="u-notice__right-icon"
 			v-if="['link', 'closable'].includes(mode)"
 		>
-			<u-icon
+			<up-icon
 				v-if="mode === 'link'"
 				name="arrow-right"
 				:size="17"
 				:color="color"
-			></u-icon>
-			<u-icon
+			></up-icon>
+			<up-icon
 				v-if="mode === 'closable'"
 				name="close"
 				:size="16"
 				:color="color"
 				@click="close"
-			></u-icon>
+			></up-icon>
 		</view>
 	</view>
 </template>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 6 - 0
node_modules/uview-plus/components/u-datetime-picker/dayjs.esm.min.js


+ 1 - 1
node_modules/uview-plus/components/u-datetime-picker/u-datetime-picker.vue

@@ -58,7 +58,7 @@
 	import { props } from './props';
 	import { mpMixin } from '../../libs/mixin/mpMixin';
 	import { mixin } from '../../libs/mixin/mixin';
-	import dayjs from 'dayjs/esm/index';
+	import dayjs from './dayjs.esm.min.js';
 	import { range, error, padZero } from '../../libs/function/index';
 	import test from '../../libs/function/test';
 	/**

+ 1 - 1
node_modules/uview-plus/components/u-dropdown/u-dropdown.vue

@@ -14,7 +14,7 @@
 					<view class="u-dropdown__menu__item__arrow" :class="{
 						'u-dropdown__menu__item__arrow--rotate': index === current
 					}">
-						<u-icon :custom-style="{display: 'flex'}" :name="menuIcon" :size="addUnit(menuIconSize)" :color="index === current || highlightIndexList.includes(index) ? activeColor : '#c0c4cc'"></u-icon>
+						<up-icon :custom-style="{display: 'flex'}" :name="menuIcon" :size="addUnit(menuIconSize)" :color="index === current || highlightIndexList.includes(index) ? activeColor : '#c0c4cc'"></up-icon>
 					</view>
 				</view>
 			</view>

+ 2 - 2
node_modules/uview-plus/components/u-empty/u-empty.vue

@@ -4,13 +4,13 @@
 	    :style="[emptyStyle]"
 	    v-if="show"
 	>
-		<u-icon
+		<up-icon
 		    v-if="!isSrc"
 		    :name="mode === 'message' ? 'chat' : `empty-${mode}`"
 		    :size="iconSize"
 		    :color="iconColor"
 		    margin-top="14"
-		></u-icon>
+		></up-icon>
 		<image
 		    v-else
 		    :style="{

+ 2 - 2
node_modules/uview-plus/components/u-form-item/u-form-item.vue

@@ -29,10 +29,10 @@
 							class="u-form-item__body__left__content__icon"
 							v-if="leftIcon"
 						>
-							<u-icon
+							<up-icon
 								:name="leftIcon"
 								:custom-style="leftIconStyle"
-							></u-icon>
+							></up-icon>
 						</view>
 						<text
 							class="u-form-item__body__left__content__label"

+ 4 - 4
node_modules/uview-plus/components/u-image/u-image.vue

@@ -36,9 +36,9 @@
 				}"
 			>
 				<slot name="loading">
-					<u-icon
+					<up-icon
 						:name="loadingIcon"
-					></u-icon>
+					></up-icon>
 				</slot>
 			</view>
 			<view
@@ -52,9 +52,9 @@
 				}"
 			>
 				<slot name="error">
-					<u-icon
+					<up-icon
 						:name="errorIcon"
-					></u-icon>
+					></up-icon>
 				</slot>
 			</view>
 		</view>

+ 6 - 6
node_modules/uview-plus/components/u-input/u-input.vue

@@ -6,11 +6,11 @@
                 v-if="prefixIcon || $slots.prefix"
             >
                 <slot name="prefix">
-                    <u-icon
+                    <up-icon
                         :name="prefixIcon"
                         size="18"
                         :customStyle="prefixIconStyle"
-                    ></u-icon>
+                    ></up-icon>
                 </slot>
             </view>
             <view class="u-input__content__field-wrapper" @tap="clickHandler">
@@ -54,23 +54,23 @@
                 v-if="isShowClear"
                 @click="onClear"
             >
-                <u-icon
+                <up-icon
                     name="close"
                     size="11"
                     color="#ffffff"
                     customStyle="line-height: 12px"
-                ></u-icon>
+                ></up-icon>
             </view>
             <view
                 class="u-input__content__subfix-icon"
                 v-if="suffixIcon || $slots.suffix"
             >
                 <slot name="suffix">
-                    <u-icon
+                    <up-icon
                         :name="suffixIcon"
                         size="18"
                         :customStyle="suffixIconStyle"
-                    ></u-icon>
+                    ></up-icon>
                 </slot>
             </view>
         </view>

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 6 - 0
node_modules/uview-plus/components/u-markdown/marked.esm.js


+ 300 - 0
node_modules/uview-plus/components/u-markdown/u-markdown.vue

@@ -0,0 +1,300 @@
+<template>
+  <view class="up-markdown" :class="theme">
+    <up-parse :content="parsedContent" :previewImg="previewImg"></up-parse>
+  </view>
+</template>
+
+<script>
+import { marked } from './marked.esm.js';
+
+export default {
+  name: 'up-markdown',
+  props: {
+    // markdown内容
+    content: {
+      type: String,
+      default: ''
+    },
+    // 是否启用图片预览
+    previewImg: {
+      type: Boolean,
+      default: true
+    },
+    // 是否显示代码块行号
+    showLineNumber: {
+      type: Boolean,
+      default: false
+    },
+    // 主题样式 'light' | 'dark'
+    theme: {
+      type: String,
+      default: 'light'
+    }
+  },
+  data() {
+    return {
+      parsedContent: ''
+    };
+  },
+  watch: {
+    content: {
+      handler(newVal) {
+        this.parseMarkdown(newVal);
+      },
+      immediate: true
+    }
+  },
+  methods: {
+    // 解析markdown内容
+    parseMarkdown(content) {
+      if (!content) {
+        this.parsedContent = '';
+        return;
+      }
+
+      // 使用marked解析markdown
+      let parsed = marked(content);
+      
+      // 处理代码块
+      parsed = this.handleCodeBlock(parsed);
+      
+      // 应用主题样式
+      parsed = this.applyTheme(parsed);
+      
+      this.parsedContent = parsed;
+    },
+    
+    // 处理代码块
+    handleCodeBlock(html) {
+      // 添加代码块样式和行号
+      return html.replace(/<pre><code([^>]*)>([^<]+)<\/code><\/pre>/g, (match, lang, code) => {
+        const language = lang.match(/class="language-([^"]+)"/);
+        const langClass = language ? `language-${language[1]}` : '';
+        
+        let result = `<pre class="up-markdown-code ${langClass}">`;
+        
+        if (this.showLineNumber) {
+          // 添加行号
+          const lines = code.split('\n').filter(line => line.trim() !== '');
+          result += '<span class="up-markdown-line-numbers">';
+          lines.push('');
+          lines.forEach((_, index) => {
+            result += `<span class="up-markdown-line-number">${index + 1}</span>`;
+          });
+          result += '</span>';
+        }
+        
+        result += `<code class='code-lang ${langClass}'>${code}</code></pre>`;
+        return result;
+      });
+    },
+    
+    // 应用主题样式
+    applyTheme(html) {
+      // 可以根据theme属性添加不同的样式类
+      return html;
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.up-markdown {
+  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+  font-size: 16px;
+  line-height: 1.6;
+  color: #333;
+  padding: 16px;
+  word-wrap: break-word;
+  
+  /* 标题样式 */
+  ::v-deep h1 {
+    font-size: 32px;
+    margin: 8px 0;
+    font-weight: bold;
+  }
+  
+  ::v-deep h2 {
+    font-size: 24px;
+    margin: 8px 0;
+    font-weight: bold;
+  }
+  
+  ::v-deep h3 {
+    font-size: 18px;
+    margin: 7px 0;
+    font-weight: bold;
+  }
+  
+  ::v-deep h4 {
+    font-size: 16px;
+    margin: 7px 0;
+    font-weight: bold;
+  }
+  
+  ::v-deep h5 {
+    font-size: 13px;
+    margin: 6px 0;
+    font-weight: bold;
+  }
+  
+  ::v-deep h6 {
+    font-size: 10px;
+    margin: 5px 0;
+    font-weight: bold;
+  }
+  
+  /* 段落样式 */
+  ::v-deep p {
+    margin: 16px 0;
+  }
+  
+  /* 链接样式 */
+  ::v-deep a {
+    color: #007AFF;
+    text-decoration: none;
+    
+    &:hover {
+      text-decoration: underline;
+    }
+  }
+  
+  /* 列表样式 */
+  ::v-deep ul,
+  ::v-deep ol {
+    margin: 16px 0;
+    padding-left: 32px;
+    
+    li {
+      margin: 8px 0;
+    }
+  }
+  
+  ::v-deep ul li {
+    list-style-type: disc;
+  }
+  
+  ::v-deep ol li {
+    list-style-type: decimal;
+  }
+  
+  /* 引用样式 */
+  ::v-deep blockquote {
+    margin: 8px 0;
+    padding: 0 10px;
+    border-left: 4px solid #ccc;
+    color: #666;
+  }
+  
+  /* 代码样式 */
+  ::v-deep &-code {
+    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
+    font-size: 14px;
+    background-color: #f6f8fa;
+    padding: 3px 6px;
+    border-radius: 3px;
+    display: flex;
+  }
+  ::v-deep .code-lang {
+    width: 100%;
+    overflow-x: auto;
+  }
+  
+  ::v-deep pre {
+    font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
+    font-size: 14px;
+    background-color: #f6f8fa;
+    padding: 16px;
+    overflow: auto;
+    border-radius: 6px;
+    margin: 16px 0;
+    
+    ::v-deep code {
+      background: none;
+      padding: 0;
+    }
+  }
+  
+  /* 表格样式 */
+  ::v-deep table {
+    border-collapse: collapse;
+    margin: 16px 0;
+    width: 100%;
+    
+    th,
+    td {
+      padding: 6px 13px;
+      border: 1px solid #dfe2e5;
+    }
+    
+    th {
+      font-weight: 600;
+    }
+    
+    tr:nth-child(2n) {
+      background-color: #f6f8fa;
+    }
+  }
+  
+  /* 图片样式 */
+  ::v-deep img {
+    max-width: 100%;
+    box-sizing: content-box;
+    background-color: #fff;
+    margin: 16px 0;
+  }
+  
+  /* 分割线样式 */
+  ::v-deep hr {
+    height: 1px;
+    padding: 0;
+    margin: 24px 0;
+    background-color: #e1e4e8;
+    border: 0;
+  }
+  
+  /* 深色主题 */
+  &.dark {
+    color: #ccc;
+    background-color: #1e1e1e;
+    
+    ::v-deep &-code {
+      background-color: #2d2d2d;
+      color: #dcdcdc;
+    }
+    
+    ::v-deep pre {
+      background-color: #2d2d2d;
+      color: #dcdcdc;
+    }
+
+    ::v-deep blockquote {
+        margin: 8px 0;
+        padding: 0 10px;
+        border-left: 4px solid #ccc;
+        color: #bbb;
+    }
+    
+    ::v-deep a {
+      color: #4da6ff;
+    }
+  }
+}
+
+/* 代码块行号样式 */
+::v-deep .up-markdown-line-numbers {
+  display: flex;
+  flex-direction: column;
+  align-items: flex-start;
+  padding-right: 10px;
+  margin-right: 10px;
+  border-right: 1px solid #ddd;
+  user-select: none;
+  
+  .up-markdown-line-number {
+    color: #999;
+    font-size: 14px;
+    line-height: 1.6;
+  }
+}
+</style>

+ 4 - 4
node_modules/uview-plus/components/u-navbar/u-navbar.vue

@@ -27,12 +27,12 @@
 					@tap="leftClick"
 				>
 					<slot name="left">
-						<u-icon
+						<up-icon
 							v-if="leftIcon"
 							:name="leftIcon"
 							:size="leftIconSize"
 							:color="leftIconColor"
-						></u-icon>
+						></up-icon>
 						<text
 							v-if="leftText"
 							:style="{
@@ -57,11 +57,11 @@
 					@tap="rightClick"
 				>
 					<slot name="right">
-						<u-icon
+						<up-icon
 							v-if="rightIcon"
 							:name="rightIcon"
 							size="20"
-						></u-icon>
+						></up-icon>
 						<text
 							v-if="rightText"
 							class="u-navbar__content__right__text"

+ 2 - 2
node_modules/uview-plus/components/u-no-network/u-no-network.vue

@@ -12,12 +12,12 @@
 		<view
 		    class="u-no-network"
 		>
-			<u-icon
+			<up-icon
 			    :name="image"
 			    size="150"
 			    imgMode="widthFit"
 			    class="u-no-network__error-icon"
-			></u-icon>
+			></up-icon>
 			<text class="u-no-network__tips">{{tips}}</text>
 			<!-- 只有APP平台,才能跳转设置页,因为需要调用plus环境 -->
 			<!-- #ifdef APP-PLUS -->

+ 2 - 2
node_modules/uview-plus/components/u-notify/u-notify.vue

@@ -12,13 +12,13 @@
 			<u-status-bar v-if="tmpConfig.safeAreaInsetTop"></u-status-bar>
 			<view class="u-notify__warpper">
 				<slot name="icon">
-					<u-icon
+					<up-icon
 						v-if="['success', 'warning', 'error'].includes(tmpConfig.type)"
 						:name="tmpConfig.icon"
 						:color="tmpConfig.color"
 						:size="1.3 * tmpConfig.fontSize"
 						:customStyle="{marginRight: '4px'}"
-					></u-icon>
+					></up-icon>
 				</slot>
 				<text
 					class="u-notify__warpper__text"

+ 4 - 4
node_modules/uview-plus/components/u-number-box/u-number-box.vue

@@ -20,13 +20,13 @@
 		    :class="{ 'u-number-box__minus--disabled': isDisabled('minus') }"
 		    :style="[buttonStyle('minus')]"
 		>
-			<u-icon
+			<up-icon
 			    name="minus"
 			    :color="isDisabled('minus') ? '#c8c9cc' : '#323233'"
 			    size="15"
 			    bold
 				:customStyle="iconStyle"
-			></u-icon>
+			></up-icon>
 		</view>
 
 		<template v-if="!hideMinus">
@@ -81,13 +81,13 @@
 		    :class="{ 'u-number-box__minus--disabled': isDisabled('plus') }"
 		    :style="[buttonStyle('plus')]"
 		>
-			<u-icon
+			<up-icon
 			    name="plus"
 			    :color="isDisabled('plus') ? '#c8c9cc' : '#323233'"
 			    size="15"
 			    bold
 				:customStyle="iconStyle"
-			></u-icon>
+			></up-icon>
 		</view>
 	</view>
 </template>

+ 2 - 2
node_modules/uview-plus/components/u-number-keyboard/u-number-keyboard.vue

@@ -28,11 +28,11 @@
 				@touchstart.stop="backspaceClick"
 				@touchend="clearTimer"
 			>
-				<u-icon
+				<up-icon
 					name="backspace"
 					color="#303133"
 					size="28"
-				></u-icon>
+				></up-icon>
 			</view>
 		</view>
 	</view>

+ 2 - 1
node_modules/uview-plus/components/u-parse/node/node.vue

@@ -149,7 +149,8 @@ export default {
   },
   mounted () {
     this.$nextTick(() => {
-      for (this.root = this?.$parent; this.root?.$options.name !== 'mp-html'; this.root = this.root?.$parent);
+      // 修复可能导致死循环的问题
+      for (this.root = this?.$parent; this.root && this.root?.$options.name !== 'mp-html'; this.root = this.root?.$parent);
     })
     // #ifdef H5 || APP-PLUS
     if (this.opts[0]) {

+ 2 - 2
node_modules/uview-plus/components/u-popup/u-popup.vue

@@ -42,12 +42,12 @@
 					hover-class="u-popup__content__close--hover"
 					hover-stay-time="150"
 				>
-					<u-icon
+					<up-icon
 						name="close"
 						color="#909399"
 						size="18"
 						bold
-					></u-icon>
+					></up-icon>
 				</view>
 				<u-safe-bottom v-if="safeAreaInsetBottom"></u-safe-bottom>
 			</view>

+ 1 - 1
node_modules/uview-plus/components/u-radio/u-radio.vue

@@ -12,7 +12,7 @@
 		    :style="[iconWrapStyle]"
 		>
 			<slot name="icon" :elIconSize="elIconSize" :elIconColor="elIconColor">
-				<u-icon
+				<up-icon
 				    class="u-radio__icon-wrap__icon"
 				    name="checkbox-mark"
 				    :size="elIconSize"

+ 5 - 5
node_modules/uview-plus/components/u-rate/u-rate.vue

@@ -21,7 +21,7 @@
                     ref="u-rate__content__item__icon-wrap"
                     @tap.stop="clickHandler($event, index + 1)"
                 >
-                    <u-icon
+                    <up-icon
                         :name="
                             Math.floor(activeIndex) > index
                                 ? activeIcon
@@ -38,7 +38,7 @@
                             padding: `0 ${addUnit(gutter / 2)}`,
                         }"
                         :size="size"
-                    ></u-icon>
+                    ></up-icon>
                 </view>
                 <view
                     v-if="allowHalf"
@@ -49,7 +49,7 @@
                     }]"
                     ref="u-rate__content__item__icon-wrap"
                 >
-                    <u-icon
+                    <up-icon
                         :name="
                             Math.ceil(activeIndex) > index
                                 ? activeIcon
@@ -66,7 +66,7 @@
                             padding: `0 ${addUnit(gutter / 2)}`
                         }"
                         :size="size"
-                    ></u-icon>
+                    ></up-icon>
                 </view>
             </view>
         </view>
@@ -319,7 +319,7 @@ $u-rate-item-icon-wrap-half-left: 0 !default;
     }
 }
 
-.u-icon {
+.up-icon {
     /* #ifndef APP-NVUE */
     box-sizing: border-box;
     /* #endif */

+ 2 - 2
node_modules/uview-plus/components/u-read-more/u-read-more.vue

@@ -33,11 +33,11 @@
 					    margin="0 5px 0 0"
 					></up-text>
 					<view class="u-read-more__toggle__icon">
-						<u-icon
+						<up-icon
 						    :color="color"
 						    :size="fontSize + 2"
 						    :name="status === 'close' ? 'arrow-down' : 'arrow-up'"
-						></u-icon>
+						></up-icon>
 					</view>
 				</view>
 			</slot>

+ 6 - 6
node_modules/uview-plus/components/u-row-notice/u-row-notice.vue

@@ -8,11 +8,11 @@
 				class="u-notice__left-icon"
 				v-if="icon"
 			>
-				<u-icon
+				<up-icon
 					:name="icon"
 					:color="color"
 					size="19"
-				></u-icon>
+				></up-icon>
 			</view>
 		</slot>
 		<view
@@ -35,19 +35,19 @@
 			class="u-notice__right-icon"
 			v-if="['link', 'closable'].includes(mode)"
 		>
-			<u-icon
+			<up-icon
 				v-if="mode === 'link'"
 				name="arrow-right"
 				:size="17"
 				:color="color"
-			></u-icon>
-			<u-icon
+			></up-icon>
+			<up-icon
 				v-if="mode === 'closable'"
 				@click="close"
 				name="close"
 				:size="16"
 				:color="color"
-			></u-icon>
+			></up-icon>
 		</view>
 	</view>
 </template>

+ 4 - 4
node_modules/uview-plus/components/u-search/u-search.vue

@@ -21,12 +21,12 @@
 				</slot>
 			</template>
 			<view class="u-search__content__icon">
-				<u-icon
+				<up-icon
 					@tap="clickIcon"
 				    :size="searchIconSize"
 				    :name="searchIcon"
 				    :color="searchIconColor ? searchIconColor : color"
-				></u-icon>
+				></up-icon>
 			</view>
 			<input
 			    confirm-type="search"
@@ -58,12 +58,12 @@
 			    v-if="keyword && clearabled && focused"
 			    @click="clear"
 			>
-				<u-icon
+				<up-icon
 				    name="close"
 				    size="11"
 				    color="#ffffff"
 					customStyle="line-height: 12px"
-				></u-icon>
+				></up-icon>
 			</view>
             <slot name="inputRight"></slot>
 		</view>

+ 1 - 1
node_modules/uview-plus/components/u-select/u-select.vue

@@ -11,7 +11,7 @@
 					</text>
 				</slot>
 				<slot name="icon">
-					<u-icon name="arrow-down" :size="iconSize" :color="iconColor"></u-icon>
+					<up-icon name="arrow-down" :size="iconSize" :color="iconColor"></up-icon>
 				</slot>
 			</view>
 			<u-overlay

+ 0 - 14
node_modules/uview-plus/components/u-signature/u-signature.vue

@@ -263,20 +263,6 @@
 				}
 			},
 			
-			// 获取相对于canvas的坐标点
-			getCanvasPoint(e) {
-				const touch = e.touches[0]
-				const rect = uni.createSelectorQuery().in(this).select('.u-signature__canvas').boundingClientRect()
-				
-				return new Promise((resolve) => {
-					rect.boundingClientRect(data => {
-						const x = touch.x - data.left
-						const y = touch.y - data.top
-						resolve({ x, y })
-					}).exec()
-				})
-			},
-			
 			// 同步获取canvas坐标点(兼容处理)
 			getCanvasPoint(e) {
 				const touch = e.touches[0]

+ 4 - 4
node_modules/uview-plus/components/u-steps-item/u-steps-item.vue

@@ -12,10 +12,10 @@
 
 				</view>
 				<view class="u-steps-item__wrapper__icon" v-else-if="parentData.activeIcon || parentData.inactiveIcon">
-					<u-icon :name="index <= parentData.current ? parentData.activeIcon : parentData.inactiveIcon"
+					<up-icon :name="index <= parentData.current ? parentData.activeIcon : parentData.inactiveIcon"
 						:size="iconSize"
 						:color="index <= parentData.current ? parentData.activeColor : parentData.inactiveColor">
-					</u-icon>
+					</up-icon>
 				</view>
 				<view v-else :style="{
 						backgroundColor: statusClass === 'process' ? parentData.activeColor : 'transparent',
@@ -25,8 +25,8 @@
 						class="u-steps-item__wrapper__circle__text" :style="{
 							color: index == parentData.current ? '#ffffff' : parentData.inactiveColor
 						}">{{ index + 1}}</text>
-					<u-icon v-else :color="statusClass === 'error' ? 'error' : parentData.activeColor" size="12"
-						:name="statusClass === 'error' ? 'close' : 'checkmark'"></u-icon>
+					<up-icon v-else :color="statusClass === 'error' ? 'error' : parentData.activeColor" size="12"
+						:name="statusClass === 'error' ? 'close' : 'checkmark'"></up-icon>
 				</view>
 			</slot>
 		</view>

+ 2 - 2
node_modules/uview-plus/components/u-swipe-action-item/u-swipe-action-item.vue

@@ -14,12 +14,12 @@
 							borderRadius: item.style && item.style.borderRadius ? item.style.borderRadius : '0',
 							padding: item.style && item.style.borderRadius ? '0' : '0 15px',
 						}, item.style]">
-						<u-icon v-if="item.icon" :name="item.icon"
+						<up-icon v-if="item.icon" :name="item.icon"
 							:color="item.style && item.style.color ? item.style.color : '#ffffff'"
 							:size="item.iconSize ? addUnit(item.iconSize) : item.style && item.style.fontSize ? getPx(item.style.fontSize) * 1.2 : 17"
 							:customStyle="{
 								marginRight: item.text ? '2px' : 0
-							}"></u-icon>
+							}"></up-icon>
 						<text v-if="item.text" class="u-swipe-action-item__right__button__wrapper__text u-line-1"
 							:style="[{
 								color: item.style && item.style.color ? item.style.color : '#ffffff',

+ 2 - 2
node_modules/uview-plus/components/u-tabbar-item/u-tabbar-item.vue

@@ -5,12 +5,12 @@
 	    @tap="clickHandler"
 	>
 		<view class="u-tabbar-item__icon">
-			<u-icon
+			<up-icon
 			    v-if="icon"
 			    :name="icon"
 			    :color="isActive? parentData.activeColor : parentData.inactiveColor"
 			    :size="20"
-			></u-icon>
+			></up-icon>
 			<template v-else>
 				<slot
 				    v-if="isActive"

+ 4 - 4
node_modules/uview-plus/components/u-tag/u-tag.vue

@@ -24,12 +24,12 @@
 							:src="icon"
 							:style="[imgStyle]"
 						></image>
-						<u-icon
+						<up-icon
 							v-else
 							:color="elIconColor"
 							:name="icon"
 							:size="iconSize"
-						></u-icon>
+						></up-icon>
 					</view>
 				</slot>
 				<view class="u-tag__content">
@@ -63,11 +63,11 @@
 				@tap.stop="closeHandler"
 				:style="{backgroundColor: closeColor}"
 			>
-				<u-icon
+				<up-icon
 					name="close"
 					:size="closeSize"
 					color="#ffffff"
-				></u-icon>
+				></up-icon>
 			</view>
 		</view>
 	</u-transition>

+ 4 - 4
node_modules/uview-plus/components/u-text/u-text.vue

@@ -13,10 +13,10 @@
             >¥</text
         >
         <view class="u-text__prefix-icon" v-if="prefixIcon">
-            <u-icon
+            <up-icon
                 :name="prefixIcon"
                 :customStyle="addStyle(iconStyle)"
-            ></u-icon>
+            ></up-icon>
         </view>
         <u-link
             v-if="mode === 'link'" class="u-text__value"
@@ -59,10 +59,10 @@
             >{{ value }}</text
         >
         <view class="u-text__suffix-icon" v-if="suffixIcon">
-            <u-icon
+            <up-icon
                 :name="suffixIcon"
                 :customStyle="addStyle(iconStyle)"
-            ></u-icon>
+            ></up-icon>
         </view>
     </view>
 </template>

+ 3 - 3
node_modules/uview-plus/components/u-toast/u-toast.vue

@@ -17,13 +17,13 @@
 					inactiveColor="rgb(120, 120, 120)"
 					size="25"
 				></u-loading-icon>
-				<u-icon
+				<up-icon
 					v-else-if="tmpConfig.type !== 'defalut' && iconName"
 					:name="iconName"
 					size="17"
 					:color="tmpConfig.type"
 					:customStyle="iconStyle"
-				></u-icon>
+				></up-icon>
 				<u-gap
 					v-if="tmpConfig.type === 'loading' || tmpConfig.loading"
 					height="12"
@@ -203,7 +203,7 @@
 	$u-toast-loading-border-padding: 20px 20px !default;
 	$u-toast-content-text-color:#fff !default;
 	$u-toast-content-text-font-size:15px !default;
-	$u-toast-u-icon:10rpx !default;
+	$u-toast-up-icon:10rpx !default;
 	$u-toast-u-type-primary-color:$u-primary !default;
 	$u-toast-u-type-primary-background-color:#ecf5ff !default;
 	$u-toast-u-type-primary-border-color:rgb(215, 234, 254) !default;

+ 13 - 3
node_modules/uview-plus/components/u-tooltip/props.js

@@ -27,7 +27,12 @@ export const props = defineMixin({
             type: String,
             default: () => defProps.tooltip.bgColor
         },
-        // 弹出提示的方向,top-上方,bottom-下方
+        // 弹出提示框的背景色
+        popupBgColor: {
+            type: String,
+            default: () => defProps.tooltip.popupBgColor
+        },
+        // 弹出提示的方向,top-上方,bottom-下方,left-左方,right-右方
         direction: {
             type: String,
             default: () => defProps.tooltip.direction
@@ -56,6 +61,11 @@ export const props = defineMixin({
         showToast: {
             type: Boolean,
             default: () => defProps.tooltip.showToast
-        }
+        },
+        // 触发方式,可选值:longpress/click
+        triggerMode: {
+            type: String,
+            default: () => defProps.tooltip.triggerMode
+        },
     }
-})
+})

+ 3 - 1
node_modules/uview-plus/components/u-tooltip/tooltip.js

@@ -20,6 +20,8 @@ export default {
         showCopy: true,
         buttons: [],
         overlay: true,
-        showToast: true
+        showToast: true,
+        popupBgColor: '',
+        triggerMode: 'longpress'
     }
 }

+ 139 - 93
node_modules/uview-plus/components/u-tooltip/u-tooltip.vue

@@ -9,18 +9,20 @@
 			@click="overlayClickHandler"
 		></u-overlay>
 		<view class="u-tooltip__wrapper">
-			<text
-				class="u-tooltip__wrapper__text"
-				:id="textId"
-				:ref="textId"
-				:userSelect="false"
-				:selectable="false"
-				@longpress.stop="longpressHandler"
-				:style="{
-					color: color,
-					backgroundColor: bgColor && showTooltip && tooltipTop !== -10000 ? bgColor : 'transparent'
-				}"
-			>{{ text }}</text>
+			<view class="u-tooltip__trigger" :id="textId"
+				:ref="textId" @click.stop="clickHander"
+				@longpress.stop="longpressHandler">
+				<slot name="trigger"></slot>
+				<text v-if="!$slots['trigger']"
+					class="u-tooltip__wrapper__text"
+					:userSelect="false"
+					:selectable="false"
+					:style="{
+						color: color,
+						backgroundColor: bgColor && showTooltip && tooltipTop !== -10000 ? bgColor : 'transparent'
+					}"
+				>{{ text }}</text>
+			</view>
 			<u-transition
 				mode="fade"
 				:show="showTooltip"
@@ -44,44 +46,52 @@
 						:style="[indicatorStyle, {
 							width: addUnit(indicatorWidth),
 							height: addUnit(indicatorWidth),
+							backgroundColor: popupBgColor
 						}]"
 					>
 						<!-- 由于nvue不支持三角形绘制,这里就做一个四方形,再旋转45deg,得到露出的一个三角 -->
 					</view>
-					<view class="u-tooltip__wrapper__popup__list">
-						<view
-							v-if="showCopy"
-							class="u-tooltip__wrapper__popup__list__btn"
-							hover-class="u-tooltip__wrapper__popup__list__btn--hover"
-							@tap="setClipboardData"
-						>
-							<text
-								class="u-tooltip__wrapper__popup__list__btn__text"
-							>复制</text>
-						</view>
-						<u-line
-							direction="column"
-							color="#8d8e90"
-							v-if="showCopy && buttons.length > 0"
-							length="18"
-						></u-line>
-						<block v-for="(item , index) in buttons" :key="index">
+					<view class="u-tooltip__wrapper__popup__list" :style="{
+						backgroundColor: popupBgColor,
+						color: color
+					}">
+						<slot name="content"></slot>
+						<template v-if="!$slots['content']">
 							<view
+								v-if="showCopy"
 								class="u-tooltip__wrapper__popup__list__btn"
 								hover-class="u-tooltip__wrapper__popup__list__btn--hover"
+								:style="{backgroundColor: popupBgColor}"
+								@tap="setClipboardData"
 							>
 								<text
 									class="u-tooltip__wrapper__popup__list__btn__text"
-									@tap="btnClickHandler(index)"
-								>{{ item }}</text>
+								>复制</text>
 							</view>
 							<u-line
 								direction="column"
 								color="#8d8e90"
-								v-if="index < buttons.length - 1"
+								v-if="showCopy && buttons.length > 0"
 								length="18"
 							></u-line>
-						</block>
+							<block v-for="(item , index) in buttons" :key="index">
+								<view
+									class="u-tooltip__wrapper__popup__list__btn"
+									hover-class="u-tooltip__wrapper__popup__list__btn--hover"
+								>
+									<text
+										class="u-tooltip__wrapper__popup__list__btn__text"
+										@tap="btnClickHandler(index)"
+									>{{ item }}</text>
+								</view>
+								<u-line
+									direction="column"
+									color="#8d8e90"
+									v-if="index < buttons.length - 1"
+									length="18"
+								></u-line>
+							</block>
+						</template>
 					</view>
 				</view>
 			</u-transition>
@@ -101,17 +111,18 @@
 	 * Tooltip 
 	 * @description 
 	 * @tutorial https://ijry.github.io/uview-plus/components/tooltip.html
-	 * @property {String | Number}	text		需要显示的提示文字
-	 * @property {String | Number}	copyText	点击复制按钮时,复制的文本,为空则使用text值
-	 * @property {String | Number}	size		文本大小(默认 14 )
-	 * @property {String}			color		字体颜色(默认 '#606266' )
-	 * @property {String}			bgColor		弹出提示框时,文本的背景色(默认 'transparent' )
-	 * @property {String}			direction	弹出提示的方向,top-上方,bottom-下方(默认 'top' )
-	 * @property {String | Number}	zIndex		弹出提示的z-index,nvue无效(默认 10071 )
-	 * @property {Boolean}			showCopy	是否显示复制按钮(默认 true )
-	 * @property {Array}			buttons		扩展的按钮组
-	 * @property {Boolean}			overlay		是否显示透明遮罩以防止触摸穿透(默认 true )
-	 * @property {Object}			customStyle	定义需要用到的外部样式
+	 * @property {String | Number}	text		 需要显示的提示文字
+	 * @property {String | Number}	copyText	 点击复制按钮时,复制的文本,为空则使用text值
+	 * @property {String | Number}	size		 文本大小(默认 14 )
+	 * @property {String}			color		 字体颜色(默认 '#606266' )
+	 * @property {String}			bgColor		 弹出提示框时,文本的背景色(默认 'transparent' )
+	 * @property {String}			popupBgColor 弹出提示框的背景色
+	 * @property {String}			direction	 弹出提示的方向,top-上方,bottom-下方(默认 'top' )
+	 * @property {String | Number}	zIndex		 弹出提示的z-index,nvue无效(默认 10071 )
+	 * @property {Boolean}			showCopy	 是否显示复制按钮(默认 true )
+	 * @property {Array}			buttons		 扩展的按钮组
+	 * @property {Boolean}			overlay		 是否显示透明遮罩以防止触摸穿透(默认 true )
+	 * @property {Object}			customStyle	 定义需要用到的外部样式
 	 * 
 	 * @event {Function} 
 	 * @example 
@@ -144,11 +155,13 @@
 				screenGap: 12,
 				// 三角形指示器的宽高,由于对元素进行了角度旋转,精确计算指示器位置时,需要用到其尺寸信息
 				indicatorWidth: 14,
+				tooltipStyle: {}
 			}
 		},
 		watch: {
-			propsChange() {
-				this.getElRect()
+			async propsChange() {
+				await this.getElRect()
+				this.getTooltipStyle();
 			}
 		},
 		computed: {
@@ -157,37 +170,7 @@
 			propsChange() {
 				return [this.text, this.buttons]
 			},
-			// 计算气泡和指示器的位置信息
-			tooltipStyle() {
-				const style = {
-						transform: `translateY(${this.direction === 'top' ? '-100%' : '100%'})`,
-					},
-					sysInfo = getWindowInfo()
-				if (this.tooltipInfo.width / 2 > this.textInfo.left + this.textInfo.width / 2 - this.screenGap) {
-					this.indicatorStyle = {}
-					style.left = `-${addUnit(this.textInfo.left - this.screenGap)}`
-					this.indicatorStyle.left = addUnit(this.textInfo.width / 2 - getPx(style.left) - this.indicatorWidth /
-						2)
-				} else if (this.tooltipInfo.width / 2 > sysInfo.windowWidth - this.textInfo.right + this.textInfo.width / 2 -
-					this.screenGap) {
-					this.indicatorStyle = {}
-					style.right = `-${addUnit(sysInfo.windowWidth - this.textInfo.right - this.screenGap)}`
-					this.indicatorStyle.right = addUnit(this.textInfo.width / 2 - getPx(style.right) - this
-						.indicatorWidth / 2)
-				} else {
-					const left = Math.abs(this.textInfo.width / 2 - this.tooltipInfo.width / 2)
-					style.left = this.textInfo.width > this.tooltipInfo.width ? addUnit(left) : -addUnit(left)
-					this.indicatorStyle = {}
-				}
-				if (this.direction === 'top') {
-					style.marginTop = '-10px'
-					this.indicatorStyle.bottom = '-4px'
-				} else {
-					style.marginBottom = '-10px'
-					this.indicatorStyle.top = '-4px'
-				}
-				return style
-			}
+			
 		},
 		mounted() {
 			this.init()
@@ -196,13 +179,76 @@
 		methods: {
 			addStyle,
 			addUnit,
-			init() {
-				this.getElRect()
+			async init() {
+				await this.getElRect()
+				this.getTooltipStyle();
+			},
+			// 计算气泡和指示器的位置信息
+			getTooltipStyle() {
+				const style = {},
+					sysInfo = getWindowInfo()
+				if (this.direction === 'left') {
+					// 右侧显示逻辑
+					style.transform = ``
+					// 垂直居中对齐
+					style.top = '-' + addUnit((this.tooltipInfo.height - this.indicatorWidth) / 2, 'px')
+					style.right = addUnit(this.textInfo.width + this.indicatorWidth, 'px')
+					this.indicatorStyle = {}
+					this.indicatorStyle.right = '-4px'
+					this.indicatorStyle.top = addUnit((this.tooltipInfo.height - this.indicatorWidth) / 2, 'px')
+				} else if (this.direction === 'right') {
+					// 右侧显示逻辑
+					style.transform = ``
+					// 垂直居中对齐
+					style.top = addUnit((this.textInfo.height - this.tooltipInfo.height) / 2, 'px')
+					style.left = addUnit(this.textInfo.width + this.indicatorWidth, 'px')
+					this.indicatorStyle = {}
+					this.indicatorStyle.left = '-4px'
+					this.indicatorStyle.top = addUnit((this.textInfo.height - this.indicatorWidth) / 2, 'px')
+				} else if (this.direction === 'top' || this.direction === 'bottom') { 
+					style.transform = `translateY(${this.direction === 'top' ? '-100%' : '100%'})`
+					if (this.tooltipInfo.width / 2 > this.textInfo.left + this.textInfo.width / 2 - this.screenGap) {
+						this.indicatorStyle = {}
+						style.left = `-${addUnit(this.textInfo.left - this.screenGap)}`
+						this.indicatorStyle.left = addUnit(this.textInfo.width / 2 - getPx(style.left) - this.indicatorWidth /
+							2, 'px')
+					} else if (this.tooltipInfo.width / 2 > sysInfo.windowWidth - this.textInfo.right + this.textInfo.width / 2 -
+						this.screenGap) {
+						this.indicatorStyle = {}
+						style.right = `-${addUnit(sysInfo.windowWidth - this.textInfo.right - this.screenGap)}`
+						this.indicatorStyle.right = addUnit(this.textInfo.width / 2 - getPx(style.right) - this
+							.indicatorWidth / 2)
+					} else {
+						const left = Math.abs(this.textInfo.width / 2 - this.tooltipInfo.width / 2)
+						style.left = this.textInfo.width > this.tooltipInfo.width ? addUnit(left) : -addUnit(left)
+						this.indicatorStyle = {}
+					}
+					if (this.direction === 'top') {
+						style.marginTop = '-10px'
+						this.indicatorStyle.bottom = '-4px'
+					} else {
+						style.marginBottom = '-10px'
+						this.indicatorStyle.top = '-4px'
+					}
+				}
+				this.tooltipStyle = style
+				return style
+			},
+			// 点击触发事件
+			async clickHander() {
+				// this.getTooltipStyle();
+				if (this.triggerMode == 'click') {
+					this.tooltipTop = 0
+					this.showTooltip = true
+				}
 			},
 			// 长按触发事件
 			async longpressHandler() {
-				this.tooltipTop = 0
-				this.showTooltip = true
+				// this.getTooltipStyle();
+				if (this.triggerMode == 'longpress') {
+					this.tooltipTop = 0
+					this.showTooltip = true
+				}
 			},
 			// 点击透明遮罩
 			overlayClickHandler() {
@@ -217,7 +263,7 @@
 			// 查询内容高度
 			queryRect(ref) {
 				// #ifndef APP-NVUE
-				// $uGetRect为uView自带的节点查询简化方法,详见文档介绍:https://ijry.github.io/uview-plus/js/getRect.html
+				// $uGetRect为uview-plus自带的节点查询简化方法,详见文档介绍:https://ijry.github.io/uview-plus/js/getRect.html
 				// 组件内部一般用this.$uGetRect,对外的为uni.$u.getRect,二者功能一致,名称不同
 				return new Promise(resolve => {
 					this.$uGetRect(`#${ref}`).then(size => {
@@ -238,17 +284,16 @@
 			},
 			// 元素尺寸
 			getElRect() {
-				// 调用之前,先将指示器调整到屏幕外,方便获取尺寸
-				this.showTooltip = true
-				this.tooltipTop = -10000
-				sleep(500).then(() => {
-					this.queryRect(this.tooltipId).then(size => {
-						this.tooltipInfo = size
+				return new Promise(async(resolve) => {
+					// 调用之前,先将指示器调整到屏幕外,方便获取尺寸
+					this.showTooltip = true
+					this.tooltipTop = -10000
+					sleep(500).then(async () => {
+						this.tooltipInfo = await this.queryRect(this.tooltipId)
 						// 获取气泡尺寸之后,将其隐藏,为了让下次切换气泡显示与隐藏时,有淡入淡出的效果
 						this.showTooltip = false
-					})
-					this.queryRect(this.textId).then(size => {
-						this.textInfo = size
+						this.textInfo = await this.queryRect(this.textId)
+						resolve()
 					})
 				})
 			},
@@ -298,6 +343,7 @@
 
 				&__list {
 					background-color: #060607;
+					color: #FFFFFF;
 					position: relative;
 					flex: 1;
 					border-radius: 5px;
@@ -338,4 +384,4 @@
 			}
 		}
 	}
-</style>
+</style>

+ 11 - 11
node_modules/uview-plus/components/u-upload/u-upload.vue

@@ -35,12 +35,12 @@
 								height: addUnit(height)
 							}]"
 						/>
-						<u-icon
+						<up-icon
 							v-else
 						    color="#80CBF9"
 						    size="26"
 						    :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'file-text'"
-						></u-icon>
+						></up-icon>
 						<view v-if="item.status === 'success'"
 							class="u-upload__wrap__play"
 							@tap="onClickPreview(item, index)">
@@ -59,11 +59,11 @@
 							height: addUnit(height)
 						}]"
 					>
-						<u-icon
+						<up-icon
 						    color="#80CBF9"
 						    size="26"
 						    :name="item.isVideo || (item.type && item.type === 'video') ? 'movie' : 'folder'"
-						></u-icon>
+						></up-icon>
 						<text class="u-upload__wrap__preview__other__text">
 							{{item.isVideo || (item.type && item.type === 'video') ? item.name || '视频' : item.name || '文件'}}
 						</text>
@@ -73,7 +73,7 @@
 					    v-if="item.status === 'uploading' || item.status === 'failed'"
 					>
 						<view class="u-upload__status__icon">
-							<u-icon
+							<up-icon
 							    v-if="item.status === 'failed'"
 							    name="close-circle"
 							    color="#ffffff"
@@ -98,11 +98,11 @@
 					    @tap.stop="deleteItem(index)"
 					>
 						<view class="u-upload__deletable__icon">
-							<u-icon
+							<up-icon
 							    name="close"
 							    color="#ffffff"
 							    size="10"
-							></u-icon>
+							></up-icon>
 						</view>
 					</view>
 					<slot name="success">
@@ -118,11 +118,11 @@
 							<!-- #endif -->
 							<!-- #ifndef APP-NVUE -->
 							<view class="u-upload__success__icon">
-								<u-icon
+								<up-icon
 									name="checkmark"
 									color="#ffffff"
 									size="12"
-								></u-icon>
+								></up-icon>
 							</view>
 							<!-- #endif -->
 						</view>
@@ -156,11 +156,11 @@
 						height: addUnit(height)
 					}]"
 				>
-					<u-icon
+					<up-icon
 					    :name="uploadIcon"
 					    size="26"
 					    :color="uploadIconColor"
-					></u-icon>
+					></up-icon>
 					<text
 					    v-if="uploadText"
 					    class="u-upload__button__text"

+ 12 - 12
node_modules/uview-plus/package.json

@@ -1,27 +1,27 @@
 {
-  "_from": "uview-plus@^3.4.93",
-  "_id": "uview-plus@3.4.93",
+  "_from": "uview-plus@3.4.106",
+  "_id": "uview-plus@3.4.106",
   "_inBundle": false,
-  "_integrity": "sha512-jAyvMDPlF2LNo0rILVMAUJNT4R8jskC8sAcTI4zkO9wYbzhRfq/5niX7c9yYaDXylhvDBK4y2FcBeJKuOIVilg==",
+  "_integrity": "sha512-YGOAAxAd82XHHcmImnS4IPv/MIgrqmmd/7UylbpP6+QohxBVPSeph2KqaVB3GG2WM2CY84xhqvdP72NcFe90iA==",
   "_location": "/uview-plus",
   "_phantomChildren": {},
   "_requested": {
-    "type": "range",
+    "type": "version",
     "registry": true,
-    "raw": "uview-plus@^3.4.93",
+    "raw": "uview-plus@3.4.106",
     "name": "uview-plus",
     "escapedName": "uview-plus",
-    "rawSpec": "^3.4.93",
+    "rawSpec": "3.4.106",
     "saveSpec": null,
-    "fetchSpec": "^3.4.93"
+    "fetchSpec": "3.4.106"
   },
   "_requiredBy": [
     "#USER",
     "/"
   ],
-  "_resolved": "https://registry.npmjs.org/uview-plus/-/uview-plus-3.4.93.tgz",
-  "_shasum": "0275621bf06bba17e1e4dd607972ec5ef16b1210",
-  "_spec": "uview-plus@^3.4.93",
+  "_resolved": "https://registry.npmjs.org/uview-plus/-/uview-plus-3.4.106.tgz",
+  "_shasum": "664aa7de39d49e1ae2ced4041e52d3bea3332342",
+  "_spec": "uview-plus@3.4.106",
   "_where": "C:\\Users\\Administrator\\Desktop\\项目\\直播\\liveH5-v3",
   "bugs": {
     "url": "https://github.com/ijry/uview-plus/issues"
@@ -55,7 +55,7 @@
     "dayjs": "^1.11.3"
   },
   "deprecated": false,
-  "description": "零云®uview-plus已兼容vue3,100+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表、签名等。",
+  "description": "零云®uview-plus已兼容vue3,100+全面的组件和便捷的工具会让您信手拈来。近期新增拖动排序、条码、图片裁剪、下拉刷新、虚拟列表、签名、Markdown等。",
   "displayName": "零云®uview-plus3.0重磅发布,全面的Vue3鸿蒙跨端移动组件库,组件丰富维护更新稳定。",
   "engines": {
     "HBuilderX": "^3.1.0",
@@ -140,5 +140,5 @@
       }
     }
   },
-  "version": "3.4.93"
+  "version": "3.4.106"
 }

+ 8 - 3
package-lock.json

@@ -40,6 +40,11 @@
       "resolved": "https://registry.npmjs.org/hls.js/-/hls.js-1.6.7.tgz",
       "integrity": "sha512-QW2fnwDGKGc9DwQUGLbmMOz8G48UZK7PVNJPcOUql1b8jubKx4/eMHNP5mGqr6tYlJNDG1g10Lx2U/qPzL6zwQ=="
     },
+    "mescroll-uni": {
+      "version": "1.3.7",
+      "resolved": "https://registry.npmjs.org/mescroll-uni/-/mescroll-uni-1.3.7.tgz",
+      "integrity": "sha512-1pQMtGA+iVRKhfJZZNXdBx05NnthIk6zm3hRbumswSA54eaKOMgpUDb9AQ2+rRdXmS6kLkEYSbW/fkb7/IyoAg=="
+    },
     "select": {
       "version": "1.1.2",
       "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
@@ -51,9 +56,9 @@
       "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q=="
     },
     "uview-plus": {
-      "version": "3.4.93",
-      "resolved": "https://registry.npmjs.org/uview-plus/-/uview-plus-3.4.93.tgz",
-      "integrity": "sha512-jAyvMDPlF2LNo0rILVMAUJNT4R8jskC8sAcTI4zkO9wYbzhRfq/5niX7c9yYaDXylhvDBK4y2FcBeJKuOIVilg==",
+      "version": "3.4.106",
+      "resolved": "https://registry.npmjs.org/uview-plus/-/uview-plus-3.4.106.tgz",
+      "integrity": "sha512-YGOAAxAd82XHHcmImnS4IPv/MIgrqmmd/7UylbpP6+QohxBVPSeph2KqaVB3GG2WM2CY84xhqvdP72NcFe90iA==",
       "requires": {
         "clipboard": "^2.0.11",
         "dayjs": "^1.11.3"

+ 2 - 1
package.json

@@ -3,6 +3,7 @@
     "crypto-js": "^4.2.0",
     "dayjs": "^1.11.13",
     "hls.js": "^1.6.7",
-    "uview-plus": "^3.4.93"
+    "mescroll-uni": "^1.3.7",
+    "uview-plus": "^3.4.106"
   }
 }

+ 34 - 8
pages.json

@@ -222,6 +222,32 @@
 						"enablePullDownRefresh": false
 					}
 
+				},
+				{
+					"path": "storeOrderRefundList",
+					"style": {
+						"navigationBarTitleText": "我的售后",
+						"enablePullDownRefresh": false,
+						"navigationBarBackgroundColor": "#ffffff",
+						"navigationBarTextStyle": "black"
+					}
+				},
+				{
+					"path": "storeOrderRefundDetails",
+					"style": {
+						"navigationBarTitleText": "售后详情",
+						"enablePullDownRefresh": false,
+						"navigationBarBackgroundColor": "#FF5C03",
+						"navigationBarTextStyle": "white"
+					}
+				},
+				{
+					"path": "storeOrderRefundSubmit",
+					"style": {
+						"navigationBarTitleText": "提交售后",
+						"navigationBarTextStyle": "black",
+						"navigationBarBackgroundColor": "#ffffff"
+					}
 				}, {
 					"path": "success",
 					"style": {
@@ -261,14 +287,14 @@
 					"navigationBarTextStyle": "black"
 				}
 			}, {
-			"path": "integralGoodsList",
-			"style": {
-				"navigationBarTitleText": "芳华币商城",
-				"enablePullDownRefresh": false,
-				"navigationBarBackgroundColor": "#ffffff",
-				"navigationBarTextStyle": "black"
-			}
-		},{
+				"path": "integralGoodsList",
+				"style": {
+					"navigationBarTitleText": "芳华币商城",
+					"enablePullDownRefresh": false,
+					"navigationBarBackgroundColor": "#ffffff",
+					"navigationBarTextStyle": "black"
+				}
+			}, {
 				"path": "addAddress",
 				"style": {
 					"navigationBarTitleText": "新建收货地址",

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 431 - 287
pages/home/living.vue


+ 13 - 8
pages_shop/goods.vue

@@ -51,10 +51,10 @@
 			</view>
 		</view>
 		<view class="guige">
-			<view class="guige-gg">
+			<!-- <view class="guige-gg">
 				<text class="gg-text">规格</text>
 				<text class="gg-text2">云南白药气雾剂 85g+30g *1 、说明书*1</text>
-			</view>
+			</view> -->
 			<view class="safe-box">
 				<text class="text">服务</text>
 				<view class="box">
@@ -131,7 +131,7 @@
 		</view>
 
 		<!-- 图文详情 -->
-		<view class="det-box">
+		<!-- <view class="det-box">
 			<view class="title">药品详情</view>
 			<view class="det-title">
 				<view class="tt">说明书</view>
@@ -157,7 +157,7 @@
 			<view class="inner">
 				<view v-html="product.description" style="font-size:0"></view>
 			</view>
-		</view>
+		</view> -->
 		<!-- 底部按钮 -->
 		<view class="btn-foot">
 			<view class="menu-box">
@@ -165,11 +165,11 @@
 					<image src="/static/images/googs1.png" mode=""></image>
 					<text class="label">店铺</text>
 				</view>
-				<!-- 	<view class="item"  @click="navgetTo('pages/home/living')" style="position: relative;">
+					<view class="item"  @click="navgetTo('./storeOrderRefundList?liveId='+liveId)" style="position: relative;">
 					<image src="/static/images/googs2.png" mode=""></image>
-					<text class="label">直播间</text>
+					<text class="label">售后</text>
 					<button class="contact-btn" open-type="contact"></button>
-				</view> -->
+				</view>
 				<!-- <view class="item" style="position: relative;">
 					<image src="/static/images/googs2.png" mode=""></image>
 					<text class="label">咨询</text>
@@ -379,7 +379,7 @@
 		mounted() {
 			this.getliveGoods()
 			var userInfo = uni.getStorageSync("userInfo")
-			console.log("之前的数据在这里", userInfo)
+			// console.log("之前的数据在这里", userInfo)
 			this.getSearchStore() //查询店铺
 		},
 		onShow() {
@@ -389,6 +389,7 @@
 		methods: {
 			//查询店铺
 			getSearchStore() {
+				if(!this.storeId) return;
 				let key=""
 				store(this.storeId,key).then(res => {
 						if (res.code == 200) {
@@ -560,8 +561,12 @@
 			openEditMoney() {
 				this.editShow = true;
 			},
+			cancelEditMoney(){
+				this.editShow = false;
+			},
 			//商品详情
 			getliveGoods() {
+				if (!this.productId) return;
 				liveGoodsDetail(this.productId).then(res => {
 						if (res.code == 200) {
 							console.log("小黄车 商品详情>>>>", res)

+ 112 - 31
pages_shop/store.vue

@@ -5,13 +5,13 @@
 			<view :style="{height: statusBarHeight + 'px',width: '100%'}"></view>
 			<view class="uni-nav-barbox">
 				<view class="uni-nav-back">
-					<u-icon name="arrow-left" color="#1a1a1a" size="20" @click="rightClick"></u-icon>
+					<u-icon name="arrow-left" color="#ffffff" size="20" @click="rightClick"></u-icon>
 				</view>
 				<view class="uni-nav-title">
-					<view class="inputbox" style="background: rgba(255, 255, 255, 0.4)" @click="toSearch">
+					<view class="inputbox" style="background: rgba(255, 255, 255, 0.4);width:70%;" @click="toSearch">
 						<image class="icon-search" src="/static/images/search_white.png"></image>
-						<input placeholder="搜索本店" v-model="inputInfo" @input="handleSearchInput" />
-						<!-- <view>搜索本店</view> -->
+						<input placeholder="搜索本店" placeholder-style="color: #ffffff;" v-model="inputInfo"
+							@input="handleSearchInput" />
 					</view>
 				</view>
 			</view>
@@ -22,7 +22,7 @@
 			<view class="store-head" v-show="storeInfo.storeName">
 				<view class="store-head-top">
 					<view class="store-head-logo">
-						<u-image shape="square" :src="storeInfo.logoUrl || logoUrl" width="100rpx" height="100rpx"
+						<u-image shape="square" :src="storeInfo?.logoUrl" width="100rpx" height="100rpx"
 							radius="6"></u-image>
 					</view>
 					<view class="store-head-name">{{storeInfo.storeName || ''}}</view>
@@ -78,12 +78,18 @@
 										<text class="del">¥19.80</text>
 									</view>
 								</view>
+
+								<!-- 加载状态提示 -->
+								<view class="load-more" v-if="page > 1">
+									<text v-if="isLoading">加载中...</text>
+									<text v-else-if="!hasMore">没有更多数据了</text>
+								</view>
 							</view>
 						</view>
 					</view>
 				</view>
 				<!-- 商家信息 -->
-				<view class="storebox-info" :style="{height: divHeight}" v-show="current == 1">
+				<!-- <view class="storebox-info" :style="{height: divHeight}" v-show="current == 1">
 					<view class="storebox-map">
 						<u-icon name="map" color="#ccc" size="18"></u-icon>
 						<view style="margin-left: 10rpx;">{{storeInfo.address || "--"}}</view>
@@ -106,7 +112,7 @@
 								@click="previewImage(i)"></u-image>
 						</view>
 					</view>
-				</view>
+				</view> -->
 			</view>
 		</view>
 	</view>
@@ -114,8 +120,8 @@
 
 <script>
 	import {
-		store, //查询店铺
-		liveStore //店铺展示,
+		queryStore, //查询店铺
+		store // 小黄车查询店铺,
 	} from '@/api/live'
 	// import {getProductCate} from '@/api/product';
 	// import {getAdv} from '@/api/adv';
@@ -124,8 +130,13 @@
 	export default {
 		data() {
 			return {
+				page: 1, // 当前页码,初始为1
+				pageSize: 6, // 每页条数(和之前保持一致)
+				isLoading: false, // 是否正在加载数据,防止重复请求
+				hasMore: true, // 是否还有更多数据
+
+
 				inputInfo: '',
-				inputno: '',
 				searchTimer: null,
 				tabList: [{
 					name: '推荐',
@@ -168,7 +179,7 @@
 			console.log("接收到的options:", options);
 			if (options.liveId) {
 				this.liveId = options.liveId;
-				this.getliveStore() // 获取小黄车 店铺展示
+				// this.getliveStore() // 获取小黄车 店铺展示
 				console.log("接收到的liveId:", this.liveId);
 			}
 
@@ -183,7 +194,8 @@
 			}
 		},
 		mounted() {
-			this.getSearchStore() //查询店铺
+			this.queryStore() //查询店铺
+			this.queryCollect()
 		},
 		onShow() {
 			this.divHeight = `calc(100vh - 44px - 88rpx - ${this.statusBarHeight}px)`
@@ -196,30 +208,94 @@
 				this.opacity = 1
 				this.opacityTxt = 1
 			}
-
+			// 监听滚动到底部(核心逻辑)
+			if (this.isLoading || !this.hasMore) return; // 过滤无效状态
+
+			// 获取页面可滚动高度、当前滚动位置、窗口高度
+			const {
+				scrollHeight,
+				scrollTop,
+				windowHeight
+			} = uni.getSystemInfoSync();
+			// 计算距离底部的距离(单位:px,1rpx ≈ 0.5px,这里按300rpx判断)
+			const distanceToBottom = scrollHeight - scrollTop - windowHeight;
+
+			// 当距离底部小于300rpx时,加载下一页
+			if (distanceToBottom < 300) {
+				this.page++; // 页码+1
+				this.queryStore(); // 触发下一页请求
+			}
 		},
 		methods: {
 			handleSearchInput() {
-				// 使用防抖优化性能,避免频繁请求
+				// 搜索时重置分页(关键词变了,重新从第1页加载)
 				clearTimeout(this.searchTimer);
 				this.searchTimer = setTimeout(() => {
-					this.getliveStore();
-				}, 500); // 500毫秒延迟
+					this.page = 1; // 重置页码为1
+					this.hasMore = true; // 重置是否有更多数据
+					this.queryStore(); // 重新请求第一页
+				}, 500);
 			},
 			getPureDecimal(num, precision = 6) {
 				const decimalPart = Math.abs(num).toFixed(precision).split('.')[1];
 				return decimalPart?.replace(/0+$/, '') || ''; // 移除末尾多余的0
 			},
-			//店铺展示
-			getliveStore() {
-				let data = {
-					pageSize: 10,
-					page: 1
-				}
-				liveStore(this.liveId, this.inputInfo, data).then(res => {
+			// // 店铺商品展示(支持分页)
+			// getliveStore() {
+			// 	// 如果正在加载或没有更多数据,直接返回
+			// 	if (this.isLoading || !this.hasMore) return;
+
+			// 	this.isLoading = true; // 标记为加载中
+			// 	const data = {
+			// 		pageSize: this.pageSize,
+			// 		page: this.page // 使用当前页码
+			// 	};
+
+			// 	liveStore(this.liveId, this.inputInfo, data)
+			// 		.then(res => {
+			// 			this.isLoading = false; // 结束加载状态
+			// 			if (res.code === 200) {
+			// 				const newProducts = res.data || [];
+			// 				// 如果是第一页,直接替换数据;否则合并数据
+			// 				if (this.page === 1) {
+			// 					this.products = newProducts;
+			// 				} else {
+			// 					this.products = [...this.products, ...newProducts];
+			// 				}
+			// 				// 判断是否还有更多数据(如果返回的数量小于每页条数,说明没有更多了)
+			// 				this.hasMore = newProducts.length >= this.pageSize;
+			// 			} else {
+			// 				uni.showToast({
+			// 					title: res.msg,
+			// 					icon: 'none'
+			// 				});
+			// 			}
+			// 		})
+			// 		.catch(rej => {
+			// 			this.isLoading = false; // 异常时也要结束加载状态
+			// 			uni.showToast({
+			// 				title: '加载失败',
+			// 				icon: 'none'
+			// 			});
+			// 		});
+			// },
+			//查询店铺
+			queryStore() {
+				if (!this.storeId) return;
+				if (this.isLoading || !this.hasMore) return;
+				this.isLoading = true; // 标记为加载中
+				queryStore(this.storeId, this.pageSize, this.page,this.inputInfo).then(res => {
+						this.isLoading = false; // 结束加载状态
 						if (res.code == 200) {
-							console.log("小黄车 店铺展示>>>>", res)
-							this.products = res.data
+							const newProducts = res.rows || [];
+							// 如果是第一页,直接替换数据;否则合并数据
+							if (this.page === 1) {
+								this.products = newProducts;
+							} else {
+								this.products = [...this.products, ...newProducts];
+							}
+							// 判断是否还有更多数据(如果返回的数量小于每页条数,说明没有更多了)
+							this.hasMore = newProducts.length >= this.pageSize;
 						} else {
 							uni.showToast({
 								title: res.msg,
@@ -230,12 +306,12 @@
 					rej => {}
 				);
 			},
-			//查询店铺
-			getSearchStore() {
+			// 小黄车查询店铺
+			queryCollect() {
 				if (!this.storeId) return;
-				store(this.storeId, this.inputno).then(res => {
+				store(this.storeId, '').then(res => {
 						if (res.code == 200) {
-							console.log("查询店铺>>>>", res)
+							console.log("查询店铺>>", res)
 							this.storeInfo = res.data
 						} else {
 							uni.showToast({
@@ -248,8 +324,6 @@
 				);
 			},
 
-
-
 			rightClick() {
 				// 获取页面栈
 				const pages = getCurrentPages();
@@ -382,6 +456,13 @@
 		margin: 0 auto;
 	}
 
+	.load-more {
+		text-align: center;
+		padding: 30rpx 0;
+		color: #999;
+		font-size: 26rpx;
+	}
+
 	@mixin u-flex($flexD, $alignI, $justifyC) {
 		display: flex;
 		flex-direction: $flexD;

+ 146 - 0
pages_shop/storeOrderRefundAddDelivery.vue

@@ -0,0 +1,146 @@
+<template>
+  <view class="content">
+  	<view class="inner">
+  		<view class="form-box">
+  			<view class="form-item">
+  				<text class="label">快递公司</text>
+  				<input type="text" v-model="form.deliveryName" placeholder="请输入物流公司" placeholder-class="form-input" />
+  			</view>
+  			<view class="form-item">
+  			 	<text class="label">快递单号</text>
+  			 	<input type="text" v-model="form.deliverySn"   placeholder="请输入物流单号" placeholder-class="form-input" />
+  			</view>
+  		</view>
+  	</view>
+  	<view class="btn-box">
+  		<view class="sub-btn" @click="submit()">提交</view>
+  	</view>
+  </view>
+</template>
+
+<script>
+// import {addDelivery} from '@/api/storeAfterSales.js'
+	
+export default {
+  data() {
+    return {
+		form:{
+			salesId:null,
+			deliverySn:null,
+			deliveryName:null,
+		}
+    }
+  },
+  onLoad(option) {
+	  this.form.id=option.id
+  },
+  methods: {
+	submit(){
+		if(this.form.deliveryName==null){
+			uni.showToast({
+				icon:'none',
+				title: '请输入快递公司'
+			});
+			return;
+		}
+		if(this.form.deliverySn==null){
+			uni.showToast({
+				icon:'none',
+				title: '请输入快递单号'
+			});
+			return;
+		}
+	 	addDelivery(this.form).then(
+	 		res => {
+	 			if(res.code==200){
+	 				 uni.showToast({
+	 				 	icon:'success',
+	 				 	title: "提交成功",
+	 				 });
+					 uni.navigateBack({
+					 	delta: 1
+					 })
+	 			}else{
+	 				uni.showToast({
+	 					icon:'none',
+	 					title: res.msg,
+	 				});
+	 			}
+	 		},
+	 		rej => {}
+	 	);
+	 },
+  },
+  
+}
+</script>
+
+<style lang="scss">
+	page{
+		height: 100%;
+	}
+	.content{
+		display: flex;
+		flex-direction: column;
+		justify-content: space-between;
+		
+		.inner{
+			padding: 20upx;
+			.form-box{
+				padding: 0 30upx;
+				background: #FFFFFF;
+				border-radius: 16upx;
+				.form-item{
+					padding: 30upx 0;
+					display: flex;
+					align-items: flex-start;
+					border-bottom: 1px solid #F1F1F1;
+					&:last-child{
+						border-bottom: none;
+					}
+					.label{
+						width: 150upx;
+						text-align: left;
+						font-size: 30upx;
+						line-height: 44upx;
+						font-family: PingFang SC;
+						font-weight: 500;
+						color: #222222;
+						flex-shrink: 0;
+					}
+					input{
+						text-align: left;
+					}
+					.form-input{
+						font-size: 30upx;
+						font-family: PingFang SC;
+						font-weight: 500;
+						color: #999999;
+						text-align: left;
+					}
+					 
+				}
+			}
+			 
+		}
+		.btn-box{
+			height: 120upx;
+			padding: 0 30upx;
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			.sub-btn{
+				width: 100%;
+				height: 88upx;
+				line-height: 88upx;
+				text-align: center;
+				font-size: 30upx;
+				font-family: PingFang SC;
+				font-weight: bold;
+				color: #FFFFFF;
+				background: #C39A58;
+				border-radius: 44upx;
+			}
+		}
+	}
+</style>

+ 603 - 0
pages_shop/storeOrderRefundDetails.vue

@@ -0,0 +1,603 @@
+<template>
+	<view>
+		<view class="top-cont">
+			<view class="bg"></view>
+			<view class="top-inner">			
+				<!-- 订单状态 -->
+				<view class="order-status" >
+					<!-- 处理中 -->
+					<view  v-if="sales.salesStatus == 0" class="inner">
+						<view class="img-box">
+							<image src="https://fs-1319721001.cos.ap-chongqing.myqcloud.com/fs/20240229/a4bf17c7fb7342118f5ba52911fbcd84.png" mode=""></image>
+						</view>
+						<view class="status-box">
+							<text class="status">售后中</text>
+							<text class="desc">请等待客服处理...</text>
+						</view>
+					</view>
+					<view  v-if="sales.salesStatus == 1" class="inner">
+						<view class="img-box">
+							<image src="https://fs-1319721001.cos.ap-chongqing.myqcloud.com/fs/20240229/5be93a404ff148c99ff41b39a3b23665.png" mode=""></image>
+						</view>
+						<view class="status-box">
+							<text class="status">售后取消</text>
+							<text class="desc">用户已取消售后</text>
+						</view>
+					</view>
+					<view  v-if="sales.salesStatus == 2" class="inner">
+						<view class="img-box">
+							<image src="https://fs-1319721001.cos.ap-chongqing.myqcloud.com/fs/20240229/5be93a404ff148c99ff41b39a3b23665.png" mode=""></image>
+						</view>
+						<view class="status-box">
+							<text class="status">售后取消</text>
+							<text class="desc">商家已拒绝...</text>
+						</view>
+					</view>
+					<!-- 退款成功 -->
+					<view  v-if="sales.salesStatus == 3" class="inner">
+						<view class="img-box">
+							<image src="https://fs-1319721001.cos.ap-chongqing.myqcloud.com/fs/20240229/09e5cf7de97d4ee6aa9f0c778599d760.png" mode=""></image>
+						</view>
+						<view class="status-box">
+							<text class="status">退款成功</text>
+							<text class="desc">已退款,退款金额将按原支付退回</text>
+						</view>
+					</view>
+					
+				</view>
+				<!-- 退款信息 -->
+				<view class="refund-info">
+					<view class="inner">
+						<view class="title-box">
+							<view class="left">
+								<text class="title">退款金额</text>
+								<view class="price-box">
+									<text class="unit">¥</text>
+									<text class="num" v-if="sales.refundAmount!=null">{{sales.refundAmount.toFixed(2)}}</text>
+								</view>
+							</view>
+							<view class="right">
+								<text class="text" >
+										<text  class="text success">
+											{{$getDictLabelName(statusOptions,sales.salesStatus)}}
+										</text>
+								</text>
+							</view>
+						</view>
+			 
+						<view class="refund-item" v-if="sales.status>=1&&sales.refundType==1">
+							<view class="text">收件人:{{sales.consignee}} </view>
+							<view class="text">电话:{{sales.address}} </view>
+							<view class="text">收件地址:{{sales.phoneNumber}}</view>
+						</view>
+						<view class="btn-box"  v-if="sales.salesStatus==0" >
+							<view   v-if="sales.status==1"  class="btn cancel" @click="addDeliverySn()"  >填写物流</view>
+							<view  v-if="sales.status==0||sales.status==1" class="btn cancel" @click="revoke()"  >撤销申请</view>
+							<!-- <view  class="btn cancel" @click="showLogs()" >查看进度</view> -->
+						</view>
+						<view class="refund-item" v-if="sales.status==0">
+							<view class="text">已提交等待平台审核 </view>
+						</view>
+						<view class="refund-item" v-if="sales.status==1">
+							<view class="text">平台已审核,等待用户发货 </view>
+						</view>
+						 <view class="refund-item" v-if="sales.status==2">
+						 	<view class="text">用户已发货,等待仓库审核 </view>
+						 </view>
+						 <view class="refund-item" v-if="sales.status==3">
+						 	<view class="text">财务审核 </view>
+						 </view>
+						 <view class="refund-item" v-if="sales.status==4">
+						 	<view class="text">已完成 </view>
+						 </view>
+					</view>
+				</view>
+				<view class="content">
+					<!-- 退货信息 -->
+					<view class="return-info">
+						<!-- <view class="title-box">
+							<text class="label">退货信息</text>
+							<text class="ret-num">共{{items.length}}件</text>
+						</view> -->
+						<!-- 退货列表 -->
+						<view class="goods-list">
+							<view v-if="sales!=null"  v-for="(item,index) in items" :key="index" class="item">
+								<view class="img-box">
+									<image :src="JSON.parse(item.jsonInfo).image==''?'/static/images/drug.svg':JSON.parse(item.jsonInfo).image" mode="aspectFill"></image>
+								</view>
+								<view class="info-box">
+									<view>
+										<view class="title ellipsis2">{{JSON.parse(item.jsonInfo).productName}}</view>
+										<view class="spec">规格:{{JSON.parse(item.jsonInfo).sku}}</view>
+									</view>
+									<view class="price-num">
+										<view class="price">
+											<text class="unit">¥</text>
+											<text class="num">{{JSON.parse(item.jsonInfo).price.toFixed(2)}}</text>
+										</view>
+										<view class="num">x{{JSON.parse(item.jsonInfo).num}}</view>
+									</view>
+								</view>
+							</view>
+							 
+							<!-- 详细信息 -->
+							<view class="refund-det-info">
+								<view class="det-item">
+									<text class="label">退货原因</text>
+									<text class="text">{{sales.reasons}}</text>
+								</view>
+								<view class="det-item">
+									<text class="label">退货说明</text>
+									<text class="text">{{sales.explains}}</text>
+								</view>
+								<view class="det-item">
+									<text class="label">退款金额</text>
+									<text class="text" v-if="sales.refundAmount!=null">¥{{sales.refundAmount.toFixed(2)}}</text>
+								</view>
+								 <view class="det-item">
+								 	<text class="label">订单编号</text>
+								 	<text class="text">{{order.orderCode}}</text>
+								 </view>
+								<view class="det-item">
+									<text class="label">申请时间</text>
+									<text class="text">{{sales.createTime}}</text>
+								</view>
+								
+							</view>
+						</view>
+					</view>
+				</view>
+			
+			</view>
+		</view>
+		
+	</view>
+</template>
+
+<script>
+	// import {getDictByKey} from '@/api/common.js'
+	
+	// import {getStoreAfterSalesById,revoke} from '@/api/storeAfterSales.js'
+	export default {
+		data() {
+			return {
+				statusOptions:[],
+				sales:{},
+				items:[],
+				order:null,
+				salesId:null,
+				// 状态栏的高度
+				statusBarHeight: uni.getStorageSync('menuInfo').statusBarHeight,
+ 
+			};
+		},
+		onLoad(option) {
+			this.id = option.id
+
+		},
+		onShow() {
+			this.getStoreAfterSalesById()
+			this.getDictByKey("sys_after_sales_status")
+		},
+		methods: {
+			getDictByKey(key){
+				var data={key:key}
+				getDictByKey(data).then(
+					res => {
+						if(res.code==200){
+							if(key=="sys_after_sales_status"){
+								this.statusOptions=res.data;
+							}
+						}
+					},
+					err => {
+					}
+				);
+				
+			},
+			addDeliverySn(){
+				uni.navigateTo({
+					url: './storeOrderRefundAddDelivery?id=' + this.id
+				}) 
+			},
+			revoke(){
+				var data={id:this.id};
+				revoke(data).then(res => {
+					if(res.code==200){
+						 uni.showToast({
+						 	icon:'success',
+						 	title:'操作成功'
+						 });
+						 setTimeout(function() {
+							 uni.$emit('refreshAfterSales');
+							 uni.navigateBack({
+								 delta: 1
+							 })
+						 }, 500);
+						 
+					}else{
+						uni.showToast({
+							icon:'none',
+							title: res.msg
+						});
+						 
+					}
+				});
+			},
+			getStoreAfterSalesById() {
+				//联网加载数据
+				var that = this;
+				var data = {
+					id:this.id,
+
+				};
+				getStoreAfterSalesById(data).then(res => {
+					if(res.code==200){
+						//设置列表数据
+						this.sales=res.sales;
+						this.items=res.items;
+						this.order=res.order;
+					}else{
+						uni.showToast({
+							icon:'none',
+							title: res.msg
+						});
+						 
+					}
+				});
+			},
+		 
+			showLogs(status) {
+				uni.navigateTo({
+					url: './refundOrderLogs?orderStatus=' + status
+				})
+			},
+			
+		}
+	}
+</script>
+
+<style lang="scss">
+	.fixed-top-box{
+		width: 100%;
+		background: linear-gradient(135deg, #2BC7B9 0%, #60CDC3 100%);
+		position: fixed;
+		top: 0;
+		left: 0;
+		z-index: 1000;
+	}
+	.top-cont{
+		width: 100%;
+		height: 500upx;
+		position: relative;
+		.bg{
+			width: 100%;
+			height: 350upx;
+			position: absolute;
+			top: 0;
+			left: 0;
+			z-index: 1;
+			background-color: #FF5C03;
+			background: linear-gradient(#FF5C03, #E2C99E);
+			border-radius: 0rpx 0rpx 100rpx 100rpx;
+		}
+		.top-inner{
+			width: 100%;
+			height: 100%;
+			position: absolute;
+			top: 0;
+			left: 0;
+			z-index: 2;
+			.back-box{
+				height: 88upx;
+				padding-left: 22upx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding: 0 20upx;
+				image{
+					width: 40upx;
+					height: 40upx;
+				}
+				.title{
+					font-size: 36upx;
+					font-family: PingFang SC;
+					font-weight: 500;
+					color: #FFFFFF;
+				}
+			}
+			.order-status{
+				margin-top: 60upx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding: 0 30upx;
+				.inner{
+					display: flex;
+					align-items: center;
+					.img-box{
+						width: 96upx;
+						height: 96upx;
+						margin-right: 30upx;
+						image{
+							width: 100%;
+							height: 100%;
+						}
+					}
+					.status-box{
+						height: 96upx;
+						display: flex;
+						flex-direction: column;
+						justify-content: center;
+						.status{
+							font-size: 40upx;
+							font-family: PingFang SC;
+							font-weight: bold;
+							color: #FFFFFF;
+							line-height: 1;
+						}
+						.desc{
+							font-size: 26upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #FFFFFF;
+							line-height: 1;
+							margin-top: 30upx;
+						}
+						.det-box{
+							display: flex;
+							align-items: center;
+							image{
+								width: 14upx;
+								height: 24upx;
+								margin-left: 12upx;
+								margin-top: 30upx;
+							}
+						}
+					}
+				}
+			}
+			.refund-info{
+				margin-top: 50upx;
+				padding: 0 20upx;
+				.inner{
+					
+					background: #FFFFFF;
+					border-radius: 16upx;
+					padding: 0 30upx 15upx 30upx;
+					.title-box{
+						height: 88upx;
+						border-bottom: 1px solid #F0F0F0;
+						display: flex;
+						align-items: center;
+						justify-content: space-between;
+						.left{
+							display: flex;
+							align-items: center;
+							.title{
+								font-size: 30upx;
+								font-family: PingFang SC;
+								font-weight: bold;
+								color: #333333;
+								line-height: 1;
+								margin-right: 20upx;
+							}
+							.price-box{
+								display: flex;
+								align-items: flex-end;
+								.unit{
+									font-size: 24upx;
+									font-family: PingFang SC;
+									font-weight: 500;
+									color: #FF6633;
+									line-height: 1.2;
+									margin-right: 4upx;
+								}
+								.num{
+									font-size: 32upx;
+									font-family: PingFang SC;
+									font-weight: bold;
+									color: #FF6633;
+									line-height: 1;
+								}
+							}
+						}
+						.right{
+							display: flex;
+							align-items: center;
+							.text{
+								font-size: 24upx;
+								font-family: PingFang SC;
+								font-weight: 500;
+								color: #999999;
+								line-height: 1;
+								margin-right: 10upx;
+							}
+							image{
+								width: 12upx;
+								height: 22upx;
+							}
+						}
+					}
+					.refund-item{
+						margin-top: 40upx;
+						.text{
+							margin-bottom: 10upx;
+							font-size: 26upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #666666;
+							line-height: 1;
+						}
+					}
+				}
+			}
+		}
+	}
+	.content{
+		padding: 0 20upx 20upx;
+		.return-info{
+			margin-top: 30upx;
+			background: #FFFFFF;
+			border-radius: 16upx;
+			.title-box{
+				height: 88upx;
+				padding: 0 30upx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				.label{
+					font-size: 30upx;
+					font-family: PingFang SC;
+					font-weight: bold;
+					color: #333333;
+				}
+				.ret-num{
+					font-size: 26upx;
+					font-family: PingFang SC;
+					font-weight: 500;
+					color: #999999;
+				}
+			}
+			.goods-list{
+				padding: 0 30upx;
+				background-color: #FFFFFF;
+				border-radius: 16upx;
+				.item{
+					padding: 30upx 0;
+					border-bottom: 1px solid #EDEEEF;
+					display: flex;
+					align-items: center;
+					.img-box{
+						width: 160upx;
+						height: 160upx;
+						margin-right: 30upx;
+						image{
+							width: 100%;
+							height: 100%;
+						}
+					}
+					.info-box{
+						width: calc(100% - 190upx);
+						height: 160upx;
+						display: flex;
+						flex-direction: column;
+						justify-content: space-between;
+						.title{
+							font-size: 28upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #333333;
+							line-height: 36upx;
+							.tag{
+								display: inline-block;
+								padding: 0 6upx;
+								height: 30upx;
+								background: linear-gradient(90deg, #2BC7B9 0%, #2BC7A4 100%);
+								border-radius: 4upx;
+								margin-right: 10upx;
+								font-size: 22upx;
+								font-family: PingFang SC;
+								font-weight: bold;
+								color: #FFFFFF;
+								line-height: 30upx;
+								float: left;
+								margin-top: 7upx;
+							}
+						}
+						.spec{
+							margin-top: 18upx;
+							font-size: 24upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #999999;
+							line-height: 1;
+						}
+						.price-num{
+							display: flex;
+							align-items: center;
+							justify-content: space-between;
+							.price{
+								display: flex;
+								align-items: flex-end;
+								.unit{
+									font-size: 24upx;
+									font-family: PingFang SC;
+									font-weight: 500;
+									color: #111111;
+									line-height: 1.2;
+									margin-right: 4upx;
+								}
+								.num{
+									font-size: 32upx;
+									font-family: PingFang SC;
+									font-weight: 500;
+									color: #111111;
+									line-height: 1;
+								}
+							}
+							.num{
+								font-size: 24upx;
+								font-family: PingFang SC;
+								font-weight: 500;
+								color: #999999;
+								line-height: 1;
+							}
+						}
+					}
+				}
+				.refund-det-info{
+					padding-bottom: 30upx;
+					.det-item{
+						margin-top: 40upx;
+						display: flex;
+						align-items: center;
+						justify-content: space-between;
+						.label{
+							font-size: 26upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #666666;
+							line-height: 1;
+						}
+						.text{
+							font-size: 26upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #222222;
+							line-height: 1;
+						}
+					}
+				}
+			}
+		}
+		
+	}
+	.btn-box{
+		margin-top: 15upx;
+		box-sizing: border-box;
+		display: flex;
+		align-items: center;
+		justify-content: flex-end;
+		.btn{
+			width: 155upx;
+			height: 64upx;
+			line-height: 64upx;
+			font-size: 26upx;
+			font-family: PingFang SC;
+			font-weight: 500;
+			text-align: center;
+			border-radius: 32upx;
+			margin-left: 15upx;
+			&:first-child{
+				margin-left: 0;
+			}
+			&.cancel{
+				border: 1px solid #DDDDDD;
+				color: #666666;
+			}
+			 
+		}
+	}
+	
+</style>

+ 387 - 0
pages_shop/storeOrderRefundList.vue

@@ -0,0 +1,387 @@
+<template>
+	<view>
+		<u-sticky>
+			<view class="top-fixed">
+				 <u-tabs
+				  :scrollable="false"
+				  :list="tabs"  
+				  lineColor="#FF5C03"
+				 @change="tabChange">
+				 </u-tabs>
+			</view>
+		</u-sticky>
+		<!-- 订单列表 -->
+		<mescroll-body top="88rpx" bottom="0"  ref="mescrollRef" @init="mescrollInit" @down="downCallback" @up="upCallback" :down="downOption" :up="upOption">
+			<view class="order-list">
+				<view v-for="(item,index) in dataList" :key="index" class="item"  >
+					<!-- 订单号,状态 -->
+					<view class="ordersn-box">
+						<view class="num">订单号:{{item.orderCode}}</view>
+						<view class="status-box">
+							<text  class="text info">
+							<!-- {{$getDictLabelName(statusOptions,item.salesStatus)}}	 -->
+							</text>
+						</view>
+					</view>
+					<!-- 药品列表 -->
+					<view class="drug-list">
+						<view v-if="item!=null" v-for="(subitem,j) in item.items" :key="j" class="drug-item">
+							<view class="img-box">
+								<image :src="JSON.parse(subitem.jsonInfo).image==''?'/static/images/drug.svg':JSON.parse(subitem.jsonInfo).image" mode="aspectFill"></image>
+							</view>
+							<view class="drug-info">
+								<view>
+									<view class="name-box ellipsis2">
+										<!-- <view class="tag">处方药</view> -->
+										{{JSON.parse(subitem.jsonInfo).productName}}
+									</view>
+									<view class="spec">规格:{{JSON.parse(subitem.jsonInfo).sku}}</view>
+								</view>
+								<view class="num-box">
+									<view class="price">
+										<text class="unit">¥</text>
+										<text class="num">{{JSON.parse(subitem.jsonInfo).price.toFixed(2)}}</text>
+									</view>
+									<view class="amount">x{{JSON.parse(subitem.jsonInfo).num}}</view>
+								</view>
+							</view>
+						</view>
+						 
+						<!-- 实付金额、按钮 -->
+						<view class="bottom-box">
+							<view class="amount-paid">
+								<text class="label">退款金额:</text>
+								<view class="price-box">
+									<view class="unit">¥</view>
+									<view class="num">{{item.refundAmount.toFixed(2)}}</view>
+								</view>
+							</view>
+							<view class="btn-box">
+								<view class="btn pay" @click="showDetail(item)">查看详情</view>
+							</view>
+						</view>
+					</view>
+				</view>
+			</view>
+		</mescroll-body>
+	</view>
+</template>
+
+<script>
+	import {getStoreAfterSalesList} from '@/api/order.js' //获取售后列表
+	// import {getDictByKey} from '@/api/common.js'
+	// import {getStoreAfterSalesList} from '@/api/storeAfterSales.js'
+	import MescrollMixin from "@/uni_modules/mescroll-uni/components/mescroll-uni/mescroll-mixins.js";
+	export default {
+		mixins: [MescrollMixin], 
+		data() {
+			return {
+				userId:uni.getStorageSync("userInfo").userId,
+				statusOptions:[],
+				tabs: [
+					// {name:"全部",id:"0"},
+					// {name:"售后中",id:"1"},
+					// {name:"已完成",id:"2"},
+					{name:"全部",id:""},
+					{name:"待平台审核",id:"0"},
+					{name:"待用户发货",id:"1"},
+					{name:"待仓库审核",id:"2"},
+					{name:"财务审核 ",id:"3"},
+					{name:"退款成功 ",id:"4"},
+					// {name:"已提交等待平台审核",id:"0"},
+					// {name:"平台已审核 等待用户发货",id:"1"},
+					// {name:"用户已发货待仓库审核",id:"2"},
+					// {name:"财务审核 ",id:"3"},
+					// {name:"退款成功 ",id:"4"},
+				],
+				status: 0,
+				mescroll:null,
+				// 上拉加载的配置
+				upOption: {
+					onScroll:true,
+					use: true, // 是否启用上拉加载; 默认true
+					page: {
+						num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始
+						size: 10 // 每页数据的数量,默认10
+					},
+					noMoreSize: 10, // 配置列表的总数量要大于等于5条才显示'-- END --'的提示
+					empty: {
+						icon:'https://cos.his.cdwjyyh.com/fs/20240423/cf4a86b913a04341bb44e34bb4d37aa2.png',
+						tip: '暂无数据'
+					}
+				},
+				// 列表数据
+				dataList: []
+			};
+		},
+		onLoad(option) {
+			
+			// var that=this;
+			// uni.$on('refreshAfterSales', () => {
+			// 	that.mescroll.resetUpScroll()
+			// })
+			
+				// this.mescroll.resetUpScroll()
+			// this.getDictByKey("sys_after_sales_status");
+		},
+		methods: {
+			
+			// getDictByKey(key){
+			// 	var data={key:key}
+			// 	getDictByKey(data).then(
+			// 		res => {
+			// 			if(res.code==200){
+			// 				if(key=="sys_after_sales_status"){
+			// 					this.statusOptions=res.data;
+			// 				}
+			// 			}
+			// 		},
+			// 		err => {
+			// 		}
+			// 	);
+				
+			// },
+			tabChange(item) {
+				this.status = item.id
+				this.mescroll.resetUpScroll()
+			},
+			mescrollInit(mescroll) {
+				this.mescroll = mescroll;
+			},
+			/*下拉刷新的回调 */
+			downCallback(mescroll) {
+				mescroll.resetUpScroll()
+			},
+			upCallback(page) {
+				//联网加载数据
+				var that = this;
+				// var data = {
+				// 	userId:672,
+				// 	// status:this.status,
+				// 	pageNum: page.num,
+				// 	pageSize: page.size
+				// };
+				getStoreAfterSalesList(672,page.size,page.num,this.status).then(res => {
+					if(res.code==200){
+						//设置列表数据
+						if (page.num == 1) {
+							that.dataList = res.data.list; 
+							
+						} else {
+							that.dataList = that.dataList.concat(res.data.list);
+							 
+						}
+						that.mescroll.endBySize(res.data.list.length, res.data.total);
+						
+					}else{
+						uni.showToast({
+							icon:'none',
+							title: "请求失败",
+						});
+						that.dataList = null;
+						that.mescroll.endErr();
+					}
+				});
+			},
+			// 查看订单详情
+			showDetail(item) {
+				uni.navigateTo({
+					url: './storeOrderRefundDetails?id=' + item.orderId
+				})
+			},
+			
+		}
+	}
+</script>
+
+<style lang="scss">
+	.top-fixed{
+		width: 100%;
+		position: absolute;
+		top: 0;
+		left: 0;
+		z-index: 10;
+		height: 88upx;
+		background-color: #fff;
+	}
+	.order-list{
+		padding: 20upx;
+		.item{
+			background: #FFFFFF;
+			border-radius: 16upx;
+			padding: 0 30upx;
+			margin-bottom: 20upx;
+			.ordersn-box{
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				padding: 34upx 0 20upx;
+				.num{
+					font-size: 26upx;
+					font-family: PingFang SC;
+					font-weight: 500;
+					color: #999999;
+					line-height: 1;
+				}
+				.status-box{
+					display: flex;
+					align-items: center;
+					.text{
+						font-size: 28upx;
+						font-family: PingFang SC;
+						font-weight: 500;
+						line-height: 1;
+						&.success{
+							color: #FF5C03;
+						}
+						&.info{
+							color: #999999;
+						}
+					}
+				}
+			}
+			.drug-list{
+				.drug-item{
+					padding: 30upx 0;
+					border-bottom: 1px soli #F0F0F0;
+					display: flex;
+					align-items: center;
+					.img-box{
+						width: 160upx;
+						height: 160upx;
+						margin-right: 30upx;
+						flex-shrink: 0;
+						image{
+							width: 100%;
+							height: 100%;
+						}
+					}
+					.drug-info{
+						width: calc(100% - 190upx);
+						height: 160upx;
+						display: flex;
+						flex-direction: column;
+						justify-content: space-between;
+						.name-box{
+							font-size: 28upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #111111;
+							line-height: 40upx;
+							.tag{
+								display: inline-block;
+								padding: 0 6upx;
+								height: 30upx;
+								background: linear-gradient(90deg, #2BC7B9 0%, #2BC7A4 100%);
+								border-radius: 4upx;
+								margin-right: 10upx;
+								font-size: 22upx;
+								font-family: PingFang SC;
+								font-weight: bold;
+								color: #FFFFFF;
+								line-height: 30upx;
+								float: left;
+								margin-top: 7upx;
+							}
+						}
+						.spec{
+							font-size: 24upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #999999;
+							line-height: 1;
+							margin-top: 10upx;
+						}
+						.num-box{
+							display: flex;
+							align-items: center;
+							justify-content: space-between;
+							.price{
+								display: flex;
+								align-items: flex-end;
+								.unit{
+									font-size: 24upx;
+									font-family: PingFang SC;
+									font-weight: 500;
+									color: #111111;
+									line-height: 1.2;
+									margin-right: 4upx;
+								}
+								.num{
+									font-size: 32upx;
+									font-family: PingFang SC;
+									font-weight: 500;
+									color: #111111;
+									line-height: 1;
+								}
+							}
+							.amount{
+								font-size: 24upx;
+								font-family: PingFang SC;
+								font-weight: 500;
+								color: #999999;
+								line-height: 1;
+							}
+						}
+					}
+				}
+				.bottom-box{
+					height: 110upx;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					.amount-paid{
+						display: flex;
+						align-items: center;
+						.label{
+							font-size: 24upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #999999;
+							line-height: 1;
+						}
+						.price-box{
+							display: flex;
+							align-items: flex-end;
+							.unit{
+								font-size: 24upx;
+								font-family: PingFang SC;
+								font-weight: 500;
+								color: #FF6633;
+								line-height: 1.2;
+								margin-right: 4upx;
+							}
+							.num{
+								font-size: 32upx;
+								font-family: PingFang SC;
+								font-weight: bold;
+								color: #FF6633;
+								line-height: 1;
+							}
+						}
+					}
+					.btn-box{
+						box-sizing: border-box;
+						display: flex;
+						align-items: center;
+						.btn{
+							width: 220upx;
+							height: 64upx;
+							line-height: 64upx;
+							font-size: 26upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							text-align: center;
+							border-radius: 32upx;
+							&.pay{
+								background: #FF5C03;
+								color: #FFFFFF;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	
+</style>

+ 474 - 0
pages_shop/storeOrderRefundSubmit.vue

@@ -0,0 +1,474 @@
+<template>
+	<view class="content">
+		<view class="inner">
+			<!-- 药品列表 -->
+			<view class="drug-list">
+				<view  class="item">
+					<!-- 药品信息 -->
+					<view v-if="order!=null" class="drug-info" v-for="(item,index) in items" :key="index">
+						<view class="img-box">
+							<image :src="JSON.parse(item.jsonInfo).image==''?'/static/images/drug.svg':JSON.parse(item.jsonInfo).image" mode="aspectFit"></image>
+						</view>
+						<view class="info">
+							<view class="top">
+								<view class="title ellipsis2">{{ JSON.parse(item.jsonInfo).productName}}</view>
+								<view class="spec">{{JSON.parse(item.jsonInfo).sku}}</view>
+							</view>
+							<view class="price-num">
+								<view class="price-box">
+									<text class="unit">¥</text>
+									<text class="price">{{JSON.parse(item.jsonInfo).price.toFixed(2)}}</text>
+								</view>
+								<view class="num">x{{JSON.parse(item.jsonInfo).num}}</view>
+							</view>
+						</view>
+					</view>
+					 
+				</view>
+			</view>
+			<!-- 申请原因 -->
+			<view class="reason-apply">
+				<view class="title-box">
+					<text class="label">退款金额</text>
+					 <input class="money" type="text" disabled v-model="refundAmount" placeholder="退款金额" placeholder-class="form-input" />
+				</view>
+				<view class="title-box" >
+					<text class="label">申请原因</text>
+					<picker @change="handleReasons" :value="reasons"  range-key="dictLabel" :range="reasonsOptions">
+						<view class="chose-box"  >
+							<text class="text">{{reasons}}</text>
+							<image src="/static/images/arrow_gray.png" mode=""></image>
+						</view>
+					</picker>
+				</view>
+				<view class="textarea-box">
+					<u--textarea :height="100" v-model="explains" placeholder="请描述申请售后服务的具体原因"  :count="100"></u--textarea>
+				</view>
+			</view>
+			<!-- 退回方式 -->
+			<!-- <view class="return-method">
+				<view class="title-box">
+					<text class="text">退货方式</text>
+					<text class="text">用户发货</text>
+				</view>
+				<view class="return-tips">
+					<text class="text">请您在审核通过后7天内,将商品发货到指定点</text>
+				</view>
+				<view class="info-item">
+					<text class="label">联系人</text>
+					<text class="text">张三  1572325612</text>
+				</view>
+				<view class="info-item">
+					<text class="label">发货地址</text>
+					<text class="text">重庆市江北区建玛特商场5楼</text>
+				</view>
+			</view> -->
+			 <!-- 底部按钮 -->
+			 <view class="btn-box">
+			 	<view class="sub-btn" @click="submit()">申请售后</view>
+			 </view>
+		</view>
+		
+	 
+	</view>
+</template>
+
+<script>
+	// import {getDictByKey} from '@/api/common.js'
+	
+	// import {applyAfterSales,getStoreOrderItems} from '@/api/storeAfterSales'
+	export default {
+		data() {
+			return {
+				orderId:null,
+				order:null,
+				orderCode:null,
+				items:[],
+				type:null,
+				reasonsOptions:[],
+				reasons:"请选择",
+				explains:"",
+				refundAmount:0.00,
+				 
+			}
+		},
+		onLoad(option) {
+			this.type=option.type;
+			this.orderId=option.orderId;
+			this.getStoreOrderItems()
+			this.getDictByKey("sys_sales_reasons")
+		},
+		methods: {
+			getDictByKey(key){
+				var data={key:key}
+				getDictByKey(data).then(
+					res => {
+						if(res.code==200){
+							if(key=="sys_sales_reasons"){
+								this.reasonsOptions=res.data;
+							}
+						}
+					},
+					err => {
+					}
+				);
+				
+			},
+			getStoreOrderItems(){
+				var data={orderId:this.orderId};
+				getStoreOrderItems(data).then(res => {
+					if(res.code==200){
+						this.order=res.order;
+						this.items=res.items;
+						this.refundAmount=this.order.payMoney.toFixed(2)
+					}else{
+						uni.showToast({
+							icon:'none',
+							title: "请求失败",
+						});
+						 
+					}
+				});
+			},
+			handleReasons(e) {
+				console.log(e.detail.value)
+				this.reasons = this.reasonsOptions[e.detail.value].dictLabel
+			},
+			submit(){
+				if(this.reasons=="请选择"){
+					uni.showToast({
+						icon:'none',
+						title: '请选择原因'
+					});
+					return;
+				}
+				if(this.refundAmount<0){
+					uni.showToast({
+						icon:'none',
+						title: '请输入退款金额'
+					});
+					return;
+				}
+				 
+				var productIds=this.items.map(item=>item.productId);
+				var products=[];
+				for(var i=0;i<productIds.length;i++){
+					var item={productId:productIds[i]};
+					products.push(item);
+				}
+				var data={
+					refundAmount:this.refundAmount,
+					orderId:this.orderId,
+					refundType:this.type,
+					reasons:this.reasons,
+					explains:this.explains,
+					productList:products,
+				};
+				applyAfterSales(data).then(res => {
+					if(res.code==200){
+						 uni.showToast({
+						 	icon:'success',
+						 	title:'提交成功'
+						 });
+						 setTimeout(function() {
+							 uni.$emit('refreshOrder');
+							 uni.navigateBack({
+								 delta: 1
+							 })
+						 }, 500);
+						 
+					}else{
+						uni.showToast({
+							icon:'none',
+							title: res.msg
+						});
+						 
+					}
+				});
+			},
+		}
+	}
+</script>
+
+<style lang="scss">
+	.content{
+		margin-bottom: 170upx;
+		.inner{
+			padding: 20upx;
+			.drug-list{
+				.item{
+					background: #FFFFFF;
+					border-radius: 16upx;
+					margin-bottom: 20upx;
+					padding: 0 30upx;
+					.drug-info{
+						display: flex;
+						align-items: center;
+						padding: 30upx 0;
+						.img-box{
+							width: 160upx;
+							height: 160upx;
+							margin-right: 30upx;
+							image{
+								width: 100%;
+								height: 100%;
+							}
+						}
+						.info{
+							width: calc(100% - 160upx);
+							height: 160upx;
+							display: flex;
+							flex-direction: column;
+							justify-content: space-between;
+							.top{
+								.title{
+									font-size: 28upx;
+									font-family: PingFang SC;
+									font-weight: 500;
+									color: #111111;
+									line-height: 1.4;
+									.tag{
+										display: inline-block;
+										padding: 0 6upx;
+										height: 30upx;
+										background: linear-gradient(90deg, #2BC7B9 0%, #2BC7A4 100%);
+										border-radius: 4upx;
+										margin-right: 10upx;
+										font-size: 22upx;
+										font-family: PingFang SC;
+										font-weight: bold;
+										color: #FFFFFF;
+										line-height: 30upx;
+										float: left;
+										margin-top: 7upx;
+									}
+								}
+								.spec{
+									font-size: 24upx;
+									font-family: PingFang SC;
+									font-weight: 500;
+									color: #999999;
+									line-height: 1;
+									margin-top: 14upx;
+								}
+							}
+							.price-num{
+								display: flex;
+								align-items: center;
+								justify-content: space-between;
+								.price-box{
+									display: flex;
+									align-items: flex-end;
+									.unit{
+										font-size: 24upx;
+										font-family: PingFang SC;
+										font-weight: 500;
+										color: #111111;
+										line-height: 1.2;
+										margin-right: 5upx;
+									}
+									.price{
+										font-size: 32upx;
+										font-family: PingFang SC;
+										font-weight: 500;
+										color: #111111;
+										line-height: 1;
+									}
+								}
+								.num{
+									font-size: 24upx;
+									font-family: PingFang SC;
+									font-weight: bold;
+									color: #666666;
+								}
+							}
+						}
+					}
+					 
+				}
+			}
+			.reason-apply{
+				margin-top: 20upx;
+				background: #FFFFFF;
+				border-radius: 16upx;
+				padding: 0 30upx;
+				.title-box{
+					height: 86upx;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					border-bottom: 1px solid #F0F0F0;
+					.label{
+						font-size: 30upx;
+						font-family: PingFang SC;
+						font-weight: bold;
+						color: #333333;
+					}
+					.money{
+						
+						font-size: 24upx;
+						font-family: PingFang SC;
+						font-weight: 500;
+						color: #999999;
+					}
+					.chose-box{
+						display: flex;
+						align-items: center;
+						.text{
+							font-size: 24upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #999999;
+						}
+						image{
+							width: 14upx;
+							height: 24upx;
+							margin-left: 10upx;
+						}
+					}
+				}
+				.textarea-box{
+					padding: 30upx 0;
+				}
+			}
+			.return-method{
+				background: #FFFFFF;
+				border-radius: 16upx;
+				margin-top: 20upx;
+				padding: 0 30upx 40upx;
+				.title-box{
+					height: 86upx;
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					border-bottom: 1px solid #F0F0F0;
+					.text{
+						font-size: 30upx;
+						font-family: PingFang SC;
+						font-weight: bold;
+						color: #333333;
+					}
+				}
+				.return-tips{
+					margin-top: 30upx;
+					margin-bottom: 30upx;
+					height: 80upx;
+					background: #FFF4E6;
+					border-radius: 16upx;
+					padding: 0 20upx;
+					display: flex;
+					align-items: center;
+					.text{
+						font-size: 24upx;
+						font-family: PingFang SC;
+						font-weight: 500;
+						color: #FF5C03;
+					}
+				}
+				.info-item{
+					display: flex;
+					align-items: center;
+					justify-content: space-between;
+					margin-bottom: 40upx;
+					&:last-child{
+						margin-bottom: 0;
+					}
+					.label{
+						font-size: 26upx;
+						font-family: PingFang SC;
+						font-weight: 500;
+						color: #666666;
+						line-height: 1;
+					}
+					.text{
+						font-size: 26upx;
+						font-family: PingFang SC;
+						font-weight: 500;
+						color: #111111;
+						line-height: 1;
+					}
+					.detail-box{
+						display: flex;
+						align-items: center;
+						.price-box{
+							display: flex;
+							align-items: flex-end;
+							margin-right: 18upx;
+							.unit{
+								font-size: 24upx;
+								font-family: PingFang SC;
+								font-weight: 500;
+								color: #111111;
+								line-height: 1.2;
+							}
+							.num{
+								font-size: 32upx;
+								font-family: PingFang SC;
+								font-weight: bold;
+								color: #111111;
+								line-height: 1;
+							}
+						}
+						.det-text{
+							font-size: 26upx;
+							font-family: PingFang SC;
+							font-weight: 500;
+							color: #111111;
+						}
+						image{
+							width: 14upx;
+							height: 24upx;
+							margin-left: 10upx;
+						}
+					}
+				}
+			}
+		}
+		 
+		.reson-box{
+			padding: 0 10upx 60upx;
+			.reson-item{
+				width: 100%;
+				height: 110upx;
+				display: flex;
+				align-items: center;
+				justify-content: space-between;
+				.title{
+					font-size: 30upx;
+					font-family: PingFang SC;
+					font-weight: 500;
+					color: #111111;
+				}
+			}
+		}
+	}
+	.btn-box{
+		height: 120upx;
+		padding: 0 30upx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+		.sub-btn{
+			width: 100%;
+			height: 88upx;
+			line-height: 88upx;
+			text-align: center;
+			font-size: 30upx;
+			font-family: PingFang SC;
+			font-weight: bold;
+			color: #FFFFFF;
+			background: #FF5C03;
+			border-radius: 44upx;
+		}
+	}
+	input{
+		text-align: right;
+	}
+	.form-input{
+		font-size: 30upx;
+		font-family: PingFang SC;
+		font-weight: 500;
+		color: #999999;
+		text-align: right;
+	}
+</style>

+ 7 - 4
pages_user/integralGoodsList.vue

@@ -65,10 +65,13 @@
 			tabIndex:0,
 			tabs: [],
 			mescroll:null,
-			downOption: {   //下拉刷新
-			 	use:true,
-				auto: false // 不自动加载 (mixin已处理第一个tab触发downCallback)
-			},
+			downOption: {   
+			                use: true,
+			                auto: false,
+			                textInOffset: '下拉刷新', // 可选:下拉提示文字
+			                textOutOffset: '释放更新', // 可选:释放提示文字
+			                textLoading: '加载中...' // 可选:加载中提示文字
+			            },
 			upOption: {
 				onScroll:false,
 				use: true, // 是否启用上拉加载; 默认true

+ 8 - 0
uni_modules/mescroll-uni/changelog.md

@@ -0,0 +1,8 @@
+## 1.3.8(2023-03-27)
+1. 新增useMescroll的hook, 支持vue3 script setup的写法  
+2. 新增vue3 script setup的示例 ( 根据vue2的示例,全部重写了一遍 )  
+3. mescroll-body 和 mescroll-uni 无需再写 ref="mescrollRef"  
+4. 解决mescroll-uni在页面渲染之后,无法动态设置height的问题  
+5. 解决renderjs在h5返回有时候无法正常滑动的问题  
+6. 修复小程序编辑器提示 Cannot read property 'nv_optDown' of undefined 的错误  
+-by 小瑾同学

+ 19 - 0
uni_modules/mescroll-uni/components/mescroll-body/mescroll-body.css

@@ -0,0 +1,19 @@
+.mescroll-body {
+	position: relative; /* 下拉刷新区域相对自身定位 */
+	height: auto; /* 不可固定高度,否则overflow:hidden导致无法滑动; 同时使设置的最小高生效,实现列表不满屏仍可下拉*/
+	overflow: hidden; /* 当有元素写在mescroll-body标签前面时,可遮住下拉刷新区域 */
+	box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */
+}
+
+/* 使sticky生效: 父元素不能overflow:hidden或者overflow:auto属性 */
+.mescroll-body.mescorll-sticky{
+	overflow: unset !important
+}
+
+/* 适配 iPhoneX */
+@supports (bottom: constant(safe-area-inset-bottom)) or (bottom: env(safe-area-inset-bottom)) {
+	.mescroll-safearea {
+		padding-bottom: constant(safe-area-inset-bottom);
+		padding-bottom: env(safe-area-inset-bottom);
+	}
+}

이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.