|
|
|
|
@ -1,78 +1,105 @@
|
|
|
|
|
<template>
|
|
|
|
|
<!-- 回复项容器 -->
|
|
|
|
|
<div>
|
|
|
|
|
<div class="flex space-x-3 xl:space-x-5">
|
|
|
|
|
<!-- 回复者头像组件,使用回复数据中的头像地址 -->
|
|
|
|
|
<Avatar :url="reply.avatar" />
|
|
|
|
|
<!-- 回复内容区域,设置背景、内边距、圆角等样式 -->
|
|
|
|
|
<div class="reply bg-white flex flex-col p-3 rounded-md relative shadow-md">
|
|
|
|
|
<!-- 回复内容,将换行符替换为<br>标签以正确显示换行 -->
|
|
|
|
|
<p class="commentContent" v-html="commentContent.replaceAll('\n', '<br>')" />
|
|
|
|
|
<!-- 回复元信息区域:回复者昵称、时间和回复按钮 -->
|
|
|
|
|
<div class="flex justify-between mt-2 text-xs text-gray-400 space-x-3 md:space-x-16">
|
|
|
|
|
<span> {{ reply.nickname }} | {{ time }}</span>
|
|
|
|
|
<div>
|
|
|
|
|
<!-- 回复按钮,点击显示回复表单(针对该回复的嵌套回复) -->
|
|
|
|
|
<span @click="clickOnSonReply" class="cursor-pointer reply-button">Reply</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 空链接(可能用于后续扩展) -->
|
|
|
|
|
<a href="" target="_blank"></a>
|
|
|
|
|
<!-- 回复表单组件,通过v-show控制显示/隐藏,用于嵌套回复 -->
|
|
|
|
|
<CommentReplyForm
|
|
|
|
|
class="mt-5"
|
|
|
|
|
v-show="show"
|
|
|
|
|
:replyUserId="reply.userId"
|
|
|
|
|
:initialContent="replyContent"
|
|
|
|
|
@changeShow="changeShow" />
|
|
|
|
|
class="mt-5"
|
|
|
|
|
v-show="show"
|
|
|
|
|
:replyUserId="reply.userId"
|
|
|
|
|
:initialContent="replyContent"
|
|
|
|
|
@changeShow="changeShow" />
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
// 导入Vue相关API
|
|
|
|
|
import { computed, defineComponent, reactive, toRefs } from 'vue'
|
|
|
|
|
// 导入头像组件
|
|
|
|
|
import Avatar from '@/components/Avatar.vue'
|
|
|
|
|
// 导入回复表单组件
|
|
|
|
|
import CommentReplyForm from './CommentReplyForm.vue'
|
|
|
|
|
|
|
|
|
|
// 定义回复项组件
|
|
|
|
|
export default defineComponent({
|
|
|
|
|
components: {
|
|
|
|
|
Avatar,
|
|
|
|
|
CommentReplyForm
|
|
|
|
|
Avatar, // 注册头像组件
|
|
|
|
|
CommentReplyForm // 注册回复表单组件
|
|
|
|
|
},
|
|
|
|
|
props: ['reply', 'commentUserId'],
|
|
|
|
|
props: ['reply', 'commentUserId'], // 接收父组件传入的回复数据和评论作者ID
|
|
|
|
|
setup(props) {
|
|
|
|
|
// 格式化时间:将时间戳转换为年月日格式
|
|
|
|
|
const formatTime = (time: any): any => {
|
|
|
|
|
let date = new Date(time)
|
|
|
|
|
let year = date.getFullYear()
|
|
|
|
|
let month = date.getMonth() + 1
|
|
|
|
|
let month = date.getMonth() + 1 // 月份从0开始,需加1
|
|
|
|
|
let day = date.getDate()
|
|
|
|
|
return year + '-' + month + '-' + day
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 响应式数据
|
|
|
|
|
const reactiveData = reactive({
|
|
|
|
|
replyContent: '' as any,
|
|
|
|
|
time: formatTime(props.reply.createTime) as any,
|
|
|
|
|
show: false as any
|
|
|
|
|
replyContent: '' as any, // 回复内容(用于嵌套回复的初始提示)
|
|
|
|
|
time: formatTime(props.reply.createTime) as any, // 格式化后的回复时间
|
|
|
|
|
show: false as any // 控制回复表单显示状态
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// 点击回复按钮:设置回复初始内容(@回复者昵称)并显示回复表单
|
|
|
|
|
const clickOnSonReply = () => {
|
|
|
|
|
reactiveData.replyContent = '@' + props.reply.nickname
|
|
|
|
|
reactiveData.show = true
|
|
|
|
|
reactiveData.replyContent = '@' + props.reply.nickname // 自动填充@回复者
|
|
|
|
|
reactiveData.show = true // 显示回复表单
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 改变回复表单显示状态(隐藏)
|
|
|
|
|
const changeShow = () => {
|
|
|
|
|
reactiveData.show = false
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 计算属性:处理回复内容,若为嵌套回复则添加@被回复者的链接
|
|
|
|
|
const commentContent = computed(() => {
|
|
|
|
|
// 判断是否为嵌套回复(回复的是另一个回复而非主评论)
|
|
|
|
|
if (props.reply.replyUserId !== props.commentUserId) {
|
|
|
|
|
// 生成@被回复者的链接,并拼接回复内容
|
|
|
|
|
return (
|
|
|
|
|
`<a href="${props.reply.replyWebsite}" target="_blank" class="reply-link">@${props.reply.replyNickname} </a>` +
|
|
|
|
|
props.reply.commentContent
|
|
|
|
|
`<a href="${props.reply.replyWebsite}" target="_blank" class="reply-link">@${props.reply.replyNickname} </a>` +
|
|
|
|
|
props.reply.commentContent
|
|
|
|
|
)
|
|
|
|
|
} else {
|
|
|
|
|
// 直接返回回复内容(非嵌套回复)
|
|
|
|
|
return props.reply.commentContent
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
...toRefs(reactiveData),
|
|
|
|
|
commentContent,
|
|
|
|
|
clickOnSonReply,
|
|
|
|
|
changeShow
|
|
|
|
|
...toRefs(reactiveData), // 将响应式数据转换为ref并返回
|
|
|
|
|
commentContent, // 处理后的回复内容
|
|
|
|
|
clickOnSonReply, // 回复按钮点击事件
|
|
|
|
|
changeShow // 改变回复表单显示状态的方法
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
// 回复框左侧小三角装饰
|
|
|
|
|
.reply::before {
|
|
|
|
|
content: '';
|
|
|
|
|
position: absolute;
|
|
|
|
|
@ -84,12 +111,18 @@ export default defineComponent({
|
|
|
|
|
left: -8px;
|
|
|
|
|
top: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 回复框背景样式
|
|
|
|
|
.reply {
|
|
|
|
|
background: var(--background-primary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 回复按钮样式
|
|
|
|
|
.reply-button {
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 回复内容样式:设置行高、自动换行等
|
|
|
|
|
.commentContent {
|
|
|
|
|
line-height: 26px;
|
|
|
|
|
white-space: pre-line;
|
|
|
|
|
@ -97,8 +130,10 @@ export default defineComponent({
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|
|
|
|
|
// 非作用域样式:回复中@被回复者的链接样式
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
.reply-link {
|
|
|
|
|
color: var(--text-accent);
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
</style>
|