Commit 0ddc8e7d by lijiabin

【需求 25171】refactor: 统一问吧和普通帖子的评论回复展示逻辑

问吧的子评论之前会收起成"查看全部N条回复"跳转弹窗,跟帖子的内联展开+分页不一致。
getQuestionComment 实际已经和帖子一样直接返回前5条 children,只是总数字段名不同
(childNum vs childrenNum),所以是纯前端展示差异,统一成和帖子一致的内联展开/分页
即可,不需要改后端。CommentListDialog 因此变成无引用组件,一并删除。

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
parent 17cdccfc
...@@ -186,112 +186,98 @@ ...@@ -186,112 +186,98 @@
</div> </div>
</div> </div>
<!-- 回复列表 --> <!-- 回复列表 -->
<!-- 问吧 和 其他 做区分 --> <div v-if="item.children?.length" class="mt-3 ml-4 space-y-3">
<template v-if="!isQuestion"> <!-- 回复评论的内容 里面可能是 展示全部的 也有可能是展示5条之内容 -->
<div v-if="item.children?.length" class="mt-3 ml-4 space-y-3"> <div
<!-- 回复评论的内容 里面可能是 展示全部的 也有可能是展示5条之内容 --> v-for="child in getCurrentChildrenList(item)"
<div v-loading="item.loadingChildren"
v-for="child in getCurrentChildrenList(item)" :key="child.id"
v-loading="item.loadingChildren" class="flex gap-2 p-3 rounded-lg"
:key="child.id" >
class="flex gap-2 p-3 rounded-lg" <img
> @click="jumpToUserHomePage({ userId: child.userId, type })"
<img :src="child.avatar"
@click="jumpToUserHomePage({ userId: child.userId, type })" alt=""
:src="child.avatar" class="w-8 h-8 rounded-full object-cover cursor-pointer ring-1 ring-slate-200"
alt="" />
class="w-8 h-8 rounded-full object-cover cursor-pointer ring-1 ring-slate-200" <div class="flex-1">
/> <div class="flex items-center gap-1">
<div class="flex-1"> <span class="font-semibold text-gray-600 text-base">{{
<div class="flex items-center gap-1"> child.replyUser
<span class="font-semibold text-gray-600 text-base">{{ }}</span>
child.replyUser <!-- v-if="item.replyName && item.replyName !== parentComment?.replyUser" -->
}}</span> <span class="text-gray-500 text-sm flex items-center gap-1 px-1">
<!-- v-if="item.replyName && item.replyName !== parentComment?.replyUser" --> <el-icon class="mx-1"><IEpCaretRight /></el-icon>
<span class="text-gray-500 text-sm flex items-center gap-1 px-1"> {{ child.replyName }}
<el-icon class="mx-1"><IEpCaretRight /></el-icon> </span>
{{ child.replyName }} <button
</span> v-if="isSelfComment(child)"
<button class="cursor-pointer text-red-500 text-xs leading-none"
v-if="isSelfComment(child)" @click="handleDeleteComment(child)"
class="cursor-pointer text-red-500 text-xs leading-none" >
@click="handleDeleteComment(child)" 删除
> </button>
删除 </div>
</button> <p
</div> class="text-gray-800 my-2 break-all whitespace-pre-wrap text-[16px]"
<p v-html="parseEmoji(child.contentHtml || child.content)"
class="text-gray-800 my-2 break-all whitespace-pre-wrap text-[16px]" v-parse-comment
v-html="parseEmoji(child.contentHtml || child.content)" ></p>
v-parse-comment <!-- 评论图片列表 -->
></p> <div class="flex flex-wrap gap-2">
<!-- 评论图片列表 --> <div
<div class="flex flex-wrap gap-2"> v-for="(img, imgIndex) in child.imgUrlList"
<div :key="imgIndex"
v-for="(img, imgIndex) in child.imgUrlList" class="w-20 h-20 rounded-lg overflow-hidden mb-2"
:key="imgIndex" >
class="w-20 h-20 rounded-lg overflow-hidden mb-2" <el-image
> :src="img"
<el-image :preview-teleported="true"
:src="img" class="w-full h-full object-cover"
:preview-teleported="true" :preview-src-list="child.imgUrlList"
class="w-full h-full object-cover" :initial-index="imgIndex"
:preview-src-list="child.imgUrlList" fit="cover"
:initial-index="imgIndex" />
fit="cover"
/>
</div>
</div> </div>
<div class="flex items-center justify-between"> </div>
<div class="flex items-center gap-6 text-gray-500 text-[14px]"> <div class="flex items-center justify-between">
<span class="flex items-center gap-2"> <div class="flex items-center gap-6 text-gray-500 text-[14px]">
<span <span class="flex items-center gap-2">
>{{ <span
dayjs(child.createTime * 1000).format('YYYY-MM-DD HH:mm:ss') >{{
}} dayjs(child.createTime * 1000).format('YYYY-MM-DD HH:mm:ss')
</span> }}
<span v-if="child.region" class="text-gray-500"
>·{{ child.region }}</span
>
</span> </span>
<div class="flex gap-2 items-center hover:text-blue-500"> <span v-if="child.region" class="text-gray-500"
<div >·{{ child.region }}</span
class="flex items-center gap-1 cursor-pointer" >
@click="handleLickComment(child)" </span>
> <div class="flex gap-2 items-center hover:text-blue-500">
<el-icon :size="16"> <div
<svg-icon class="flex items-center gap-1 cursor-pointer"
:name="child.hasPraise ? 'praise_fill' : 'praise'" @click="handleLickComment(child)"
></svg-icon>
</el-icon>
<span>{{ child.postPriseCount }}</span>
</div>
</div>
<button
@click="handleReply(child, index)"
class="cursor-pointer hover:text-blue-500 transition-colors"
> >
回复 <el-icon :size="16">
</button> <svg-icon
:name="child.hasPraise ? 'praise_fill' : 'praise'"
></svg-icon>
</el-icon>
<span>{{ child.postPriseCount }}</span>
</div>
</div> </div>
<button
@click="handleReply(child, index)"
class="cursor-pointer hover:text-blue-500 transition-colors"
>
回复
</button>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</template> </div>
<!-- 问吧 如果有 子评论 直接变成 查看更多 -->
<template v-else>
<div v-if="item.childNum">
<button
class="cursor-pointer text-sm text-gray-500 mt-2"
@click="handleOpenCommentDialog(item)"
>
查看全部{{ item.childNum }} 条回复>
</button>
</div>
</template>
<!-- 只有大于5 才会显示这个 --> <!-- 只有大于5 才会显示这个 -->
<div class="ml-4" v-show="item.childrenNum > 5 && !isQuestion"> <div class="ml-4" v-show="item.childrenNum > 5">
<!-- 展示 展开回复 --> <!-- 展示 展开回复 -->
<button <button
v-show="!item.showChildrenPage" v-show="!item.showChildrenPage"
...@@ -386,7 +372,6 @@ ...@@ -386,7 +372,6 @@
</div> </div>
</div> </div>
</div> </div>
<CommentListDialog ref="commentListDialogRef" :articleId="id" :pid="currentDialogCommentPid" />
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
...@@ -396,6 +381,7 @@ import { ...@@ -396,6 +381,7 @@ import {
addComment, addComment,
getCommentChildren, getCommentChildren,
getSecondCommentList, getSecondCommentList,
getSecondCommentChildren,
topOrCancelTopComment, topOrCancelTopComment,
deleteComment, deleteComment,
} from '@/api' } from '@/api'
...@@ -404,7 +390,6 @@ import { ArticleTypeEnum, BooleanFlag } from '@/constants' ...@@ -404,7 +390,6 @@ import { ArticleTypeEnum, BooleanFlag } from '@/constants'
import type { CommentItemDto } from '@/api' import type { CommentItemDto } from '@/api'
import { useUserStore } from '@/stores' import { useUserStore } from '@/stores'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import CommentListDialog from '../CommentListDialog/index.vue'
import { useNavigation } from '@/hooks' import { useNavigation } from '@/hooks'
import { parseEmoji } from '@/utils/emoji' import { parseEmoji } from '@/utils/emoji'
import CommentBox from '../CommentBox/index.vue' import CommentBox from '../CommentBox/index.vue'
...@@ -456,7 +441,6 @@ const isAuthor = computed(() => { ...@@ -456,7 +441,6 @@ const isAuthor = computed(() => {
return authorId === userInfo.value.userId return authorId === userInfo.value.userId
}) })
const commentRef = useTemplateRef<HTMLElement | null>('commentRef') const commentRef = useTemplateRef<HTMLElement | null>('commentRef')
const commentListDialogRef = useTemplateRef<typeof CommentListDialog>('commentListDialogRef')
const replyToOtherBoxRefList = ref<InstanceType<typeof CommentBox>[]>([]) const replyToOtherBoxRefList = ref<InstanceType<typeof CommentBox>[]>([])
const commentItemRefList = ref<HTMLElement[]>([]) const commentItemRefList = ref<HTMLElement[]>([])
// 回滚到评论框 // 回滚到评论框
...@@ -491,6 +475,9 @@ const { ...@@ -491,6 +475,9 @@ const {
...item, ...item,
imgUrlList: getCommentImageList(item.imgUrl), imgUrlList: getCommentImageList(item.imgUrl),
children: item.children?.map(formatCommentImageList) ?? [], children: item.children?.map(formatCommentImageList) ?? [],
// 问吧(getQuestionComment)用的是 childNum,帖子(getComment)用的是 childrenNum,
// 这里统一映射成 childrenNum,模板就不用再按 isQuestion 分支判断了
childrenNum: item.childrenNum ?? item.childNum,
} }
// if (obj.showChildrenPage == undefined) { // if (obj.showChildrenPage == undefined) {
obj.showChildrenPage = false obj.showChildrenPage = false
...@@ -712,7 +699,8 @@ const handleChildrenCurrentChange = (e: number, item: CommentItemDto, index: num ...@@ -712,7 +699,8 @@ const handleChildrenCurrentChange = (e: number, item: CommentItemDto, index: num
// 根据页数获取子评论列表 // 根据页数获取子评论列表
const getCommentChildrenList = async (item: CommentItemDto) => { const getCommentChildrenList = async (item: CommentItemDto) => {
const { data } = await getCommentChildren({ const fetchChildren = isQuestion ? getSecondCommentChildren : getCommentChildren
const { data } = await fetchChildren({
pid: item.id, pid: item.id,
articleId: id, articleId: id,
current: item.childrenPageCurrent, current: item.childrenPageCurrent,
...@@ -730,12 +718,6 @@ const getCurrentChildrenList = (item: CommentItemDto) => { ...@@ -730,12 +718,6 @@ const getCurrentChildrenList = (item: CommentItemDto) => {
} }
} }
const currentDialogCommentPid = ref(0)
const handleOpenCommentDialog = (item: CommentItemDto) => {
currentDialogCommentPid.value = item.id
commentListDialogRef.value?.open()
}
defineExpose({ defineExpose({
scrollToCommentBox: () => handleBackTop(), scrollToCommentBox: () => handleBackTop(),
search: () => search(), search: () => search(),
......
<template>
<el-dialog
v-model="visible"
:title="dialogTitle"
width="650px"
class="rounded-2xl!"
:show-close="false"
top="5vh"
append-to-body
destroy-on-close
>
<!-- 自定义头部 -->
<template #header="{ close, titleId, titleClass }">
<div class="flex items-center gap-3 py-4 px-2 border-b border-gray-100">
<el-icon
class="cursor-pointer hover:text-blue-500 transition-colors"
@click="close"
:size="24"
>
<IEpArrowLeft />
</el-icon>
<span :id="titleId" :class="titleClass" class="text-lg font-bold text-gray-800">
评论回复
</span>
</div>
</template>
<div class="flex flex-col h-[75vh]">
<!-- 中间滚动区域 -->
<div ref="scrollContainer" class="flex-1 overflow-y-auto custom-scrollbar p-6 pt-0">
<!-- 1. 顶部:父级评论展示 -->
<div v-if="parentComment" class="flex gap-4 bg-gray-50 p-5 rounded-xl">
<img
:src="parentComment.avatar"
class="w-12 h-12 rounded-full object-cover border-2 border-gray-200 flex-shrink-0"
/>
<div class="flex-1">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-1.5">
<span class="font-bold text-gray-900 text-base">{{ parentComment.replyUser }}</span>
<button
v-if="isSelfComment(parentComment)"
class="cursor-pointer text-red-500 text-13px leading-none"
@click="handleDeleteComment(parentComment, true)"
>
删除
</button>
</div>
<div class="flex items-center gap-3">
<!-- 点赞按钮 -->
<div
class="flex items-center gap-1.5 cursor-pointer text-gray-500 hover:text-blue-500 transition-colors px-3 py-1.5 rounded-full hover:bg-blue-50"
@click="handleLike(parentComment)"
>
<el-icon :size="18">
<svg-icon :name="parentComment.hasPraise ? 'praise_fill' : 'praise'"></svg-icon>
</el-icon>
<span class="text-sm font-medium">{{ parentComment.postPriseCount || 0 }}</span>
</div>
</div>
</div>
<div
class="text-gray-800 text-base leading-relaxed mb-2"
v-html="parseEmoji(parentComment.contentHtml || parentComment.content)"
v-parse-comment="closeDialog"
></div>
<!-- 下方图片 -->
<div class="flex flex-wrap gap-2" v-if="parentComment.imgUrl">
<div
v-for="(img, imgIndex) in parentComment.imgUrl.split(',').filter(Boolean)"
:key="imgIndex"
class="w-20 h-20 rounded-lg overflow-hidden mb-2 cursor-pointer"
>
<el-image
:src="img"
:preview-teleported="true"
class="w-full h-full object-cover"
:preview-src-list="parentComment.imgUrl.split(',').filter(Boolean)"
:initial-index="imgIndex"
fit="cover"
/>
</div>
</div>
<div class="text-sm text-gray-500 flex items-center gap-2">
<span>{{ dayjs(parentComment.createTime * 1000).format('MM-DD HH:mm') }}</span>
<span v-if="parentComment.region" class="text-xs text-gray-500"
>·{{ parentComment.region }}</span
>
</div>
</div>
</div>
<!-- 分割线 & 统计 -->
<div class="h-2.5"></div>
<div class="text-base font-bold text-gray-800 mb-6 px-1">{{ total }} 条回复</div>
<!-- 2. 下方:回复列表 -->
<div v-loading="loading" class="space-y-6">
<div v-for="(item, index) in list" :key="item.id" class="flex gap-4 relative group">
<img
:src="item.avatar"
class="w-10 h-10 rounded-full object-cover cursor-pointer hover:opacity-80 transition-opacity flex-shrink-0"
/>
<div class="flex-1 border-b border-gray-100">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-1">
<span class="font-semibold text-gray-900 text-base">{{ item.replyUser }}</span>
<!-- v-if="item.replyName && item.replyName !== parentComment?.replyUser" -->
<span class="text-gray-500 text-sm flex items-center gap-1">
<el-icon class="mx-1"><IEpCaretRight /></el-icon>
{{ item.replyName }}
</span>
<button
v-if="isSelfComment(item)"
class="cursor-pointer text-red-500 text-13px leading-none"
@click="handleDeleteComment(item)"
>
删除
</button>
</div>
<!-- 列表项点赞 -->
<div class="flex items-center gap-4">
<div
class="flex items-center gap-1.5 cursor-pointer text-gray-500 hover:text-blue-500 transition-colors px-3 py-1.5 rounded-full hover:bg-blue-50"
@click="handleReplyInline(item, index)"
>
<span class="text-sm font-medium">回复</span>
</div>
<div
class="flex items-center gap-1.5 cursor-pointer text-gray-500 hover:text-blue-500 transition-colors px-3 py-1.5 rounded-full hover:bg-blue-50"
@click="handleLike(item)"
>
<el-icon :size="18">
<svg-icon :name="item.hasPraise ? 'praise_fill' : 'praise'"></svg-icon>
</el-icon>
<span class="text-sm font-medium">{{ item.postPriseCount || 0 }}</span>
</div>
</div>
</div>
<p
class="text-gray-700 text-base mb-2 break-all leading-relaxed whitespace-pre-wrap"
v-html="parseEmoji(item.contentHtml || item.content)"
v-parse-comment="closeDialog"
></p>
<!-- 下方图片 -->
<div class="flex flex-wrap gap-2" v-if="item.imgUrl">
<div
v-for="(img, imgIndex) in item.imgUrl.split(',').filter(Boolean)"
:key="imgIndex"
class="w-20 h-20 rounded-lg overflow-hidden mb-2 cursor-pointer"
>
<el-image
:src="img"
:preview-teleported="true"
class="w-full h-full object-cover"
:preview-src-list="item.imgUrl.split(',').filter(Boolean)"
:initial-index="imgIndex"
fit="cover"
/>
</div>
</div>
<div class="text-[14px] text-gray-500 flex items-center gap-2">
<span>{{ dayjs(item.createTime * 1000).format('MM-DD HH:mm') }}</span>
<span v-if="item.region" class="text-xs text-gray-500">·{{ item.region }}</span>
</div>
<!-- 内嵌回复框 -->
<div
v-show="currentInlineReplyId === item.id"
class="mt-4 bg-gray-50 p-4 rounded-xl border border-gray-200 animate-fade-in animate-fade-out"
>
<!-- 评论box -->
<CommentBox
v-model:inputText="commentStr"
v-model:inputImg="imgUrl"
:textAreaHeight="60"
:placeholder="`回复 ${item.replyUser}`"
:ref="
(el) => (replyToOtherBoxRefList[index] = el as InstanceType<typeof CommentBox>)
"
>
<template #submit>
<el-button
:disabled="isDisabled"
:loading="loadingBtn"
type="primary"
@click="submitReply(item.id, index)"
class="px-6 py-2 bg-blue-500 hover:bg-blue-600 rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200"
>发表</el-button
>
</template>
</CommentBox>
</div>
</div>
</div>
<!-- 加载更多 / 分页 -->
<div class="flex justify-end" v-if="total > 0">
<el-pagination
v-model:current-page="searchParams.current"
:page-size="searchParams.size"
:total="total"
layout="prev, pager, next,total"
@current-change="goToPage"
@size-change="changePageSize"
/>
</div>
<el-empty v-if="!loading && list.length === 0" description="暂无回复" :image-size="80" />
</div>
</div>
<!-- 3. 底部固定回复框 -->
<div
class="border-t border-gray-100 p-5 bg-white flex items-start gap-4 z-10 shadow-[0_-4px_16px_rgba(0,0,0,0.04)] pb-0"
>
<img :src="currentUserAvatar" class="w-10 h-10 rounded-full object-cover flex-shrink-0" />
<CommentBox
class="flex-1"
v-model:inputText="bottomCommentContent"
v-model:inputImg="bottomImgUrl"
:textAreaHeight="40"
:placeholder="`回复 ${parentComment?.replyUser}`"
ref="commentBoxRef"
/>
<el-button
type="primary"
size="large"
circle
class="px-6 py-2 bg-blue-500 hover:bg-blue-600 rounded-lg text-white text-sm font-medium shadow-sm hover:shadow-md transition-all duration-200"
:disabled="!bottomCommentContent.trim() || bottomLoadingBtn"
@click="submitReply(parentComment?.id)"
>
<el-icon :size="20"><IEpPosition /></el-icon>
</el-button>
</div>
</div>
</el-dialog>
</template>
<script setup lang="ts">
import { useUserStore } from '@/stores'
import { storeToRefs } from 'pinia'
import {
getSecondCommentChildren,
addComment,
addOrCancelCommentLike,
getCommentDetail,
deleteComment,
} from '@/api'
import type { CommentItemDto } from '@/api'
import { BooleanFlag } from '@/constants'
import { usePageSearch } from '@/hooks'
import { parseEmoji } from '@/utils/emoji'
import CommentBox from '../CommentBox/index.vue'
import dayjs from 'dayjs'
import { push } from 'notivue'
import { IS_REAL_KEY_COMMENT } from '@/constants/symbolKey'
import { useMessageBox } from '@/hooks'
const { articleId, pid } = defineProps<{
articleId: number
pid: number
}>()
const emit = defineEmits<{
(e: 'refresh'): void // 通知父组件刷新
}>()
provide(IS_REAL_KEY_COMMENT, BooleanFlag.YES)
// Store
const userStore = useUserStore()
const { userInfo } = storeToRefs(userStore)
const currentUserAvatar = computed(() => userInfo.value.avatar)
const { confirm } = useMessageBox()
// State
const visible = ref(false)
const parentComment = ref<CommentItemDto | null>(null)
const dialogTitle = ref('详情')
// Inline Reply State
const currentInlineReplyId = ref<number | null>(null)
const bottomCommentContent = ref('')
const bottomImgUrl = ref('')
const bottomLoadingBtn = ref(false)
const replyToOtherBoxRefList = ref<InstanceType<typeof CommentBox>[]>([])
const commentBoxRef = useTemplateRef<InstanceType<typeof CommentBox>>('commentBoxRef')
const scrollContainerRef = useTemplateRef('scrollContainer')
const commentStr = ref('')
const imgUrl = ref('')
const loadingBtn = ref(false)
const isDisabled = computed(() => !commentStr.value.trim() || loadingBtn.value)
const isSelfComment = (item: CommentItemDto) =>
String(item.userId || '') === String(userInfo.value.userId || '')
// --- Actions ---
const { list, total, search, searchParams, goToPage, changePageSize, refresh, loading } =
usePageSearch(getSecondCommentChildren, {
defaultParams: {
articleId: articleId,
pid: 0,
},
immediate: false,
})
const open = async () => {
// const { data } = await getSecondCommentChildren({
// pid: item.id,
// current: 1,
// size: 10,
// articleId: articleId,
// })
// list.value = data.list
// parentComment.value = item
// // Reset state
// currentInlineReplyId.value = null
// commentStr.value = ''
// bottomCommentContent.value = ''
await nextTick()
searchParams.value.pid = pid
search()
const { data } = await getCommentDetail(pid)
parentComment.value = data
visible.value = true
}
// 点击列表中的“回复”
const handleReplyInline = (item: CommentItemDto, index: number) => {
if (currentInlineReplyId.value === item.id) {
currentInlineReplyId.value = null // Toggle off
} else {
currentInlineReplyId.value = item.id
commentStr.value = '' // Clear previous
imgUrl.value = ''
// 聚焦到输入框
replyToOtherBoxRefList.value[index]?.focus()
}
}
// 提交评论 (共用逻辑)
// targetId: 如果是回复父评论,传 parentComment.id;如果是回复子评论,传 item.id
const submitReply = async (targetId: number | undefined, index?: number) => {
if (!targetId) return
// 判断使用的是哪个输入框的内容
const isBottom = targetId === parentComment.value?.id
const content = isBottom ? bottomCommentContent.value : commentStr.value
const imgStr = isBottom ? bottomImgUrl.value : imgUrl.value
try {
let mentionUserIdList: string | undefined = undefined
let contentHtml: string | undefined = undefined
if (isBottom) {
bottomLoadingBtn.value = true
mentionUserIdList = commentBoxRef.value?.getMentionFns?.()?.getMentionUserIds?.()?.join?.(',')
contentHtml = commentBoxRef.value?.getAnswerHtml?.()
} else {
loadingBtn.value = true
mentionUserIdList = replyToOtherBoxRefList.value[index!]
?.getMentionFns?.()
?.getMentionUserIds?.()
?.join?.(',')
contentHtml = replyToOtherBoxRefList.value[index!]?.getAnswerHtml?.()
}
await addComment({
articleId: articleId,
content: content,
pid: targetId, // 这里的pid逻辑根据您的后端接口来,通常回复子评论也是传该子评论ID作为pid
imgUrl: imgStr,
mentionUserIdList,
contentHtml,
})
push.success('回复成功')
// 清空输入框
if (isBottom) {
bottomCommentContent.value = ''
bottomImgUrl.value = ''
} else {
commentStr.value = ''
currentInlineReplyId.value = null
}
// 刷新列表
await refresh()
// 通知父组件可能需要更新评论数
emit('refresh')
scrollContainerRef.value?.scrollTo({
top: scrollContainerRef.value?.scrollHeight,
behavior: 'smooth',
})
// 下滑到底部
} catch (error) {
console.error(error)
} finally {
if (isBottom) {
bottomLoadingBtn.value = false
} else {
loadingBtn.value = false
}
}
}
// 点赞
const handleLike = async (item: CommentItemDto) => {
try {
await addOrCancelCommentLike(item.id)
// 乐观更新 UI
if (item.hasPraise === BooleanFlag.YES) {
item.hasPraise = BooleanFlag.NO
item.postPriseCount--
push.success('取消点赞成功')
} else {
item.hasPraise = BooleanFlag.YES
item.postPriseCount++
push.success('点赞成功')
}
} catch (error) {
console.error(error)
}
}
const handleDeleteComment = async (item: CommentItemDto, isParent = false) => {
await confirm({
title: '提示',
message: '确定删除该评论吗?',
type: 'danger',
})
await deleteComment(item.id)
push.success('删除成功')
if (isParent) {
visible.value = false
emit('refresh')
return
}
await refresh()
emit('refresh')
}
const closeDialog = () => {
visible.value = false
}
defineExpose({
open,
})
</script>
<style scoped lang="scss">
/* 使用 UnoCSS 这里写一些 Element Plus 的样式覆盖 */
:deep(.el-dialog__header) {
margin-right: 0;
padding: 0 16px;
}
:deep(.el-dialog__body) {
padding: 0;
}
/* 自定义滚动条 */
.custom-scrollbar::-webkit-scrollbar {
width: 6px;
}
.custom-scrollbar::-webkit-scrollbar-thumb {
background-color: #e5e7eb;
border-radius: 4px;
}
.custom-scrollbar::-webkit-scrollbar-track {
background-color: transparent;
}
.animate-fade-in {
animation: fadeIn 0.2s ease-out;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment