1
This commit is contained in:
272
pages/forum-detail/forum-detail.js
Normal file
272
pages/forum-detail/forum-detail.js
Normal 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}`
|
||||
}
|
||||
}
|
||||
})
|
||||
3
pages/forum-detail/forum-detail.json
Normal file
3
pages/forum-detail/forum-detail.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"navigationBarTitleText": "帖子详情"
|
||||
}
|
||||
111
pages/forum-detail/forum-detail.wxml
Normal file
111
pages/forum-detail/forum-detail.wxml
Normal 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>
|
||||
278
pages/forum-detail/forum-detail.wxss
Normal file
278
pages/forum-detail/forum-detail.wxss
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user