This commit is contained in:
ChuXun
2025-10-19 20:28:31 +08:00
parent c81f8a8b03
commit eaab9a762a
100 changed files with 23416 additions and 0 deletions

View File

@@ -0,0 +1,272 @@
// pages/forum-detail/forum-detail.js
const {forumData} = require('../../utils/data.js')
const learningTracker = require('../../utils/learningTracker.js')
const { showSuccess } = require('../../utils/util.js')
// pages/forum-detail/forum-detail.js
const userManager = require('../../utils/userManager.js')
Page({
data: {
post: null,
commentText: '',
comments: [],
postId: null
},
onLoad(options) {
const { id } = options
this.setData({ postId: parseInt(id) })
this.loadPostDetail(id)
},
onShow() {
// 开始跟踪学习时间
learningTracker.onPageShow('forum')
},
onHide() {
// 停止跟踪学习时间
learningTracker.onPageHide()
},
onUnload() {
// 记录学习时长
learningTracker.onPageUnload()
},
// 加载帖子详情
loadPostDetail(id) {
let posts = wx.getStorageSync('forumPosts') || forumData
const post = posts.find(p => p.id === parseInt(id))
if (post) {
// 增加浏览量
post.views += 1
// 检查是否已收藏
const favoritePosts = wx.getStorageSync('favoritePosts') || []
post.isFavorite = favoritePosts.some(fav => fav.id === post.id)
posts = posts.map(p => p.id === post.id ? post : p)
wx.setStorageSync('forumPosts', posts)
// 加载评论
const comments = this.loadComments(parseInt(id))
this.setData({
post,
comments
})
}
},
// 加载评论
loadComments(postId) {
const allComments = wx.getStorageSync('forumComments') || {}
return allComments[postId] || []
},
// 保存评论
saveComments(postId, comments) {
const allComments = wx.getStorageSync('forumComments') || {}
allComments[postId] = comments
wx.setStorageSync('forumComments', allComments)
// 更新帖子的评论数
let posts = wx.getStorageSync('forumPosts') || forumData
posts = posts.map(p => {
if (p.id === postId) {
p.comments = comments.length
}
return p
})
wx.setStorageSync('forumPosts', posts)
},
// 点赞
onLike() {
const { post } = this.data
let posts = wx.getStorageSync('forumPosts') || forumData
posts = posts.map(p => {
if (p.id === post.id) {
p.isLiked = !p.isLiked
p.likes = p.isLiked ? p.likes + 1 : p.likes - 1
}
return p
})
wx.setStorageSync('forumPosts', posts)
this.setData({
post: {
...post,
isLiked: !post.isLiked,
likes: post.isLiked ? post.likes - 1 : post.likes + 1
}
})
},
// 收藏帖子
onFavorite() {
const { post } = this.data
if (!post || !post.id || !post.title) {
wx.showToast({
title: '帖子数据异常',
icon: 'none'
})
console.error('帖子数据不完整:', post)
return
}
let favoritePosts = wx.getStorageSync('favoritePosts') || []
const index = favoritePosts.findIndex(p => p.id === post.id)
if (index > -1) {
// 已收藏,取消收藏
favoritePosts.splice(index, 1)
wx.setStorageSync('favoritePosts', favoritePosts)
console.log('取消收藏后的列表:', favoritePosts)
// 更新帖子状态
let posts = wx.getStorageSync('forumPosts') || []
posts = posts.map(p => {
if (p.id === post.id) {
p.isFavorite = false
}
return p
})
wx.setStorageSync('forumPosts', posts)
this.setData({
post: {
...post,
isFavorite: false
}
})
wx.showToast({
title: '已取消收藏',
icon: 'success'
})
} else {
// 未收藏,添加收藏
const favoritePost = {
id: post.id,
title: post.title,
category: post.category,
time: new Date().toLocaleString()
}
favoritePosts.push(favoritePost)
wx.setStorageSync('favoritePosts', favoritePosts)
console.log('收藏成功,当前收藏列表:', favoritePosts)
// 更新帖子状态
let posts = wx.getStorageSync('forumPosts') || []
posts = posts.map(p => {
if (p.id === post.id) {
p.isFavorite = true
}
return p
})
wx.setStorageSync('forumPosts', posts)
this.setData({
post: {
...post,
isFavorite: true
}
})
wx.showToast({
title: '收藏成功',
icon: 'success'
})
}
},
// 评论输入
onCommentInput(e) {
this.setData({ commentText: e.detail.value })
},
// 发表评论
onSubmitComment() {
const { commentText, comments, postId } = this.data
if (!commentText.trim()) {
wx.showToast({
title: '请输入评论内容',
icon: 'none'
})
return
}
// 获取用户信息
const userInfo = userManager.getUserInfo()
const userName = userInfo.nickname || '我'
const userAvatar = userInfo.avatar || '/images/avatar-default.png'
const newComment = {
id: Date.now(),
author: userName,
avatar: userAvatar,
content: commentText,
time: '刚刚'
}
const newComments = [newComment, ...comments]
// 保存评论
this.saveComments(postId, newComments)
// 同时将评论保存到帖子的commentList中
let posts = wx.getStorageSync('forumPosts') || []
posts = posts.map(p => {
if (p.id === postId) {
if (!p.commentList) {
p.commentList = []
}
p.commentList.unshift(newComment)
}
return p
})
wx.setStorageSync('forumPosts', posts)
this.setData({
comments: newComments,
commentText: ''
})
showSuccess('评论成功')
console.log('评论已发布', {
作者: userName,
内容: commentText,
帖子ID: postId
})
},
// 预览图片
onPreviewImage(e) {
const { url, urls } = e.currentTarget.dataset
wx.previewImage({
current: url, // 当前显示图片的链接
urls: urls // 需要预览的图片链接列表
})
},
// 分享
onShareAppMessage() {
const { post } = this.data
return {
title: post.title,
path: `/pages/forum-detail/forum-detail?id=${post.id}`
}
}
})

View File

@@ -0,0 +1,3 @@
{
"navigationBarTitleText": "帖子详情"
}

View File

@@ -0,0 +1,111 @@
<!--pages/forum-detail/forum-detail.wxml-->
<view class="container" wx:if="{{post}}">
<!-- 帖子内容 -->
<view class="post-detail">
<!-- 帖子头部 -->
<view class="post-header">
<image class="avatar" src="{{post.avatar || '/images/avatar-default.png'}}" mode="aspectFill"></image>
<view class="author-info">
<view class="author-name">{{post.author}}</view>
<view class="post-time">{{post.time}}</view>
</view>
<view class="category-tag">{{post.category}}</view>
</view>
<!-- 帖子标题 -->
<view class="post-title">{{post.title}}</view>
<!-- 帖子正文 -->
<view class="post-content">{{post.content}}</view>
<!-- 帖子图片 -->
<view class="post-images" wx:if="{{post.images && post.images.length > 0}}">
<image
class="post-image"
wx:for="{{post.images}}"
wx:key="index"
wx:for-item="img"
src="{{img}}"
mode="aspectFill"
bindtap="onPreviewImage"
data-url="{{img}}"
data-urls="{{post.images}}"
></image>
</view>
<!-- 统计信息 -->
<view class="post-stats">
<view class="stat-item">
<text class="stat-icon">👀</text>
<text class="stat-text">{{post.views}}次浏览</text>
</view>
<view class="stat-item">
<text class="stat-icon">❤️</text>
<text class="stat-text">{{post.likes}}次点赞</text>
</view>
<view class="stat-item">
<text class="stat-icon">💬</text>
<text class="stat-text">{{comments.length}}条评论</text>
</view>
</view>
</view>
<!-- 评论区 -->
<view class="comments-section">
<view class="section-title">
<text class="title-text">全部评论</text>
<text class="title-count">({{comments.length}})</text>
</view>
<view class="comments-list">
<view class="comment-item" wx:for="{{comments}}" wx:key="id">
<image class="comment-avatar" src="{{item.avatar || '/images/avatar-default.png'}}" mode="aspectFill"></image>
<view class="comment-content">
<view class="comment-author">{{item.author}}</view>
<view class="comment-text">{{item.content}}</view>
<view class="comment-time">{{item.time}}</view>
</view>
</view>
<view class="no-comments" wx:if="{{comments.length === 0}}">
<text class="no-comments-text">暂无评论,快来发表第一条评论吧~</text>
</view>
</view>
</view>
<!-- 底部操作栏 -->
<view class="action-bar">
<!-- 评论输入区 -->
<view class="input-row">
<input
class="comment-input"
placeholder="说点什么..."
value="{{commentText}}"
bindinput="onCommentInput"
/>
<button class="send-btn" bindtap="onSubmitComment">发送</button>
</view>
<!-- 操作按钮区 -->
<view class="action-row">
<view
class="action-btn {{post.isLiked ? 'liked' : ''}}"
bindtap="onLike"
>
<text class="action-icon">{{post.isLiked ? '❤️' : '🤍'}}</text>
<text class="action-text">点赞</text>
</view>
<button class="action-btn" open-type="share">
<text class="action-icon">📤</text>
<text class="action-text">转发</text>
</button>
<view
class="action-btn {{post.isFavorite ? 'favorited' : ''}}"
bindtap="onFavorite"
>
<text class="action-icon">{{post.isFavorite ? '⭐' : '☆'}}</text>
<text class="action-text">收藏</text>
</view>
</view>
</view>
</view>

View File

@@ -0,0 +1,278 @@
/* pages/forum-detail/forum-detail.wxss */
.container {
padding-bottom: 150rpx;
background-color: #F5F5F5;
}
/* 帖子详情 */
.post-detail {
background-color: #ffffff;
padding: 30rpx;
margin-bottom: 20rpx;
}
.post-header {
display: flex;
align-items: center;
margin-bottom: 30rpx;
}
.avatar {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
margin-right: 20rpx;
background-color: #E0E0E0;
}
.author-info {
flex: 1;
}
.author-name {
font-size: 30rpx;
font-weight: bold;
color: #333333;
margin-bottom: 8rpx;
}
.post-time {
font-size: 24rpx;
color: #999999;
}
.category-tag {
padding: 10rpx 24rpx;
background-color: #E8F8F0;
color: #50C878;
border-radius: 20rpx;
font-size: 24rpx;
}
.post-title {
font-size: 36rpx;
font-weight: bold;
color: #333333;
line-height: 1.5;
margin-bottom: 20rpx;
}
.post-content {
font-size: 30rpx;
color: #666666;
line-height: 1.8;
margin-bottom: 20rpx;
text-align: justify;
}
.post-images {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15rpx;
margin-bottom: 20rpx;
}
.post-image {
width: 100%;
height: 200rpx;
border-radius: 8rpx;
background-color: #F5F5F5;
}
.post-stats {
display: flex;
gap: 40rpx;
padding-top: 20rpx;
border-top: 1rpx solid #EEEEEE;
}
.stat-item {
display: flex;
align-items: center;
gap: 8rpx;
font-size: 24rpx;
color: #999999;
}
.stat-icon {
font-size: 28rpx;
}
/* 评论区 */
.comments-section {
background-color: #ffffff;
padding: 30rpx;
}
.section-title {
display: flex;
align-items: baseline;
margin-bottom: 30rpx;
}
.title-text {
font-size: 32rpx;
font-weight: bold;
color: #333333;
}
.title-count {
font-size: 26rpx;
color: #999999;
margin-left: 10rpx;
}
.comments-list {
margin-top: 20rpx;
}
.comment-item {
display: flex;
padding: 20rpx 0;
border-bottom: 1rpx solid #F5F5F5;
}
.comment-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 30rpx;
margin-right: 15rpx;
background-color: #E0E0E0;
}
.comment-content {
flex: 1;
}
.comment-author {
font-size: 26rpx;
font-weight: bold;
color: #333333;
margin-bottom: 10rpx;
}
.comment-text {
font-size: 28rpx;
color: #666666;
line-height: 1.6;
margin-bottom: 10rpx;
}
.comment-time {
font-size: 22rpx;
color: #999999;
}
.no-comments {
text-align: center;
padding: 60rpx 0;
}
.no-comments-text {
font-size: 26rpx;
color: #999999;
}
/* 底部操作栏 */
.action-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
background-color: #ffffff;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.08);
padding: 20rpx 30rpx;
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
/* 输入行 */
.input-row {
display: flex;
align-items: center;
gap: 15rpx;
margin-bottom: 15rpx;
}
.comment-input {
flex: 1;
height: 70rpx;
background-color: #F5F5F5;
border-radius: 35rpx;
padding: 0 30rpx;
font-size: 28rpx;
}
.send-btn {
padding: 0 35rpx;
height: 70rpx;
line-height: 70rpx;
background: linear-gradient(135deg, #50C878 0%, #3CB371 100%);
color: #ffffff;
border-radius: 35rpx;
font-size: 28rpx;
font-weight: bold;
box-shadow: 0 4rpx 12rpx rgba(80, 200, 120, 0.3);
transition: all 0.3s ease;
}
.send-btn:active {
transform: scale(0.95);
}
/* 操作按钮行 */
.action-row {
display: flex;
justify-content: space-around;
align-items: center;
gap: 20rpx;
}
.action-btn {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 12rpx 0;
background-color: transparent;
border: none;
margin: 0;
line-height: 1;
transition: all 0.3s ease;
}
.action-btn::after {
border: none;
}
.action-btn:active {
transform: scale(1.1);
}
.action-icon {
font-size: 36rpx;
margin-bottom: 6rpx;
}
.action-text {
font-size: 22rpx;
color: #666666;
}
.action-btn.liked .action-icon {
animation: pulse 0.6s ease;
}
.action-btn.liked .action-text {
color: #FF6B6B;
font-weight: bold;
}
.action-btn.favorited .action-icon {
animation: pulse 0.6s ease;
}
.action-btn.favorited .action-text {
color: #FFD700;
font-weight: bold;
}