Files
taimed-international-app/components/comment/CommentList.vue

407 lines
9.0 KiB
Vue

<template>
<view class="comment-list">
<!-- 评论列表 -->
<view v-for="comment in comments" :key="comment.id" class="comment-item">
<!-- 一级评论 -->
<view class="comment-main">
<view class="user-info">
<image
:src="comment.user.avatar || defaultAvatar"
class="avatar"
mode="aspectFill"
/>
<text class="username">{{ comment.user.name }}</text>
</view>
<view class="comment-content">
<view class="content-html" v-html="comment.content"></view>
<!-- 图片列表 -->
<view v-if="comment.imgList && comment.imgList.length > 0" class="image-list">
<image
v-for="(img, imgIndex) in comment.imgList"
:key="imgIndex"
:src="img"
class="comment-image"
mode="aspectFill"
@click="previewImage(img, comment.imgList)"
/>
</view>
</view>
<view class="comment-actions">
<text class="time">{{ comment.createTime }}</text>
<view class="action-btns">
<wd-button
size="small"
custom-class="action-btn"
:type="comment.support ? 'primary' : 'default'"
@click="handleLike(comment.id)"
>
<wd-icon name="thumb-up" />
<text class="btn-text">{{ comment.supportCount || 0 }}</text>
</wd-button>
<wd-button
size="small"
custom-class="action-btn"
@click="handleReply(comment)"
>
<wd-icon name="chat" />
<text class="btn-text">回复</text>
</wd-button>
</view>
</view>
</view>
<!-- 子评论列表 -->
<view v-if="comment.Bchildren && comment.Bchildren.length > 0" class="sub-comments">
<view v-for="subComment in comment.Bchildren" :key="subComment.id" class="sub-comment-item">
<view class="sub-user-info">
<image
:src="subComment.user.avatar || defaultAvatar"
class="sub-avatar"
mode="aspectFill"
/>
<text class="sub-username">{{ subComment.user.name }}</text>
</view>
<view class="sub-comment-content">
<view class="content-html" v-html="subComment.content"></view>
<!-- 子评论图片 -->
<view v-if="subComment.imgList && subComment.imgList.length > 0" class="image-list">
<image
v-for="(img, imgIndex) in subComment.imgList"
:key="imgIndex"
:src="img"
class="comment-image"
mode="aspectFill"
@click="previewImage(img, subComment.imgList)"
/>
</view>
</view>
<view class="sub-comment-actions">
<text class="time">{{ subComment.createTime }}</text>
<view class="action-btns">
<wd-button
size="small"
custom-class="action-btn"
:type="subComment.support ? 'primary' : 'default'"
@click="handleLike(subComment.id)"
>
<wd-icon name="thumb-up" />
<text class="btn-text">{{ subComment.supportCount || 0 }}</text>
</wd-button>
<wd-button
size="small"
custom-class="action-btn"
@click="handleReply(subComment)"
>
<wd-icon name="chat" />
<text class="btn-text">回复</text>
</wd-button>
</view>
</view>
</view>
<!-- 查看更多回复 -->
<view
v-if="comment.children && comment.children.length > comment.Bchildren.length"
class="load-more-replies"
@click="loadMoreReplies(comment)"
>
<text>查看更多回复 ({{ comment.children.length - comment.Bchildren.length }})</text>
</view>
</view>
</view>
<!-- 加载更多 -->
<view v-if="hasMore" class="load-more-btn">
<wd-button
@click="handleLoadMore"
:loading="loading"
block
>
加载更多
</wd-button>
</view>
<!-- 已加载全部 -->
<view v-else-if="comments.length > 0" class="no-more">
<wd-divider>已加载全部</wd-divider>
</view>
<!-- 暂无评论 -->
<view v-else-if="!loading" class="no-comments">
<text>暂无留言数据</text>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import type { IComment } from '@/types/comment'
interface Props {
comments: IComment[]
loading: boolean
hasMore: boolean
type: 'course' | 'book'
}
const props = defineProps<Props>()
const emit = defineEmits<{
like: [commentId: number]
reply: [comment: IComment]
loadMore: []
}>()
const defaultAvatar = '/static/icon/default-avatar.png'
/**
* 预览图片
*/
const previewImage = (current: string, urls: string[]) => {
uni.previewImage({
current,
urls,
longPressActions: {
itemList: ['很抱歉,暂不支持保存图片到本地'],
},
})
}
/**
* 点赞
*/
const handleLike = (commentId: number) => {
emit('like', commentId)
}
/**
* 回复
*/
const handleReply = (comment: IComment) => {
emit('reply', comment)
}
/**
* 加载更多
*/
const handleLoadMore = () => {
emit('loadMore')
}
/**
* 加载更多回复
*/
const loadMoreReplies = (comment: IComment) => {
// 显示所有子评论
comment.Bchildren = [...comment.children]
}
</script>
<style lang="scss" scoped>
.comment-list {
padding: 20rpx;
background-color: #fff;
}
.comment-item {
margin-bottom: 30rpx;
padding-bottom: 30rpx;
border-bottom: 1px solid #f0f0f0;
&:last-child {
border-bottom: none;
}
}
.comment-main {
.user-info {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.username {
font-size: 28rpx;
color: #333;
font-weight: 500;
}
}
.comment-content {
margin-left: 80rpx;
margin-bottom: 20rpx;
.content-html {
font-size: 28rpx;
line-height: 1.6;
color: #666;
word-break: break-all;
}
.image-list {
display: flex;
flex-wrap: wrap;
margin-top: 20rpx;
gap: 10rpx;
.comment-image {
width: 200rpx;
height: 200rpx;
border-radius: 8rpx;
}
}
}
.comment-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-left: 80rpx;
.time {
font-size: 24rpx;
color: #999;
}
.action-btns {
display: flex;
gap: 20rpx;
.action-btn {
display: flex;
align-items: center;
gap: 8rpx;
.btn-text {
font-size: 24rpx;
}
}
}
}
}
.sub-comments {
margin-left: 80rpx;
margin-top: 20rpx;
padding: 20rpx;
background-color: #f7f8f9;
border-radius: 8rpx;
.sub-comment-item {
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.sub-user-info {
display: flex;
align-items: center;
margin-bottom: 15rpx;
.sub-avatar {
width: 40rpx;
height: 40rpx;
border-radius: 50%;
margin-right: 15rpx;
}
.sub-username {
font-size: 24rpx;
color: #666;
}
}
.sub-comment-content {
margin-left: 55rpx;
margin-bottom: 15rpx;
.content-html {
font-size: 26rpx;
line-height: 1.5;
color: #666;
word-break: break-all;
}
.image-list {
display: flex;
flex-wrap: wrap;
margin-top: 15rpx;
gap: 10rpx;
.comment-image {
width: 150rpx;
height: 150rpx;
border-radius: 8rpx;
}
}
}
.sub-comment-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-left: 55rpx;
.time {
font-size: 22rpx;
color: #999;
}
.action-btns {
display: flex;
gap: 15rpx;
.action-btn {
display: flex;
align-items: center;
gap: 6rpx;
.btn-text {
font-size: 22rpx;
}
}
}
}
}
.load-more-replies {
text-align: center;
padding: 15rpx 0;
margin-top: 15rpx;
text {
font-size: 24rpx;
color: #2979ff;
}
}
}
.load-more-btn {
margin-top: 30rpx;
}
.no-more {
margin-top: 30rpx;
}
.no-comments {
text-align: center;
padding: 80rpx 0;
color: #999;
font-size: 28rpx;
}
</style>