load-more.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. // [z-paging]滚动到底部加载更多模块
  2. import u from '.././z-paging-utils'
  3. import Enum from '.././z-paging-enum'
  4. export default {
  5. props: {
  6. //自定义底部加载更多样式
  7. loadingMoreCustomStyle: {
  8. type: Object,
  9. default: function() {
  10. return u.gc('loadingMoreCustomStyle', {});
  11. }
  12. },
  13. //自定义底部加载更多文字样式
  14. loadingMoreTitleCustomStyle: {
  15. type: Object,
  16. default: function() {
  17. return u.gc('loadingMoreTitleCustomStyle', {});
  18. }
  19. },
  20. //自定义底部加载更多加载中动画样式
  21. loadingMoreLoadingIconCustomStyle: {
  22. type: Object,
  23. default: function() {
  24. return u.gc('loadingMoreLoadingIconCustomStyle', {});
  25. }
  26. },
  27. //自定义底部加载更多加载中动画图标类型,可选flower或circle,默认为flower
  28. loadingMoreLoadingIconType: {
  29. type: String,
  30. default: u.gc('loadingMoreLoadingIconType', 'flower')
  31. },
  32. //自定义底部加载更多加载中动画图标图片
  33. loadingMoreLoadingIconCustomImage: {
  34. type: String,
  35. default: u.gc('loadingMoreLoadingIconCustomImage', '')
  36. },
  37. //底部加载更多加载中view是否展示旋转动画,默认为是
  38. loadingMoreLoadingAnimated: {
  39. type: Boolean,
  40. default: u.gc('loadingMoreLoadingAnimated', true)
  41. },
  42. //是否启用加载更多数据(含滑动到底部加载更多数据和点击加载更多数据),默认为是
  43. loadingMoreEnabled: {
  44. type: Boolean,
  45. default: u.gc('loadingMoreEnabled', true)
  46. },
  47. //是否启用滑动到底部加载更多数据,默认为是
  48. toBottomLoadingMoreEnabled: {
  49. type: Boolean,
  50. default: u.gc('toBottomLoadingMoreEnabled', true)
  51. },
  52. //滑动到底部状态为默认状态时,以加载中的状态展示,默认为否。若设置为是,可避免滚动到底部看到默认状态然后立刻变为加载中状态的问题,但分页数量未超过一屏时,不会显示【点击加载更多】
  53. loadingMoreDefaultAsLoading: {
  54. type: [Boolean],
  55. default: u.gc('loadingMoreDefaultAsLoading', false)
  56. },
  57. //滑动到底部"默认"文字,默认为【点击加载更多】
  58. loadingMoreDefaultText: {
  59. type: [String, Object],
  60. default: u.gc('loadingMoreDefaultText', null)
  61. },
  62. //滑动到底部"加载中"文字,默认为【正在加载...】
  63. loadingMoreLoadingText: {
  64. type: [String, Object],
  65. default: u.gc('loadingMoreLoadingText', null)
  66. },
  67. //滑动到底部"没有更多"文字,默认为【没有更多了】
  68. loadingMoreNoMoreText: {
  69. type: [String, Object],
  70. default: u.gc('loadingMoreNoMoreText', null)
  71. },
  72. //滑动到底部"加载失败"文字,默认为【加载失败,点击重新加载】
  73. loadingMoreFailText: {
  74. type: [String, Object],
  75. default: u.gc('loadingMoreFailText', null)
  76. },
  77. //当没有更多数据且分页内容未超出z-paging时是否隐藏没有更多数据的view,默认为否
  78. hideNoMoreInside: {
  79. type: Boolean,
  80. default: u.gc('hideNoMoreInside', false)
  81. },
  82. //当没有更多数据且分页数组长度少于这个值时,隐藏没有更多数据的view,默认为0,代表不限制。
  83. hideNoMoreByLimit: {
  84. type: Number,
  85. default: u.gc('hideNoMoreByLimit', 0)
  86. },
  87. //是否显示默认的加载更多text,默认为是
  88. showDefaultLoadingMoreText: {
  89. type: Boolean,
  90. default: u.gc('showDefaultLoadingMoreText', true)
  91. },
  92. //是否显示没有更多数据的view
  93. showLoadingMoreNoMoreView: {
  94. type: Boolean,
  95. default: u.gc('showLoadingMoreNoMoreView', true)
  96. },
  97. //是否显示没有更多数据的分割线,默认为是
  98. showLoadingMoreNoMoreLine: {
  99. type: Boolean,
  100. default: u.gc('showLoadingMoreNoMoreLine', true)
  101. },
  102. //自定义底部没有更多数据的分割线样式
  103. loadingMoreNoMoreLineCustomStyle: {
  104. type: Object,
  105. default: function() {
  106. return u.gc('loadingMoreNoMoreLineCustomStyle', {});
  107. },
  108. },
  109. //当分页未满一屏时,是否自动加载更多,默认为否(nvue无效)
  110. insideMore: {
  111. type: Boolean,
  112. default: u.gc('insideMore', false)
  113. },
  114. //距底部/右边多远时(单位px),触发 scrolltolower 事件,默认为100rpx
  115. lowerThreshold: {
  116. type: [Number, String],
  117. default: u.gc('lowerThreshold', '100rpx')
  118. },
  119. },
  120. data() {
  121. return {
  122. M: Enum.More,
  123. //底部加载更多状态
  124. loadingStatus: Enum.More.Default,
  125. loadingStatusAfterRender: Enum.More.Default,
  126. loadingMoreTimeStamp: 0,
  127. loadingMoreDefaultSlot: null,
  128. showLoadingMore: false,
  129. customNoMore: -1,
  130. }
  131. },
  132. computed: {
  133. zLoadMoreConfig() {
  134. return {
  135. status: this.loadingStatusAfterRender,
  136. defaultAsLoading: this.loadingMoreDefaultAsLoading,
  137. defaultThemeStyle: this.finalLoadingMoreThemeStyle,
  138. customStyle: this.loadingMoreCustomStyle,
  139. titleCustomStyle: this.loadingMoreTitleCustomStyle,
  140. iconCustomStyle: this.loadingMoreLoadingIconCustomStyle,
  141. loadingIconType: this.loadingMoreLoadingIconType,
  142. loadingIconCustomImage: this.loadingMoreLoadingIconCustomImage,
  143. loadingAnimated: this.loadingMoreLoadingAnimated,
  144. showNoMoreLine: this.showLoadingMoreNoMoreLine,
  145. noMoreLineCustomStyle: this.loadingMoreNoMoreLineCustomStyle,
  146. defaultText: this.finalLoadingMoreDefaultText,
  147. loadingText: this.finalLoadingMoreLoadingText,
  148. noMoreText: this.finalLoadingMoreNoMoreText,
  149. failText: this.finalLoadingMoreFailText,
  150. hideContent: !this.loadingMoreDefaultAsLoading && this.listRendering,
  151. };
  152. },
  153. finalLoadingMoreThemeStyle() {
  154. return this.loadingMoreThemeStyle.length ? this.loadingMoreThemeStyle : this.defaultThemeStyle;
  155. },
  156. showLoadingMoreDefault() {
  157. return this._showLoadingMore('Default');
  158. },
  159. showLoadingMoreLoading() {
  160. return this._showLoadingMore('Loading');
  161. },
  162. showLoadingMoreNoMore() {
  163. return this._showLoadingMore('NoMore');
  164. },
  165. showLoadingMoreFail() {
  166. return this._showLoadingMore('Fail');
  167. },
  168. showLoadingMoreCustom() {
  169. return this._showLoadingMore('Custom');
  170. }
  171. },
  172. methods: {
  173. //页面滚动到底部时通知z-paging进行进一步处理
  174. pageReachBottom() {
  175. !this.useChatRecordMode && this._onLoadingMore('toBottom');
  176. },
  177. //手动触发上拉加载更多(非必须,可依据具体需求使用)
  178. doLoadMore(type) {
  179. this._onLoadingMore(type);
  180. },
  181. //通过@scroll事件检测是否滚动到了底部
  182. _checkScrolledToBottom(scrollDiff, checked = false) {
  183. if (this.checkScrolledToBottomTimeOut) {
  184. clearTimeout(this.checkScrolledToBottomTimeOut);
  185. this.checkScrolledToBottomTimeOut = null;
  186. }
  187. if (this.cacheScrollNodeHeight === -1) {
  188. this._getNodeClientRect('.zp-scroll-view').then((res) => {
  189. if (res) {
  190. let pageScrollNodeHeight = res[0].height;
  191. this.cacheScrollNodeHeight = pageScrollNodeHeight;
  192. if (scrollDiff - pageScrollNodeHeight <= this.finalLowerThreshold) {
  193. this._onLoadingMore('toBottom');
  194. }
  195. }
  196. });
  197. } else {
  198. if (scrollDiff - this.cacheScrollNodeHeight <= this.finalLowerThreshold) {
  199. this._onLoadingMore('toBottom');
  200. } else if (scrollDiff - this.cacheScrollNodeHeight <= 500 && !checked) {
  201. this.checkScrolledToBottomTimeOut = setTimeout(() => {
  202. this._getNodeClientRect('.zp-scroll-view', true, true).then((res) => {
  203. this.oldScrollTop = res[0].scrollTop;
  204. const newScrollDiff = res[0].scrollHeight - this.oldScrollTop;
  205. this._checkScrolledToBottom(newScrollDiff, true);
  206. })
  207. }, 150)
  208. }
  209. }
  210. },
  211. //触发加载更多时调用,from:0-滑动到底部触发;1-点击加载更多触发
  212. _onLoadingMore(from = 'click') {
  213. if (from === 'toBottom' && !this.scrollToBottomBounceEnabled && this.scrollEnable) {
  214. this.scrollEnable = false;
  215. this.$nextTick(() => {
  216. this.scrollEnable = true;
  217. })
  218. }
  219. this.$emit('scrolltolower', from);
  220. if (from === 'toBottom' && (!this.toBottomLoadingMoreEnabled || this.useChatRecordMode)) return;
  221. if (this.refresherOnly || !this.loadingMoreEnabled || !(this.loadingStatus === Enum.More.Default || this.loadingStatus === Enum.More.Fail) || this.loading) return;
  222. // #ifdef MP-WEIXIN
  223. if (!this.isIos && !this.refresherOnly && !this.usePageScroll) {
  224. const currentTimestamp = u.getTime();
  225. if (this.loadingMoreTimeStamp > 0 && currentTimestamp - this.loadingMoreTimeStamp < 100) {
  226. this.loadingMoreTimeStamp = 0;
  227. return;
  228. }
  229. }
  230. // #endif
  231. this._doLoadingMore();
  232. },
  233. //处理开始加载更多
  234. _doLoadingMore() {
  235. if (this.pageNo >= this.defaultPageNo && this.loadingStatus !== Enum.More.NoMore) {
  236. this.pageNo ++;
  237. this._startLoading(false);
  238. if (this.isLocalPaging) {
  239. this._localPagingQueryList(this.pageNo, this.defaultPageSize, this.localPagingLoadingTime, (res) => {
  240. this.addData(res);
  241. })
  242. } else {
  243. this._emitQuery(this.pageNo, this.defaultPageSize, Enum.QueryFrom.LoadingMore);
  244. this._callMyParentQuery();
  245. }
  246. this.loadingType = Enum.LoadingType.LoadingMore;
  247. }
  248. },
  249. //(预处理)判断当没有更多数据且分页内容未超出z-paging时是否显示没有更多数据的view
  250. _preCheckShowNoMoreInside(newVal, scrollViewNode, pagingContainerNode) {
  251. if (this.loadingStatus === Enum.More.NoMore && this.hideNoMoreByLimit > 0 && newVal.length) {
  252. this.showLoadingMore = newVal.length > this.hideNoMoreByLimit;
  253. } else if ((this.loadingStatus === Enum.More.NoMore && this.hideNoMoreInside && newVal.length) || (this.insideMore && this.insideOfPaging !== false && newVal.length)) {
  254. this.$nextTick(() => {
  255. this._checkShowNoMoreInside(newVal, scrollViewNode, pagingContainerNode);
  256. })
  257. if (this.insideMore && this.insideOfPaging !== false && newVal.length) {
  258. this.showLoadingMore = newVal.length;
  259. }
  260. } else {
  261. this.showLoadingMore = newVal.length;
  262. }
  263. },
  264. //判断当没有更多数据且分页内容未超出z-paging时是否显示没有更多数据的view
  265. async _checkShowNoMoreInside(totalData, oldScrollViewNode, oldPagingContainerNode) {
  266. try {
  267. const scrollViewNode = oldScrollViewNode || await this._getNodeClientRect('.zp-scroll-view');
  268. if (this.usePageScroll) {
  269. if (scrollViewNode) {
  270. const scrollViewTotalH = scrollViewNode[0].top + scrollViewNode[0].height;
  271. this.insideOfPaging = scrollViewTotalH < this.windowHeight;
  272. if (this.hideNoMoreInside) {
  273. this.showLoadingMore = !this.insideOfPaging;
  274. }
  275. this._updateInsideOfPaging();
  276. }
  277. } else {
  278. const pagingContainerNode = oldPagingContainerNode || await this._getNodeClientRect('.zp-paging-container-content');
  279. const pagingContainerH = pagingContainerNode ? pagingContainerNode[0].height : 0;
  280. const scrollViewH = scrollViewNode ? scrollViewNode[0].height : 0;
  281. this.insideOfPaging = pagingContainerH < scrollViewH;
  282. if (this.hideNoMoreInside) {
  283. this.showLoadingMore = !this.insideOfPaging;
  284. }
  285. this._updateInsideOfPaging();
  286. }
  287. } catch (e) {
  288. this.insideOfPaging = !totalData.length;
  289. if (this.hideNoMoreInside) {
  290. this.showLoadingMore = !this.insideOfPaging;
  291. }
  292. this._updateInsideOfPaging();
  293. }
  294. },
  295. //是否要展示上拉加载更多view
  296. _showLoadingMore(type) {
  297. if (!this.showLoadingMoreWhenReload && (!(this.loadingStatus === Enum.More.Default ? this.nShowBottom : true) || !this.totalData.length)) return false;
  298. if (((!this.showLoadingMoreWhenReload || this.isUserPullDown || this.loadingStatus !== Enum.More.Loading) && !this.showLoadingMore) ||
  299. (!this.loadingMoreEnabled && (!this.showLoadingMoreWhenReload || this.isUserPullDown || this.loadingStatus !== Enum.More.Loading)) ||
  300. this.refresherOnly) return false;
  301. if (this.useChatRecordMode && type !== 'Loading') return false;
  302. if (!this.$slots) return false;
  303. if (type === 'Custom') {
  304. return this.showDefaultLoadingMoreText && !(this.loadingStatus === Enum.More.NoMore && !this.showLoadingMoreNoMoreView);
  305. }
  306. const res = this.loadingStatus === Enum.More[type] && this.$slots[`loadingMore${type}`] && (type === 'NoMore' ? this.showLoadingMoreNoMoreView : true);
  307. if (res) {
  308. // #ifdef APP-NVUE
  309. if (!this.isIos) {
  310. this.nLoadingMoreFixedHeight = false;
  311. }
  312. // #endif
  313. }
  314. return res;
  315. },
  316. }
  317. }