PC-vote/components/DetailsComments.vue
2024-01-18 17:25:40 +08:00

733 lines
26 KiB
Vue

<template>
<div class="comment-title flexacenter">
讨论
<span class="comment-amount">{{ commentComments || "" }}</span>
</div>
<div class="post-comment flexacenter" @click="loginJudgment()">
<textarea class="post-input flex1" placeholder="说说你的想法或疑问…" v-model="commentInputTop"></textarea>
<div class="post-ok flexcenter" @click="submitAnswerComments()">发送</div>
</div>
<div class="empty-box" v-if="isEmptyState">
<Empty hint="说说你的观点吧"></Empty>
</div>
<template v-else>
<div class="comment-list">
<div class="comment-item flexflex" v-for="(item, index) in commentList" :key="item.id">
<el-popover placement="bottom-start" :width="140" trigger="click" popper-class="avatar-box-popper" :show-arrow="false" v-model:visible="item['popoverState']">
<template #reference>
<img class="comment-avatar" :src="item['avatar']" />
</template>
<div class="avatar-box flexflex" v-if="item['uin']">
<a class="avatar-item flexcenter" target="_blank" @click.prevent="sendMessage(item['uin'])">
<img class="avatar-icon" src="@/assets/img/send-messages-icon.png" />
发送信息
</a>
<a class="avatar-item flexcenter" target="_blank" @click.prevent="TAHomePage(item['uin'])">
<img class="avatar-icon" src="@/assets/img/homepage-icon.png" />
TA的主页
</a>
</div>
</el-popover>
<div class="comment-content flex1">
<div class="comment-header flexacenter">
<div class="comment-header-left flexacenter">
<div class="comments-username" @click="openAvatarPopover(index)">{{ item["nickname"] }}</div>
<div class="comments-time">{{ handleDate(item["timestamp"]) }}</div>
<div class="comments-identity" v-if="item['isauthor']">作者</div>
<img class="comments-title" v-if="item['groupid'] === 14" src="@/assets/img/title.png" />
</div>
<div class="comment-header-right flexacenter">
<div class="menu-box flexacenter">
<img class="menu-icon" src="@/assets/img/menu-icon-gray.svg" />
<div class="report-box flexcenter" @click="report(item['token'])">举报</div>
</div>
<img class="comment-icon" title="回复" @click="openAnswerCommentsChild(index)" src="@/assets/img/comment-icon-gray.svg" />
<div class="flexacenter like-box" @click="commentLike(index)">
<img class="like-icon" v-if="item['islike'] == 1" src="@/assets/img/like-icon-colours.png" />
<img class="like-icon" v-else src="@/assets/img/like-icon-gray.png" />
<div class="like-quantity">{{ item["likenum"] || 0 }}</div>
</div>
</div>
</div>
<div class="comment-text" @click="openAnswerCommentsChild(index)">{{ item["content"] }}</div>
<div class="alreadyVoted" v-if="item.voteoption">已投:{{ item.voteoption }}</div>
<div class="comments-input-box flexacenter" v-if="item['childState']">
<div class="comments-input flexflex">
<textarea class="flex1" placeholder="回复" v-model="commentInput"></textarea>
<div class="comments-btn flexcenter" @click="submitAnswerComments(index)">发送</div>
</div>
<img class="forkfork" @click="closeAnswerCommentsChild(index)" src="@/assets/img/cross-icon.png" />
</div>
<!-- 子评论 -->
<div class="child-comments" v-if="item['child'].length > 0">
<div class="comment-item flexflex" v-for="(ite, i) in item['child']" :key="ite.id">
<!-- <img class="comment-avatar" :src="ite['avatar']" /> -->
<el-popover placement="bottom-start" :width="140" trigger="click" popper-class="avatar-box-popper" :show-arrow="false" v-model:visible="ite['popoverState']">
<template #reference>
<img class="comment-avatar" :src="ite['avatar']" />
</template>
<div class="avatar-box flexflex" v-if="ite['uin']">
<a class="avatar-item flexcenter" target="_blank" @click.prevent="sendMessage(ite['uin'])">
<img class="avatar-icon" src="@/assets/img/send-messages-icon.png" />
发送信息
</a>
<a class="avatar-item flexcenter" target="_blank" @click.prevent="TAHomePage(ite['uin'])">
<img class="avatar-icon" src="@/assets/img/homepage-icon.png" />
TA的主页
</a>
</div>
</el-popover>
<div class="comment-content flex1">
<div class="comment-header flexacenter">
<div class="comment-header-left flexacenter">
<div class="comments-username" @click="openAvatarPopover(index, i)">{{ ite["nickname"] }}</div>
<div class="comments-time">{{ handleDate(ite["timestamp"]) }}</div>
<div class="comments-identity" v-if="ite['isauthor']">作者</div>
<img class="comments-title" v-if="ite['groupid'] == 14" src="@/assets/img/title.png" />
</div>
<div class="comment-header-right flexacenter">
<div class="menu-box flexacenter">
<img class="menu-icon" src="@/assets/img/menu-icon-gray.svg" />
<div class="report-box flexcenter" @click="report(ite['token'])">举报</div>
</div>
<img class="comment-icon" title="回复" @click="openAnswerCommentsChild(index, i)" src="@/assets/img/comment-icon-gray.svg" />
<div class="flexacenter like-box" @click="commentLike(index, i)">
<img class="like-icon" v-if="ite['islike'] == 1" src="@/assets/img/like-icon-colours.png" />
<img class="like-icon" v-else src="@/assets/img/like-icon-gray.png" />
<div class="like-quantity">{{ ite["likenum"] || 0 }}</div>
</div>
</div>
</div>
<div class="comment-text" @click="openAnswerCommentsChild(index, i)">
<div class="comments-reply" v-if="ite?.reply?.nickname">@{{ ite?.reply?.nickname }}</div>
{{ ite["content"] }}
</div>
<div class="alreadyVoted" v-if="ite.voteoption">已投:{{ ite.voteoption }}</div>
<div class="comments-input-box flexacenter" v-if="ite['childState']">
<div class="comments-input flexflex">
<textarea class="flex1" placeholder="回复" v-model="commentInput"></textarea>
<div class="comments-btn flexcenter" @click="submitAnswerComments(index, i)">发送</div>
</div>
<img class="forkfork" @click="closeAnswerCommentsChild(index, i)" src="@/assets/img/cross-icon.png" />
</div>
</div>
</div>
</div>
<!-- 还有几个 -->
<div class="comments-also flexacenter" v-if="item['childnum'] > item['child'].length" @click="alsoCommentsData(index)">
<div class="">还有{{ item["childnum"] - item["child"].length }}条回复</div>
<img class="also-icon" src="@/assets/img/arrow-circular-gray.png" />
</div>
</div>
</div>
</div>
<div class="comment-end" v-if="commentPage == 0 && commentList.length != 0">· End ·</div>
</template>
<Report v-if="reportAlertShow" :reportToken="reportToken"></Report>
</template>
<script setup>
import { ElMessage } from "element-plus"
import { isEmpty } from "element-plus/es/utils"
let isNeedLogin = inject("isNeedLogin")
const goLogin = inject("goLogin")
const props = defineProps({ token: String })
watch(
() => props.token,
() => getCommentList(),
{ immediate: false }
)
onMounted(() => window.addEventListener("scroll", handleScroll))
const sendMessage = inject("sendMessage")
const TAHomePage = inject("TAHomePage")
let commentCount = ref(0)
let commentComments = ref(0) // 所有的评论数
let commentPage = ref(1)
let commentList = ref([])
let commentLoading = false
let isEmptyState = ref(false) // 评论是否为空
// 获取详情评论数据
const getCommentList = () => {
if (commentPage.value == 0 || commentLoading) return
commentLoading = true
commentListHttp({
page: commentPage.value,
childlimit: 1,
limit: 10,
token: props.token,
})
.then(res => {
if (res.code != 200) return
let data = res.data
commentCount.value = data["count"]
if (data["count"] == 0) isEmptyState.value = true
else isEmptyState.value = false
commentList.value = commentList.value.concat(data["data"])
commentComments.value = data["comments"]
if (commentList.value.length == data["count"]) commentPage.value = 0
else commentPage.value++
})
.finally(() => (commentLoading = false))
}
// 评论点赞
const commentLike = (index, i) => {
if (isNeedLogin.value) {
goLogin()
return
}
const targetCommentList = [...commentList.value]
let token = ""
if (i != null) token = targetCommentList[index]["child"][i].token
else token = targetCommentList[index].token
detailsLikeCommentHttp({ token }).then(res => {
if (res.code != 200) return
let data = res.data
if (i != null) {
targetCommentList[index]["child"][i].islike = data["status"]
targetCommentList[index]["child"][i].likenum = data["likenum"]
} else {
targetCommentList[index].islike = data["status"]
targetCommentList[index].likenum = data["likenum"]
}
ElMessage.success(res.message)
})
}
// 打开 回答-评论 的子评论
const openAnswerCommentsChild = (index, i) => {
if (isNeedLogin.value) {
goLogin()
return
}
closeAnswerCommentsChild(false)
if (i == null) commentList.value[index]["childState"] = true
else commentList.value[index]["child"][i]["childState"] = true
// commentInput.value = ""
}
// 关闭 回答-评论 的子评论 isempty 是否需要清空输入框 默认需要清空
const closeAnswerCommentsChild = (isempty = true) => {
console.log("isempty", isempty)
if (isempty) commentInput.value = ""
commentList.value.forEach(ele => {
ele["childState"] = false
if (ele["child"] && ele["child"].length != 0) ele["child"].forEach(el => (el["childState"] = false))
})
}
// 讨论的输入框
let commentInputTop = ref("")
let commentInput = ref("")
// 提交回答-评论
const submitAnswerComments = (index, i) => {
if (isNeedLogin.value) {
goLogin()
return
}
const targetCommentList = [...commentList.value]
let content = ""
let parentid = null
if (index == null) content = commentInputTop.value
else content = commentInput.value
if (i != null) parentid = targetCommentList[index]["child"][i]["id"]
else if (index != null) parentid = targetCommentList[index]["id"]
detailsSubmitommentListHttp({
content,
token: props.token,
parentid,
}).then(res => {
if (res.code != 200) {
ElMessage.error(res.message)
return
}
let data = res.data
if (i != null) {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
reply: {
nickname: targetCommentList[index]["child"][i]["nickname"],
},
...data,
}
targetCommentList[index]["child"].unshift(targetData)
targetCommentList[index]["childnum"]++
} else {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
...data,
child: [],
}
if (index != null) {
targetCommentList[index]["child"].unshift(targetData)
targetCommentList[index]["childnum"]++
} else {
targetCommentList.unshift(targetData)
commentCount.value++
}
}
commentComments.value++
commentList.value = targetCommentList
// 请求 输入框的数据
commentInputTop.value = ""
commentInput.value = ""
isEmptyState.value = false // 取消有可能的 没有评论
closeAnswerCommentsChild()
ElMessage.success(res.message)
})
}
// 获取剩下的子评论
const alsoCommentsData = (index, ind) => {
if (isNeedLogin.value) {
goLogin()
return
}
let targetCommentItem = { ...commentList.value[index] }
const token = targetCommentItem["token"]
const parentid = targetCommentItem["id"]
let page = targetCommentItem["childPage"] ?? 1
detailsChildCommentListHttp({
childlimit: 1,
limit: 10,
page,
parentid,
token: props.token,
}).then(res => {
if (res.code != 200) return
let data = res.data
let childData = targetCommentItem.child.concat(data.data)
const filteredData = childData.filter((obj, index, self) => {
// 检查当前对象在数组中的第一个索引是否与当前索引相等
return self.findIndex(item => item.id == obj.id) == index
})
targetCommentItem.child = filteredData
targetCommentItem["childnum"] = data.count
if (targetCommentItem.child.length == data["count"]) page = 0
else page++
targetCommentItem["childPage"] = page
commentList.value[index] = targetCommentItem
})
}
let reportAlertShow = ref(false)
let reportToken = ref("")
// 点击打开举报
const report = token => {
if (isNeedLogin.value) {
goLogin()
return
}
reportToken.value = token
reportAlertShow.value = true
}
// 打开评论的 信息框
const openAvatarPopover = (index, i) => {
if (isNeedLogin.value) {
goLogin()
return
}
if (i != null) commentList.value[index]["child"][i]["popoverState"] = true
else commentList.value[index]["popoverState"] = true
}
// 监听滚动到底部
const handleScroll = () => {
// return
const scrollTop = document.documentElement.scrollTop || document.body.scrollTop
const scrollHeight = document.documentElement.scrollHeight
const clientHeight = document.documentElement.clientHeight
// 列表下 滑动到底部 获取新数据
if (scrollTop + clientHeight >= scrollHeight - 40) getCommentList()
}
provide("reportAlertShow", reportAlertShow)
// 登录判断
const loginJudgment = () => {
if (isNeedLogin.value) goLogin()
}
// 修改投票的值
const changeCommentVoteoption = voteoption => {
const uin = window["userInfoWin"]["uin"]
commentList.value.forEach(element => {
if (uin == element["uin"]) element["voteoption"] = voteoption
element.child.forEach(el => {
if (uin == element["uin"]) el["voteoption"] = voteoption
})
})
}
// 修改投票的值
const wipeCommentVoteoption = () => {
const uin = window["userInfoWin"]["uin"]
commentList.value.forEach(element => {
if (uin == element["uin"]) element["voteoption"] = null
element.child.forEach(el => {
if (uin == element["uin"]) el["voteoption"] = null
})
})
}
defineExpose({ changeCommentVoteoption, wipeCommentVoteoption })
</script>
<style scoped lang="less">
.comment-title {
font-weight: 650;
color: #000000;
font-size: 16px;
margin-bottom: 16px;
.comment-amount {
color: #555;
font-weight: 400;
margin-left: 8px;
}
}
.post-comment {
// width: 100%;
height: 60px;
background-color: rgba(255, 255, 255, 1);
border: 1px solid rgba(215, 215, 215, 1);
border-right: none;
border-radius: 6px;
margin-bottom: 30px;
margin-right: 30px;
.post-input {
height: 100%;
border: none;
outline: none;
background-color: transparent;
padding: 10px;
font-size: 14px;
resize: none;
&::placeholder {
color: #aaaaaa;
}
&::-webkit-scrollbar {
width: 0 !important;
}
scrollbar-width: none;
-ms-overflow-style: none;
}
.post-ok {
width: 60px;
height: 60px;
background-color: var(--main-color);
color: #fff;
font-size: 14px;
cursor: pointer;
border-radius: 6px;
}
}
.comment-list {
margin-bottom: 78px;
.comment-item {
&:not(:first-of-type) {
.comment-avatar {
margin-top: 10px;
}
.comment-header {
padding-top: 10px;
border-top: 1px dotted #d7d7d7;
}
}
padding-right: 30px;
.comment-avatar {
width: 20px;
height: 20px;
border-radius: 50%;
margin-right: 10px;
cursor: pointer;
}
.comment-content {
.comment-header {
display: flex;
justify-content: space-between;
// padding-right: 30px;
margin-bottom: 10px;
.comment-header-left {
font-size: 13px;
.comments-avatar {
width: 20px;
height: 20px;
margin-right: 10px;
border-radius: 50%;
}
.comments-username {
color: #555;
margin-right: 10px;
cursor: pointer;
}
.comments-time {
color: #aaaaaa;
// margin-right: 8px;
margin-right: 10px;
}
.comments-title {
height: 16px;
}
.comments-identity {
font-size: 12px;
color: #7f7f7f;
padding: 0 3px;
height: 20px;
background-color: rgba(240, 242, 245, 1);
border: 1px solid rgba(215, 215, 215, 1);
border-radius: 5px;
}
}
.comment-header-right {
.menu-box {
position: relative;
&:hover .report-box {
display: flex;
}
.menu-icon {
width: 14px;
height: 14px;
cursor: pointer;
}
.report-box {
display: none;
position: absolute;
top: 24px;
right: 0;
width: 60px;
height: 24px;
background-color: rgba(246, 246, 246, 1);
border: 1px solid rgba(215, 215, 215, 1);
border-radius: 5px;
font-size: 12px;
color: #7f7f7f;
cursor: pointer;
&::after {
content: "";
width: 58px;
height: 36px;
position: absolute;
top: -14px;
right: 0;
}
}
}
.comment-icon {
width: 14px;
height: 13px;
margin-left: 30px;
cursor: pointer;
}
.like-box {
font-size: 12px;
color: #aaa;
margin-left: 30px;
cursor: pointer;
.like-icon {
width: 14px;
height: 14px;
}
.like-quantity {
margin-left: 6px;
}
}
}
}
.comment-text {
font-size: 14px;
line-height: 22px;
color: #333;
margin-bottom: 10px;
word-break: break-all;
min-height: 22px;
cursor: pointer;
.comments-reply {
color: #92a1bf;
display: inline;
}
}
.alreadyVoted {
font-size: 12px;
color: #aaaaaa;
background-color: rgba(246, 246, 246, 1);
line-height: 17px;
width: fit-content;
margin-bottom: 15px;
word-break: break-word;
}
.comments-input-box {
margin-top: 13px;
margin-bottom: 10px;
.comments-input {
// width: 519px;
flex: 1;
height: 60px;
border: 1px solid rgba(215, 215, 215, 1);
border-right: none;
border-radius: 8px;
margin-right: 16px;
position: relative;
z-index: 1;
&::after {
content: "";
width: 20px;
height: 20px;
display: block;
background-color: rgba(215, 215, 215, 1);
position: absolute;
top: -2px;
left: 21px;
transform: rotate(45deg);
z-index: -1;
}
textarea {
border: none;
outline: none;
resize: none;
padding: 11px 16px;
border-radius: 7px 0 0 7px;
}
.comments-btn {
width: 58px;
height: 58px;
// background-color: #31d72e;
background-color: var(--main-color);
border-radius: 0 7px 7px 0;
font-size: 14px;
color: #ffffff;
cursor: pointer;
}
}
.forkfork {
width: 12px;
height: 12px;
cursor: pointer;
}
}
}
.child-comments {
.comment-avatar {
margin-top: 10px;
}
.comment-header {
padding-top: 10px;
border-top: 1px dotted #d7d7d7;
}
.comment-item {
padding-right: 0;
}
}
.comments-also {
color: #62b1ff;
line-height: 22px;
font-size: 13px;
height: 46px;
margin-left: 30px;
cursor: pointer;
border-top: 1px dotted #d7d7d7;
.also-icon {
width: 10px;
height: 10px;
margin-left: 8px;
}
}
}
}
.comment-end {
font-size: 12px;
color: #d7d7d7;
text-align: center;
margin-bottom: 118px;
padding-right: 30px;
}
.empty-box {
padding: 80px 0 110px;
}
</style>