Add server and mini-program pagination for shared lists
This commit is contained in:
+18
-14
@@ -1,3 +1,4 @@
|
||||
const { itemsOf, startList, current, mergeUnique, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
|
||||
const { currentUserLocation } = require('../../utils/user-location');
|
||||
const { photoForm, metadataRows } = require('../../utils/photo-metadata');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
@@ -5,7 +6,7 @@ const { getLayout } = require('../../utils/layout');
|
||||
// pages/album/album.js - 日记式分组相册
|
||||
const { api, getImageUrl, API_BASE, uploadFileCompat } = require('../../utils/request');
|
||||
const { requestSubscribeAfterAction } = require('../../utils/subscribe');
|
||||
Page(withDataCache({
|
||||
Page(withDataCache(withListPagination({
|
||||
onResize: function() { this.setData(getLayout()); },
|
||||
data: { loading: true, loadError: false,
|
||||
batches: [],
|
||||
@@ -86,7 +87,8 @@ Page(withDataCache({
|
||||
return { ...batch, photos, metadata: metadataRows(photos) };
|
||||
});
|
||||
const previewImages = this.data.previewImages.map(item => String(item.id) === String(photo.id) ? { ...item, ...form } : item);
|
||||
this.setData({ batches, previewImages, showPhotoEditor: false });
|
||||
this._photoRows = (this._photoRows || []).map(item => String(item.id) === String(photo.id) ? { ...item, ...form } : item);
|
||||
this.setData({ batches, photoItems: this._photoRows, previewImages, showPhotoEditor: false });
|
||||
wx.showToast({ title: '照片信息已保存', icon: 'success' });
|
||||
} catch (error) {
|
||||
wx.showToast({ title: (error && (error.error || error.message)) || '保存失败,请重试', icon: 'none' });
|
||||
@@ -129,15 +131,14 @@ Page(withDataCache({
|
||||
};
|
||||
},
|
||||
|
||||
loadData: async function() {
|
||||
if (this._pageLoading) return;
|
||||
this._pageLoading = true;
|
||||
this.setData({loading: !this._hasCachedData, loadError:false});
|
||||
loadData: async function(page) {
|
||||
const ticket = startList(this,page);
|
||||
if (!ticket) return;
|
||||
try {
|
||||
var self = this;
|
||||
var albumId = this.data.albumId;
|
||||
if (!albumId) {
|
||||
var albums = await api.albumList();
|
||||
var albums = itemsOf(await api.albumList({page:1,page_size:1}));
|
||||
if (albums && albums.length > 0) {
|
||||
albumId = albums[0].id;
|
||||
this.setData({ albumId: albumId });
|
||||
@@ -145,11 +146,14 @@ Page(withDataCache({
|
||||
}
|
||||
if (albumId) {
|
||||
// 共享内存缓存;写操作成功后自动失效。
|
||||
var photos = await api.photos(albumId);
|
||||
var result = await api.photos(albumId, {page:ticket.page,page_size:20});
|
||||
if (!current(this,ticket)) return;
|
||||
var photos = ticket.page === 1 ? itemsOf(result) : mergeUnique(this._photoRows || [], itemsOf(result));
|
||||
// 根据 batch_id 分组
|
||||
var batchMap = {};
|
||||
var myUid = wx.getStorageSync('userId');
|
||||
var info = await api.coupleInfo();
|
||||
if (!current(this,ticket)) return;
|
||||
var p1 = info.partner1 || {}, p2 = info.partner2 || {};
|
||||
var nameMap = {};
|
||||
var avatarMap = {};
|
||||
@@ -182,13 +186,13 @@ Page(withDataCache({
|
||||
var batches = Object.values(batchMap);
|
||||
batches.sort(function(a, b) { return b.created_at > a.created_at ? 1 : -1; });
|
||||
batches.forEach(function(batch) { batch.metadata = metadataRows(batch.photos); });
|
||||
this.setData({ batches: batches });
|
||||
} else { this.setData({batches:[]}); }
|
||||
this._photoRows = photos;
|
||||
applyList(this,ticket,result,itemsOf(result),'photoItems',{batches:batches});
|
||||
} else { applyList(this,ticket,{items:[],total:0,has_more:false},[],'photoItems',{batches:[]}); }
|
||||
} catch (e) {
|
||||
this.setData({loadError:true}); console.error(e); }
|
||||
failList(this,ticket); console.error(e); }
|
||||
finally {
|
||||
this._pageLoading = false;
|
||||
this.setData({loading:false});
|
||||
finishList(this,ticket);
|
||||
if (this._visible && this._pendingPhotoPicker && !this.data.loadError && this.data.albumId) {
|
||||
this._pendingPhotoPicker = false;
|
||||
this.chooseImage();
|
||||
@@ -522,4 +526,4 @@ Page(withDataCache({
|
||||
goToAlbums: function() { wx.navigateBack(); },
|
||||
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
|
||||
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
|
||||
}, {"loadData": ["photos", "albumList"]}));
|
||||
}, 'loadData'), {"loadData": ["photos", "albumList"]}));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
|
||||
<scroll-view class="page-bg" scroll-y show-scrollbar="{{false}}" bounces="{{true}}" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd" wx:if="{{!showUploadModal}}">
|
||||
<scroll-view class="page-bg" scroll-y bindscrolltolower="loadMore" lower-threshold="120" show-scrollbar="{{false}}" bounces="{{true}}" bindtouchstart="onTouchStart" bindtouchend="onTouchEnd" wx:if="{{!showUploadModal}}">
|
||||
<view class="container"><view class="j-row"><view><text class="j-eyebrow">MOMENTS WE KEEP</text><text class="j-page-title">相册里的片段</text></view><button class="j-button j-button-small" bindtap="chooseImage">+ 照片</button></view>
|
||||
<page-state wx:if="{{loading}}" kind="loading" title="正在加载" /><page-state wx:elif="{{loadError}}" kind="error" title="加载失败" description="请检查网络后重新加载" action="重试" bind:action="loadData" />
|
||||
<view class="moments-list" wx:if="{{!loading && !loadError && batches.length > 0}}">
|
||||
@@ -36,6 +36,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="j-pagination" wx:if="{{!loading && !loadError && total > 0}}"><text wx:if="{{loadingMore}}">正在加载…</text><text wx:elif="{{moreError}}" bindtap="loadMore">加载失败,点击重试</text><text wx:elif="{{hasMore}}" bindtap="loadMore">加载更多</text><text wx:else>已加载全部照片</text></view>
|
||||
</scroll-view>
|
||||
|
||||
<view class="preview-overlay" wx:if="{{showPreview}}" bindtap="closePreview">
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
// pages/albums/albums.js
|
||||
const { api, getImageUrl } = require('../../utils/request');
|
||||
Page(withDataCache({
|
||||
Page(withDataCache(withListPagination({
|
||||
onResize: function() { this.setData(getLayout()); },
|
||||
data: {
|
||||
loading: true, loadError: false,
|
||||
@@ -47,21 +48,21 @@ Page(withDataCache({
|
||||
|
||||
noop: function() {},
|
||||
|
||||
loadAlbums: async function() {
|
||||
if (this._loading) return;
|
||||
this._loading = true;
|
||||
this.setData({ loading: !this._hasCachedData, loadError: false });
|
||||
loadAlbums: async function(page) {
|
||||
const ticket = startList(this, page);
|
||||
if (!ticket) return;
|
||||
try {
|
||||
var list = await api.albumList(null, 'GET', true);
|
||||
list = (list || []).map(function(a) {
|
||||
var list = await api.albumList({page:ticket.page,page_size:20});
|
||||
var result = list;
|
||||
list = itemsOf(list).map(function(a) {
|
||||
a.cover = getImageUrl(a.cover);
|
||||
a.photo_count = a.photo_count || 0;
|
||||
// 地理位置:优先显示地址名称,其次显示经纬度
|
||||
a.location_text = a.location || ((a.lat && a.lng) ? (a.lat + ', ' + a.lng) : '');
|
||||
return a;
|
||||
});
|
||||
this.setData({ albums: list });
|
||||
} catch(e) { console.error(e); this.setData({ loadError: true }); } finally { this._loading = false; this.setData({ loading: false }); }
|
||||
applyList(this, ticket, result, list, 'albums');
|
||||
} catch(e) { console.error(e); failList(this,ticket); } finally { finishList(this,ticket); }
|
||||
},
|
||||
|
||||
openAlbum: function(e) {
|
||||
@@ -174,4 +175,4 @@ Page(withDataCache({
|
||||
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
|
||||
goBack: function() { wx.navigateBack(); },
|
||||
goToProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
|
||||
}, {"loadAlbums": ["albumList"]}));
|
||||
}, 'loadAlbums'), {"loadAlbums": ["albumList"]}));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- pages/albums/albums.wxml - 相册列表 -->
|
||||
<app-nav title="{{uploadMode ? '选择相册' : '相册'}}" back="{{true}}" />
|
||||
|
||||
<scroll-view class="page-bg" scroll-y show-scrollbar="{{false}}" bounces="{{true}}">
|
||||
<scroll-view class="page-bg" scroll-y bindscrolltolower="loadMore" lower-threshold="120" show-scrollbar="{{false}}" bounces="{{true}}">
|
||||
<view class="j-body"><view class="j-row"><view><text class="j-eyebrow">MOMENTS WE KEEP</text><text class="j-page-title">{{uploadMode ? '选择上传照片的相册' : '照片里的日常'}}</text></view><button class="j-button j-button-small" bindtap="goToPublish">+ 建相册</button></view>
|
||||
<page-state wx:if="{{loading}}" kind="loading" title="正在打开相册" /><page-state wx:elif="{{loadError}}" kind="error" title="加载失败" action="重试" bind:action="loadAlbums" />
|
||||
<view class="j-album-list" wx:else><view class="j-album-print" wx:for="{{albums}}" wx:key="id" bindtap="openAlbum" data-id="{{item.id}}"><image wx:if="{{item.cover}}" src="{{item.cover}}" mode="aspectFill" /><view wx:else class="j-album-placeholder"><image src="/images/album.svg" /><text>还没有照片</text></view><view class="j-row"><view><text class="j-story-title">{{item.title || '我们的相册'}}</text><text class="j-caption">{{item.photo_count || 0}} 张照片</text></view><view class="j-link j-album-menu" catchtap="showMoreMenu" data-id="{{item.id}}" data-title="{{item.title}}" data-desc="{{item.description}}" data-cover="{{item.cover}}">•••</view></view><text wx:if="{{item.description}}" class="j-caption">{{item.description}}</text></view></view>
|
||||
@@ -41,6 +41,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="j-pagination" wx:if="{{!loading && !loadError && total > 0}}"><text wx:if="{{loadingMore}}">正在加载…</text><text wx:elif="{{moreError}}" bindtap="loadMore">加载失败,点击重试</text><text wx:elif="{{hasMore}}" bindtap="loadMore">加载更多</text><text wx:else>已加载全部</text></view>
|
||||
</scroll-view>
|
||||
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
// Diary journal with server pagination.
|
||||
const { api, getImageUrl } = require('../../utils/request');
|
||||
const { diaryEntry } = require('../../utils/journal');
|
||||
const { saveScrollPosition, getScrollPosition } = require('../../utils/scroll-pos');
|
||||
Page(withDataCache({
|
||||
Page(withDataCache(withListPagination({
|
||||
onResize: function() { this.setData(getLayout()); },
|
||||
data: {
|
||||
loading: true, loadError: false,
|
||||
@@ -51,45 +52,14 @@ Page(withDataCache({
|
||||
return { title: '让所有的爱有迹可循', imageUrl: '/images/share.png' };
|
||||
},
|
||||
|
||||
loadData: async function() {
|
||||
if (this._loading) return;
|
||||
this._loading = true;
|
||||
this.setData({ loading: !this._hasCachedData, loadError: false });
|
||||
loadData: async function(page) {
|
||||
const ticket=startList(this,page);if(!ticket)return;
|
||||
try {
|
||||
var data = await api.diaryList({ page: 1, page_size: this.data.pageSize });
|
||||
var items = this._formatItems(data.items || []);
|
||||
this.setData({
|
||||
page: 1,
|
||||
list: items,
|
||||
total: data.total || 0,
|
||||
hasMore: (data.total || 0) > this.data.pageSize
|
||||
});
|
||||
const data=await api.diaryList({page:ticket.page,page_size:20});
|
||||
applyList(this,ticket,data,this._formatItems(itemsOf(data)),'list');
|
||||
this._loadPartnerInfo();
|
||||
} catch (e) {
|
||||
this.setData({ loadError: true });
|
||||
console.error(e);
|
||||
wx.showToast({ title: (e && (e.error || e.errMsg || e.message)) || '加载失败', icon: 'none' });
|
||||
} finally { this._loading = false; this.setData({ loading: false }); }
|
||||
},
|
||||
|
||||
loadMore: async function() {
|
||||
if (this.data.loadingMore || !this.data.hasMore) return;
|
||||
this.setData({ loadingMore: true });
|
||||
try {
|
||||
var nextPage = this.data.page + 1;
|
||||
var data = await api.diaryList({ page: nextPage, page_size: this.data.pageSize });
|
||||
var newItems = this._formatItems(data.items || []);
|
||||
var merged = this.data.list.concat(newItems);
|
||||
this.setData({
|
||||
list: merged,
|
||||
page: nextPage,
|
||||
hasMore: merged.length < (data.total || 0),
|
||||
loadingMore: false
|
||||
});
|
||||
this._loadPartnerInfo();
|
||||
} catch (e) {
|
||||
this.setData({ loadingMore: false });
|
||||
}
|
||||
} catch(e) { failList(this,ticket); }
|
||||
finally { finishList(this,ticket); }
|
||||
},
|
||||
|
||||
_formatItems: function(items) {
|
||||
@@ -194,4 +164,4 @@ Page(withDataCache({
|
||||
goHome: function() { this._savePosBeforeLeave(); wx.switchTab({ url: '/pages/index/index' }); },
|
||||
goBack: function() { wx.navigateBack(); },
|
||||
goProfile: function() { this._savePosBeforeLeave(); wx.switchTab({ url: '/pages/profile/profile' }); }
|
||||
}, {"loadData": ["diaryList", "coupleInfo"]}));
|
||||
}, 'loadData'), {"loadData": ["diaryList", "coupleInfo"]}));
|
||||
|
||||
@@ -5,4 +5,5 @@
|
||||
<view class="j-body"><view class="j-row"><view><text class="j-eyebrow">LIFE IN WORDS</text><text class="j-page-title">生活里的每一页</text></view><button class="j-button j-button-small" bindtap="goToPublish">+ 写日记</button></view>
|
||||
<page-state wx:if="{{loading}}" kind="loading" title="正在翻开日记" /><page-state wx:elif="{{loadError}}" kind="error" title="加载失败" action="重试" bind:action="loadData" />
|
||||
<block wx:else><block wx:for="{{list}}" wx:key="id"><template is="journal-entry" data="{{entry:item}}" /></block><page-state wx:if="{{!list.length}}" title="故事,从今天开始" description="写下第一篇,记录一个平常又特别的瞬间" action="写日记" bind:action="goToPublish" /></block>
|
||||
<view class="j-caption j-center" wx:if="{{loadingMore}}">正在加载更多…</view></view></scroll-view></view>
|
||||
<view class="j-caption j-center" wx:if="{{loadingMore}}">正在加载更多…</view></view><view class="j-pagination" wx:if="{{!loading && !loadError && total > 0}}"><text wx:if="{{loadingMore}}">正在加载…</text><text wx:elif="{{moreError}}" bindtap="loadMore">加载失败,点击重试</text><text wx:elif="{{hasMore}}" bindtap="loadMore">加载更多</text><text wx:else>已加载全部日记</text></view>
|
||||
</scroll-view></view>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { itemsOf, totalOf, mergeUnique } = require('../../utils/list-pagination');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
const { api, getImageUrl } = require('../../utils/request');
|
||||
@@ -8,7 +9,7 @@ Page(withDataCache({
|
||||
diary: null,
|
||||
loading: true,
|
||||
comments: [],
|
||||
commentPage: 1,
|
||||
commentPage: 1, commentsLoadingMore: false, commentsMoreError: false, commentsTotal: 0,
|
||||
hasMoreComments: true,
|
||||
showCommentInput: false,
|
||||
commentText: '',
|
||||
@@ -78,24 +79,28 @@ Page(withDataCache({
|
||||
return data;
|
||||
},
|
||||
|
||||
loadComments: async function() {
|
||||
this.setData({ commentsError: false });
|
||||
loadComments: function() { return this._loadCommentsPage(1); },
|
||||
_loadCommentsPage: async function(page) {
|
||||
page = Number(page) || 1;
|
||||
if (this._commentsFirst || (page > 1 && (this.data.commentsLoadingMore || !this.data.hasMoreComments))) return;
|
||||
if (page === 1) { this._commentsFirst = true; this._commentsGeneration = (this._commentsGeneration || 0) + 1; }
|
||||
var generation = this._commentsGeneration;
|
||||
this.setData(page === 1 ? {commentsError:false,commentsMoreError:false,commentsLoadingMore:false} : {commentsLoadingMore:true,commentsMoreError:false});
|
||||
try {
|
||||
var data = await api.diaryCommentList({ diary_id: this.data.diaryId });
|
||||
if (data && data.length > 0) {
|
||||
data.forEach(function(c) {
|
||||
if (c.created_at) {
|
||||
c._time = c.created_at.substring(0, 16).replace('T', ' ');
|
||||
} else if (c.create_time) {
|
||||
c._time = c.create_time.substring(0, 16).replace('T', ' ');
|
||||
} else {
|
||||
c._time = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
this.setData({ comments: data || [] });
|
||||
} catch (e) { this.setData({ commentsError: true }); console.error(e); }
|
||||
var result = await api.diaryCommentList({diary_id:this.data.diaryId,page:page,page_size:20});
|
||||
if (generation !== this._commentsGeneration) return;
|
||||
var added = itemsOf(result).map(function(comment) {
|
||||
return Object.assign({}, comment, {_time:String(comment.created_at || comment.create_time || '').slice(0,16).replace('T',' ')});
|
||||
});
|
||||
this.setData({comments:page===1?added:mergeUnique(this.data.comments,added),commentPage:page,commentsTotal:totalOf(result),hasMoreComments:!Array.isArray(result)&&(result.has_more != null?!!result.has_more:page*20<totalOf(result))});
|
||||
} catch(e) {
|
||||
if (generation === this._commentsGeneration) this.setData(page===1?{commentsError:true}:{commentsMoreError:true});
|
||||
} finally {
|
||||
if (generation === this._commentsGeneration) { this._commentsFirst=false;this.setData({commentsLoadingMore:false}); }
|
||||
}
|
||||
},
|
||||
loadMoreComments: function() { return this._loadCommentsPage(this.data.commentPage+1); },
|
||||
|
||||
|
||||
onShareTap: function() {
|
||||
wx.showActionSheet({
|
||||
@@ -177,6 +182,9 @@ Page(withDataCache({
|
||||
}
|
||||
var res = await api.diaryCommentAdd(params);
|
||||
if (res.success) {
|
||||
this._commentsGeneration = (this._commentsGeneration || 0) + 1;
|
||||
this._commentsFirst = false;
|
||||
this.setData({commentsLoadingMore:false});
|
||||
var newComment = res.comment || {};
|
||||
if (newComment.created_at) {
|
||||
newComment._time = newComment.created_at.substring(0, 16).replace('T', ' ');
|
||||
@@ -187,9 +195,10 @@ Page(withDataCache({
|
||||
newComment._time = now.getFullYear() + '-' + String(now.getMonth()+1).padStart(2,'0') + '-' + String(now.getDate()).padStart(2,'0') + ' ' + String(now.getHours()).padStart(2,'0') + ':' + String(now.getMinutes()).padStart(2,'0');
|
||||
}
|
||||
var newComments = this.data.comments.slice();
|
||||
newComments.push(newComment);
|
||||
newComments.unshift(newComment);
|
||||
this.setData({
|
||||
comments: newComments,
|
||||
commentsTotal: this.data.commentsTotal + 1,
|
||||
showCommentInput: false,
|
||||
replyTo: null,
|
||||
commentText: '',
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<app-nav title="日记详情" back="{{true}}" /><view class="page-bg">
|
||||
|
||||
<view class="read-state" wx:if="{{loading || loadError}}"><page-state wx:if="{{loading}}" kind="loading" title="正在加载日记" /><page-state wx:else kind="error" title="日记加载失败" action="重试" bind:action="loadDiary" /></view>
|
||||
<scroll-view scroll-y class="read-scroll" wx:if="{{!loading && !loadError && diary}}" show-scrollbar="{{false}}" enhanced>
|
||||
<scroll-view scroll-y bindscrolltolower="loadMoreComments" lower-threshold="120" class="read-scroll" wx:if="{{!loading && !loadError && diary}}" show-scrollbar="{{false}}" enhanced>
|
||||
<view class="read-page">
|
||||
<view class="read-author"><image class="read-avatar" src="{{diary.creator_avatar || '/images/default-avatar.svg'}}" mode="aspectFill" /><view class="read-author-info"><text class="read-name">{{diary.creator_name || 'TA'}}</text><text class="read-date">{{diary.created_time}}</text><text class="read-location" wx:if="{{diary.location_text}}">{{diary.location_text}}</text></view><view class="read-menu" catchtap="onMoreTap" aria-label="日记操作"><image src="/images/more.svg" mode="aspectFit" /></view></view>
|
||||
<view class="read-paper">
|
||||
@@ -58,6 +58,8 @@
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="j-pagination" wx:if="{{commentsError}}" bindtap="loadComments">留言加载失败,点击重试</view>
|
||||
<view class="j-pagination" wx:elif="{{comments.length}}"><text wx:if="{{commentsLoadingMore}}">正在加载…</text><text wx:elif="{{commentsMoreError}}" bindtap="loadMoreComments">加载失败,点击重试</text><text wx:elif="{{hasMoreComments}}" bindtap="loadMoreComments">加载更多留言</text><text wx:else>已加载全部留言</text></view>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { itemsOf, totalOf } = require('../../utils/list-pagination');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
// pages/discover/discover.js
|
||||
@@ -46,18 +47,13 @@ Page(withDataCache({
|
||||
const [timeline, album, diary] = await Promise.all([
|
||||
api.timelineList(), api.albumList(), api.diaryList()
|
||||
]);
|
||||
var photoCount = 0;
|
||||
if (Array.isArray(album)) {
|
||||
for (var i = 0; i < album.length; i++) {
|
||||
photoCount += parseInt(album[i].photo_count || 0);
|
||||
}
|
||||
}
|
||||
var photoCount = album && album.photo_total != null ? Number(album.photo_total) : itemsOf(album).reduce((sum,item)=>sum+(Number(item.photo_count)||0),0);
|
||||
var stats = {
|
||||
timeline: Array.isArray(timeline) ? timeline.length : 0,
|
||||
timeline: totalOf(timeline),
|
||||
album: photoCount,
|
||||
diary: (diary && diary.total) ? diary.total : 0,
|
||||
};
|
||||
this.setData({ stats: stats, entries: ((diary && diary.items) || []).map(diaryEntry), albums: (Array.isArray(album) ? album : []).map(albumEntry), memories: (Array.isArray(timeline) ? timeline : []).map(memoryEntry) });
|
||||
this.setData({ stats: stats, entries: ((diary && diary.items) || []).map(diaryEntry), albums: itemsOf(album).map(albumEntry), memories: itemsOf(timeline).map(memoryEntry) });
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this.setData({loadError:true});
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { itemsOf, totalOf } = require('../../utils/list-pagination');
|
||||
let lastLocationReport = 0;
|
||||
let locationReportUser = null;
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
@@ -221,29 +222,24 @@ Page(withDataCache({
|
||||
api.songsList(), api.checkinList(),
|
||||
this.data.coupleId ? api.petInfo() : Promise.resolve({ has_pet: false })
|
||||
]);
|
||||
var photoCount = 0;
|
||||
if (Array.isArray(album)) {
|
||||
for (var i = 0; i < album.length; i++) {
|
||||
photoCount += parseInt(album[i].photo_count || 0);
|
||||
}
|
||||
}
|
||||
var photoCount = album && album.photo_total != null ? Number(album.photo_total) : itemsOf(album).reduce((sum,item)=>sum+(Number(item.photo_count)||0),0);
|
||||
var petLevel = 0;
|
||||
if (pet && pet.has_pet && pet.pet) {
|
||||
petLevel = parseInt(pet.pet.level || 0);
|
||||
}
|
||||
var recentDiary = ((diary && diary.items) || []).slice(0, 1).map(diaryEntry);
|
||||
var today = new Date(); today.setHours(0,0,0,0);
|
||||
var future = (Array.isArray(timeline) ? timeline : []).filter(function(item) { return item.event_date && new Date(item.event_date.replace(/-/g, '/')) >= today; }).sort(function(a,b) { return a.event_date.localeCompare(b.event_date); });
|
||||
var next = future[0];
|
||||
var future = itemsOf(timeline).filter(function(item) { return item.event_date && new Date(item.event_date.replace(/-/g, '/')) >= today; }).sort(function(a,b) { return a.event_date.localeCompare(b.event_date); });
|
||||
var next = timeline && !Array.isArray(timeline) && 'upcoming' in timeline ? timeline.upcoming : future[0];
|
||||
this.setData({
|
||||
recentDiary: recentDiary,
|
||||
recentAlbums: (Array.isArray(album) ? album : []).filter(function(item) { return !!item.cover; }).slice(0,3).map(function(item) { return { id:item.id, title:item.title || '我们的相册', cover:getImageUrl(item.cover) }; }),
|
||||
recentAlbums: itemsOf(album).filter(function(item) { return !!item.cover; }).slice(0,3).map(function(item) { return { id:item.id, title:item.title || '我们的相册', cover:getImageUrl(item.cover) }; }),
|
||||
upcomingMemory: next ? { title: next.title || '下一个记忆日', date: next.event_date, dateMonth: next.event_date.slice(5,7), dateDay: next.event_date.slice(8,10), days: Math.ceil((new Date(next.event_date.replace(/-/g, '/')) - today) / 86400000) } : null,
|
||||
stats: {
|
||||
timeline: Array.isArray(timeline) ? timeline.length : 0,
|
||||
timeline: totalOf(timeline),
|
||||
album: photoCount,
|
||||
diary: (diary && diary.total) ? diary.total : 0,
|
||||
songs: Array.isArray(songs) ? songs.length : 0,
|
||||
songs: totalOf(songs),
|
||||
checkin: (checkin && checkin.checkin_log_total !== undefined) ? checkin.checkin_log_total : ((checkin && checkin.items) ? checkin.items.length : (Array.isArray(checkin) ? checkin.length : 0)),
|
||||
pet: petLevel
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
// pages/lovewords/lovewords.js - 一起听过的歌
|
||||
const { api } = require('../../utils/request');
|
||||
Page(withDataCache({
|
||||
Page(withDataCache(withListPagination({
|
||||
onResize: function() { this.setData(getLayout()); },
|
||||
data: { loading: true, loadError: false,
|
||||
list: [],
|
||||
@@ -30,13 +31,12 @@ Page(withDataCache({
|
||||
return { title: '让所有的爱有迹可循', imageUrl: '/images/share.png' };
|
||||
},
|
||||
|
||||
loadData: async function() {
|
||||
if (this._pageLoading) return;
|
||||
this._pageLoading = true;
|
||||
this.setData({loading: !this._hasCachedData, loadError:false});
|
||||
loadData: async function(page) {
|
||||
const ticket = startList(this, page);
|
||||
if (!ticket) return;
|
||||
try {
|
||||
const list = await api.songsList();
|
||||
const items = (list || []).map(function(item) {
|
||||
const list = await api.songsList({page:ticket.page,page_size:20});
|
||||
const items = itemsOf(list).map(function(item) {
|
||||
if (item.created_at) {
|
||||
var d = new Date(item.created_at);
|
||||
item.date_month = String(d.getMonth() + 1).padStart(2, '0');
|
||||
@@ -44,13 +44,13 @@ Page(withDataCache({
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.setData({ list: items });
|
||||
applyList(this, ticket, list, items, 'list');
|
||||
} catch (e) {
|
||||
this.setData({loadError:true});
|
||||
failList(this,ticket);
|
||||
console.error(e);
|
||||
wx.showToast({ title: '加载失败', icon: 'none' });
|
||||
}
|
||||
finally { this._pageLoading = false; this.setData({loading:false}); }
|
||||
finally { finishList(this,ticket); }
|
||||
},
|
||||
|
||||
noop: function() {},
|
||||
@@ -124,4 +124,4 @@ Page(withDataCache({
|
||||
goBack: function() { wx.navigateBack(); },
|
||||
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
|
||||
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
|
||||
}, {"loadData": ["songsList"]}));
|
||||
}, 'loadData'), {"loadData": ["songsList"]}));
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
|
||||
|
||||
<scroll-view class="page-bg" scroll-y show-scrollbar="{{false}}" bounces="{{true}}">
|
||||
<scroll-view class="page-bg" scroll-y bindscrolltolower="loadMore" lower-threshold="120" show-scrollbar="{{false}}" bounces="{{true}}">
|
||||
<view class="c-body"><view class="c-heading j-row"><view><text class="j-eyebrow">OUR SOUNDTRACK</text><text class="j-page-title">一起听过的旋律</text></view><button class="j-button j-button-small" bindtap="goToPublish">+ 添加</button></view>
|
||||
<page-state wx:if="{{loading}}" kind="loading" title="正在加载" /><page-state wx:elif="{{loadError}}" kind="error" title="加载失败" description="请检查网络后重新加载" action="重试" bind:action="loadData" />
|
||||
<view class="song-list" wx:if="{{!loading && !loadError && list.length}}">
|
||||
@@ -21,6 +21,7 @@
|
||||
</view>
|
||||
<page-state wx:if="{{!loading && !loadError && !list.length}}" title="收藏我们的第一首歌" description="让某一段旋律,成为两个人的记忆" action="添加歌曲" bind:action="goToPublish" />
|
||||
</view>
|
||||
<view class="j-pagination" wx:if="{{!loading && !loadError && total > 0}}"><text wx:if="{{loadingMore}}">正在加载…</text><text wx:elif="{{moreError}}" bindtap="loadMore">加载失败,点击重试</text><text wx:elif="{{hasMore}}" bindtap="loadMore">加载更多</text><text wx:else>已加载全部</text></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 添加弹窗 -->
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { itemsOf, totalOf } = require('../../utils/list-pagination');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
// pages/profile/profile.js - 与原网站一致的个人中心
|
||||
@@ -1025,11 +1026,11 @@ Page(withDataCache({
|
||||
const key=keys[index];
|
||||
if(result.status !== 'fulfilled') { counts[key]='—';failed=true;return; }
|
||||
const value=result.value;
|
||||
if(key==='album') counts[key]=(Array.isArray(value)?value:[]).reduce((sum,album)=>sum+(Number(album.photo_count)||0),0);
|
||||
if(key==='album') counts[key]=value && value.photo_total != null ? Number(value.photo_total) : itemsOf(value).reduce((sum,album)=>sum+(Number(album.photo_count)||0),0);
|
||||
else if(key==='diary') counts[key]=Number(value && value.total)||0;
|
||||
else if(key==='checkin') counts[key]=value && value.checkin_log_total !== undefined ? Number(value.checkin_log_total)||0 : (Array.isArray(value)?value.length:(value && value.items || []).length);
|
||||
else if(key==='pet') counts[key]=value && value.has_pet && value.pet ? 'Lv.'+(Number(value.pet.level)||0) : '未领养';
|
||||
else counts[key]=Array.isArray(value)?value.length:0;
|
||||
else counts[key]=totalOf(value);
|
||||
});
|
||||
this.setData({statisticItems:this.data.statisticItems.map(item=>({...item,count:counts[item.key]===undefined?item.count:counts[item.key]})),statsLoading:false,statsError:failed});
|
||||
},
|
||||
|
||||
+12
-11
@@ -1,8 +1,9 @@
|
||||
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
// pages/punch/punch.js - 情侣打卡
|
||||
const { api, getImageUrl } = require('../../utils/request');
|
||||
Page(withDataCache({
|
||||
Page(withDataCache(withListPagination({
|
||||
onResize: function() { this.setData(getLayout()); },
|
||||
data: { loadError: false,
|
||||
checkins: [],
|
||||
@@ -54,16 +55,15 @@ Page(withDataCache({
|
||||
});
|
||||
},
|
||||
|
||||
loadData: async function() {
|
||||
if (this._pageLoading) return;
|
||||
this._pageLoading = true;
|
||||
this.setData({loading: !this._hasCachedData, loadError:false});
|
||||
loadData: async function(page) {
|
||||
const ticket = startList(this, page);
|
||||
if (!ticket) return;
|
||||
try {
|
||||
const [res, stats] = await Promise.all([
|
||||
api.checkinList(),
|
||||
api.checkinList({page:ticket.page,page_size:20}),
|
||||
api.checkinStats().catch(() => ({}))
|
||||
]);
|
||||
const list = (res.items || res || []).map(function(item) {
|
||||
const list = itemsOf(res).map(function(item) {
|
||||
item.type = item.type || 'both';
|
||||
var typeLabels = { both: '双方', first: '第一视角', her: '她打卡' };
|
||||
item.type_label = typeLabels[item.type] || '双方';
|
||||
@@ -89,10 +89,11 @@ Page(withDataCache({
|
||||
todayPartner: stats.today_partner_count || 0,
|
||||
loading: false
|
||||
};
|
||||
this.setData(data);
|
||||
delete data.checkins;
|
||||
applyList(this, ticket, res, list, 'checkins', data);
|
||||
} catch(e) {
|
||||
this.setData({loadError:true}); console.error(e); this.setData({ loading: false }); }
|
||||
finally { this._pageLoading = false; this.setData({loading:false}); }
|
||||
failList(this,ticket); console.error(e); this.setData({ loading: false }); }
|
||||
finally { finishList(this,ticket); }
|
||||
},
|
||||
|
||||
// ===== 点击卡片 → 详情弹窗 =====
|
||||
@@ -119,4 +120,4 @@ Page(withDataCache({
|
||||
goBack: function() { wx.navigateBack(); },
|
||||
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
|
||||
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
|
||||
}, {"loadData": ["checkinList", "checkinStats", "coupleInfo"]}));
|
||||
}, 'loadData'), {"loadData": ["checkinList", "checkinStats", "coupleInfo"]}));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- pages/punch/punch.wxml - 情侣打卡 -->
|
||||
<app-nav title="打卡" back="{{true}}" />
|
||||
|
||||
<scroll-view class="page-bg" scroll-y show-scrollbar="{{false}}" bounces="{{true}}">
|
||||
<scroll-view class="page-bg" scroll-y bindscrolltolower="loadMore" lower-threshold="120" show-scrollbar="{{false}}" bounces="{{true}}">
|
||||
<view class="c-body"><view class="c-heading j-row"><view><text class="j-eyebrow">LITTLE PROMISES</text><text class="j-page-title">把约定变成习惯</text></view><button class="j-button j-button-small" bindtap="showAddModal">+ 添加</button></view>
|
||||
<page-state wx:if="{{loading}}" kind="loading" title="正在加载" /><page-state wx:elif="{{loadError}}" kind="error" title="加载失败" description="请检查网络后重新加载" action="重试" bind:action="loadData" />
|
||||
<!-- 顶部 Banner - 卡片式 -->
|
||||
@@ -83,6 +83,7 @@
|
||||
<page-state wx:if="{{!loading && !loadError && !checkins.length}}" title="从一个小约定开始" description="一起运动、按时吃饭,也可以成为日常仪式" action="添加打卡" bind:action="showAddModal" />
|
||||
</view>
|
||||
|
||||
<view class="j-pagination" wx:if="{{!loading && !loadError && total > 0}}"><text wx:if="{{loadingMore}}">正在加载…</text><text wx:elif="{{moreError}}" bindtap="loadMore">加载失败,点击重试</text><text wx:elif="{{hasMore}}" bindtap="loadMore">加载更多</text><text wx:else>已加载全部</text></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 浮动按钮 -->
|
||||
|
||||
@@ -3,5 +3,6 @@ Page({
|
||||
data:{},
|
||||
onLoad(){this.setData(getLayout());},
|
||||
onResize(){this.setData(getLayout());},
|
||||
loadMore(){const card=this.selectComponent('#smoking');if(card)return card.loadMoreLogs();},
|
||||
async onPullDownRefresh(){try{const card=this.selectComponent('#smoking');if(card)await card.load(true);}finally{wx.stopPullDownRefresh();}}
|
||||
});
|
||||
|
||||
@@ -1 +1 @@
|
||||
<view class="app-shell app-shell--secondary" style="{{layoutStyle}}"><launch-transition /><app-nav title="抽烟记录" back="{{true}}" /><scroll-view class="page-bg" scroll-y show-scrollbar="{{false}}"><view class="c-body"><smoking-card id="smoking" detailed="{{true}}" /></view></scroll-view></view>
|
||||
<view class="app-shell app-shell--secondary" style="{{layoutStyle}}"><launch-transition /><app-nav title="抽烟记录" back="{{true}}" /><scroll-view class="page-bg" scroll-y bindscrolltolower="loadMore" lower-threshold="120" show-scrollbar="{{false}}"><view class="c-body"><smoking-card id="smoking" detailed="{{true}}" /></view></scroll-view></view>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
const { itemsOf, startList, applyList, failList, finishList, withListPagination } = require('../../utils/list-pagination');
|
||||
const { withDataCache } = require('../../utils/page-cache');
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
// pages/timeline/timeline.js
|
||||
@@ -56,7 +57,7 @@ function calcNearestRepeatDate(eventDate, today, interval, unit) {
|
||||
return new Date(eventDate);
|
||||
}
|
||||
|
||||
Page(withDataCache({
|
||||
Page(withDataCache(withListPagination({
|
||||
onResize: function() { this.setData(getLayout()); },
|
||||
data: { loading: true, loadError: false,
|
||||
list: []
|
||||
@@ -82,12 +83,11 @@ Page(withDataCache({
|
||||
return { title: '让所有的爱有迹可循', imageUrl: '/images/share.png' };
|
||||
},
|
||||
|
||||
loadData: async function() {
|
||||
if (this._pageLoading) return;
|
||||
this._pageLoading = true;
|
||||
this.setData({loading: !this._hasCachedData, loadError:false});
|
||||
loadData: async function(page) {
|
||||
const ticket = startList(this, page);
|
||||
if (!ticket) return;
|
||||
try {
|
||||
const list = await api.timelineList();
|
||||
const list = await api.timelineList({page:ticket.page,page_size:20});
|
||||
const myUid = wx.getStorageSync('userId');
|
||||
var nameMap = {};
|
||||
var avatarMap = {};
|
||||
@@ -100,12 +100,12 @@ Page(withDataCache({
|
||||
} catch (e) {}
|
||||
|
||||
var today = new Date(); today.setHours(0,0,0,0);
|
||||
var items = (list || []).map(function(item, idx) {
|
||||
var items = itemsOf(list).map(function(item, idx) {
|
||||
var cid = parseInt(item.created_by) || 0;
|
||||
item.creator_name = nameMap[cid] || (cid == myUid ? '我' : 'TA');
|
||||
item.creator_avatar = avatarMap[cid] || '';
|
||||
item.creator_initial = (item.creator_name || '?')[0].toUpperCase();
|
||||
item.side = idx % 2 === 0 ? 'left' : 'right';
|
||||
item.side = ((ticket.page-1)*20+idx) % 2 === 0 ? 'left' : 'right';
|
||||
item.bg_image_url = item.bg_image ? getImageUrl(item.bg_image) : '';
|
||||
|
||||
// 计算纪念日/倒数日(支持重复事件)
|
||||
@@ -150,13 +150,13 @@ Page(withDataCache({
|
||||
}
|
||||
return item;
|
||||
});
|
||||
this.setData({ list: items });
|
||||
applyList(this, ticket, list, items, 'list');
|
||||
} catch (e) {
|
||||
this.setData({loadError:true});
|
||||
failList(this,ticket);
|
||||
console.error(e);
|
||||
wx.showToast({ title: (e && (e.error || e.errMsg || e.message)) || '加载失败', icon: 'none' });
|
||||
}
|
||||
finally { this._pageLoading = false; this.setData({loading:false}); }
|
||||
finally { finishList(this,ticket); }
|
||||
},
|
||||
|
||||
|
||||
@@ -192,4 +192,4 @@ Page(withDataCache({
|
||||
goHome: function() { wx.switchTab({ url: '/pages/index/index' }); },
|
||||
goBack: function() { wx.navigateBack(); },
|
||||
goProfile: function() { wx.switchTab({ url: '/pages/profile/profile' }); }
|
||||
}, {"loadData": ["timelineList", "coupleInfo"]}));
|
||||
}, 'loadData'), {"loadData": ["timelineList", "coupleInfo"]}));
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<!-- 添加按钮 -->
|
||||
|
||||
|
||||
<scroll-view class="page-bg tl-scroll" scroll-y show-scrollbar="{{false}}" bounces="{{true}}">
|
||||
<scroll-view class="page-bg tl-scroll" scroll-y bindscrolltolower="loadMore" lower-threshold="120" show-scrollbar="{{false}}" bounces="{{true}}">
|
||||
<view class="container"><view class="j-row"><view><text class="j-eyebrow">MEMORIES OF US</text><text class="j-page-title">值得记住的日子</text></view><button class="j-button j-button-small" bindtap="goToPublish">+ 记忆日</button></view>
|
||||
<page-state wx:if="{{loading}}" kind="loading" title="正在加载" /><page-state wx:elif="{{loadError}}" kind="error" title="加载失败" description="请检查网络后重新加载" action="重试" bind:action="loadData" />
|
||||
<view class="tl-list" wx:if="{{!loading && !loadError && list.length > 0}}">
|
||||
@@ -48,6 +48,7 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="j-pagination" wx:if="{{!loading && !loadError && total > 0}}"><text wx:if="{{loadingMore}}">正在加载…</text><text wx:elif="{{moreError}}" bindtap="loadMore">加载失败,点击重试</text><text wx:elif="{{hasMore}}" bindtap="loadMore">加载更多</text><text wx:else>已加载全部</text></view>
|
||||
</scroll-view>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user