PC-vote/components/DetailsComments.vue
2024-01-24 17:20:53 +08:00

861 lines
30 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="comment-title flexacenter">
讨论
<span class="comment-amount">{{ commentComments || "" }}</span>
</div>
<div class="post-comment flexacenter" ref="postInputRef" :class="{ 'post-comment-focus': postCommentFocusState }" @click="loginJudgment()">
<el-input class="post-input flex1" type="textarea" :autosize="postCommentFocusState" placeholder="说说你的想法或疑问…" v-model="commentInputTop" @blur="postCommentFocusBlur" @focus="postCommentFocusState = true"></el-input>
<div class="post-ok flexcenter" @click="submitAnswerComments(commentInputTop)">发送</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(commentInput, 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(commentInput, 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>
<!-- 投票后自动评论 -->
<el-dialog class="default-popup automatic-reviews-popup" v-model="reviewsPopoverState" width="720px" align-center autosize>
<div class="automatic-header">
<div class="automatic-title">说说您的投票理由</div>
<div class="automatic-have">已投{{ haveVotedValue }}</div>
</div>
<el-input class="automatic-input" placeholder="请输入…" v-model="reviewsPopoverInput" type="textarea"></el-input>
<div class="automatic-bottom flexflex">
<div class="automatic-send flexcenter" @click="submitAnswerComments(reviewsPopoverInput)">发送</div>
</div>
</el-dialog>
</template>
<script setup>
import { ElMessage } from "element-plus"
import { isEmpty } from "element-plus/es/utils"
let haveVotedValue = inject("haveVotedValue")
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 postCommentFocusState = ref(false)
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 || !props.token) 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 = () => {
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 = (content, 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"]
if (!content) {
ElMessage.error("请填写评论内容")
return
}
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"],
},
voteoption: haveVotedValue.value || null,
...data,
}
targetCommentList[index]["child"].unshift(targetData)
targetCommentList[index]["childnum"]++
} else {
let targetData = {
id: data["commentid"],
content,
isauthor: 1,
islike: 0,
likenum: 0,
...data,
child: [],
voteoption: haveVotedValue.value || null,
}
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 = ""
reviewsPopoverInput.value = ""
reviewsPopoverState.value = false
isEmptyState.value = false // 取消有可能的 没有评论
closeAnswerCommentsChild()
if (bottomNavigationState) {
bottomNavigationState = false
floorCommentBtn("back")
}
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
})
})
}
let reviewsPopoverState = ref(false) // 自动投票弹窗状态
let reviewsPopoverInput = ref("") // 自动投票理由
// 调用自动评论
const reviewsComment = value => {
reviewsPopoverState.value = true
}
let bottomNavigationState = false
// 底部导航栏的 评论
const bottomNavigationBar = value => {
bottomNavigationState = true
submitAnswerComments(value)
}
const floorCommentBtn = inject("floorCommentBtn")
const postInputRef = ref(null)
const postCommentFocusBlur = () => {
postCommentFocusState.value = false
const refref = postInputRef.value
nextTick(() => {
let targetDom = refref.querySelector(".el-textarea__inner")
targetDom.style.height = ""
})
}
defineExpose({ changeCommentVoteoption, wipeCommentVoteoption, reviewsComment, bottomNavigationBar })
</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 {
background-color: rgba(255, 255, 255, 1);
border: 1px solid rgba(215, 215, 215, 1);
// border-right-width: 0;
border-radius: 6px;
margin-bottom: 30px;
margin-right: 30px;
transition: all 5s;
justify-content: space-between;
overflow: hidden;
&.post-comment-focus {
// border-right-width: 1px;
flex-direction: column;
.post-input {
/deep/ .el-textarea__inner {
width: 468px;
min-height: 148px !important;
max-height: 80vh;
// height: 100% !important;
}
}
.post-ok {
align-self: flex-end;
height: 32px;
margin-bottom: 10px;
margin-right: 10px;
}
}
.post-input {
background-color: transparent;
font-size: 14px;
resize: none;
transition: all 0.5s;
&::placeholder {
color: #aaaaaa;
}
&::-webkit-scrollbar {
width: 0 !important;
}
scrollbar-width: none;
-ms-overflow-style: none;
border: none;
/deep/ .el-textarea__inner {
border: none;
box-shadow: none;
resize: none;
min-height: 60px !important;
// height: 60px !important;
padding: 10px;
transition: all 0.5s;
}
}
.post-ok {
width: 60px;
height: 62px;
background-color: var(--main-color);
color: #fff;
font-size: 14px;
cursor: pointer;
border-radius: 6px;
transition: all 0.5s;
}
}
.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;
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: 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;
font-size: 14px;
}
.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>
<style>
.automatic-reviews-popup {
border-radius: 10px;
.automatic-header {
padding: 20px;
border-bottom: 1px dotted #ebebeb;
.automatic-title {
font-weight: 650;
font-size: 18px;
color: #000000;
margin-bottom: 12px;
}
.automatic-have {
background-color: rgba(246, 246, 246, 1);
font-size: 12px;
color: #aaa;
width: fit-content;
}
}
.automatic-input {
.el-textarea__inner {
min-height: 256px !important;
box-shadow: none;
padding: 20px;
resize: none;
}
}
.automatic-bottom {
justify-content: flex-end;
padding: 0 10px 10px;
.automatic-send {
background-color: var(--main-color);
color: #fff;
font-size: 16px;
width: 100px;
height: 40px;
border-radius: 6px;
cursor: pointer;
}
}
}
</style>