data-handle.js 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738
  1. // [z-paging]数据处理模块
  2. import u from '.././z-paging-utils'
  3. import c from '.././z-paging-constant'
  4. import Enum from '.././z-paging-enum'
  5. import interceptor from '../z-paging-interceptor'
  6. export default {
  7. props: {
  8. // 自定义初始的pageNo,默认为1
  9. defaultPageNo: {
  10. type: [Number, String],
  11. default: u.gc('defaultPageNo', 1),
  12. observer: function(newVal) {
  13. this.pageNo = newVal;
  14. },
  15. },
  16. // 自定义pageSize,默认为10
  17. defaultPageSize: {
  18. type: [Number, String],
  19. default: u.gc('defaultPageSize', 10),
  20. validator: (value) => {
  21. if (value <= 0) u.consoleErr('default-page-size必须大于0!');
  22. return value > 0;
  23. }
  24. },
  25. // 为保证数据一致,设置当前tab切换时的标识key,并在complete中传递相同key,若二者不一致,则complete将不会生效
  26. dataKey: {
  27. type: [Number, String, Object],
  28. default: u.gc('dataKey', null),
  29. },
  30. // 使用缓存,若开启将自动缓存第一页的数据,默认为否。请注意,因考虑到切换tab时不同tab数据不同的情况,默认仅会缓存组件首次加载时第一次请求到的数据,后续的下拉刷新操作不会更新缓存。
  31. useCache: {
  32. type: Boolean,
  33. default: u.gc('useCache', false)
  34. },
  35. // 使用缓存时缓存的key,用于区分不同列表的缓存数据,useCache为true时必须设置,否则缓存无效
  36. cacheKey: {
  37. type: String,
  38. default: u.gc('cacheKey', null)
  39. },
  40. // 缓存模式,默认仅会缓存组件首次加载时第一次请求到的数据,可设置为always,即代表总是缓存,每次列表刷新(下拉刷新、调用reload等)都会更新缓存
  41. cacheMode: {
  42. type: String,
  43. default: u.gc('cacheMode', Enum.CacheMode.Default)
  44. },
  45. // 自动注入的list名,可自动修改父view(包含ref="paging")中对应name的list值
  46. autowireListName: {
  47. type: String,
  48. default: u.gc('autowireListName', '')
  49. },
  50. // 自动注入的query名,可自动调用父view(包含ref="paging")中的query方法
  51. autowireQueryName: {
  52. type: String,
  53. default: u.gc('autowireQueryName', '')
  54. },
  55. // 获取分页数据Function,功能与@query类似。若设置了fetch则@query将不再触发
  56. fetch: {
  57. type: Function,
  58. default: null
  59. },
  60. // fetch的附加参数,fetch配置后有效
  61. fetchParams: {
  62. type: Object,
  63. default: u.gc('fetchParams', null)
  64. },
  65. // z-paging mounted后自动调用reload方法(mounted后自动调用接口),默认为是
  66. auto: {
  67. type: Boolean,
  68. default: u.gc('auto', true)
  69. },
  70. // 用户下拉刷新时是否触发reload方法,默认为是
  71. reloadWhenRefresh: {
  72. type: Boolean,
  73. default: u.gc('reloadWhenRefresh', true)
  74. },
  75. // reload时自动滚动到顶部,默认为是
  76. autoScrollToTopWhenReload: {
  77. type: Boolean,
  78. default: u.gc('autoScrollToTopWhenReload', true)
  79. },
  80. // reload时立即自动清空原list,默认为是,若立即自动清空,则在reload之后、请求回调之前页面是空白的
  81. autoCleanListWhenReload: {
  82. type: Boolean,
  83. default: u.gc('autoCleanListWhenReload', true)
  84. },
  85. // 列表刷新时自动显示下拉刷新view,默认为否
  86. showRefresherWhenReload: {
  87. type: Boolean,
  88. default: u.gc('showRefresherWhenReload', false)
  89. },
  90. // 列表刷新时自动显示加载更多view,且为加载中状态,默认为否
  91. showLoadingMoreWhenReload: {
  92. type: Boolean,
  93. default: u.gc('showLoadingMoreWhenReload', false)
  94. },
  95. // 组件created时立即触发reload(可解决一些情况下先看到页面再看到loading的问题),auto为true时有效。为否时将在mounted+nextTick后触发reload,默认为否
  96. createdReload: {
  97. type: Boolean,
  98. default: u.gc('createdReload', false)
  99. },
  100. // 本地分页时上拉加载更多延迟时间,单位为毫秒,默认200毫秒
  101. localPagingLoadingTime: {
  102. type: [Number, String],
  103. default: u.gc('localPagingLoadingTime', 200)
  104. },
  105. // 自动拼接complete中传过来的数组(使用聊天记录模式时无效)
  106. concat: {
  107. type: Boolean,
  108. default: u.gc('concat', true)
  109. },
  110. // 请求失败是否触发reject,默认为是
  111. callNetworkReject: {
  112. type: Boolean,
  113. default: u.gc('callNetworkReject', true)
  114. },
  115. // 父组件v-model所绑定的list的值
  116. value: {
  117. type: Array,
  118. default: function() {
  119. return [];
  120. }
  121. },
  122. // #ifdef VUE3
  123. modelValue: {
  124. type: Array,
  125. default: function() {
  126. return [];
  127. }
  128. }
  129. // #endif
  130. },
  131. data (){
  132. return {
  133. currentData: [],
  134. totalData: [],
  135. realTotalData: [],
  136. totalLocalPagingList: [],
  137. dataPromiseResultMap: {
  138. reload: null,
  139. complete: null,
  140. localPaging: null
  141. },
  142. isSettingCacheList: false,
  143. pageNo: 1,
  144. currentRefreshPageSize: 0,
  145. isLocalPaging: false,
  146. isAddedData: false,
  147. isTotalChangeFromAddData: false,
  148. privateConcat: true,
  149. myParentQuery: -1,
  150. firstPageLoaded: false,
  151. pagingLoaded: false,
  152. loaded: false,
  153. isUserReload: true,
  154. fromEmptyViewReload: false,
  155. queryFrom: '',
  156. listRendering: false,
  157. isHandlingRefreshToPage: false,
  158. isFirstPageAndNoMore: false,
  159. totalDataChangeThrow: true
  160. }
  161. },
  162. computed: {
  163. pageSize() {
  164. return this.defaultPageSize;
  165. },
  166. finalConcat() {
  167. return this.concat && this.privateConcat;
  168. },
  169. finalUseCache() {
  170. if (this.useCache && !this.cacheKey) {
  171. u.consoleErr('use-cache为true时,必须设置cache-key,否则缓存无效!');
  172. }
  173. return this.useCache && !!this.cacheKey;
  174. },
  175. finalCacheKey() {
  176. return this.cacheKey ? `${c.cachePrefixKey}-${this.cacheKey}` : null;
  177. },
  178. isFirstPage() {
  179. return this.pageNo === this.defaultPageNo;
  180. }
  181. },
  182. watch: {
  183. totalData(newVal, oldVal) {
  184. this._totalDataChange(newVal, oldVal, this.totalDataChangeThrow);
  185. this.totalDataChangeThrow = true;
  186. },
  187. currentData(newVal, oldVal) {
  188. this._currentDataChange(newVal, oldVal);
  189. },
  190. useChatRecordMode(newVal, oldVal) {
  191. if (newVal) {
  192. this.nLoadingMoreFixedHeight = false;
  193. }
  194. },
  195. value: {
  196. handler(newVal) {
  197. // 当v-model绑定的数据源被更改时,此时数据源改变不emit input事件,避免循环调用
  198. if (newVal !== this.totalData) {
  199. this.totalDataChangeThrow = false;
  200. this.totalData = newVal;
  201. }
  202. },
  203. immediate: true
  204. },
  205. // #ifdef VUE3
  206. modelValue: {
  207. handler(newVal) {
  208. // 当v-model绑定的数据源被更改时,此时数据源改变不emit input事件,避免循环调用
  209. if (newVal !== this.totalData) {
  210. this.totalDataChangeThrow = false;
  211. this.totalData = newVal;
  212. }
  213. },
  214. immediate: true
  215. }
  216. // #endif
  217. },
  218. methods: {
  219. // 请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为是否成功(默认为是)
  220. complete(data, success = true) {
  221. this.customNoMore = -1;
  222. return this.addData(data, success);
  223. },
  224. //【保证数据一致】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为dataKey,需与:data-key绑定的一致,第三个参数为是否成功(默认为是)
  225. completeByKey(data, dataKey = null, success = true) {
  226. if (dataKey !== null && this.dataKey !== null && dataKey !== this.dataKey) {
  227. this.isFirstPage && this.endRefresh();
  228. return new Promise(resolve => resolve());
  229. }
  230. this.customNoMore = -1;
  231. return this.addData(data, success);
  232. },
  233. //【通过total判断是否有更多数据】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为total(列表总数),第三个参数为是否成功(默认为是)
  234. completeByTotal(data, total, success = true) {
  235. if (total == 'undefined') {
  236. this.customNoMore = -1;
  237. } else {
  238. const dataTypeRes = this._checkDataType(data, success, false);
  239. data = dataTypeRes.data;
  240. success = dataTypeRes.success;
  241. if (total >= 0 && success) {
  242. return new Promise((resolve, reject) => {
  243. this.$nextTick(() => {
  244. let nomore = false;
  245. const realTotalDataCount = this.pageNo == this.defaultPageNo ? 0 : this.realTotalData.length;
  246. const dataLength = this.privateConcat ? data.length : 0;
  247. let exceedCount = realTotalDataCount + dataLength - total;
  248. // 没有更多数据了
  249. if (exceedCount >= 0) {
  250. nomore = true;
  251. // 仅截取total内部分的数据
  252. exceedCount = this.defaultPageSize - exceedCount;
  253. if (this.privateConcat && exceedCount > 0 && exceedCount < data.length) {
  254. data = data.splice(0, exceedCount);
  255. }
  256. }
  257. this.completeByNoMore(data, nomore, success).then(res => resolve(res)).catch(() => reject());
  258. })
  259. });
  260. }
  261. }
  262. return this.addData(data, success);
  263. },
  264. //【自行判断是否有更多数据】请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging处理,第一个参数为请求结果数组,第二个参数为是否没有更多数据,第三个参数为是否成功(默认是是)
  265. completeByNoMore(data, nomore, success = true) {
  266. if (nomore != 'undefined') {
  267. this.customNoMore = nomore == true ? 1 : 0;
  268. }
  269. return this.addData(data, success);
  270. },
  271. // 请求结束且请求失败时调用,支持传入请求失败原因
  272. completeByError(errorMsg) {
  273. this.customerEmptyViewErrorText = errorMsg;
  274. return this.complete(false);
  275. },
  276. // 与上方complete方法功能一致,新版本中设置服务端回调数组请使用complete方法
  277. addData(data, success = true) {
  278. if (!this.fromCompleteEmit) {
  279. this.disabledCompleteEmit = true;
  280. this.fromCompleteEmit = false;
  281. }
  282. const currentTimeStamp = u.getTime();
  283. const disTime = currentTimeStamp - this.requestTimeStamp;
  284. let minDelay = this.minDelay;
  285. if (this.isFirstPage && this.finalShowRefresherWhenReload) {
  286. minDelay = Math.max(400, minDelay);
  287. }
  288. const addDataDalay = (this.requestTimeStamp > 0 && disTime < minDelay) ? minDelay - disTime : 0;
  289. this.$nextTick(() => {
  290. u.delay(() => {
  291. this._addData(data, success, false);
  292. }, this.delay > 0 ? this.delay : addDataDalay)
  293. })
  294. return new Promise((resolve, reject) => {
  295. this.dataPromiseResultMap.complete = { resolve, reject };
  296. });
  297. },
  298. // 从顶部添加数据,不会影响分页的pageNo和pageSize
  299. addDataFromTop(data, toTop = true, toTopWithAnimate = true) {
  300. // 数据是否拼接到顶部,如果是聊天记录模式并且列表没有倒置,则应该拼接在底部
  301. let addFromTop = !this.isChatRecordModeAndNotInversion;
  302. data = Object.prototype.toString.call(data) !== '[object Array]' ? [data] : (addFromTop ? data.reverse() : data);
  303. // #ifndef APP-NVUE
  304. this.finalUseVirtualList && this._setCellIndex(data, 'top')
  305. // #endif
  306. this.totalData = addFromTop ? [...data, ...this.totalData] : [...this.totalData, ...data];
  307. if (toTop) {
  308. u.delay(() => this.useChatRecordMode ? this.scrollToBottom(toTopWithAnimate) : this.scrollToTop(toTopWithAnimate));
  309. }
  310. },
  311. // 重新设置列表数据,调用此方法不会影响pageNo和pageSize,也不会触发请求。适用场景:当需要删除列表中某一项时,将删除对应项后的数组通过此方法传递给z-paging。(当出现类似的需要修改列表数组的场景时,请使用此方法,请勿直接修改page中:list.sync绑定的数组)
  312. resetTotalData(data) {
  313. this.isTotalChangeFromAddData = true;
  314. data = Object.prototype.toString.call(data) !== '[object Array]' ? [data] : data;
  315. this.totalData = data;
  316. },
  317. // 设置本地分页数据,请求结束(成功或者失败)调用此方法,将请求的结果传递给z-paging作分页处理(若调用了此方法,则上拉加载更多时内部会自动分页,不会触发@query所绑定的事件)
  318. setLocalPaging(data, success = true) {
  319. this.isLocalPaging = true;
  320. this.$nextTick(() => {
  321. this._addData(data, success, true);
  322. })
  323. return new Promise((resolve, reject) => {
  324. this.dataPromiseResultMap.localPaging = { resolve, reject };
  325. });
  326. },
  327. // 重新加载分页数据,pageNo会恢复为默认值,相当于下拉刷新的效果(animate为true时会展示下拉刷新动画,默认为false)
  328. reload(animate = this.showRefresherWhenReload) {
  329. if (animate) {
  330. this.privateShowRefresherWhenReload = animate;
  331. this.isUserPullDown = true;
  332. }
  333. if (!this.showLoadingMoreWhenReload) {
  334. this.listRendering = true;
  335. }
  336. this.$nextTick(() => {
  337. this._preReload(animate, false);
  338. })
  339. return new Promise((resolve, reject) => {
  340. this.dataPromiseResultMap.reload = { resolve, reject };
  341. });
  342. },
  343. // 刷新列表数据,pageNo和pageSize不会重置,列表数据会重新从服务端获取。必须保证@query绑定的方法中的pageNo和pageSize和传给服务端的一致
  344. refresh() {
  345. return this._handleRefreshWithDisPageNo(this.pageNo - this.defaultPageNo + 1);
  346. },
  347. // 刷新列表数据至指定页,例如pageNo=5时则代表刷新列表至第5页,此时pageNo会变为5,列表会展示前5页的数据。必须保证@query绑定的方法中的pageNo和pageSize和传给服务端的一致
  348. refreshToPage(pageNo) {
  349. this.isHandlingRefreshToPage = true;
  350. return this._handleRefreshWithDisPageNo(pageNo + this.defaultPageNo - 1);
  351. },
  352. // 手动更新列表缓存数据,将自动截取v-model绑定的list中的前pageSize条覆盖缓存,请确保在list数据更新到预期结果后再调用此方法
  353. updateCache() {
  354. if (this.finalUseCache && this.totalData.length) {
  355. this._saveLocalCache(this.totalData.slice(0, Math.min(this.totalData.length, this.pageSize)));
  356. }
  357. },
  358. // 清空分页数据
  359. clean() {
  360. this._reload(true);
  361. this._addData([], true, false);
  362. },
  363. // 清空分页数据
  364. clear() {
  365. this.clean();
  366. },
  367. // reload之前的一些处理
  368. _preReload(animate = this.showRefresherWhenReload, isFromMounted = true, retryCount = 0) {
  369. const showRefresher = this.finalRefresherEnabled && this.useCustomRefresher;
  370. // #ifndef APP-NVUE
  371. // 如果获取slot="refresher"高度失败,则不触发reload,直到获取slot="refresher"高度成功
  372. if (this.customRefresherHeight === -1 && showRefresher) {
  373. u.delay(() => {
  374. retryCount ++;
  375. // 如果重试次数是10的倍数(也就是每500毫秒),尝试重新获取一下slot="refresher"高度
  376. // 此举是为了解决在某些特殊情况下,z-paging组件mounted了,但是未展示在用户面前,(比如在tabbar页面中,未切换到对应tabbar但是通过代码让z-paging展示了,此时控制台会报Error: Not Found:Page,因为这时候去获取dom节点信息获取不到)
  377. // 当用户在某个时刻让此z-paging展示在面前时,即可顺利获取到slot="refresher"高度,递归停止
  378. if (retryCount % 10 === 0) {
  379. this._updateCustomRefresherHeight();
  380. }
  381. this._preReload(animate, isFromMounted, retryCount);
  382. }, c.delayTime / 2);
  383. return;
  384. }
  385. // #endif
  386. this.isUserReload = true;
  387. this.loadingType = Enum.LoadingType.Refresher;
  388. if (animate) {
  389. this.privateShowRefresherWhenReload = animate;
  390. // #ifndef APP-NVUE
  391. if (this.useCustomRefresher) {
  392. this._doRefresherRefreshAnimate();
  393. } else {
  394. this.refresherTriggered = true;
  395. }
  396. // #endif
  397. // #ifdef APP-NVUE
  398. this.refresherStatus = Enum.Refresher.Loading;
  399. this.refresherRevealStackCount ++;
  400. u.delay(() => {
  401. this._getNodeClientRect('zp-n-refresh-container', false).then((node) => {
  402. if (node) {
  403. let nodeHeight = node[0].height;
  404. this.nShowRefresherReveal = true;
  405. this.nShowRefresherRevealHeight = nodeHeight;
  406. u.delay(() => {
  407. this._nDoRefresherEndAnimation(0, -nodeHeight, false, false);
  408. u.delay(() => {
  409. this._nDoRefresherEndAnimation(nodeHeight, 0);
  410. }, 10)
  411. }, 10)
  412. }
  413. this._reload(false, isFromMounted);
  414. this._doRefresherLoad(false);
  415. });
  416. }, this.pagingLoaded ? 10 : 100)
  417. return;
  418. // #endif
  419. } else {
  420. this._refresherEnd(false, false, false, false);
  421. }
  422. this._reload(false, isFromMounted);
  423. },
  424. // 重新加载分页数据
  425. _reload(isClean = false, isFromMounted = false, isUserPullDown = false) {
  426. this.isAddedData = false;
  427. this.insideOfPaging = -1;
  428. this.cacheScrollNodeHeight = -1;
  429. this.pageNo = this.defaultPageNo;
  430. this._cleanRefresherEndTimeout();
  431. !this.privateShowRefresherWhenReload && !isClean && this._startLoading(true);
  432. this.firstPageLoaded = true;
  433. this.isTotalChangeFromAddData = false;
  434. if (!this.isSettingCacheList) {
  435. this.totalData = [];
  436. }
  437. if (!isClean) {
  438. this._emitQuery(this.pageNo, this.defaultPageSize, isUserPullDown ? Enum.QueryFrom.UserPullDown : Enum.QueryFrom.Reload);
  439. let delay = 0;
  440. // #ifdef MP-TOUTIAO
  441. delay = 5;
  442. // #endif
  443. u.delay(this._callMyParentQuery, delay);
  444. if (!isFromMounted && this.autoScrollToTopWhenReload) {
  445. let checkedNRefresherLoading = true;
  446. // #ifdef APP-NVUE
  447. checkedNRefresherLoading = !this.nRefresherLoading;
  448. // #endif
  449. checkedNRefresherLoading && this._scrollToTop(false);
  450. }
  451. }
  452. // #ifdef APP-NVUE
  453. this.$nextTick(() => {
  454. this.nShowBottom = this.realTotalData.length > 0;
  455. })
  456. // #endif
  457. },
  458. // 处理服务端返回的数组
  459. _addData(data, success, isLocal) {
  460. this.isAddedData = true;
  461. this.fromEmptyViewReload = false;
  462. this.isTotalChangeFromAddData = true;
  463. this.refresherTriggered = false;
  464. this._endSystemLoadingAndRefresh();
  465. const tempIsUserPullDown = this.isUserPullDown;
  466. if (this.showRefresherUpdateTime && this.isFirstPage) {
  467. u.setRefesrherTime(u.getTime(), this.refresherUpdateTimeKey);
  468. this.$refs.refresh && this.$refs.refresh.updateTime();
  469. }
  470. if (!isLocal && tempIsUserPullDown && this.isFirstPage) {
  471. this.isUserPullDown = false;
  472. }
  473. if (!this.isFirstPage) {
  474. this.listRendering = true;
  475. this.$nextTick(() => {
  476. u.delay(() => this.listRendering = false);
  477. })
  478. } else {
  479. this.listRendering = false;
  480. }
  481. let dataTypeRes = this._checkDataType(data, success, isLocal);
  482. data = dataTypeRes.data;
  483. success = dataTypeRes.success;
  484. let delayTime = c.delayTime;
  485. if (this.useChatRecordMode) delayTime = 0;
  486. this.loadingForNow = false;
  487. u.delay(() => {
  488. this.pagingLoaded = true;
  489. this.$nextTick(()=>{
  490. !isLocal && this._refresherEnd(delayTime > 0, true, tempIsUserPullDown);
  491. })
  492. })
  493. if (this.isFirstPage) {
  494. this.isLoadFailed = !success;
  495. this.$emit('isLoadFailedChange', this.isLoadFailed);
  496. if (this.finalUseCache && success && (this.cacheMode === Enum.CacheMode.Always ? true : this.isSettingCacheList)) {
  497. this._saveLocalCache(data);
  498. }
  499. }
  500. this.isSettingCacheList = false;
  501. if (success) {
  502. if (!(this.privateConcat === false && !this.isHandlingRefreshToPage && this.loadingStatus === Enum.More.NoMore)) {
  503. this.loadingStatus = Enum.More.Default;
  504. }
  505. if (isLocal) {
  506. // 如果当前是本地分页,则必然是由setLocalPaging方法触发,此时直接本地加载第一页数据即可。后续本地分页加载更多方法由滚动到底部加载更多事件处理
  507. this.totalLocalPagingList = data;
  508. const localPageNo = this.defaultPageNo;
  509. const localPageSize = this.queryFrom !== Enum.QueryFrom.Refresh ? this.defaultPageSize : this.currentRefreshPageSize;
  510. this._localPagingQueryList(localPageNo, localPageSize, 0, res => {
  511. this.completeByTotal(res, this.totalLocalPagingList.length);
  512. })
  513. } else {
  514. // 如果当前不是本地分页,则按照正常分页逻辑进行数据处理&emit数据
  515. let dataChangeDelayTime = 0;
  516. // #ifdef APP-NVUE
  517. if (this.privateShowRefresherWhenReload && this.finalNvueListIs === 'waterfall') {
  518. dataChangeDelayTime = 150;
  519. }
  520. // #endif
  521. u.delay(() => {
  522. this._currentDataChange(data, this.currentData);
  523. this._callDataPromise(true, this.totalData);
  524. }, dataChangeDelayTime)
  525. }
  526. if (this.isHandlingRefreshToPage) {
  527. this.isHandlingRefreshToPage = false;
  528. this.pageNo = this.defaultPageNo + Math.ceil(data.length / this.pageSize) - 1;
  529. if (data.length % this.pageSize !== 0) {
  530. this.customNoMore = 1;
  531. }
  532. }
  533. } else {
  534. this._currentDataChange(data, this.currentData);
  535. this._callDataPromise(false);
  536. this.loadingStatus = Enum.More.Fail;
  537. this.isHandlingRefreshToPage = false;
  538. if (this.loadingType === Enum.LoadingType.LoadingMore) {
  539. this.pageNo --;
  540. }
  541. }
  542. },
  543. // 所有数据改变时调用
  544. _totalDataChange(newVal, oldVal, eventThrow=true) {
  545. if ((!this.isUserReload || !this.autoCleanListWhenReload) && this.firstPageLoaded && !newVal.length && oldVal.length) {
  546. return;
  547. }
  548. this._doCheckScrollViewShouldFullHeight(newVal);
  549. if(!this.realTotalData.length && !newVal.length){
  550. eventThrow = false;
  551. }
  552. this.realTotalData = newVal;
  553. // emit列表更新事件
  554. if (eventThrow) {
  555. this.$emit('input', newVal);
  556. // #ifdef VUE3
  557. this.$emit('update:modelValue', newVal);
  558. // #endif
  559. this.$emit('update:list', newVal);
  560. this.$emit('listChange', newVal);
  561. this._callMyParentList(newVal);
  562. }
  563. this.firstPageLoaded = false;
  564. this.isTotalChangeFromAddData = false;
  565. this.$nextTick(() => {
  566. u.delay(()=>{
  567. // emit z-paging内容区域高度改变事件
  568. this._getNodeClientRect('.zp-paging-container-content').then(res => {
  569. res && this.$emit('contentHeightChanged', res[0].height);
  570. });
  571. }, c.delayTime * (this.isIos ? 1 : 3))
  572. // #ifdef APP-NVUE
  573. // 在nvue中延时600毫秒展示底部加载更多,避免底部加载更多太早加载闪一下的问题
  574. u.delay(() => {
  575. this.nShowBottom = true;
  576. }, c.delayTime * 6, 'nShowBottomDelay');
  577. // #endif
  578. })
  579. },
  580. // 当前数据改变时调用
  581. _currentDataChange(newVal, oldVal) {
  582. newVal = [...newVal];
  583. // #ifndef APP-NVUE
  584. this.finalUseVirtualList && this._setCellIndex(newVal, 'bottom');
  585. // #endif
  586. if (this.isFirstPage && this.finalConcat) {
  587. this.totalData = [];
  588. }
  589. // customNoMore:-1代表交由z-paging自行判断;1代表没有更多了;0代表还有更多数据
  590. if (this.customNoMore !== -1) {
  591. // 如果customNoMore等于1 或者 customNoMore不是0并且新增数组长度为0(也就是不是明确的还有更多数据并且新增的数组长度为0),则没有更多数据了
  592. if (this.customNoMore === 1 || (this.customNoMore !== 0 && !newVal.length)) {
  593. this.loadingStatus = Enum.More.NoMore;
  594. }
  595. } else {
  596. // 如果新增的数据数组长度为0 或者 新增的数组长度小于默认的pageSize,则没有更多数据了
  597. if (!newVal.length || (newVal.length && newVal.length < this.defaultPageSize)) {
  598. this.loadingStatus = Enum.More.NoMore;
  599. }
  600. }
  601. if (!this.totalData.length) {
  602. // #ifdef APP-NVUE
  603. // 如果在聊天记录模式+nvue中,并且数据不满一页时需要将列表倒序,因为此时没有将列表旋转180度,数组中第0条数据应当在最底下显示
  604. if (this.useChatRecordMode && this.finalConcat && this.isFirstPage && this.loadingStatus === Enum.More.NoMore) {
  605. newVal.reverse();
  606. }
  607. // #endif
  608. this.totalData = newVal;
  609. } else {
  610. if (this.finalConcat) {
  611. const currentScrollTop = this.oldScrollTop;
  612. this.totalData = [...this.totalData, ...newVal];
  613. // 此处是为了解决在微信小程序中,在某些情况下滚动到底部加载更多后滚动位置直接变为最底部的问题,因此需要通过代码强制滚动回加载更多前的位置
  614. // #ifdef MP-WEIXIN
  615. if (!this.isIos && !this.refresherOnly && !this.usePageScroll && newVal.length) {
  616. this.loadingMoreTimeStamp = u.getTime();
  617. this.$nextTick(() => {
  618. this.scrollToY(currentScrollTop);
  619. })
  620. }
  621. // #endif
  622. } else {
  623. this.totalData = newVal;
  624. }
  625. }
  626. this.privateConcat = true;
  627. },
  628. // 根据pageNo处理refresh操作
  629. _handleRefreshWithDisPageNo(pageNo) {
  630. if (!this.isHandlingRefreshToPage && !this.realTotalData.length) return this.reload();
  631. if (pageNo >= 1) {
  632. this.loading = true;
  633. this.privateConcat = false;
  634. const totalPageSize = pageNo * this.pageSize;
  635. this.currentRefreshPageSize = totalPageSize;
  636. // 如果调用refresh时是本地分页,则在组件内部自己处理分页逻辑,不emit query相关事件
  637. if (this.isLocalPaging && this.isHandlingRefreshToPage) {
  638. this._localPagingQueryList(this.defaultPageNo, totalPageSize, 0, res => {
  639. this.complete(res);
  640. })
  641. } else {
  642. // emit query相关事件
  643. this._emitQuery(this.defaultPageNo, totalPageSize, Enum.QueryFrom.Refresh);
  644. this._callMyParentQuery(this.defaultPageNo, totalPageSize);
  645. }
  646. }
  647. return new Promise((resolve, reject) => {
  648. this.dataPromiseResultMap.reload = { resolve, reject };
  649. });
  650. },
  651. // 本地分页请求
  652. _localPagingQueryList(pageNo, pageSize, localPagingLoadingTime, callback) {
  653. pageNo = Math.max(1, pageNo);
  654. pageSize = Math.max(1, pageSize);
  655. const totalPagingList = [...this.totalLocalPagingList];
  656. const pageNoIndex = (pageNo - 1) * pageSize;
  657. const finalPageNoIndex = Math.min(totalPagingList.length, pageNoIndex + pageSize);
  658. const resultPagingList = totalPagingList.splice(pageNoIndex, finalPageNoIndex - pageNoIndex);
  659. u.delay(() => callback(resultPagingList), localPagingLoadingTime)
  660. },
  661. // 存储列表缓存数据
  662. _saveLocalCache(data) {
  663. uni.setStorageSync(this.finalCacheKey, data);
  664. },
  665. // 通过缓存数据填充列表数据
  666. _setListByLocalCache() {
  667. this.totalData = uni.getStorageSync(this.finalCacheKey) || [];
  668. this.isSettingCacheList = true;
  669. },
  670. // 修改父view的list
  671. _callMyParentList(newVal) {
  672. if (this.autowireListName.length) {
  673. const myParent = u.getParent(this.$parent);
  674. if (myParent && myParent[this.autowireListName]) {
  675. myParent[this.autowireListName] = newVal;
  676. }
  677. }
  678. },
  679. // 调用父view的query
  680. _callMyParentQuery(customPageNo = 0, customPageSize = 0) {
  681. if (this.autowireQueryName) {
  682. if (this.myParentQuery === -1) {
  683. const myParent = u.getParent(this.$parent);
  684. if (myParent && myParent[this.autowireQueryName]) {
  685. this.myParentQuery = myParent[this.autowireQueryName];
  686. }
  687. }
  688. if (this.myParentQuery !== -1) {
  689. customPageSize > 0 ? this.myParentQuery(customPageNo, customPageSize) : this.myParentQuery(this.pageNo, this.defaultPageSize);
  690. }
  691. }
  692. },
  693. // emit query事件
  694. _emitQuery(pageNo, pageSize, from){
  695. this.queryFrom = from;
  696. this.requestTimeStamp = u.getTime();
  697. const [lastItem] = this.realTotalData.slice(-1);
  698. if (this.fetch) {
  699. const fetchParams = interceptor._handleFetchParams({pageNo, pageSize, from, lastItem: lastItem || null}, this.fetchParams);
  700. const fetchResult = this.fetch(fetchParams);
  701. if (!interceptor._handleFetchResult(fetchResult, this, fetchParams)) {
  702. u.isPromise(fetchResult) ? fetchResult.then(res => {
  703. this.complete(res);
  704. }).catch(err => {
  705. this.complete(false);
  706. }) : this.complete(fetchResult)
  707. }
  708. } else {
  709. this.$emit('query', ...interceptor._handleQuery(pageNo, pageSize, from, lastItem || null));
  710. }
  711. },
  712. // 触发数据改变promise
  713. _callDataPromise(success, totalList) {
  714. for (const key in this.dataPromiseResultMap) {
  715. const obj = this.dataPromiseResultMap[key];
  716. if (!obj) continue;
  717. success ? obj.resolve({ totalList, noMore: this.loadingStatus === Enum.More.NoMore }) : this.callNetworkReject && obj.reject(`z-paging-${key}-error`);
  718. }
  719. },
  720. // 检查complete data的类型
  721. _checkDataType(data, success, isLocal) {
  722. const dataType = Object.prototype.toString.call(data);
  723. if (dataType === '[object Boolean]') {
  724. success = data;
  725. data = [];
  726. } else if (dataType !== '[object Array]') {
  727. data = [];
  728. if (dataType !== '[object Undefined]' && dataType !== '[object Null]') {
  729. u.consoleErr(`${isLocal ? 'setLocalPaging' : 'complete'}参数类型不正确,第一个参数类型必须为Array!`);
  730. }
  731. }
  732. return { data, success };
  733. },
  734. }
  735. }