feat(留言板): 实现留言点赞功能并优化留言显示

- 新增留言点赞API接口及前端处理逻辑
- 优化留言时间显示格式,使用统一格式化函数
- 修复留言列表props传递问题,支持外部传入articleid
- 移除无用图标和冗余代码,清理样式
This commit is contained in:
qingfeng1121
2025-10-23 18:18:40 +08:00
parent 5b3fba7bfb
commit 6c4d14d06a
5 changed files with 101 additions and 15 deletions

View File

@@ -17,12 +17,15 @@
<img :src="getAvatar()" class="avatar">
<div class="user-info">
<div class="username">{{ comment.displayName || comment.nickname }}</div>
<div class="time">{{ comment.createdAt || '刚刚' }}</div>
<div class="time">{{ formatDate(comment.createdAt) || '刚刚' }}</div>
</div>
</div>
<div class="comment-content" v-html="comment.content"></div>
<div class="comment-actions">
<!-- <span v-if="comment.likes" class="likes">{{ comment.likes }} </span> -->
<span class="likes-btn" @click="handleLike(comment)">
<span v-if="comment.likes && comment.likes > 0" class="likes-count">{{ comment.likes }}</span>
👍
</span>
<span class="reply-btn" @click="handleReply(null, comment)">回复</span>
</div>
<!-- 回复列表 -->
@@ -32,11 +35,15 @@
<img :src="getAvatar()" class="avatar">
<div class="user-info">
<div class="username">{{ reply.displayName || reply.nickname }}</div>
<div class="time">{{ reply.createdAt || '刚刚' }}</div>
<div class="time">{{ formatDate(reply.createdAt) || '刚刚' }}</div>
</div>
</div>
<div class="reply-content">{{ reply.content }}</div>
<div class="reply-actions">
<span class="likes-btn" @click="handleLike(reply)">
<span v-if="reply.likes && reply.likes > 0" class="likes-count">{{ reply.likes }}</span>
👍
</span>
<span class="reply-btn" @click="handleReply(comment, reply)">回复</span>
</div>
</div>
@@ -106,10 +113,19 @@
</template>
<script setup>
import { reactive, ref, onMounted } from 'vue'
import { reactive, ref, onMounted, watch } from 'vue'
import { messageService } from '@/services'
import { ElMessage, ElForm } from 'element-plus'
import { useGlobalStore } from '@/store/globalStore'
import { formatDate } from '@/utils/dateUtils'
// 定义组件属性
const props = defineProps({
comments: {
type: Number,
default: null
}
})
const globalStore = useGlobalStore()
const messageBoardData = ref([]) // 留言板留言articleid为空的主留言及其回复
const loading = ref(false)
@@ -213,7 +229,7 @@ const rules = {
// 生成头像URL
const getAvatar = (email) => {
if (!email) return 'https://www.gravatar.com/avatar?d=mp&s=40'
return `https://www.gravatar.com/avatar/${email}?d=mp&s=40`
return `https://www.gravatar.com/avatar/${email}?d=mp&s=40`
}
// 从后端获取留言列表
@@ -222,9 +238,15 @@ const fetchMessages = async () => {
loading.value = true
let res = null
// 安全获取文章ID如果globalStore中没有articlebutn则返回null
const articleData = globalStore.getValue('articlebutn')
const articleid = (articleData && typeof articleData === 'object' && 'id' in articleData) ? articleData.id : null
// 优先使用props传递的articleid其次使用globalStore中的数据
let articleid = props.comments || null
if (!articleid) {
// 安全获取文章ID如果globalStore中没有articlebutn则返回null
const articleData = globalStore.getValue('articlebutn')
articleid = (articleData && typeof articleData === 'object' && 'id' in articleData) ? articleData.id : null
}
form.articleid = articleid
// 根据是否有文章ID选择不同的API调用
@@ -327,6 +349,13 @@ const cancelReply = () => {
form.content = ''
}
// 监听articleid变化重新加载留言
watch(() => props.comments, (newVal) => {
if (newVal) {
fetchMessages()
}
}, { immediate: false })
// 组件挂载时获取留言列表
onMounted(() => {
fetchMessages()
@@ -410,6 +439,29 @@ const post_comment_reply_cancel = () => {
replyingTo.value = { id: null, nickname: '', content: '' }
}
// 处理点赞
const handleLike = async (msg) => {
try {
// 显示加载状态或禁用按钮
msg.isLiking = true
const res = await messageService.likeMessage(msg.messageid)
if (res.success && res.data) {
// 更新点赞数
msg.likes = res.data.likes || 0
ElMessage.success('点赞成功')
} else {
ElMessage.error('点赞失败:' + (res.message || '未知错误'))
}
} catch (error) {
console.error('点赞失败:', error)
ElMessage.error('网络错误,请稍后重试')
} finally {
msg.isLiking = false
}
}
</script>
<style scoped>
@@ -508,6 +560,29 @@ const post_comment_reply_cancel = () => {
text-decoration: underline;
}
.likes-btn {
margin-right: 15px;
color: #666;
cursor: pointer;
transition: all 0.3s ease;
padding: 4px 8px;
border-radius: 4px;
}
.likes-btn:hover {
color: #e74c3c;
background-color: rgba(231, 76, 60, 0.1);
}
.likes-btn:active {
transform: scale(0.95);
}
.likes-count {
margin-right: 4px;
font-weight: 600;
}
.replies {
margin-top: 15px;
padding-left: 20px;