<template> <div class="comment-box"> <!-- 编辑评论 --> <div v-if="editCommentState" class="edit-comment flexcenter"> <div class="box"> <div class="text">编辑评论</div> <div class="input-box"> <div class="top flexflex"> <textarea ref="editInputRef" class="input-textarea flex1" maxlength="500" v-model="editInput" @focus="judgeLogin" @input="autoResize" @paste="handleInputPaste" placeholder="说说你的想法或疑问…"></textarea> </div> <div class="picture-box" v-if="editPicture.url"> <div class="picture"> <img class="close" @click="closeEditFileUpload()" src="@/assets/img/close-icon.png" /> <img class="img" @click="handleAnswerText" :src="editPicture.base64 || editPicture.url" /> </div> </div> <div class="bottom flexacenter"> <div class="operate flexacenter"> <div class="item" :class="{ pitch: editEmojiState }"> <img class="icon" src="@/assets/img/smiling-face.png" @click="openEditEmoji()" alt="" /> <div class="emoji-box"> <div class="emoji-icon" v-for="item in emojiData" :key="item" @click="selectEditEmoji(item)">{{ item }}</div> </div> </div> <div class="item flexacenter" @click="judgeLogin()"> <input class="file" type="file" @change="handleFileUpload($event)" accept=".png, .jpg, .jpeg" /> <img class="icon" style="border-radius: 0" src="@/assets/img/picture-icon.png" alt="" /> <span class="file-hint">最多可上传1张图片,支持在输入框中直接粘贴图片。</span> </div> </div> </div> </div> <div class="btn-list flexacenter"> <div class="btn" @click="closeEdit()">取消</div> <div class="btn send" @click="postEditComment()">发送</div> </div> </div> </div> <div class="comment-title flexacenter"> 讨论 <span class="comment-amount">{{ commentComments || "" }}</span> </div> <!-- <div class="post-comment" ref="postInputRef" :class="{ 'post-comment-focus': postCommentFocusState }" @click="loginJudgment()"> <div class="post-comment-input"> <el-input class="post-input flex1" type="textarea" :autosize="postCommentFocusState" :maxlength="500" show-word-limit placeholder="说说你的想法或疑问…" v-model="commentInputTop" @blur="postCommentFocusBlur" @focus="postCommentFocusState = true"></el-input> </div> <div class="post-ok flexcenter" @click="submitAnswerComments(commentInputTop)">发送</div> </div> --> <div class="input-box"> <div class="top flexflex"> <img class="avatar" v-if="userInfoWin.avatar" :src="userInfoWin.avatar" /> <textarea class="input-textarea flex1" maxlength="500" v-model="commentInputTop" @focus="judgeLogin" @input="autoResize" @paste="handleInputPaste" placeholder="说说你的想法或疑问…"></textarea> </div> <div class="picture-box" v-if="picture.url"> <div class="picture"> <img class="close" @click="closeFileUpload()" src="@/assets/img/close-icon.png" /> <img class="img" @click="handleAnswerText" :src="picture.base64 || picture.url" /> </div> </div> <div class="bottom flexacenter"> <div class="operate flexacenter"> <div class="item" :class="{ pitch: emojiState }"> <img class="icon" src="@/assets/img/smiling-face.png" @click="openEmoji()" alt="" /> <div class="emoji-box"> <div class="emoji-icon" v-for="item in emojiData" :key="item" @click="selectEmoji(item)">{{ item }}</div> </div> </div> <div class="item flexacenter" @click="judgeLogin()"> <input class="file" type="file" @change="handleFileUpload($event)" accept=".png, .jpg, .jpeg" /> <img class="icon" src="@/assets/img/picture-icon.png" alt="" /> <span class="file-hint">最多可上传1张图片,支持在输入框中直接粘贴图片。</span> </div> </div> <div class="btn" @click="submitAnswerComments(commentInputTop)">发送</div> </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">{{ item["timestampnow"] || 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" /> --> <img class="comments-title" v-if="item['groupimage']" :src="item.groupimage" :alt="item.grouptitle" /> </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="operate-box"> <div class="item flexcenter" @click="report(item['token'])">举报</div> <div class="item flexcenter" v-if="permissions.includes('comment.edit')" @click="openEdit(item['token'], index)">编辑</div> <div class="item flexcenter" v-if="permissions.includes('comment.delete')" @click="commentDelete(item['token'], index)">删除</div> </div> </div> <!-- <img class="comment-icon" title="回复" @click="openAnswerCommentsChild(index)" src="@/assets/img/comment-icon-gray.svg" /> --> <img class="comment-icon" title="回复" @click="!item['childState'] ? openAnswerCommentsChild(index) : closeAnswerCommentsChild()" 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> <!-- shutAnswerCommentsChild openAnswerCommentsChild --> <div class="comment-text" v-if="item['content']" @click="!item['childState'] ? openAnswerCommentsChild(index) : closeAnswerCommentsChild()">{{ item["content"] }}</div> <img class="comments-img" @click="handleAnswerText" :src="item.image?.base64 || item.image?.url" v-if="item.image?.url" /> <div class="alreadyVoted" v-if="item.voteoption">已投:{{ item.voteoption }}</div> <div class="input-box" v-if="item['childState']"> <img class="cross" @click="closeAnswerCommentsChild()" src="@/assets/img/cross-icon.png" /> <div class="top flexflex"> <textarea class="input-textarea flex1" maxlength="500" placeholder="说说你的想法或疑问…" v-model="item['commentInput']" @input="autoResize" @paste="handleInputPaste($event, index)"></textarea> </div> <div class="picture-box" v-if="item.picture?.url"> <div class="picture"> <img class="close" @click="closeFileUpload(index)" src="@/assets/img/close-icon.png" /> <img class="img" @click="handleAnswerText" :src="item.picture?.base64 || item.picture.url" /> </div> </div> <div class="bottom flexacenter"> <div class="operate flexacenter"> <div class="item" :class="{ pitch: item.emojiState }"> <img class="icon" src="@/assets/img/smiling-face.png" @click="openEmoji(index)" alt="" /> <div class="emoji-box"> <div class="emoji-icon" v-for="item in emojiData" :key="item" @click="selectEmoji(item, index)">{{ item }}</div> </div> </div> <div class="item flexacenter"> <input class="file" type="file" @change="handleFileUpload($event, index)" accept=".png, .jpg, .jpeg" /> <img class="icon" src="@/assets/img/picture-icon.png" alt="" /> <span class="file-hint">最多可上传1张图片,支持在输入框中直接粘贴图片。</span> </div> </div> <div class="btn" @click="submitAnswerComments(item['commentInput'], index)">发送</div> </div> </div> <!-- <div class="comments-input-masking" @click="closeAnswerCommentsChild()" v-if="item['childState']"></div> <div class="comments-input-box" :class="{ 'comments-input-box-show': item['childState'] }"> <div class="comments-input"> <el-input v-model="commentInput" type="textarea" placeholder="回复" :maxlength="500" show-word-limit></el-input> <div class="operate-bottom flexacenter"> <div class="comments-btn comments-btn-cancel flexcenter" @click="closeAnswerCommentsChild()">取消</div> <div class="comments-btn flexcenter" @click="submitAnswerComments(commentInput, index)">发送</div> </div> </div> </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"> <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">{{ ite["timestampnow"] || handleDate(ite["timestamp"]) }}</div> <div class="comments-identity" v-if="ite['isauthor']">作者</div> <img class="comments-title" v-if="ite['groupimage']" :src="ite.groupimage" :alt="ite.grouptitle" /> </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="operate-box"> <div class="item flexcenter" @click="report(ite['token'])">举报</div> <div class="item flexcenter" v-if="permissions.includes('comment.edit')" @click="openEdit(ite['token'], index, i)">编辑</div> <div class="item flexcenter" v-if="permissions.includes('comment.delete')" @click="commentDelete(ite['token'], index, i)">删除</div> </div> </div> <img class="comment-icon" title="回复" @click="!ite['childState'] ? openAnswerCommentsChild(index, i) : closeAnswerCommentsChild()" 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" v-if="ite['content']" @click="!ite['childState'] ? openAnswerCommentsChild(index, i) : closeAnswerCommentsChild()"> <div class="comments-reply" v-if="ite?.reply?.nickname">@{{ ite?.reply?.nickname }}</div> {{ ite["content"] }} </div> <img class="comments-img" @click="handleAnswerText" :src="ite.image?.base64 || ite.image?.url" v-if="ite.image?.url" /> <div class="alreadyVoted" v-if="ite.voteoption">已投:{{ ite.voteoption }}</div> <div class="input-box" v-if="ite['childState']"> <img class="cross" @click="closeAnswerCommentsChild()" src="@/assets/img/cross-icon.png" /> <div class="top flexflex"> <textarea class="input-textarea flex1" maxlength="500" :placeholder="'回复“' + (ite['nickname'] || '匿名用户') + '”:'" v-model="ite['commentInput']" @input="autoResize" @paste="handleInputPaste($event, index)"></textarea> </div> <div class="picture-box" v-if="ite.picture?.url"> <div class="picture"> <img class="close" @click="closeFileUpload(index, i)" src="@/assets/img/close-icon.png" /> <img class="img" @click="handleAnswerText" :src="ite.picture.base64 || ite.picture.url" /> </div> </div> <div class="bottom flexacenter"> <div class="operate flexacenter"> <div class="item" :class="{ pitch: ite.emojiState }"> <img class="icon" src="@/assets/img/smiling-face.png" @click="openEmoji(index, i)" alt="" /> <div class="emoji-box"> <div class="emoji-icon" v-for="item in emojiData" :key="item" @click="selectEmoji(item, index, i)">{{ item }}</div> </div> </div> <div class="item flexacenter"> <input class="file" type="file" @change="handleFileUpload($event, index, i)" accept=".png, .jpg, .jpeg" /> <img class="icon" src="@/assets/img/picture-icon.png" alt="" /> <span class="file-hint">最多可上传1张图片,支持在输入框中直接粘贴图片。</span> </div> </div> <div class="btn" @click="submitAnswerComments(ite['commentInput'], index, i)">发送</div> </div> </div> <!-- <div class="comments-input-masking" @click="closeAnswerCommentsChild()" v-if="ite['childState']"></div> <div class="comments-input-box" :class="{ 'comments-input-box-show': ite['childState'] }"> <div class="comments-input"> <el-input v-model="commentInput" type="textarea" placeholder="回复" :maxlength="500" show-word-limit></el-input> <div class="operate-bottom flexacenter"> <div class="comments-btn comments-btn-cancel flexcenter" @click="closeAnswerCommentsChild()">取消</div> <div class="comments-btn flexcenter" @click="submitAnswerComments(commentInput, index, i)">发送</div> </div> </div> </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> </div> <Report v-if="reportAlertShow" :reportToken="reportToken"></Report> <!-- 投票后自动评论 --> <el-dialog class="default-popup automatic-reviews-popup" v-model="reviewsPopoverState" width="720px" align-center autosize :close-on-click-modal="false"> <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" maxlength="500" show-word-limit></el-input> <div class="automatic-bottom flexflex"> <div class="automatic-send flexcenter" @click="submitAnswerComments(reviewsPopoverInput)">发送</div> </div> </el-dialog> <!-- 大图 --> <div class="detail-image-mask flexcenter" v-if="dialogSrc" @click="dialogSrc = ''"> <div class="detail-image flexcenter"> <img class="detail-img" :src="dialogSrc" /> </div> </div> <el-dialog v-model="dialogVisible" title="提示" width="500"> <span>确定删除该讨论吗?</span> <template #footer> <div class="dialog-footer"> <el-button @click="dialogVisible = false">取 消</el-button> <el-button type="primary" @click="confirmCommentDelete">确 定</el-button> </div> </template> </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 }); const userInfoWin = inject("userInfoWin"); watch( () => props.token, () => getCommentList(), { immediate: false } ); onMounted(() => window.addEventListener("scroll", handleScroll)); let permissions = ref([]); onMounted(() => { setTimeout(() => { permissions.value = window["permissions"] || []; // permissions.value = ["comment.edit", "comment.delete"] }, 1000); }); 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 emit = defineEmits(["update:commentComments"]); // 获取详情评论数据 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"]; emit("update:commentComments", 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(); if (i == null) commentList.value[index]["childState"] = true; else commentList.value[index]["child"][i]["childState"] = true; }; // 关闭 回答-评论 的子评论 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 let image = {}; if (i != null) { parentid = targetCommentList[index]["child"][i]["id"]; image = commentList.value[index]["child"][i]["picture"] || {}; } else if (index != null) { parentid = targetCommentList[index]["id"]; image = commentList.value[index]["picture"] || {}; } else image = picture.value; // if (!content) { // ElMessage.error("请填写评论内容") // return // } detailsSubmitommentListHttp({ content, token: props.token, parentid, image: image ? { aid: image.aid, url: image.url } : null, }).then((res) => { if (res.code != 200) { ElMessage.error(res.message); return; } let data = res.data; if (i != null) { // console.log("targetCommentList[index]", targetCommentList[index]) targetCommentList[index]["child"][i]["commentInput"] = ""; targetCommentList[index]["child"][i]["picture"] = {}; let targetData = { id: data["commentid"], content, isauthor: 1, islike: 0, likenum: 0, reply: { nickname: targetCommentList[index]["child"][i]["nickname"], }, voteoption: haveVotedValue.value || null, ...data, // ...data.data timestampnow: "刚刚", uin: data.data?.uin, image, }; targetCommentList[index]["child"].unshift(targetData); targetCommentList[index]["childnum"]++; } else { let targetData = { id: data["commentid"], content, isauthor: 1, islike: 0, likenum: 0, ...data, // ...data.data, timestampnow: "刚刚", uin: data.data?.uin, child: [], voteoption: haveVotedValue.value || null, image, }; if (index != null) { targetCommentList[index]["commentInput"] = ""; targetCommentList[index]["picture"] = {}; 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(); picture.value = {}; 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 = () => { const refref = postInputRef.value; setTimeout(() => { postCommentFocusState.value = false; nextTick(() => { let targetDom = refref.querySelector(".el-textarea__inner"); targetDom.style.height = "41px"; }); }, 200); }; let picture = ref({}); let emojiState = ref(false); let emojiMaskState = ref(false); const emojiData = ["😀", "😁", "😆", "😅", "😂", "😉", "😍", "🥰", "😋", "😜", "🤪", "😎", "🤩", "🥳", "😔", "🙁", "😭", "😡", "😳", "🤗", "🤔", "🤭", "🤫", "😯", "😵", "🙄", "🥴", "🤢", "🤑", "🤠", "👌", "✌️", "🤟", "🤘", "🤙", "👍", "👎", "✊", "👏", "🤝", "🙏", "💪", "❤️", "💔", "🌹", "🥀", "🎉", "🎁", "🧧", "🌙", "⭐", "🌍", "💌", "📬", "🚗", "🚕", "🚲", "🛵", "🚀", "🚁", "⛵", "🚢", "🍎", "🍐", "🍊", "🍉", "🍓", "🍑", "🍔", "🍟", "🍕", "🥪", "🍜", "🍡", "🍨", "🍦", "🎂", "🍰", "🍭", "🍿", "🍩", "🧃", "🍹"]; // 打开 Emoji const openEmoji = (index, i) => { if (isNeedLogin.value) { goLogin(); return; } if (i != undefined) commentList.value[index].child[i]["emojiState"] = true; else if (index != undefined) commentList.value[index]["emojiState"] = true; else { closeEmoji(); closeAnswerCommentsChild(); emojiState.value = true; } emojiMaskState.value = true; }; // 关闭 Emoji const closeEmoji = (index, i) => { commentList.value.forEach((ele) => { ele["emojiState"] = false; if (ele["child"] && ele["child"].length != 0) { ele["child"].forEach((el) => { el["emojiState"] = false; }); } }); emojiState.value = false; emojiMaskState.value = false; editEmojiState.value = false; }; // 选择 Emoji const selectEmoji = (key, index, i) => { closeEmoji(); if (i != undefined) { if (!commentList.value[index]["child"][i]["commentInput"]) commentList.value[index]["child"][i]["commentInput"] = ""; commentList.value[index]["child"][i]["commentInput"] += key; } else if (index != undefined) { if (!commentList.value[index]["commentInput"]) commentList.value[index]["commentInput"] = ""; commentList.value[index]["commentInput"] += key; } else { commentInputTop.value += key; } }; // 自动输入框增高 const autoResize = (e) => { e.target.style.height = "auto"; // 重置高度 e.target.style.height = `${e.target.scrollHeight}px`; // 设置为内容高度 }; const maxSize = 20 * 1024 * 1024; // 20MB const handleInputPaste = (event, index, ii) => { const items = event.clipboardData.items; // 获取粘贴的内容 for (let i = 0; i < items.length; i++) { const item = items[i]; if (item.type.startsWith("image/")) { event.preventDefault(); const file = item.getAsFile(); // 获取文件 if (file.size > maxSize) { ElMessage({ message: "文件大小不能超过 20MB", type: "error", }); return; } const reader = new FileReader(); reader.onload = (e) => { const base64 = e.target.result; uploadImg(base64).then((res) => { const obj = { base64, ...res, }; if (editCommentState.value) editPicture.value = obj; else { if (ii != undefined) commentList.value[index].child[ii]["picture"] = obj; else if (index != undefined) commentList.value[index]["picture"] = obj; else picture.value = obj; } ElMessage({ message: "上传成功", type: "success", }); }); }; reader.readAsDataURL(file); } } }; const handleFileUpload = (event, index, i) => { closeEmoji(); const file = event.target.files[0]; // 获取选择的文件 if (!file) return; if (file.size > maxSize) { ElMessage({ message: "文件大小不能超过 20MB", type: "error", }); return; } const reader = new FileReader(); reader.onload = (e) => { const base64 = e.target.result; uploadImg(base64).then((res) => { const obj = { base64, ...res, }; if (editCommentState.value) editPicture.value = obj; else { if (i != undefined) commentList.value[index].child[i]["picture"] = obj; else if (index != undefined) commentList.value[index]["picture"] = obj; else picture.value = obj; } ElMessage({ message: "上传成功", type: "success", }); }); }; reader.readAsDataURL(file); }; // 删除上传的图片 const closeFileUpload = (index, i) => { if (i != undefined) commentList.value[index].child[i]["picture"] = {}; else if (index != undefined) commentList.value[index]["picture"] = {}; else picture.value = {}; }; // 上传图片 获取图片url const uploadImg = (base64) => { return new Promise((resolve, reject) => { // detailLoading.value = true commonUploadHttp({ data: base64, }).then((res) => { if (res.code != 200) { ElMessage({ message: res.message || "上传失败", type: "error", }); return; } let data = res.data; resolve(data); }); }); }; let dialogSrc = ref(""); // 大图的src // 处理点击答案图片 展开大图 const handleAnswerText = (e) => { if (e.target.tagName === "IMG") { var src = e.target.getAttribute("src"); dialogSrc.value = src; window.addEventListener("keydown", handleKeydown); } }; // 大图的监听 esc 键盘按钮 const handleKeydown = (event) => { if (event.key !== "Escape") return; dialogSrc.value = ""; window.removeEventListener("keydown", handleKeydown); // 取消监听 }; let dialogVisible = ref(false); let commemtDelete = {}; // 点击删除 const commentDelete = (token, index, i) => { commemtDelete = { token, index, i, }; dialogVisible.value = true; }; const confirmCommentDelete = () => { commentDeleteHttp({ token: commemtDelete.token, }).then((res) => { if (res.code != 200) { ElMessage.error(res.message); return; } if (commemtDelete.i >= 0) { commentList.value[commemtDelete.index].child.splice(commemtDelete.i, 1); commentList.value[commemtDelete.index].childnum -= 1; } else { commentComments.value -= commentList.value[commemtDelete.index].childnum; commentList.value.splice(commemtDelete.index, 1); } commentComments.value -= 1; dialogVisible.value = false; ElMessage.success(res.message || "操作成功"); }); }; const judgeLogin = () => { if (isNeedLogin.value) goLogin(); }; let editCommentState = ref(false); let editToken = ""; let editPicture = ref({}); let editInput = ref(""); let editEmojiState = ref(false); const editInputRef = ref(null); const openEdit = (token, index, i) => { const list = JSON.parse(JSON.stringify(commentList.value)); let target = {}; if (i != null) target = list[index]["child"][i]; else target = list[index]; // console.log(token, index, i, target) editToken = target.token || ""; editInput.value = target.content || ""; editPicture.value = target.image || {}; editCommentState.value = true; nextTick(() => { // console.log("editInput.value", editInputRef.value) editInputRef.value.style.height = `${editInputRef.value.scrollHeight}px`; }); }; const closeEdit = () => { editPicture.value = {}; editToken = ""; editInput.value = ""; editCommentState.value = false; }; // 打开 Emoji const openEditEmoji = (index, i) => { if (isNeedLogin.value) { goLogin(); return; } editEmojiState.value = true; }; const selectEditEmoji = (key) => { closeEmoji(); editInput.value += key; }; const postEditComment = () => { if (isNeedLogin.value) { goLogin(); return; } const image = editPicture.value; commentsEditSubmit({ content: editInput.value, token: editToken, image: image ? { aid: image.aid, url: image.url } : null, }).then((res) => { if (res.code != 200) { ElMessage.error(res.message); return; } commentList.value.forEach((element) => { if (element.token == editToken) { element["content"] = editInput.value; element["image"] = image; } element.child && element.child.forEach((ele) => { if (ele.token == editToken) { ele["content"] = editInput.value; ele["image"] = image; } }); }); editPicture.value = {}; editToken = ""; editCommentState.value = false; editEmojiState.value = false; ElMessage.success(res.message); }); }; const closeEditFileUpload = () => (editPicture.value = {}); defineExpose({ changeCommentVoteoption, wipeCommentVoteoption, reviewsComment, bottomNavigationBar, closeAnswerCommentsChild }); </script> <style scoped lang="less"> @import url(@/assets/css/DetailsComments.css); </style> <style lang="less"> .automatic-reviews-popup { border-radius: 10px !important; padding: 0 !important; .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; } .el-input__count { left: 10px; bottom: -40px; width: fit-content; } } .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>