Files
2026-02-23 16:31:39 +08:00

733 lines
23 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// [z-paging]数据处理模块
import u from '.././z-paging-utils'
import c from '.././z-paging-constant'
import Enum from '.././z-paging-enum'
import interceptor from '../z-paging-interceptor'
const ZPData = {
props: {
//自定义初始的pageNo默认为1
defaultPageNo: {
type: [Number, String],
default: u.gc('defaultPageNo', 1),
observer: function(newVal, oldVal) {
this.pageNo = newVal;
},
},
//自定义pageSize默认为10
defaultPageSize: {
type: [Number, String],
default: u.gc('defaultPageSize', 10),
validator: (value) => {
if(value <= 0) u.consoleErr('default-page-size必须大于0');
return value > 0;
}
},
//为保证数据一致设置当前tab切换时的标识key并在complete中传递相同key若二者不一致则complete将不会生效
dataKey: {
type: [Number, String, Object],
default: function() {
return u.gc('dataKey', null);
},
},
//自动注入的list名可自动修改父view(包含ref="paging")中对应name的list值
autowireListName: {
type: String,
default: function() {
return u.gc('autowireListName', '');
},
},
//自动注入的query名可自动调用父view(包含ref="paging")中的query方法
autowireQueryName: {
type: String,
default: function() {
return u.gc('autowireQueryName', '');
},
},
//z-paging mounted后自动调用reload方法(mounted后自动调用接口)默认为是。请使用简便写法auto
mountedAutoCallReload: {
type: Boolean,
default: u.gc('mountedAutoCallReload', true)
},
//z-paging mounted后自动调用reload方法(mounted后自动调用接口),默认为是
auto: {
type: Boolean,
default: u.gc('auto', true)
},
//用户下拉刷新时是否触发reload方法默认为是
reloadWhenRefresh: {
type: Boolean,
default: u.gc('reloadWhenRefresh', true)
},
//reload时自动滚动到顶部默认为是
autoScrollToTopWhenReload: {
type: Boolean,
default: u.gc('autoScrollToTopWhenReload', true)
},
//reload时立即自动清空原list默认为是若立即自动清空则在reload之后、请求回调之前页面是空白的
autoCleanListWhenReload: {
type: Boolean,
default: u.gc('autoCleanListWhenReload', true)
},
//列表刷新时自动显示下拉刷新view默认为否
showRefresherWhenReload: {
type: Boolean,
default: u.gc('showRefresherWhenReload', false)
},
//列表刷新时自动显示加载更多view且为加载中状态默认为否
showLoadingMoreWhenReload: {
type: Boolean,
default: u.gc('showLoadingMoreWhenReload', false)
},
//组件created时立即触发reload(可解决一些情况下先看到页面再看到loading的问题)auto为true时有效。为否时将在mounted+nextTick后触发reload默认为否
createdReload: {
type: Boolean,
default: u.gc('createdReload', false)
},
//本地分页时上拉加载更多延迟时间单位为毫秒默认200毫秒
localPagingLoadingTime: {
type: [Number, String],
default: u.gc('localPagingLoadingTime', 200)
},
//当分页未满一屏时,是否自动加载更多,默认为否(nvue无效)
insideMore: {
type: Boolean,
default: u.gc('insideMore', false)
},
//使用聊天记录模式,默认为否
useChatRecordMode: {
type: Boolean,
default: u.gc('useChatRecordMode', false)
},
//自动拼接complete中传过来的数组(使用聊天记录模式时无效)
concat: {
type: Boolean,
default: u.gc('concat', true)
},
//父组件v-model所绑定的list的值
value: {
type: Array,
default: function() {
return [];
}
},
// #ifdef VUE3
modelValue: {
type: Array,
default: function() {
return [];
}
}
// #endif
},
data (){
return {
currentData: [],
totalData: [],
realTotalData: [],
totalLocalPagingList: [],
pageNo: 1,
isLocalPaging: false,
isAddedData: false,
isTotalChangeFromAddData: false,
privateConcat: true,
myParentQuery: -1,
firstPageLoaded: false,
pagingLoaded: false,
loaded: false,
isUserReload: true,
fromEmptyViewReload: false,
listRendering: false
}
},
computed: {
pageSize() {
return this.defaultPageSize;
},
finalConcat() {
return this.concat && this.privateConcat;
},
isFirstPage() {
return this.pageNo === this.defaultPageNo;
}
},
watch: {
totalData(newVal, oldVal) {
this._totalDataChange(newVal, oldVal);
},
currentData(newVal, oldVal) {
this._currentDataChange(newVal, oldVal);
},
useChatRecordMode(newVal, oldVal) {
if (newVal) {
this.nLoadingMoreFixedHeight = false;
}
},
value: {
handler(newVal) {
this.realTotalData = newVal;
},
immediate: true
},
// #ifdef VUE3
modelValue: {
handler(newVal) {
this.realTotalData = newVal;
},
immediate: true
}
// #endif
},
methods: {
//请求结束(成功或者失败)调用此方法将请求的结果传递给z-paging处理第一个参数为请求结果数组第二个参数为是否成功(默认是是)
complete(data, success = true) {
this.customNoMore = -1;
this.addData(data, success);
},
//简写与complete完全相同
end(data, success = true) {
this.complete(data, success);
},
//【保证数据一致】请求结束(成功或者失败)调用此方法将请求的结果传递给z-paging处理第一个参数为请求结果数组第二个参数为dataKey需与:data-key绑定的一致第三个参数为是否成功(默认为是)
completeByKey(data, dataKey = null, success = true) {
if (dataKey !== null && this.dataKey !== null && dataKey !== this.dataKey) {
if (this.isFirstPage) {
this.endRefresh();
}
return;
}
this.customNoMore = -1;
this.addData(data, success);
},
//简写与completeByKey完全相同
endByKey(data, dataKey = null, success = true) {
this.completeByKey(data, dataKey, success);
},
//【通过totalCount判断是否有更多数据】请求结束(成功或者失败)调用此方法将请求的结果传递给z-paging处理第一个参数为请求结果数组第二个参数为totalCount(列表总数),第三个参数为是否成功(默认为是)
completeByTotalCount(data, totalCount, success = true) {
if (totalCount == 'undefined') {
this.customNoMore = -1;
} else {
let dataTypeRes = this._checkDataType(data, success, false);
data = dataTypeRes.data;
success = dataTypeRes.success;
if (totalCount >= 0 && success) {
this.$nextTick(() => {
let nomore = true;
let realTotalDataCount = this.realTotalData.length;
if (this.pageNo == this.defaultPageNo) {
realTotalDataCount = 0;
}
let exceedCount = realTotalDataCount + data.length - totalCount;
if (exceedCount >= 0) {
nomore = false;
exceedCount = this.defaultPageSize - exceedCount;
if (exceedCount > 0 && exceedCount < data.length) {
data = data.splice(0, exceedCount);
}
}
this.completeByNoMore(data, nomore, success);
})
return;
}
}
this.addData(data, success);
},
//简写与completeByTotalCount完全相同
completeByTotal(data, totalCount, success = true) {
this.completeByTotalCount(data, totalCount, success);
},
//简写与completeByTotalCount完全相同
endByTotalCount(data, totalCount, success = true) {
this.completeByTotalCount(data, totalCount, success);
},
//简写与completeByTotalCount完全相同
endByTotal(data, totalCount, success = true) {
this.completeByTotalCount(data, totalCount, success);
},
//【自行判断是否有更多数据】请求结束(成功或者失败)调用此方法将请求的结果传递给z-paging处理第一个参数为请求结果数组第二个参数为是否有更多数据第三个参数为是否成功(默认是是)
completeByNoMore(data, nomore, success = true) {
if (nomore != 'undefined') {
this.customNoMore = nomore == true ? 1 : 0;
}
this.addData(data, success);
},
//简写与completeByNoMore完全相同
endByNoMore(data, nomore, success = true) {
this.completeByNoMore(data, nomore, success);
},
//与上方complete方法功能一致新版本中设置服务端回调数组请使用complete方法
addData(data, success = true) {
if (!this.fromCompleteEmit) {
this.disabledCompleteEmit = true;
this.fromCompleteEmit = false;
}
const currentTimeStamp = u.getTime();
let addDataDalay = 0;
const disTime = currentTimeStamp - this.requestTimeStamp;
let minDelay = this.minDelay;
if(this.isFirstPage && this.finalShowRefresherWhenReload){
minDelay = Math.max(400,minDelay);
}
if(this.requestTimeStamp > 0 && disTime < minDelay){
addDataDalay = minDelay - disTime;
}
this.$nextTick(() => {
let delay = this.delay > 0 ? this.delay : addDataDalay;
setTimeout(() => {
this._addData(data, success, false);
}, delay)
})
},
//从顶部添加数据不会影响分页的pageNo和pageSize
addDataFromTop(data, toTop = true, toTopWithAnimate = true) {
let dataType = Object.prototype.toString.call(data);
if (dataType !== '[object Array]') {
data = [data];
}
this.totalData = [...data, ...this.totalData];
if (toTop) {
setTimeout(() => {
this._scrollToTop(toTopWithAnimate);
}, c.delayTime)
}
},
//重新设置列表数据调用此方法不会影响pageNo和pageSize也不会触发请求。适用场景当需要删除列表中某一项时将删除对应项后的数组通过此方法传递给z-paging。(当出现类似的需要修改列表数组的场景时请使用此方法请勿直接修改page中:list.sync绑定的数组)
resetTotalData(data) {
if (data == undefined) {
if (this.showConsoleError) {
u.consoleErr('方法resetTotalData参数缺失');
}
return;
}
this.isTotalChangeFromAddData = true;
let dataType = Object.prototype.toString.call(data);
if (dataType !== '[object Array]') {
data = [data];
}
this.totalData = data;
},
//添加聊天记录
addChatRecordData(data, toBottom = true, toBottomWithAnimate = true) {
let dataType = Object.prototype.toString.call(data);
if (dataType !== '[object Array]') {
data = [data];
}
if (!this.useChatRecordMode) return;
this.isTotalChangeFromAddData = true;
//#ifndef APP-NVUE
this.totalData = [...this.totalData, ...data];
//#endif
//#ifdef APP-NVUE
this.totalData = this.nIsFirstPageAndNoMore ? [...this.totalData, ...data] : [...data, ...this.totalData];
//#endif
if (toBottom) {
setTimeout(() => {
//#ifndef APP-NVUE
this._scrollToBottom(toBottomWithAnimate);
//#endif
//#ifdef APP-NVUE
if (this.nIsFirstPageAndNoMore) {
this._scrollToBottom(toBottomWithAnimate);
} else {
this._scrollToTop(toBottomWithAnimate);
}
//#endif
}, c.delayTime)
}
},
//设置本地分页数据,请求结束(成功或者失败)调用此方法将请求的结果传递给z-paging作分页处理若调用了此方法则上拉加载更多时内部会自动分页不会触发@query所绑定的事件
setLocalPaging(data, success = true) {
this.isLocalPaging = true;
this.$nextTick(() => {
this._addData(data, success, true);
})
},
//重新加载分页数据pageNo会恢复为默认值相当于下拉刷新的效果(animate为true时会展示下拉刷新动画默认为false)
reload(animate = this.showRefresherWhenReload) {
if (animate) {
this.privateShowRefresherWhenReload = animate;
this.isUserPullDown = true;
}
this._preReload(animate, false);
},
//刷新列表数据pageNo和pageSize不会重置列表数据会重新从服务端获取。必须保证@query绑定的方法中的pageNo和pageSize和传给服务端的一致
refresh() {
if(!this.realTotalData.length){
this.reload();
return;
}
const disPageNo = this.pageNo - this.defaultPageNo + 1;
if (disPageNo >= 1) {
this.loading = true;
this.privateConcat = false;
const totalPageSize = disPageNo * this.pageSize;
this._emitQuery(this.defaultPageNo, totalPageSize, Enum.QueryFrom.Refresh);
this._callMyParentQuery(this.defaultPageNo, totalPageSize);
}
},
//清空分页数据
clean() {
this._reload(true);
this._addData([], true, false);
},
//清空分页数据
clear() {
this.clean();
},
//手动触发滚动到顶部加载更多,聊天记录模式时有效
doChatRecordLoadMore() {
this.useChatRecordMode && this._onLoadingMore('click');
},
//reload之前的一些处理
_preReload(animate = this.showRefresherWhenReload, isFromMounted = true) {
this.isUserReload = true;
this.loadingType = Enum.LoadingType.Refresher;
if (animate) {
this.privateShowRefresherWhenReload = animate;
// #ifndef APP-NVUE
if (this.useCustomRefresher) {
this._doRefresherRefreshAnimate();
} else {
this.refresherTriggered = true;
}
// #endif
// #ifdef APP-NVUE
this.refresherStatus = Enum.Refresher.Loading;
this.refresherRevealStackCount++;
setTimeout(() => {
this._getNodeClientRect('zp-n-refresh-container', false).then((node) => {
if (node) {
let nodeHeight = node[0].height;
this.nShowRefresherReveal = true;
this.nShowRefresherRevealHeight = nodeHeight;
setTimeout(() => {
this._nDoRefresherEndAnimation(0, -nodeHeight, false, false);
setTimeout(() => {
this._nDoRefresherEndAnimation(nodeHeight, 0);
}, 10)
}, 10)
}
this._reload(false, isFromMounted);
this._doRefresherLoad(false);
});
}, 10)
return;
// #endif
} else {
this._refresherEnd(false, false, false, false);
}
this._reload(false, isFromMounted);
},
//重新加载分页数据
_reload(isClean = false, isFromMounted = false, isUserPullDown = false) {
this.isAddedData = false;
this.cacheScrollNodeHeight = -1;
this.insideOfPaging = -1;
this.pageNo = this.defaultPageNo;
this._cleanRefresherEndTimeout();
!this.privateShowRefresherWhenReload && !isClean && this._startLoading(true);
this.firstPageLoaded = true;
this.isTotalChangeFromAddData = false;
this.totalData = [];
if (!isClean) {
this._emitQuery(this.pageNo, this.defaultPageSize, isUserPullDown ? Enum.QueryFrom.UserPullDown : Enum.QueryFrom.Reload);
let delay = 0;
// #ifdef MP-TOUTIAO
delay = 5;
// #endif
setTimeout(() => {
this._callMyParentQuery();
}, delay)
if (!isFromMounted && this.autoScrollToTopWhenReload) {
let checkedNRefresherLoading = true;
// #ifdef APP-NVUE
checkedNRefresherLoading = !this.nRefresherLoading;
// #endif
if (checkedNRefresherLoading) {
this._scrollToTop(false);
}
}
// #ifndef APP-NVUE
if (!this.usePageScroll && this.useChatRecordMode) {
if (this.showConsoleError) {
u.consoleWarn('使用聊天记录模式时建议使用页面滚动可将usePageScroll设置为true以启用页面滚动');
}
}
// #endif
}
this.$nextTick(() => {
// #ifdef APP-NVUE
this.nShowBottom = this.realTotalData.length > 0;
// #endif
})
},
//处理服务端返回的数组
_addData(data, success, isLocal) {
this.isAddedData = true;
this.fromEmptyViewReload = false;
this.isTotalChangeFromAddData = true;
this.refresherTriggered = false;
!this.useCustomRefresher && uni.stopPullDownRefresh();
// #ifdef APP-NVUE
this.usePageScroll && uni.stopPullDownRefresh();
// #endif
const tempIsUserPullDown = this.isUserPullDown;
if (this.showRefresherUpdateTime && this.isFirstPage) {
u.setRefesrherTime(u.getTime(), this.refresherUpdateTimeKey);
this.tempLanguageUpdateKey = u.getTime();
this.$refs.refresh && this.$refs.refresh.updateTime();
}
if (tempIsUserPullDown && this.isFirstPage) {
this.isUserPullDown = false;
}
let dataTypeRes = this._checkDataType(data, success, isLocal);
data = dataTypeRes.data;
success = dataTypeRes.success;
let delayTime = c.delayTime;
// #ifdef APP-NVUE
if (this.useChatRecordMode) delayTime = 0;
// #endif
this.loadingForNow = false;
setTimeout(() => {
this.pagingLoaded = true;
this.$nextTick(()=>{
this._refresherEnd(delayTime > 0, true, tempIsUserPullDown);
})
}, delayTime)
if (this.isFirstPage) {
this.isLoadFailed = !success;
}
if (success) {
if (!(this.privateConcat === false && this.loadingStatus === Enum.More.NoMore)) {
this.loadingStatus = Enum.More.Default;
}
if (isLocal) {
this.totalLocalPagingList = data;
this._localPagingQueryList(this.defaultPageNo, this.defaultPageSize, 0, (res) => {
this.complete(res);
})
} else {
let dataChangeDelayTime = 0;
// #ifdef APP-NVUE
if (this.privateShowRefresherWhenReload && this.finalNvueListIs === 'waterfall') {
dataChangeDelayTime = 150;
}
// #endif
setTimeout(() => {
this._currentDataChange(data, this.currentData);
}, dataChangeDelayTime)
}
} else {
this._currentDataChange(data, this.currentData);
this.loadingStatus = Enum.More.Fail;
if (this.loadingType === Enum.LoadingType.LoadingMore) {
this.pageNo--;
}
}
},
//所有数据改变时调用
_totalDataChange(newVal, oldVal, eventThrow=true) {
if ((!this.isUserReload || !this.autoCleanListWhenReload) && this.firstPageLoaded && !newVal.length && oldVal.length) {
return;
}
this._doCheckScrollViewShouldFullHeight(newVal);
if(!this.realTotalData.length && !newVal.length){
eventThrow = false;
}
this.realTotalData = newVal;
if (eventThrow) {
this.$emit('input', newVal);
// #ifdef VUE3
this.$emit('update:modelValue', newVal);
// #endif
this.$emit('update:list', newVal);
this.$emit('listChange', newVal);
this._callMyParentList(newVal);
}
this.firstPageLoaded = false;
this.isTotalChangeFromAddData = false;
this.$nextTick(() => {
setTimeout(()=>{
this._getNodeClientRect('.zp-paging-container-content').then((res) => {
if (res) {
this.$emit('contentHeightChanged', res[0].height);
}
});
},this.isIos?100:300)
// #ifdef APP-NVUE
if (this.useChatRecordMode && this.nIsFirstPageAndNoMore && this.isFirstPage && !this.nFirstPageAndNoMoreChecked) {
this.nFirstPageAndNoMoreChecked = true;
this._scrollToBottom(false);
}
// #endif
})
},
//当前数据改变时调用
_currentDataChange(newVal, oldVal) {
newVal = [...newVal];
this.listRendering = true;
this.$nextTick(() => {
setTimeout(() => {
this.listRendering = false;
},50)
})
// #ifndef APP-NVUE
if (this.finalUseVirtualList) {
this._setCellIndex(newVal,this.totalData.length === 0);
}
this.useChatRecordMode && newVal.reverse();
// #endif
if (this.isFirstPage && this.finalConcat) {
this.totalData = [];
}
if (this.customNoMore !== -1) {
if (this.customNoMore === 0 || !newVal.length) {
this.loadingStatus = Enum.More.NoMore;
}
} else {
if (!newVal.length || (newVal.length && newVal.length < this.defaultPageSize)) {
this.loadingStatus = Enum.More.NoMore;
}
}
if (!this.totalData.length) {
if (this.finalConcat) {
// #ifdef APP-NVUE
if(this.useChatRecordMode && this.isFirstPage && this.loadingStatus === Enum.More.NoMore){
newVal.reverse();
}
// #endif
this.totalData = newVal;
}
if (this.useChatRecordMode) {
// #ifndef APP-NVUE
this.$nextTick(() => {
this._scrollToBottom(false);
})
// #endif
}
} else {
if (this.useChatRecordMode) {
// #ifdef APP-NVUE
this.totalData = [...this.totalData, ...newVal];
// #endif
//#ifndef APP-NVUE
const idIndex = newVal.length;
let idIndexStr = `z-paging-${idIndex}`;
this.totalData = [...newVal, ...this.totalData];
if (this.pageNo !== this.defaultPageNo) {
this.privateScrollWithAnimation = 0;
let delayTime = 200;
this.$emit('update:chatIndex', idIndex);
setTimeout(() => {
this._scrollIntoView(idIndexStr, 30 + this.cacheTopHeight, false, () => {
this.$emit('update:chatIndex', 0);
});
}, this.usePageScroll ? 0 : delayTime)
} else {
this.$nextTick(() => {
this._scrollToBottom(false);
})
}
//#endif
} else {
if (this.finalConcat) {
const currentScrollTop = this.oldScrollTop;
this.totalData = [...this.totalData, ...newVal];
// #ifdef MP-WEIXIN
if (!this.isIos && !this.refresherOnly && !this.usePageScroll && newVal.length) {
this.loadingMoreTimeStamp = u.getTime();
this.$nextTick(()=>{
this.scrollToY(currentScrollTop);
})
}
// #endif
} else {
this.totalData = newVal;
}
}
}
this.privateConcat = true;
},
//本地分页请求
_localPagingQueryList(pageNo, pageSize, localPagingLoadingTime, callback) {
pageNo = parseInt(pageNo);
pageSize = parseInt(pageSize);
if (pageNo < 0 || pageSize <= 0) {
this._localPagingQueryResult(callback, [], localPagingLoadingTime);
return;
}
pageNo = Math.max(1,pageNo);
let totalPagingList = [...this.totalLocalPagingList];
let pageNoIndex = (pageNo - 1) * pageSize;
if (pageNoIndex + pageSize <= totalPagingList.length) {
this._localPagingQueryResult(callback, totalPagingList.splice(pageNoIndex, pageSize), localPagingLoadingTime);
} else if (pageNoIndex < totalPagingList.length) {
this._localPagingQueryResult(callback, totalPagingList.splice(pageNoIndex, totalPagingList.length - pageNoIndex), localPagingLoadingTime);
} else {
this._localPagingQueryResult(callback, [], localPagingLoadingTime);
}
},
//本地分页请求回调
_localPagingQueryResult(callback, arg, localPagingLoadingTime) {
setTimeout(() => {
callback(arg);
}, localPagingLoadingTime)
},
//修改父view的list
_callMyParentList(newVal) {
if (this.autowireListName.length) {
const myParent = u.getParent(this.$parent);
if (myParent && myParent[this.autowireListName]) {
myParent[this.autowireListName] = newVal;
}
}
},
//调用父view的query
_callMyParentQuery(customPageNo = 0, customPageSize = 0) {
if (this.autowireQueryName) {
if (this.myParentQuery === -1) {
const myParent = u.getParent(this.$parent);
if (myParent && myParent[this.autowireQueryName]) {
this.myParentQuery = myParent[this.autowireQueryName];
}
}
if (this.myParentQuery !== -1) {
if (customPageSize > 0) {
this.myParentQuery(customPageNo, customPageSize);
} else {
this.myParentQuery(this.pageNo, this.defaultPageSize);
}
}
}
},
//发射query事件
_emitQuery(pageNo, pageSize, from){
this.requestTimeStamp = u.getTime();
this.$emit('query', ...interceptor._handleQuery(pageNo, pageSize, from));
},
//检查complete data的类型
_checkDataType(data, success, isLocal) {
const dataType = Object.prototype.toString.call(data);
if (dataType === '[object Boolean]') {
success = data;
data = [];
} else if (dataType === '[object Null]') {
data = [];
} else if (dataType !== '[object Array]') {
data = [];
let methodStr = isLocal ? 'setLocalPaging' : 'complete';
if (dataType !== '[object Undefined]') {
if (this.showConsoleError) {
u.consoleErr(`${methodStr}参数类型不正确第一个参数类型必须为Array!`);
}
}
}
return {data,success};
},
}
}
export default ZPData;