feat: 任务和社区页面

This commit is contained in:
Blizzard
2026-02-06 17:27:35 +08:00
parent d42471e1d5
commit b800ea03b5
30 changed files with 1777 additions and 551 deletions
+168 -57
View File
@@ -1,5 +1,5 @@
// pages/community/index.js
import { MOCK_POSTS } from '../../utils/mockData';
import request from '../../utils/request';
Page({
data: {
@@ -8,31 +8,110 @@ Page({
activePostId: null, // For showing action popup
showCommentBar: false,
commentingPostId: null,
commentText: ''
commentText: '',
isLoading: false,
current: 1,
pageSize: 10,
hasMore: true
},
onLoad() {
this.setData({ posts: MOCK_POSTS });
this.updateDisplayedPosts();
this.fetchPosts(true);
},
onShow() {
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().setData({ selected: 2 });
}
// Refresh posts in case new ones were added
this.setData({ posts: MOCK_POSTS });
this.updateDisplayedPosts();
},
// Called by create post page
onRefresh() {
this.fetchPosts(true);
},
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 });
// 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 || {};
return {
id: item.id,
user: publisher.nickName || publisher.name || '花友',
avatar: avatarObj.url || '/assets/default_avatar.png',
content: item.content,
images: (item.imgList || []).map(img => img.url),
time: item.createdAtStr || '刚刚',
likes: (item.likeList || []).map(l => l.liker ? (l.liker.nickName || l.liker.name) : '花友'),
comments: (item.commentList || []).map(c => ({
id: c.id,
user: c.commentator ? (c.commentator.nickName || c.commentator.name) : '花友',
content: c.content
})),
likedByMe: item.hasLiked === 1,
likeCount: item.likeCount || 0,
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() {
const { posts } = this.data;
// Show all posts with likedByMe flag
const displayed = posts.map(post => ({
...post,
likedByMe: post.likes.includes('我的花园')
}));
this.setData({ displayedPosts: displayed });
// Just sync posts to displayedPosts
this.setData({ displayedPosts: this.data.posts });
},
// Preview image in full screen
@@ -72,31 +151,37 @@ Page({
},
// Like post
likePost(e) {
async likePost(e) {
const postId = e.currentTarget.dataset.id;
const posts = this.data.posts.map(post => {
if (post.id === postId) {
const likes = [...post.likes];
const myName = '我的花园';
if (likes.includes(myName)) {
// Unlike
const idx = likes.indexOf(myName);
likes.splice(idx, 1);
} else {
// Like
likes.push(myName);
}
return { ...post, likes };
}
return post;
});
const post = this.data.posts.find(p => p.id === postId);
if (!post) return;
this.setData({
posts,
activePostId: null // Hide popup after action
}, () => {
this.updateDisplayedPosts();
});
const type = post.likedByMe ? 2 : 1;
try {
await request.get('/post/like', { id: postId, type });
// Optimistic Update: Only toggle button state. Do NOT modify likes list text.
const updatedPosts = this.data.posts.map(p => {
if (p.id === postId) {
return { ...p, likedByMe: !p.likedByMe };
}
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' });
}
},
// Show comment input bar
@@ -122,39 +207,65 @@ Page({
this.setData({ commentText: e.detail.value });
},
submitComment() {
async submitComment() {
const { commentText, commentingPostId } = this.data;
if (!commentText.trim()) {
return;
}
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;
});
try {
await request.post('/post/comment', {
postId: commentingPostId,
content: commentText.trim()
});
this.setData({
posts,
showCommentBar: false,
commentingPostId: null,
commentText: ''
}, () => {
this.updateDisplayedPosts();
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 });
}
})