From 0b0cbd3c729f4534c4c06276f8c5061750bfbe43 Mon Sep 17 00:00:00 2001 From: zhouwei1994 <465081029@qq.com> Date: Fri, 15 May 2020 18:58:43 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.vue | 4 +- api/goods-edit.js | 157 ++++ api/goods.js | 169 ++++ api/mock.js | 169 ++++ components/common/address.vue | 4 +- components/common/address_popup.vue | 2 +- .../mescroll-uni/components/mescroll-down.css | 55 ++ .../mescroll-uni/components/mescroll-down.vue | 47 + .../components/mescroll-empty.vue | 90 ++ .../mescroll-uni/components/mescroll-top.vue | 81 ++ .../mescroll-uni/components/mescroll-up.css | 46 + .../mescroll-uni/components/mescroll-up.vue | 39 + .../common/mescroll-uni/mescroll-body.css | 10 + .../common/mescroll-uni/mescroll-body.vue | 292 ++++++ .../common/mescroll-uni/mescroll-mixins.js | 60 ++ .../mescroll-uni/mescroll-uni-option.js | 34 + .../common/mescroll-uni/mescroll-uni.css | 29 + .../common/mescroll-uni/mescroll-uni.js | 862 ++++++++++++++++++ .../common/mescroll-uni/mescroll-uni.vue | 364 ++++++++ .../mescroll-uni/mixins/mescroll-comp.js | 23 + .../mescroll-uni/mixins/mescroll-more-item.js | 48 + .../mescroll-uni/mixins/mescroll-more.js | 56 ++ components/common/popup.vue | 5 +- components/common/prompt.vue | 53 +- components/common/zhouWei-navBar/index.vue | 4 +- config/baseUrl.js | 5 +- config/html5Utils.js | 67 +- config/login.js | 11 +- config/requestConfig.js | 40 +- config/socket.js | 24 +- config/utils.js | 68 +- main.js | 4 +- manifest.json | 18 +- pages.json | 66 +- pages/apiDemo/common.vue | 90 ++ pages/demo/areaSelect.vue | 133 +++ pages/demo/banner.vue | 102 +++ pages/demo/common.vue | 98 ++ pages/demo/list.vue | 93 ++ pages/demo/popup.vue | 173 ++++ pages/demo/prompt.vue | 151 +++ pages/demo/uploadFile.vue | 134 +++ pages/home/loading18.vue | 81 -- pages/message/message.vue | 41 - pages/my/my.vue | 89 -- pages/ta/ta.vue | 41 - pages/template/common.vue | 90 ++ pages/{home => template}/home.vue | 89 +- .../home.1.vue => template/template.vue} | 0 pages/{home => template}/webView.vue | 0 pages/user/forget.vue | 34 +- pages/user/login.vue | 36 +- pages/user/protocol.vue | 6 +- pages/user/register.vue | 36 +- plugins/APPUpdate/APPUpdate.md | 4 +- plugins/APPUpdate/index.js | 2 +- plugins/request/fileUpload.js | 19 +- plugins/request/request.js | 93 +- plugins/request/request.md | 24 +- plugins/share/share.md | 7 +- plugins/wxJsSDK.js | 2 + static/{images => demo}/1.jpg | Bin static/demo/icon_case.png | Bin 0 -> 853 bytes static/demo/icon_right.png | Bin 0 -> 480 bytes static/icon/mescroll-empty.png | Bin 0 -> 37455 bytes static/icon/mescroll-totop.png | Bin 0 -> 288 bytes style/common.scss | 3 +- style/mixin.scss | 3 +- style/table.scss | 54 ++ template.h5.html | 2 - ...1\347\233\256\350\265\204\346\226\231.txt" | 1 + 71 files changed, 4185 insertions(+), 552 deletions(-) create mode 100644 api/goods-edit.js create mode 100644 api/goods.js create mode 100644 api/mock.js create mode 100644 components/common/mescroll-uni/components/mescroll-down.css create mode 100644 components/common/mescroll-uni/components/mescroll-down.vue create mode 100644 components/common/mescroll-uni/components/mescroll-empty.vue create mode 100644 components/common/mescroll-uni/components/mescroll-top.vue create mode 100644 components/common/mescroll-uni/components/mescroll-up.css create mode 100644 components/common/mescroll-uni/components/mescroll-up.vue create mode 100644 components/common/mescroll-uni/mescroll-body.css create mode 100644 components/common/mescroll-uni/mescroll-body.vue create mode 100644 components/common/mescroll-uni/mescroll-mixins.js create mode 100644 components/common/mescroll-uni/mescroll-uni-option.js create mode 100644 components/common/mescroll-uni/mescroll-uni.css create mode 100644 components/common/mescroll-uni/mescroll-uni.js create mode 100644 components/common/mescroll-uni/mescroll-uni.vue create mode 100644 components/common/mescroll-uni/mixins/mescroll-comp.js create mode 100644 components/common/mescroll-uni/mixins/mescroll-more-item.js create mode 100644 components/common/mescroll-uni/mixins/mescroll-more.js create mode 100644 pages/apiDemo/common.vue create mode 100644 pages/demo/areaSelect.vue create mode 100644 pages/demo/banner.vue create mode 100644 pages/demo/common.vue create mode 100644 pages/demo/list.vue create mode 100644 pages/demo/popup.vue create mode 100644 pages/demo/prompt.vue create mode 100644 pages/demo/uploadFile.vue delete mode 100644 pages/home/loading18.vue delete mode 100644 pages/message/message.vue delete mode 100644 pages/my/my.vue delete mode 100644 pages/ta/ta.vue create mode 100644 pages/template/common.vue rename pages/{home => template}/home.vue (62%) rename pages/{home/home.1.vue => template/template.vue} (100%) rename pages/{home => template}/webView.vue (100%) rename static/{images => demo}/1.jpg (100%) create mode 100644 static/demo/icon_case.png create mode 100644 static/demo/icon_right.png create mode 100644 static/icon/mescroll-empty.png create mode 100644 static/icon/mescroll-totop.png create mode 100644 style/table.scss create mode 100644 "\351\241\271\347\233\256\350\265\204\346\226\231.txt" diff --git a/App.vue b/App.vue index 8c9070a..434ad16 100644 --- a/App.vue +++ b/App.vue @@ -7,10 +7,9 @@ } from "@/config/html5Utils"; // #endif // #ifdef APP-PLUS - import APPUpdate from "@/utils/APPUpdate"; + import APPUpdate from "@/plugins/APPUpdate"; // #endif export default { - // 只有 app 才会有 onLaunch 的生命周期 onLaunch: function(e) { //取出缓存数据 store.commit("setCacheData"); @@ -88,6 +87,7 @@ diff --git a/components/common/mescroll-uni/components/mescroll-empty.vue b/components/common/mescroll-uni/components/mescroll-empty.vue new file mode 100644 index 0000000..763c2c4 --- /dev/null +++ b/components/common/mescroll-uni/components/mescroll-empty.vue @@ -0,0 +1,90 @@ + + + + + + diff --git a/components/common/mescroll-uni/components/mescroll-top.vue b/components/common/mescroll-uni/components/mescroll-top.vue new file mode 100644 index 0000000..79e415b --- /dev/null +++ b/components/common/mescroll-uni/components/mescroll-top.vue @@ -0,0 +1,81 @@ + + + + + + diff --git a/components/common/mescroll-uni/components/mescroll-up.css b/components/common/mescroll-uni/components/mescroll-up.css new file mode 100644 index 0000000..dea37a6 --- /dev/null +++ b/components/common/mescroll-uni/components/mescroll-up.css @@ -0,0 +1,46 @@ +/* 上拉加载区域 */ +.mescroll-upwarp { + min-height: 60rpx; + 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); + } +} \ No newline at end of file diff --git a/components/common/mescroll-uni/components/mescroll-up.vue b/components/common/mescroll-uni/components/mescroll-up.vue new file mode 100644 index 0000000..69dc558 --- /dev/null +++ b/components/common/mescroll-uni/components/mescroll-up.vue @@ -0,0 +1,39 @@ + + + + + + diff --git a/components/common/mescroll-uni/mescroll-body.css b/components/common/mescroll-uni/mescroll-body.css new file mode 100644 index 0000000..93102ed --- /dev/null +++ b/components/common/mescroll-uni/mescroll-body.css @@ -0,0 +1,10 @@ +page { + -webkit-overflow-scrolling: touch; /* 使iOS滚动流畅 */ +} + +.mescroll-body { + position: relative; /* 下拉刷新区域相对自身定位 */ + height: auto; /* 不可固定高度,否则overflow: hidden, 可通过设置最小高度使列表不满屏仍可下拉*/ + overflow: hidden; /* 遮住顶部下拉刷新区域 */ + box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */ +} \ No newline at end of file diff --git a/components/common/mescroll-uni/mescroll-body.vue b/components/common/mescroll-uni/mescroll-body.vue new file mode 100644 index 0000000..684efa8 --- /dev/null +++ b/components/common/mescroll-uni/mescroll-body.vue @@ -0,0 +1,292 @@ + + + + + diff --git a/components/common/mescroll-uni/mescroll-mixins.js b/components/common/mescroll-uni/mescroll-mixins.js new file mode 100644 index 0000000..fac53d8 --- /dev/null +++ b/components/common/mescroll-uni/mescroll-mixins.js @@ -0,0 +1,60 @@ +// mescroll-body 和 mescroll-uni 通用 + +// import MescrollUni from "./mescroll-uni.vue"; +// import MescrollBody from "./mescroll-body.vue"; + +const MescrollMixin = { + // components: { // 非H5端无法通过mixin注册组件, 只能在main.js中注册全局组件或具体界面中注册 + // MescrollUni, + // MescrollBody + // }, + 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对象 (兼容字节跳动小程序: http://www.mescroll.com/qa.html?v=20200107#q26) + mescrollInitByRef() { + if(!this.mescroll || !this.mescroll.resetUpScroll){ + let mescrollRef = this.$refs.mescrollRef; + if(mescrollRef) this.mescroll = mescrollRef.mescroll + } + }, + // 下拉刷新的回调 + downCallback() { + // mixin默认resetUpScroll + this.mescroll.resetUpScroll() + }, + // 上拉加载的回调 + upCallback() { + // mixin默认延时500自动结束加载 + setTimeout(()=>{ + this.mescroll.endErr(); + }, 500) + } + }, + mounted() { + this.mescrollInitByRef(); // 兼容字节跳动小程序, 避免未设置@init或@init此时未能取到ref的情况 + } + +} + +export default MescrollMixin; diff --git a/components/common/mescroll-uni/mescroll-uni-option.js b/components/common/mescroll-uni/mescroll-uni-option.js new file mode 100644 index 0000000..6ff72df --- /dev/null +++ b/components/common/mescroll-uni/mescroll-uni-option.js @@ -0,0 +1,34 @@ +// 全局配置 +// mescroll-body 和 mescroll-uni 通用 +const GlobalOption = { + down: { + // 其他down的配置参数也可以写,这里只展示了常用的配置: + textInOffset: '下拉刷新', // 下拉的距离在offset范围内的提示文本 + textOutOffset: '释放更新', // 下拉的距离大于offset范围的提示文本 + textLoading: '加载中 ...', // 加载中的提示文本 + offset: 80, // 在列表顶部,下拉大于80px,松手即可触发下拉刷新的回调 + native: false // 是否使用系统自带的下拉刷新; 默认false; 仅在mescroll-body生效 (值为true时,还需在pages配置enablePullDownRefresh:true;详请参考mescroll-native的案例) + }, + up: { + // 其他up的配置参数也可以写,这里只展示了常用的配置: + textLoading: '加载中 ...', // 加载中的提示文本 + textNoMore: '-- END --', // 没有更多数据的提示文本 + offset: 80, // 距底部多远时,触发upCallback + isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25 + toTop: { + // 回到顶部按钮,需配置src才显示 + src: "http://www.mescroll.com/img/mescroll-totop.png?v=1", // 图片路径 (建议放入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: "http://www.mescroll.com/img/mescroll-empty.png?v=1", // 图标路径 (建议放入static目录, 如 /static/img/mescroll-empty.png ) + tip: '~ 暂无相关数据 ~' // 提示 + } + } +} + +export default GlobalOption diff --git a/components/common/mescroll-uni/mescroll-uni.css b/components/common/mescroll-uni/mescroll-uni.css new file mode 100644 index 0000000..9b9a1e7 --- /dev/null +++ b/components/common/mescroll-uni/mescroll-uni.css @@ -0,0 +1,29 @@ +page { + height: 100%; + box-sizing: border-box; /* 避免设置padding出现双滚动条的问题 */ +} + +.mescroll-uni-warp{ + 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生效 */ +} diff --git a/components/common/mescroll-uni/mescroll-uni.js b/components/common/mescroll-uni/mescroll-uni.js new file mode 100644 index 0000000..f0ef5d7 --- /dev/null +++ b/components/common/mescroll-uni/mescroll-uni.js @@ -0,0 +1,862 @@ +/* mescroll + * version 1.2.5 + * 2020-03-15 wenju + * http://www.mescroll.com + */ + +export default function MeScroll(options, isScrollBody) { + let me = this; + me.version = '1.2.5'; // 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.auto && hasDownCallback) { + if (me.optDown.autoShowLoading) { + me.triggerDownScroll(); // 显示下拉进度,执行下拉回调 + } else { + me.optDown.callback && me.optDown.callback(me); // 不显示下拉进度,直接执行下拉回调 + } + } + // 自动触发上拉加载 + setTimeout(function(){ // 延时确保先执行down的callback,再执行up的callback,因为部分小程序emit是异步,会导致isUpAutoLoad判断有误 + 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, 此值用于控制最大的误差 + fps: 80, // 下拉节流 (值越大每秒刷新频率越高) + 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: '加载中 ...', // 加载中的提示文本 + 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, // 准备结束下拉的回调. 返回结束下拉的延时执行时间,默认0ms; 常用于结束下拉之前再显示另外一小段动画,才去隐藏下拉刷新的场景, 参考案例【dotJump】 + endDownScroll: null, // 结束下拉刷新的回调 + 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,两者可同时触发; + isBounce: false, // 默认禁止橡皮筋的回弹效果, 必读事项: http://www.mescroll.com/qa.html?v=190725#q25 + callback: null, // 上拉加载的回调;function(page,mescroll){ } + page: { + num: 0, // 当前页码,默认0,回调之前会加1,即callback(page)会从1开始 + size: 10, // 每页数据的数量 + time: null // 加载第一页数据服务器返回的时间; 防止用户翻页时,后台新增了数据从而导致下一页数据重复; + }, + noMoreSize: 5, // 如果列表已无数据,可设置列表的总数量要大于等于5条才显示无更多数据;避免列表数据过少(比如只有一条数据),显示无更多数据会不好看 + offset: 80, // 距底部多远时,触发upCallback + 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.lastPoint = this.startPoint; // 重置上次move的点 + this.maxTouchmoveY = this.getBodyHeight() - this.optDown.bottomOffset; // 手指触摸的最大范围(写在touchstart避免body获取高度为0的情况) + this.inTouchend = false; // 标记不是touchend +} + +/* 列表touchmove事件 */ +MeScroll.prototype.touchmoveEvent = function(e) { + // #ifdef H5 + window.isPreventDefault = false // 标记不需要阻止window事件 + // #endif + + if (!this.optDown.use) return; + if (!this.startPoint) return; + let me = this; + + // 节流 + let t = new Date().getTime(); + if (me.moveTime && t - me.moveTime < me.moveTimeDiff) { // 小于节流时间,则不处理 + return; + } else { + me.moveTime = t + if(!me.moveTimeDiff) me.moveTimeDiff = 1000 / me.optDown.fps + } + + 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; 在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))) { + + // 下拉的角度是否在配置的范围内 + let angle = me.getAngle(me.lastPoint, curPoint); // 两点之间的角度,区间 [0,90] + if (angle < me.optDown.minAngle) return; // 如果小于配置的角度,则不往下执行下拉刷新 + + // 如果手指的位置超过配置的距离,则提前结束下拉,避免Webview嵌套导致touchend无法触发 + if (me.maxTouchmoveY > 0 && curPoint.y >= me.maxTouchmoveY) { + me.inTouchend = true; // 标记执行touchend + me.touchendEvent(); // 提前触发touchend + return; + } + + // #ifdef H5 + window.isPreventDefault = true // 标记阻止window事件 + // #endif + 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.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 += Math.round(diff * me.optDown.outOffsetRate); // 越往下,高度变化越小 + } else { // 向上收 + me.downHight += diff; // 向上收回高度,则向上滑多少收多少高度 + } + } + + 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.optDown.endDownScroll && this.optDown.endDownScroll(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.callback && this.optDown.callback(this); // 执行回调,联网加载数据 + } +} + +/* 显示下拉进度布局 */ +MeScroll.prototype.showDownScroll = function() { + this.isDownScrolling = true; // 标记下拉中 + if (this.optDown.native) { + uni.startPullDownRefresh(); // 系统自带的下拉刷新 + this.optDown.showLoading && this.optDown.showLoading(this, 0); // 仍触发showLoading,因为上拉加载用到 + } else{ + this.downHight = this.optDown.offset; // 更新下拉区域高度 + this.optDown.showLoading && this.optDown.showLoading(this, this.downHight); // 下拉刷新中... + } +} + +/* 显示系统自带的下拉刷新时需要处理的业务 */ +MeScroll.prototype.onPullDownRefresh = function() { + this.isDownScrolling = true; // 标记下拉中 + this.optDown.showLoading && this.optDown.showLoading(this, 0); // 仍触发showLoading,因为上拉加载用到 + this.optDown.callback && this.optDown.callback(this); // 执行回调,联网加载数据 +} + +/* 结束下拉刷新 */ +MeScroll.prototype.endDownScroll = function() { + if (this.optDown.native) { // 结束原生下拉刷新 + this.isDownScrolling = false; + this.optDown.endDownScroll && this.optDown.endDownScroll(this); + uni.stopPullDownRefresh(); + return + } + let me = this; + // 结束下拉刷新的方法 + let endScroll = function() { + me.downHight = 0; + me.isDownScrolling = false; + me.optDown.endDownScroll && me.optDown.endDownScroll(me); + !me.isScrollBody && me.setScrollHeight(0) // scroll-view重置滚动区域,使数据不满屏时仍可检查触发翻页 + } + // 结束下拉刷新时的回调 + let delay = 0; + if (me.optDown.afterLoading) delay = me.optDown.afterLoading(me); // 结束下拉刷新的延时,单位ms + if (typeof delay === 'number' && delay > 0) { + setTimeout(endScroll, delay); + } else { + endScroll(); + } +} + +/* 锁定下拉刷新: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.isBounce) me.setBounce(false); // 不允许bounce时,需禁止window的touchmove事件 + + 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.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) { + 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 + // app的bounce只能通过配置pages.json的style.app-plus.bounce为"none"来禁止 + // cancelable:是否可以被禁用; defaultPrevented:是否已经被禁用 + if (e && e.cancelable && !e.defaultPrevented) e.preventDefault() +} + +/* 是否允许下拉回弹(橡皮筋效果); true或null为允许; false禁止bounce */ +MeScroll.prototype.setBounce = function(isBounce) { + // #ifdef H5 + if (isBounce === false) { + this.optUp.isBounce = false; // 禁止 + // 标记当前页使用了mescroll (需延时,确保page已切换) + setTimeout(function() { + let uniPageDom = document.getElementsByTagName('uni-page')[0]; + uniPageDom && uniPageDom.setAttribute('use_mescroll', true) + }, 30); + // 避免重复添加事件 + if (window.isSetBounce) return; + window.isSetBounce = true; + // 需禁止window的touchmove事件才能有效的阻止bounce + window.bounceTouchmove = function(e) { + if(!window.isPreventDefault) return; // 根据标记判断是否阻止 + + let el = e.target; + // 当前touch的元素及父元素是否要拦截touchmove事件 + let isPrevent = true; + while (el !== document.body && el !== document) { + if (el.tagName === 'UNI-PAGE') { // 只扫描当前页 + if (!el.getAttribute('use_mescroll')) { + isPrevent = false; // 如果当前页没有使用mescroll,则不阻止 + } + break; + } + let cls = el.classList; + if (cls) { + if (cls.contains('mescroll-touch')) { // 采用scroll-view 此处不能过滤mescroll-uni,否则下拉仍然有回弹 + isPrevent = false; // mescroll-touch无需拦截touchmove事件 + break; + } else if (cls.contains('mescroll-touch-x') || cls.contains('mescroll-touch-y')) { + // 如果配置了水平或者垂直滑动 + let curX = e.touches ? e.touches[0].pageX : e.clientX; // 当前第一个手指距离列表顶部的距离x + let curY = e.touches ? e.touches[0].pageY : e.clientY; // 当前第一个手指距离列表顶部的距离y + if (!this.preWinX) this.preWinX = curX; // 设置上次移动的距离x + if (!this.preWinY) this.preWinY = curY; // 设置上次移动的距离y + // 计算两点之间的角度 + let x = Math.abs(this.preWinX - curX); + let y = Math.abs(this.preWinY - curY); + let z = Math.sqrt(x * x + y * y); + this.preWinX = curX; // 记录本次curX的值 + this.preWinY = curY; // 记录本次curY的值 + if (z !== 0) { + let angle = Math.asin(y / z) / Math.PI * 180; // 角度区间 [0,90] + if ((angle <= 45 && cls.contains('mescroll-touch-x')) || (angle > 45 && cls.contains('mescroll-touch-y'))) { + isPrevent = false; // 水平滑动或者垂直滑动,不拦截touchmove事件 + break; + } + } + } + } + el = el.parentNode; // 继续检查其父元素 + } + // 拦截touchmove事件:是否可以被禁用&&是否已经被禁用 (这里不使用me.preventDefault(e)的方法,因为某些情况下会报找不到方法的异常) + if (isPrevent && e.cancelable && !e.defaultPrevented && typeof e.preventDefault === "function") e.preventDefault(); + } + window.addEventListener('touchmove', window.bounceTouchmove, { + passive: false + }); + } else { + this.optUp.isBounce = true; // 允许 + if (window.bounceTouchmove) { + window.removeEventListener('touchmove', window.bounceTouchmove); + window.bounceTouchmove = null; + window.isSetBounce = false; + } + } + // #endif +} diff --git a/components/common/mescroll-uni/mescroll-uni.vue b/components/common/mescroll-uni/mescroll-uni.vue new file mode 100644 index 0000000..02bda08 --- /dev/null +++ b/components/common/mescroll-uni/mescroll-uni.vue @@ -0,0 +1,364 @@ + + + + + diff --git a/components/common/mescroll-uni/mixins/mescroll-comp.js b/components/common/mescroll-uni/mixins/mescroll-comp.js new file mode 100644 index 0000000..f376379 --- /dev/null +++ b/components/common/mescroll-uni/mixins/mescroll-comp.js @@ -0,0 +1,23 @@ +/** + * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期: + * 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例) + * 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例) + */ +const MescrollCompMixin = { + // 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 + onPageScroll(e) { + let item = this.$refs["mescrollItem"]; + if(item && item.mescroll) item.mescroll.onPageScroll(e); + }, + onReachBottom() { + let item = this.$refs["mescrollItem"]; + if(item && item.mescroll) item.mescroll.onReachBottom(); + }, + // 当down的native: true时, 还需传递此方法进到子组件 + onPullDownRefresh(){ + let item = this.$refs["mescrollItem"]; + if(item && item.mescroll) item.mescroll.onPullDownRefresh(); + } +} + +export default MescrollCompMixin; diff --git a/components/common/mescroll-uni/mixins/mescroll-more-item.js b/components/common/mescroll-uni/mixins/mescroll-more-item.js new file mode 100644 index 0000000..81bc45c --- /dev/null +++ b/components/common/mescroll-uni/mixins/mescroll-more-item.js @@ -0,0 +1,48 @@ +/** + * mescroll-more-item的mixins, 仅在多个 mescroll-body 写在子组件时使用 (参考 mescroll-more 案例) + */ +const MescrollMoreItemMixin = { + props:{ + i: Number, // 每个tab页的专属下标 + index: { // 当前tab的下标 + type: Number, + default(){ + return 0 + } + } + }, + data() { + return { + downOption:{ + auto:false // 不自动加载 + }, + upOption:{ + auto:false // 不自动加载 + }, + isInit: false // 当前tab是否已初始化 + } + }, + watch:{ + // 监听下标的变化 + index(val){ + if (this.i === val && !this.isInit) { + this.isInit = true; // 标记为true + this.mescroll && this.mescroll.triggerDownScroll(); + } + } + }, + methods: { + // mescroll组件初始化的回调,可获取到mescroll对象 + mescrollInit(mescroll) { + this.mescroll = mescroll; + this.mescrollInitByRef && this.mescrollInitByRef(); // 兼容字节跳动小程序 (mescroll-mixins.js) + // 自动加载当前tab的数据 + if(this.i === this.index){ + this.isInit = true; // 标记为true + this.mescroll.triggerDownScroll(); + } + }, + } +} + +export default MescrollMoreItemMixin; diff --git a/components/common/mescroll-uni/mixins/mescroll-more.js b/components/common/mescroll-uni/mixins/mescroll-more.js new file mode 100644 index 0000000..142aa75 --- /dev/null +++ b/components/common/mescroll-uni/mixins/mescroll-more.js @@ -0,0 +1,56 @@ +/** + * mescroll-body写在子组件时,需通过mescroll的mixins补充子组件缺少的生命周期: + * 当一个页面只有一个mescroll-body写在子组件时, 则使用mescroll-comp.js (参考 mescroll-comp 案例) + * 当一个页面有多个mescroll-body写在子组件时, 则使用mescroll-more.js (参考 mescroll-more 案例) + */ +const MescrollMoreMixin = { + data() { + return { + tabIndex: 0 // 当前tab下标 + } + }, + // 因为子组件无onPageScroll和onReachBottom的页面生命周期,需在页面传递进到子组件 + onPageScroll(e) { + let mescroll = this.getMescroll(this.tabIndex); + mescroll && mescroll.onPageScroll(e); + }, + onReachBottom() { + let mescroll = this.getMescroll(this.tabIndex); + mescroll && mescroll.onReachBottom(); + }, + // 当down的native: true时, 还需传递此方法进到子组件 + onPullDownRefresh(){ + let mescroll = this.getMescroll(this.tabIndex); + mescroll && mescroll.onPullDownRefresh(); + }, + methods:{ + // 根据下标获取对应子组件的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; diff --git a/components/common/popup.vue b/components/common/popup.vue index 9ac8004..1790b72 100644 --- a/components/common/popup.vue +++ b/components/common/popup.vue @@ -21,11 +21,12 @@ }, //点击遮罩层关闭弹窗 hideOnBlur: { + type: Boolean, default: function() { return true; } }, - //禁止页面滚动 + //禁止页面滚动(H5生效) scroll: { type: Boolean, default: true @@ -52,7 +53,7 @@ this.setAnimation(val); }, currentValue(val) { - this.$emit(val ? "on-show" : "on-hide"); + this.$emit("change", val); this.$emit("input", val); } }, diff --git a/components/common/prompt.vue b/components/common/prompt.vue index a804b27..2ff8a88 100644 --- a/components/common/prompt.vue +++ b/components/common/prompt.vue @@ -1,12 +1,12 @@ - + + diff --git a/pages/demo/areaSelect.vue b/pages/demo/areaSelect.vue new file mode 100644 index 0000000..0723cb1 --- /dev/null +++ b/pages/demo/areaSelect.vue @@ -0,0 +1,133 @@ + + + + diff --git a/pages/demo/banner.vue b/pages/demo/banner.vue new file mode 100644 index 0000000..611f493 --- /dev/null +++ b/pages/demo/banner.vue @@ -0,0 +1,102 @@ + + + + diff --git a/pages/demo/common.vue b/pages/demo/common.vue new file mode 100644 index 0000000..25e1a3f --- /dev/null +++ b/pages/demo/common.vue @@ -0,0 +1,98 @@ + + + + + diff --git a/pages/demo/list.vue b/pages/demo/list.vue new file mode 100644 index 0000000..03896a6 --- /dev/null +++ b/pages/demo/list.vue @@ -0,0 +1,93 @@ + + + + + diff --git a/pages/demo/popup.vue b/pages/demo/popup.vue new file mode 100644 index 0000000..d0f0768 --- /dev/null +++ b/pages/demo/popup.vue @@ -0,0 +1,173 @@ + + + + diff --git a/pages/demo/prompt.vue b/pages/demo/prompt.vue new file mode 100644 index 0000000..3ea1791 --- /dev/null +++ b/pages/demo/prompt.vue @@ -0,0 +1,151 @@ + + + + diff --git a/pages/demo/uploadFile.vue b/pages/demo/uploadFile.vue new file mode 100644 index 0000000..151acbb --- /dev/null +++ b/pages/demo/uploadFile.vue @@ -0,0 +1,134 @@ + + + + diff --git a/pages/home/loading18.vue b/pages/home/loading18.vue deleted file mode 100644 index 52368ed..0000000 --- a/pages/home/loading18.vue +++ /dev/null @@ -1,81 +0,0 @@ - - - - - diff --git a/pages/message/message.vue b/pages/message/message.vue deleted file mode 100644 index aae6b23..0000000 --- a/pages/message/message.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - diff --git a/pages/my/my.vue b/pages/my/my.vue deleted file mode 100644 index 1d92157..0000000 --- a/pages/my/my.vue +++ /dev/null @@ -1,89 +0,0 @@ - - - - diff --git a/pages/ta/ta.vue b/pages/ta/ta.vue deleted file mode 100644 index f51ce73..0000000 --- a/pages/ta/ta.vue +++ /dev/null @@ -1,41 +0,0 @@ - - - - - diff --git a/pages/template/common.vue b/pages/template/common.vue new file mode 100644 index 0000000..29023af --- /dev/null +++ b/pages/template/common.vue @@ -0,0 +1,90 @@ + + + + + diff --git a/pages/home/home.vue b/pages/template/home.vue similarity index 62% rename from pages/home/home.vue rename to pages/template/home.vue index bb5dbd4..30935b2 100644 --- a/pages/home/home.vue +++ b/pages/template/home.vue @@ -1,15 +1,11 @@