Files
2026-03-05 09:11:08 +08:00

340 lines
10 KiB
JavaScript

// pages/community/index.js
import request from '../../utils/request';
Page({
data: {
posts: [],
displayedPosts: [],
activePostId: null, // For showing action popup
showCommentBar: false,
commentingPostId: null,
commentText: '',
isLoading: false,
current: 1,
pageSize: 10,
hasMore: true,
userInfo: null,
scrollTop: 0,
isRefreshing: false
},
onLoad() {
this.fetchPosts(true);
},
onShow() {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 2 });
}
// Update user info for header
const app = getApp();
const info = app.globalData.userInfo || wx.getStorageSync('userInfo');
if (info) {
this.setData({ userInfo: info });
}
},
// Called by create post page or Pull Down
onRefresh() {
this.setData({ isRefreshing: true });
this.fetchPosts(true).then(() => {
this.setData({ isRefreshing: false });
});
},
onTabItemTap() {
this.setData({ scrollTop: Math.random() * 0.01 });
},
onPullDownRefresh() {
this.fetchPosts(true).then(() => {
wx.stopPullDownRefresh();
});
},
onReachBottom() {
if (this.data.hasMore && !this.data.isLoading) {
this.fetchPosts(false);
}
},
async fetchPosts(reset = false) {
if (this.data.isLoading) return;
this.setData({ isLoading: true });
const current = reset ? 1 : this.data.current;
const pageSize = this.data.pageSize;
try {
// Correct API Endpoint and Params
const res = await request.post('/post/page', { current, pageSize, hasReviewed: 1 });
// Handle response structure: { code: 200, data: { list: [], ... } }
// OR if request.js unwraps it: { list: [], ... }
const data = res.data || res || {};
const rawList = data.list || [];
// Map backend data to UI model
const newPosts = rawList.map(item => {
const publisher = item.publisher || {};
const avatarObj = publisher.avatar || {};
// 从 likeList 提取所有点赞者名字,当前用户显示"我"
const app = getApp();
const myId = (app.globalData.userInfo || {}).id;
const likeNames = (item.likeList || []).map(like => {
const liker = like.liker || {};
if (myId && liker.id === myId) return '我';
return liker.nickName || liker.name || '花友';
});
return {
id: item.id,
user: publisher.nickName || publisher.name || '花友',
avatar: avatarObj.url,
content: item.content,
images: (item.imgList || []).map(img => img.url),
location: item.location || '',
time: item.createdAtStr || '刚刚',
likes: likeNames,
comments: (item.commentList || []).map(c => ({
id: c.id,
user: c.commentator ? (c.commentator.nickName || c.commentator.name) : '花友',
content: c.content
})),
likedByMe: item.hasLiked === 1,
isFavorited: item.hasStar === 1,
likeCount: item.likeCount || 0,
stars: (item.starList || []).map(s => s.starer ? (s.starer.nickName || s.starer.name) : '花友'),
commentCount: item.commentCount || 0,
isExpanded: false
};
});
if (reset) {
this.setData({
posts: newPosts,
displayedPosts: newPosts,
current: 2,
hasMore: newPosts.length >= pageSize
});
} else {
const combined = [...this.data.posts, ...newPosts];
this.setData({
posts: combined,
displayedPosts: combined,
current: current + 1,
hasMore: newPosts.length >= pageSize
});
}
} catch (err) {
console.error('Fetch posts failed', err);
wx.showToast({ title: '加载失败', icon: 'none' });
} finally {
this.setData({ isLoading: false });
}
},
updateDisplayedPosts() {
// Just sync posts to displayedPosts
this.setData({ displayedPosts: this.data.posts });
},
// Preview image in full screen
previewImage(e) {
const { url, urls } = e.currentTarget.dataset;
const resolvedUrls = urls.map(img => img);
wx.previewImage({
current: url,
urls: resolvedUrls
});
},
// Toggle action popup (WeChat style)
toggleActionPopup(e) {
const postId = e.currentTarget.dataset.id;
if (this.data.activePostId === postId) {
this.setData({ activePostId: null });
} else {
this.setData({ activePostId: postId });
}
},
hideActionPopup() {
if (this.data.activePostId) {
this.setData({ activePostId: null });
}
},
stopPropagation() {
// Just stop event propagation, do nothing
},
// Like post
async likePost(e) {
const postId = e.currentTarget.dataset.id;
const post = this.data.posts.find(p => p.id === postId);
if (!post) return;
const type = post.likedByMe ? 2 : 1;
try {
await request.get('/post/like', { id: postId, type });
// Optimistic Update
const updatedPosts = this.data.posts.map(p => {
if (p.id === postId) {
const liked = !p.likedByMe;
const updatedLikes = liked
? (p.likes.includes('我') ? p.likes : [...p.likes, '我'])
: p.likes.filter(n => n !== '我');
return {
...p,
likedByMe: liked,
likes: updatedLikes
};
}
return p;
});
this.setData({
posts: updatedPosts,
displayedPosts: updatedPosts,
activePostId: null
});
// Call page API to refresh list data (including Like List text)
this.fetchPosts(true);
} catch (err) {
console.error('Like failed', err);
wx.showToast({ title: '操作失败', icon: 'none' });
}
},
// Collect post
async collectPost(e) {
const postId = e.currentTarget.dataset.id;
const post = this.data.posts.find(p => p.id === postId);
if (!post) return;
const type = post.isFavorited ? 2 : 1; // 1: Collect, 2: Cancel
try {
await request.get('/post/star', { id: postId, type });
// Optimistic Update
const updatedPosts = this.data.posts.map(p => {
if (p.id === postId) {
return { ...p, isFavorited: !p.isFavorited };
}
return p;
});
this.setData({
posts: updatedPosts,
displayedPosts: updatedPosts,
activePostId: null
});
// Refresh list to sync detailed data (like IDs if needed)
this.fetchPosts(true);
wx.showToast({ title: type === 1 ? '已收藏' : '已取消', icon: 'success' });
} catch (err) {
console.error('Collect failed', err);
wx.showToast({ title: '操作失败', icon: 'none' });
}
},
// Show comment input bar
showCommentInput(e) {
const postId = e.currentTarget.dataset.id;
this.setData({
showCommentBar: true,
commentingPostId: postId,
commentText: '',
activePostId: null // Hide popup
});
},
hideCommentBar() {
this.setData({
showCommentBar: false,
commentingPostId: null,
commentText: ''
});
},
onCommentInput(e) {
this.setData({ commentText: e.detail.value });
},
async submitComment() {
const { commentText, commentingPostId } = this.data;
if (!commentText.trim()) {
return;
}
try {
await request.post('/post/comment', {
postId: commentingPostId,
content: commentText.trim()
});
wx.showToast({ title: '评论成功', icon: 'success' });
// Optimistic update
const posts = this.data.posts.map(post => {
if (post.id === commentingPostId) {
const comments = [...post.comments, {
id: Date.now().toString(),
user: '我',
content: commentText.trim()
}];
return { ...post, comments };
}
return post;
});
this.setData({
posts,
displayedPosts: posts,
showCommentBar: false,
commentingPostId: null,
commentText: ''
});
// Silent refresh from server
this.fetchPosts(true);
} catch (err) {
console.error('Comment failed', err);
wx.showToast({ title: '评论失败', icon: 'none' });
}
},
goToCreatePost() {
wx.navigateTo({
url: '/pages/community/create/index'
});
},
toggleCommentExpand(e) {
const postId = e.currentTarget.dataset.id;
const posts = this.data.posts.map(p => {
if (p.id === postId) {
return { ...p, isExpanded: !p.isExpanded };
}
return p;
});
this.setData({ posts, displayedPosts: posts });
}
})