381 lines
9.5 KiB
Vue
381 lines
9.5 KiB
Vue
<template>
|
||
<div class="comment">
|
||
<div class="comment-scroll">
|
||
<van-list
|
||
v-model:loading="loading"
|
||
:finished="finished"
|
||
finished-text="没有更多评论了"
|
||
@load="loadMore"
|
||
>
|
||
<div class="scroll-main" id="dataListComment">
|
||
<div class="comment-card" v-for="(item, index) in getCommentList" :key="index">
|
||
<div class="comment-card-main">
|
||
<div class="headImg">
|
||
<img v-if="item.headImg" :src="item.headImg" alt />
|
||
<img v-else :src="defaultAvatar" alt />
|
||
</div>
|
||
<span class="top-name">{{item.userName||'游客'}}: </span>
|
||
<span class="content-bottom">{{item.text}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</van-list>
|
||
</div>
|
||
|
||
<div class="comment-input">
|
||
<img :src="defaultAvatar"/>
|
||
<form @submit.prevent="submitComment">
|
||
<van-search
|
||
class="input-class"
|
||
v-model="commentText"
|
||
type="text"
|
||
placeholder="我来说几句......"
|
||
clearable
|
||
background="#ededed"
|
||
left-icon
|
||
@search="submitComment"
|
||
/>
|
||
<canvas id="thumsCanvas" width="200" height="1000"></canvas>
|
||
<div class="iconShare">
|
||
<img src="../assets/img/like.svg" alt @click="clickLikeThumb" />
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import {ref, onMounted, watch, onUnmounted} from 'vue';
|
||
import {commentList, addComment} from '../api/index';
|
||
import {getCommentLike} from '../api/detail';
|
||
import {showNotify} from 'vant';
|
||
import defaultAvatar from '../assets/img/logo.png';
|
||
import ThumbsUpAni from '../utils/canvas';
|
||
|
||
const getCommentList = ref([]);
|
||
const commentText = ref('');
|
||
const loading = ref(false);
|
||
const finished = ref(false);
|
||
const page = ref(1);
|
||
const pageSize = ref(10);
|
||
let thumbsUpAnimation = null;
|
||
let thumbsUpInterval = null;
|
||
const clickDebounce = ref(false);
|
||
|
||
const props = defineProps({
|
||
sceneData: {
|
||
type: Object,
|
||
default: () => ({})
|
||
}
|
||
});
|
||
|
||
// 点赞功能
|
||
const clickLikeThumb = async () => {
|
||
if (clickDebounce.value) return;
|
||
clickDebounce.value = true;
|
||
|
||
try {
|
||
const res = await getCommentLike({
|
||
sceneId: props.sceneData.id
|
||
});
|
||
|
||
if (res.code === 0) {
|
||
// 播放点赞动画
|
||
if (!thumbsUpAnimation) {
|
||
thumbsUpAnimation = new ThumbsUpAni();
|
||
}
|
||
thumbsUpAnimation.start();
|
||
|
||
// showNotify({ type: 'success', message: '点赞成功' });
|
||
}
|
||
} catch (error) {
|
||
console.error('点赞失败:', error);
|
||
// showNotify({ type: 'danger', message: '点赞失败,请稍后重试' });
|
||
} finally {
|
||
// 300ms后重置防抖标志
|
||
setTimeout(() => {
|
||
clickDebounce.value = false;
|
||
}, 300);
|
||
}
|
||
};
|
||
|
||
// 加载评论列表
|
||
const loadMore = async () => {
|
||
console.log('loadMore被调用,当前场景ID:', props.sceneData?.id);
|
||
|
||
if (!props.sceneData?.id) {
|
||
console.log('场景ID不存在,终止加载评论');
|
||
loading.value = false;
|
||
finished.value = true;
|
||
return;
|
||
}
|
||
|
||
if (loading.value) {
|
||
console.log('正在加载中,忽略此次加载请求');
|
||
return;
|
||
}
|
||
|
||
loading.value = true;
|
||
console.log('开始加载评论列表,页码:', page.value, '每页数量:', pageSize.value);
|
||
|
||
try {
|
||
const res = await commentList({
|
||
sceneId: props.sceneData.id,
|
||
page: page.value,
|
||
size: pageSize.value
|
||
});
|
||
|
||
if (res && res.code === 0) {
|
||
const processedComments = initCommentLikeStatus(res.data || []);
|
||
|
||
if (page.value === 1) {
|
||
getCommentList.value = processedComments;
|
||
} else {
|
||
getCommentList.value.push(...processedComments);
|
||
}
|
||
|
||
if (!res.data || res.data.length < pageSize.value) {
|
||
finished.value = true;
|
||
console.log("没有更多评论数据,设置完成状态");
|
||
} else {
|
||
page.value += 1;
|
||
console.log("还有更多评论,页码增加到:", page.value);
|
||
}
|
||
} else {
|
||
// API返回错误码
|
||
console.error("评论列表API返回错误:", res);
|
||
finished.value = true;
|
||
loading.value = false;
|
||
|
||
if (res) {
|
||
showNotify({ type: 'warning', message: res.msg || '获取评论失败' });
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取评论列表失败:', error);
|
||
showNotify({ type: 'danger', message: '获取评论失败' });
|
||
finished.value = true;
|
||
} finally {
|
||
loading.value = false;
|
||
console.log('评论加载完成,loading:', loading.value, 'finished:', finished.value);
|
||
}
|
||
};
|
||
|
||
// 提交评论
|
||
const submitComment = async () => {
|
||
if (!commentText.value.trim()) {
|
||
showNotify({ type: 'warning', message: '请输入评论内容' });
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const formData = new FormData();
|
||
formData.append('sceneId', props.sceneData.id);
|
||
formData.append('text', commentText.value);
|
||
|
||
const res = await addComment(formData);
|
||
if (res.code === 0) {
|
||
showNotify({ type: 'success', message: '评论成功' });
|
||
commentText.value = '';
|
||
// 重新加载评论列表
|
||
page.value = 1;
|
||
finished.value = false;
|
||
getCommentList.value = [];
|
||
await loadMore();
|
||
}
|
||
} catch (error) {
|
||
console.error('提交评论失败:', error);
|
||
showNotify({ type: 'danger', message: '评论失败,请稍后重试' });
|
||
}
|
||
};
|
||
|
||
// 初始化评论列表时,添加点赞状态
|
||
const initCommentLikeStatus = (comments) => {
|
||
if (!comments) return [];
|
||
|
||
return comments.map(comment => {
|
||
const storageKey = `comment_liked_${comment.id}`;
|
||
const isLiked = localStorage.getItem(storageKey) === 'true';
|
||
return {
|
||
...comment,
|
||
isLiked,
|
||
likeCount: comment.likeCount || 0
|
||
};
|
||
});
|
||
};
|
||
|
||
// 父组件调用的加载数据方法
|
||
const loadData = () => {
|
||
console.log('loadData被调用,当前场景ID:', props.sceneData?.id);
|
||
if (!props.sceneData?.id) {
|
||
console.log('场景ID不存在,无法加载评论');
|
||
loading.value = false;
|
||
finished.value = true;
|
||
return;
|
||
}
|
||
|
||
// 强制重置状态,无论是否已有数据都重新加载
|
||
console.log('重置评论列表状态,准备重新加载数据');
|
||
page.value = 1;
|
||
finished.value = false;
|
||
loading.value = false; // 确保loading为false,避免loadMore中的loading检查阻止加载
|
||
getCommentList.value = []; // 清空现有数据
|
||
|
||
// 立即执行loadMore,不需要延迟
|
||
loadMore();
|
||
};
|
||
|
||
// 暴露方法给父组件
|
||
defineExpose({
|
||
loadData
|
||
});
|
||
|
||
// 格式化时间
|
||
const formatTime = (time) => {
|
||
if (!time) return '';
|
||
const date = new Date(time);
|
||
const year = date.getFullYear();
|
||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||
const day = String(date.getDate()).padStart(2, '0');
|
||
const hours = String(date.getHours()).padStart(2, '0');
|
||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||
};
|
||
|
||
// 组件卸载时清理
|
||
onUnmounted(() => {
|
||
if (thumbsUpInterval) {
|
||
clearInterval(thumbsUpInterval);
|
||
thumbsUpInterval = null;
|
||
}
|
||
if (thumbsUpAnimation) {
|
||
thumbsUpAnimation = null;
|
||
}
|
||
});
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.comment {
|
||
height: 100%;
|
||
|
||
.comment-scroll {
|
||
height: calc(100vh - 175px);
|
||
overflow: scroll;
|
||
.scroll-main {
|
||
height: 100%;
|
||
|
||
.comment-card {
|
||
padding: 10px 20px;
|
||
|
||
.comment-card-main {
|
||
display: inline-block;
|
||
padding: 5px 10px;
|
||
width: auto;
|
||
background-color: #EEEEEE;
|
||
border-radius: 10px;
|
||
|
||
.headImg {
|
||
display: inline-block;
|
||
vertical-align: middle;
|
||
width: 30px;
|
||
height: 30px;
|
||
img {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 50%;
|
||
}
|
||
}
|
||
|
||
.top-name {
|
||
margin-left: 10px;
|
||
font-size: 16px;
|
||
color: #777882;
|
||
}
|
||
|
||
.content-bottom {
|
||
font-family: MicrosoftYaHei-Bold;
|
||
font-size: 16px;
|
||
color: rgba(0,0,0,80%);
|
||
line-height: 19px;
|
||
letter-spacing: 0px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.comment-input {
|
||
position: fixed;
|
||
bottom: 0;
|
||
display: flex;
|
||
padding: 0 10px;
|
||
width: 100%;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
height: 50px;
|
||
background-color: #fff;
|
||
z-index: 1;
|
||
|
||
img {
|
||
width: 40px;
|
||
height: 40px;
|
||
border-radius: 50%;
|
||
}
|
||
|
||
form {
|
||
display: flex;
|
||
width: 85%;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
.iconShare {
|
||
width: 35px;
|
||
height: 35px;
|
||
img {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
}
|
||
|
||
.input-class.van-search {
|
||
padding: 0;
|
||
width: 85%;
|
||
|
||
.van-search__content {
|
||
background-color: rgba(255,255,255,0.3);
|
||
border-radius: 15px;
|
||
color: #454545;
|
||
|
||
.van-cell {
|
||
font-size: 12px !important;
|
||
padding: 5px 7px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
#thumsCanvas {
|
||
position: fixed;
|
||
bottom: 50px;
|
||
right: 0;
|
||
width: 100px;
|
||
margin: auto;
|
||
display: flex;
|
||
flex-direction: column;
|
||
justify-content: center;
|
||
align-items: center;
|
||
background-color: transparent !important;
|
||
pointer-events: none;
|
||
z-index: 2;
|
||
}
|
||
</style>
|
||
|
||
<style lang="scss">
|
||
.van-pull-refresh__track {
|
||
height: 100% !important;
|
||
}
|
||
</style> |