时间线说明
本文主体记录 2026-02-28 的评论管理优化。
其中“前台评论可编辑删除”“移动端独立详情页”“统一评论详情组件”等能力在 2026-03-06 有后续增强,本篇已合并追记。
评论管理优化概述
评论管理系统是辣评平台的核心功能之一。本次优化涵盖了评论筛选、详情展示、编辑删除、代评功能等多个方面,显著提升了管理效率和用户体验。
优化目标
- 完善评论筛选功能
- 优化评论详情展示
- 改进编辑与删除功能
- 修复代评功能验证
- 优化评分处理逻辑
评论筛选功能
统一筛选组件
<template>
<div class="comment-filter">
<el-form :inline="true" :model="filters">
<el-form-item label="比赛届次">
<el-select
v-model="filters.competitionId"
placeholder="选择届次"
@change="handleFilterChange"
clearable
>
<el-option
v-for="comp in competitions"
:key="comp.id"
:label="`第${comp.number}届`"
:value="comp.id"
/>
</el-select>
</el-form-item>
<el-form-item label="投稿作品">
<el-select
v-model="filters.submissionId"
placeholder="选择作品"
@change="handleFilterChange"
clearable
filterable
>
<el-option
v-for="sub in submissions"
:key="sub.id"
:label="sub.title"
:value="sub.id"
/>
</el-select>
</el-form-item>
<el-form-item label="评论者">
<el-input
v-model="filters.username"
placeholder="输入用户名"
@input="handleUsernameInput"
clearable
/>
</el-form-item>
<el-form-item label="评分">
<el-select
v-model="filters.score"
placeholder="选择评分"
@change="handleFilterChange"
clearable
>
<el-option label="全部" :value="null" />
<el-option label="5星" :value="5" />
<el-option label="4星" :value="4" />
<el-option label="3星" :value="3" />
<el-option label="2星" :value="2" />
<el-option label="1星" :value="1" />
<el-option label="0星" :value="0" />
</el-select>
</el-form-item>
</el-form>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { debounce } from 'lodash-es'
const emit = defineEmits(['filter-change'])
const filters = ref({
competitionId: null,
submissionId: null,
username: '',
score: null
})
const competitions = ref([])
const submissions = ref([])
const handleFilterChange = () => {
emit('filter-change', filters.value)
}
const handleUsernameInput = debounce(() => {
handleFilterChange()
}, 500)
onMounted(async () => {
// 加载比赛和投稿列表
await loadCompetitions()
await loadSubmissions()
})
</script>
评论详情展示优化
PC端对话框展示
<template>
<el-dialog
v-model="visible"
title="评论详情"
width="600px"
:close-on-click-modal="false"
>
<div class="comment-detail">
<!-- 评论者信息 -->
<div class="comment-header">
<el-avatar :src="comment.user.avatar" />
<div class="user-info">
<div class="username"></div>
<div class="time"></div>
</div>
<el-rate v-model="comment.score" disabled show-score />
</div>
<!-- 投稿信息 -->
<div class="submission-info">
<el-tag type="info"></el-tag>
<span class="author">作者:</span>
</div>
<!-- 评论内容 -->
<div class="comment-content">
<el-input
v-if="isEditing"
v-model="editContent"
type="textarea"
:rows="10"
placeholder="请输入评论内容"
/>
<div v-else class="content-text">
</div>
</div>
<!-- 统计信息 -->
<div class="comment-stats">
<el-descriptions :column="2" size="small" border>
<el-descriptions-item label="字数">
</el-descriptions-item>
<el-descriptions-item label="评分">
星
</el-descriptions-item>
<el-descriptions-item label="创建时间">
</el-descriptions-item>
<el-descriptions-item label="更新时间">
</el-descriptions-item>
</el-descriptions>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button v-if="!isEditing" @click="handleEdit">编辑</el-button>
<el-button v-if="!isEditing" type="danger" @click="handleDelete">删除</el-button>
<el-button v-if="isEditing" @click="cancelEdit">取消</el-button>
<el-button v-if="isEditing" type="primary" @click="saveEdit">保存</el-button>
<el-button @click="visible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import { ref, watch } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { updateComment, deleteComment } from '@/api/comment'
const props = defineProps({
modelValue: Boolean,
comment: Object
})
const emit = defineEmits(['update:modelValue', 'refresh'])
const visible = ref(props.modelValue)
const isEditing = ref(false)
const editContent = ref('')
watch(() => props.modelValue, (val) => {
visible.value = val
if (val && props.comment) {
editContent.value = props.comment.content
}
})
watch(visible, (val) => {
emit('update:modelValue', val)
})
const handleEdit = () => {
isEditing.value = true
}
const cancelEdit = () => {
isEditing.value = false
editContent.value = props.comment.content
}
const saveEdit = async () => {
try {
await updateComment(props.comment.id, {
content: editContent.value
})
ElMessage.success('保存成功')
isEditing.value = false
emit('refresh')
} catch (error) {
ElMessage.error('保存失败')
}
}
const handleDelete = async () => {
try {
await ElMessageBox.confirm('确定删除此评论吗?', '提示', {
type: 'warning'
})
await deleteComment(props.comment.id)
ElMessage.success('删除成功')
visible.value = false
emit('refresh')
} catch (error) {
if (error !== 'cancel') {
ElMessage.error('删除失败')
}
}
}
</script>
<style scoped>
.comment-detail {
padding: 16px;
}
.comment-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid #ebeef5;
}
.user-info {
flex: 1;
}
.username {
font-size: 16px;
font-weight: bold;
color: #303133;
}
.time {
font-size: 12px;
color: #909399;
margin-top: 4px;
}
.submission-info {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
}
.author {
font-size: 14px;
color: #606266;
}
.comment-content {
margin-bottom: 16px;
}
.content-text {
line-height: 1.8;
color: #606266;
white-space: pre-wrap;
}
.comment-stats {
margin-top: 16px;
}
</style>
移动端全屏页面
<template>
<div class="mobile-comment-detail">
<div class="detail-header">
<el-button @click="goBack" circle>
<el-icon><ArrowLeft /></el-icon>
</el-button>
<span class="title">评论详情</span>
<el-dropdown @command="handleCommand">
<el-button circle>
<el-icon><More /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="edit">编辑</el-dropdown-item>
<el-dropdown-item command="delete">删除</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
<div class="detail-content">
<!-- 评论卡片 -->
<el-card class="info-card">
<div class="user-section">
<el-avatar :src="comment.user.avatar" :size="48" />
<div class="user-info">
<div class="username"></div>
<div class="time"></div>
</div>
</div>
<el-rate v-model="comment.score" disabled show-score />
</el-card>
<!-- 投稿信息卡片 -->
<el-card class="submission-card">
<template #header>
<span>投稿作品</span>
</template>
<div class="submission-info">
<div class="title"></div>
<div class="author">作者:</div>
</div>
</el-card>
<!-- 评论内容卡片 -->
<el-card class="content-card">
<template #header>
<span>评论内容</span>
</template>
<div class="content-text">
</div>
</el-card>
<!-- 统计信息卡片 -->
<el-card class="stats-card">
<template #header>
<span>统计信息</span>
</template>
<el-descriptions :column="1" size="small">
<el-descriptions-item label="字数">
</el-descriptions-item>
<el-descriptions-item label="评分">
星
</el-descriptions-item>
<el-descriptions-item label="创建时间">
</el-descriptions-item>
</el-descriptions>
</el-card>
</div>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { ArrowLeft, More } from '@element-plus/icons-vue'
const router = useRouter()
const goBack = () => {
router.back()
}
const handleCommand = (command) => {
if (command === 'edit') {
// 跳转到编辑页面
router.push(`/comment/edit/${props.comment.id}`)
} else if (command === 'delete') {
// 删除评论
handleDelete()
}
}
</script>
<style scoped>
.mobile-comment-detail {
min-height: 100vh;
background-color: #f5f7fa;
}
.detail-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
background-color: #fff;
border-bottom: 1px solid #ebeef5;
position: sticky;
top: 0;
z-index: 100;
}
.title {
font-size: 16px;
font-weight: bold;
}
.detail-content {
padding: 16px;
}
.info-card,
.submission-card,
.content-card,
.stats-card {
margin-bottom: 16px;
}
.user-section {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
}
.content-text {
line-height: 1.8;
color: #606266;
white-space: pre-wrap;
}
</style>
代评功能验证修复
问题描述
原有代码中,代评功能的验证逻辑存在错误,导致评分为0时无法通过验证。
修复前
// 错误的验证逻辑
if req.Score < 1 || req.Score > 5 {
return errors.New("评分必须在1-5之间")
}
修复后
// 正确的验证逻辑 - 允许评分为0
func (s *CommentService) ValidateScore(score int) error {
if score < 0 || score > 5 {
return errors.New("评分必须在0-5之间")
}
return nil
}
// 代评功能中的特殊处理
func (s *CommentService) CreateCommentForUser(ctx context.Context, req *CreateCommentRequest) error {
// 验证评分(允许0分)
if err := s.ValidateScore(req.Score); err != nil {
return err
}
// 创建评论
comment := &Comment{
UserID: req.UserID,
SubmissionID: req.SubmissionID,
Content: req.Content,
Score: req.Score, // 允许为0
CreatedBy: req.AdminID, // 记录管理员ID
}
return s.db.Create(comment).Error
}
评分处理逻辑优化
评分显示组件
<template>
<div class="score-display">
<el-rate
v-model="displayScore"
:disabled="disabled"
:show-score="showScore"
:allow-half="allowHalf"
/>
<span v-if="score === 0" class="zero-score-tip">
(未评分)
</span>
</div>
</template>
<script setup>
import { computed } from 'vue'
const props = defineProps({
score: {
type: Number,
default: 0
},
disabled: {
type: Boolean,
default: true
},
showScore: {
type: Boolean,
default: true
},
allowHalf: {
type: Boolean,
default: false
}
})
// 处理0分的显示
const displayScore = computed(() => {
return props.score === 0 ? 0 : props.score
})
</script>
<style scoped>
.score-display {
display: inline-flex;
align-items: center;
gap: 8px;
}
.zero-score-tip {
font-size: 12px;
color: #909399;
}
</style>
前台评论编辑删除功能
时间补充:该能力在 2026-03-06 完成主要实现并与详情组件统一改造联动。
功能实现
<template>
<div class="comment-item">
<div class="comment-header">
<div class="user-info">
<el-avatar :src="comment.user.avatar" :size="32" />
<span class="username"></span>
</div>
<div class="actions" v-if="canEdit">
<el-button type="text" @click="handleEdit">编辑</el-button>
<el-button type="text" @click="handleDelete">删除</el-button>
</div>
</div>
<div class="comment-content">
</div>
<div class="comment-footer">
<el-rate v-model="comment.score" disabled show-score />
<span class="time"></span>
</div>
</div>
</template>
<script setup>
import { computed } from 'vue'
import { useUserStore } from '@/stores/user'
const props = defineProps({
comment: Object
})
const userStore = useUserStore()
// 判断是否可以编辑(只能编辑自己的评论)
const canEdit = computed(() => {
return userStore.user.id === props.comment.userId
})
const handleEdit = () => {
emit('edit', props.comment)
}
const handleDelete = () => {
emit('delete', props.comment)
}
</script>
总结
评论管理系统的全面优化显著提升了管理效率和用户体验。通过完善筛选功能、优化详情展示、修复代评验证、改进评分处理等措施,我们打造了一个功能完善、体验良好的评论管理系统。
关键改进
- 统一了评论筛选功能
- 优化了PC端和移动端的详情展示
- 修复了代评功能的验证错误
- 改进了评分处理逻辑
- 实现了前台评论编辑删除功能
用户反馈
- 筛选功能更加便捷
- 详情展示更加清晰
- 代评功能正常工作
- 评分显示更加准确
- 前台操作更加灵活