refactor(reaction): 清理旧的评论代码

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-06 03:55:11 -06:00
parent 9c6c6e1784
commit c91c923fd6
9 changed files with 3 additions and 379 deletions

View File

@@ -287,16 +287,6 @@ export function createAnnouncement(announcement: AnnouncementEdit) {
return http.post("admin/announcement", announcement)
}
export function getCommentList(offset = 0, limit = 10, problem: string) {
return http.get("admin/comment", {
params: { offset, limit, problem },
})
}
export function deleteComment(id: number) {
return http.delete("admin/comment", { params: { id } })
}
export function getReactionStats(
offset = 0,
limit = 10,

View File

@@ -1,114 +0,0 @@
<template>
<n-flex justify="space-between" class="titleWrapper">
<h2 class="title">评论列表只列出有内容的</h2>
<div>
<n-input
v-model:value="query.problem"
clearable
placeholder="输入题目序号"
/>
</div>
</n-flex>
<n-data-table striped :columns="columns" :data="comments" />
<Pagination
:total="total"
v-model:limit="query.limit"
v-model:page="query.page"
/>
</template>
<script lang="ts" setup>
import { NButton } from "naive-ui"
import Pagination from "shared/components/Pagination.vue"
import { parseTime } from "utils/functions"
import type { Comment } from "utils/types"
import { getCommentList } from "../api"
import CommentActions from "./components/CommentActions.vue"
const comments = ref<Comment[]>([])
const total = ref(0)
const query = reactive({
limit: 10,
page: 1,
problem: "",
})
const columns: DataTableColumn<Comment>[] = [
{
title: "题目",
key: "problem",
width: 100,
render: (row) =>
h(
NButton,
{
text: true,
type: "info",
onClick: () => window.open("/problem/" + row.problem, "_blank"),
},
() => row.problem,
),
},
{
title: "提交",
key: "submission",
width: 200,
render: (row) =>
h(
NButton,
{
text: true,
type: "info",
onClick: () => window.open("/submission/" + row.submission, "_blank"),
},
() => row.submission.slice(0, 12),
),
},
{ title: "描述评分", key: "description_rating", width: 100 },
{ title: "难度评分", key: "difficulty_rating", width: 100 },
{ title: "综合评分", key: "comprehensive_rating", width: 100 },
{ title: "用户", key: "user.username", width: 150 },
{
title: "时间",
key: "create_time",
render: (row) => parseTime(row.create_time, "YYYY-MM-DD HH:mm:ss"),
width: 200,
},
{
title: "内容",
key: "content",
minWidth: 200,
maxWidth: 300,
ellipsis: true,
},
{
title: "选项",
key: "action",
width: 100,
render: (row) =>
h(CommentActions, { commentID: row.id, onDeleted: listComments }),
},
]
async function listComments() {
const offset = (query.page - 1) * query.limit
const res = await getCommentList(offset, query.limit, query.problem)
comments.value = res.data.results
total.value = res.data.total
}
onMounted(listComments)
watch(() => [query.page, query.limit], listComments)
watchDebounced(() => query.problem, listComments, {
debounce: 500,
maxWait: 1000,
})
</script>
<style scoped>
.titleWrapper {
margin-bottom: 16px;
}
.title {
margin: 0;
}
</style>

View File

@@ -1,22 +0,0 @@
<script lang="ts" setup>
import { deleteComment } from "admin/api"
const props = defineProps<{ commentID: number }>()
const emit = defineEmits(["deleted"])
const message = useMessage()
async function submit() {
await deleteComment(props.commentID)
message.success("成功删除")
emit("deleted")
}
</script>
<template>
<n-popconfirm @positive-click="submit">
<template #trigger>
<n-button secondary size="small" type="error">删除</n-button>
</template>
确定删除这条评论吗
</n-popconfirm>
</template>

View File

@@ -224,24 +224,6 @@ export function getMessageList(offset = 0, limit = 10) {
return http.get("message", { params: { limit, offset } })
}
export function createComment(data: {
problem_id: number
description_rating: number
difficulty_rating: number
comprehensive_rating: number
content: string
}) {
return http.post("comment", data)
}
export function getComment(problemID: number) {
return http.get("comment", { params: { problem_id: problemID } })
}
export function getCommentStatistics(problemID: number) {
return http.get("comment/statistics", { params: { problem_id: problemID } })
}
export function getReaction(problemID: number) {
return http.get("reaction", { params: { problem_id: problemID } })
}

View File

@@ -1,197 +0,0 @@
<template>
<n-alert type="error" v-if="!userStore.isAuthed" title="请先登录" />
<div v-else>
<n-alert
v-if="problem?.my_status !== 0"
class="title"
type="error"
title="请先完成该题"
></n-alert>
<div v-else>
<n-alert class="title" type="success" :title="title"></n-alert>
<n-form>
<n-form-item
label-width="220"
label-align="left"
label="题目是否描述清楚"
label-placement="left"
>
<Icon
v-if="hasCommented"
icon="fluent-emoji:star"
:width="24"
v-for="(_, i) in description_rating"
:key="i"
/>
<n-rate v-else size="large" v-model:value="description_rating" />
</n-form-item>
<n-form-item
label-width="220"
label-align="left"
:label="
'难度是否匹配(此题是' + DIFFICULTY[problem.difficulty] + '的)'
"
label-placement="left"
>
<Icon
v-if="hasCommented"
icon="fluent-emoji:star"
:width="24"
v-for="(_, i) in difficulty_rating"
:key="i"
/>
<n-rate v-else size="large" v-model:value="difficulty_rating" />
</n-form-item>
<n-form-item
label-width="220"
label-align="left"
label="综合评分"
label-placement="left"
>
<Icon
v-if="hasCommented"
icon="fluent-emoji:star"
:width="24"
v-for="(_, i) in difficulty_rating"
:key="i"
/>
<n-rate v-else size="large" v-model:value="comprehensive_rating" />
</n-form-item>
<n-form-item
v-if="!hasCommented"
label="对这道题的评价(可选,注意文明用语)"
>
<n-input v-model:value="content" type="textarea" />
</n-form-item>
<n-form-item v-if="hasCommented && content" label="你对这道题的评价:">
{{ content }}
</n-form-item>
<n-button
v-if="hasCommented && showStatistics"
type="primary"
@click="getComments"
>
查看统计
</n-button>
<n-button v-if="!hasCommented" type="primary" @click="submit">
提交
</n-button>
</n-form>
<div v-if="showStatistics">
<n-descriptions
class="list"
v-if="count"
:column="4"
bordered
label-placement="left"
>
<n-descriptions-item label="评论">
{{ count }}
</n-descriptions-item>
<n-descriptions-item label="描述">
{{ rating.description }}
</n-descriptions-item>
<n-descriptions-item label="难度">
{{ rating.difficulty }}
</n-descriptions-item>
<n-descriptions-item label="综合">
{{ rating.comprehensive }}
</n-descriptions-item>
</n-descriptions>
<n-empty class="list" v-if="show && count === 0">
暂无记录快去评论吧
</n-empty>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { Icon } from "@iconify/vue"
import { storeToRefs } from "pinia"
import { useProblemStore } from "oj/store/problem"
import { DIFFICULTY } from "utils/constants"
import { createComment, getComment, getCommentStatistics } from "oj/api"
import { useUserStore } from "shared/store/user"
interface Props {
showStatistics?: boolean
}
const { showStatistics = true } = defineProps<Props>()
const userStore = useUserStore()
const problemStore = useProblemStore()
const { problem } = storeToRefs(problemStore)
const message = useMessage()
const content = ref("")
const description_rating = ref(0)
const difficulty_rating = ref(0)
const comprehensive_rating = ref(0)
const [show, toggleShow] = useToggle()
const [hasCommented, toggleHasCommented] = useToggle()
const count = ref(0)
const rating = reactive({
description: 0,
difficulty: 0,
comprehensive: 0,
})
const title = computed(() => {
if (hasCommented.value) return "这道题你已经打过分了,你的评分如下:"
return "你已经完成了这道题,请给这道题打分吧!"
})
async function submit() {
if (
description_rating.value === 0 ||
difficulty_rating.value === 0 ||
comprehensive_rating.value === 0
) {
message.error("请完成打分")
return
}
const data = {
problem_id: problem.value!.id,
content: content.value,
description_rating: description_rating.value,
difficulty_rating: difficulty_rating.value,
comprehensive_rating: comprehensive_rating.value,
}
await createComment(data)
toggleHasCommented(true)
message.success("提交成功")
}
async function getComments() {
const res = await getCommentStatistics(problem.value!.id)
toggleShow(true)
if (!res.data) return
count.value = res.data.count
rating.description = res.data.rating.description
rating.difficulty = res.data.rating.difficulty
rating.comprehensive = res.data.rating.comprehensive
}
async function getMyComment() {
const res = await getComment(problem.value!.id)
if (!res.data) return
content.value = res.data.content
description_rating.value = res.data.description_rating
difficulty_rating.value = res.data.difficulty_rating
comprehensive_rating.value = res.data.comprehensive_rating
toggleHasCommented(true)
}
onMounted(getMyComment)
</script>
<style scoped>
.title {
margin-bottom: 24px;
}
.list {
margin-top: 24px;
}
</style>

View File

@@ -3,7 +3,7 @@ import { Icon } from "@iconify/vue"
import { storeToRefs } from "pinia"
import {
formatCode,
getComment,
getReaction,
submitCode,
updateProblemSetProgress,
} from "oj/api"
@@ -72,8 +72,8 @@ const { start: startCooldown, isPending: isCooldown } = useTimeout(5000, {
// ==================== AC后显示评论框 ====================
const { start: showCommentPanelDelayed } = useTimeoutFn(
async () => {
const res = await getComment(problem.value!.id)
if (!res.data) {
const res = await getReaction(problem.value!.id)
if (res.data.mine.length === 0) {
commentPanel.value = true
}
},

View File

@@ -177,7 +177,6 @@ const active = computed(() => {
if (path.startsWith("/admin/problem")) return "admin problem list"
if (path.startsWith("/admin/contest")) return "admin contest list"
if (path.startsWith("/admin/user")) return "admin user list"
if (path.startsWith("/admin/comment")) return "admin comment list"
if (path.startsWith("/admin/announcement")) return "admin announcement list"
if (path.startsWith("/admin/tutorial")) return "admin tutorial list"
if (path.startsWith("/admin/ai")) return "admin ai reports"

View File

@@ -12,7 +12,6 @@ export function usePermissions() {
canManageUsers: computed(() => userStore.isSuperAdmin),
canManageAnnouncements: computed(() => userStore.isSuperAdmin),
canManageComments: computed(() => userStore.isSuperAdmin),
canManageTutorials: computed(() => userStore.isSuperAdmin),
canManageSystemConfig: computed(() => userStore.isSuperAdmin),
canSendMessages: computed(() => userStore.isSuperAdmin),
@@ -65,7 +64,6 @@ export function checkRoutePermission(routeName: string): boolean {
"admin announcement list",
"admin announcement create",
"admin announcement edit",
"admin comment list",
"admin message list",
"admin tutorial list",
"admin tutorial create",

View File

@@ -594,18 +594,6 @@ export interface CreateMessage {
message: string
}
export interface Comment {
id: number
problem: string
submission: string
content: string
description_rating: 1 | 2 | 3 | 4 | 5
difficulty_rating: 1 | 2 | 3 | 4 | 5
comprehensive_rating: 1 | 2 | 3 | 4 | 5
create_time: Date
user: SampleUser
}
export type ReactionKey =
| "too_easy"
| "too_hard"