feat(留言板): 实现留言点赞功能并优化留言显示
- 新增留言点赞API接口及前端处理逻辑 - 优化留言时间显示格式,使用统一格式化函数 - 修复留言列表props传递问题,支持外部传入articleid - 移除无用图标和冗余代码,清理样式
This commit is contained in:
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user