<template> <div class="answer-discuss"> <div class="header flexacenter"> 回答&讨论 <span class="num">{{ 17 }}</span> </div> <div class="input-box"> <div class="top flexflex"> <img class="avatar" src="https://axure-file.lanhuapp.com/md5__61e148c1ead80d48108f4e5a7f93abc3.svg" /> <div class="input-textarea flex1" ref="inputTextareaRef" :class="{ 'placeholder': isPlaceholderVisible }" contenteditable="true" @focus="clearPlaceholder()" @blur="setPlaceholder($event)" @paste="handleInputPaste"></div> </div> <div class="picture-box" v-if="picture.url"> <div class="picture"> <img class="close" @click="closeFileUpload()" src="/img/close-icon.png" /> <img class="img" :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="/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"> <input class="file" type="file" @change="handleFileUpload($event)" accept=".png, .jpg, .jpeg" /> <img class="icon" src="/img/picture-icon.png" alt="" /> <span class="file-hint">最多可上传1张图片,支持在输入框中直接粘贴图片。</span> </div> </div> <div class="btn" @click="submitAnswerComments()">发送</div> </div> </div> <div class="comments-box"> <div class="comments-item" v-for="(item, index) in commentList" :key="index"> <div class="comments-header flexacenter"> <div class="comments-header-left flexacenter"> <img class="comments-avatar" :src="item['avatar']" /> <div class="comments-username">{{ item["nickname"] }}</div> <div class="comments-time">{{ handleDate(item["timestamp"]) }}</div> <!-- <div class="comments-identity" v-if="item['questioner'] == 1">提问者</div> <div class="comments-identity" v-else-if="item['isauthor'] == 1">回答者</div> --> <div class="comments-identity" v-if="item['isauthor'] == 1">提问者</div> <div class="avatar-box flexflex" v-if="item['avatarState']"> <a class="avatar-item flexcenter" target="_blank" @click.prevent="sendMessage(item['uin'])"> <img class="avatar-icon" src="@/img/send-messages-icon.png" /> 发送信息 </a> <a class="avatar-item flexcenter" target="_blank" @click.prevent="TAHomePage(item['uin'])"> <img class="avatar-icon" src="@/img/homepage-icon.png" /> TA的主页 </a> <div class="avatar-mask"></div> </div> </div> <div class="comments-header-right flexacenter"> <div class="menu-box flexacenter" @click="openMenuState(index)"> <img class="menu-icon" src="/img/menu-icon-gray.svg" /> <div class="report-box flexcenter">举报</div> </div> <img class="comment-icon" @click="openAnswerCommentsChild(index)" src="/img/comment-icon-gray.svg" /> <div class="flexacenter like-box" @click="operateAnswerCommentsLike(item.token, index)"> <img class="like-icon" v-if="item['islike'] == 0" src="/img/like-icon-gray.png" /> <img class="like-icon" v-else src="/img/like-icon-colours.png" /> <div class="like-quantity">{{ item["likenum"] || "" }}</div> </div> </div> </div> <div class="comments-content"> <div class="comments-text">{{ item["content"] }}</div> <img class="comments-img" :src="item.image?.base64 || item.image?.url" v-if="item.image?.url" /> <div class="input-box" v-if="item['childState']"> <img class="cross" @click="closeAnswerCommentsChild(index)" src="/img/cross-icon.png" /> <div class="top flexflex"> <div class="input-textarea flex1" :class="{ 'placeholder': item.isPlaceholderVisible }" contenteditable="true" @focus="clearPlaceholder(index)" @blur="setPlaceholder($event, index)" @paste="handleInputPaste($event, index)"></div> </div> <div class="picture-box" v-if="item.picture?.url"> <div class="picture"> <img class="close" @click="closeFileUpload(index)" src="/img/close-icon.png" /> <img class="img" :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="/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="/img/picture-icon.png" alt="" /> <span class="file-hint">最多可上传1张图片,支持在输入框中直接粘贴图片。</span> </div> </div> <div class="btn" @click="submitAnswerComments(index)">发送</div> </div> </div> </div> <div class="child-comments" v-if="item['child'].length != 0"> <div class="comments-item" v-for="(ite, i) in item['child']" :key="ite.id"> <div class="comments-header flexacenter"> <div class="comments-header-left flexacenter"> <img class="comments-avatar" :src="ite['avatar']" /> <div class="comments-username">{{ ite["nickname"] }}</div> <div class="comments-time">{{ handleDate(ite["timestamp"]) }}</div> <div class="comments-identity" v-if="ite['questioner'] == 1">提问者</div> <div class="comments-identity" v-else-if="ite['isauthor'] == 1">回答者</div> <div class="avatar-box flexflex" v-if="ite['avatarState']"> <a class="avatar-item flexcenter" target="_blank"> <img class="avatar-icon" src="@/img/send-messages-icon.png" /> 发送信息 </a> <a class="avatar-item flexcenter" target="_blank"> <img class="avatar-icon" src="@/img/homepage-icon.png" /> TA的主页 </a> <div class="avatar-mask"></div> </div> </div> <div class="comments-header-right flexacenter"> <div class="menu-box flexacenter"> <img class="menu-icon" src="/img/menu-icon-gray.svg" /> <div class="report-box flexcenter">举报</div> </div> <img class="comment-icon" @click="openAnswerCommentsChild(index, i)" src="/img/comment-icon-gray.svg" /> <div class="flexacenter like-box" @click="operateAnswerCommentsLike(ite.token, index, i)"> <img class="like-icon" v-if="ite['islike'] == 0" src="/img/like-icon-gray.png" /> <img class="like-icon" v-else src="/img/like-icon-colours.png" /> <div class="like-quantity">{{ ite["likenum"] || "" }}</div> </div> </div> </div> <div class="comments-content"> <div class="comments-text"> <div class="comments-reply" v-if="JSON.stringify(ite['reply']) != '[]'">@{{ ite["reply"]["nickname"] }}</div> {{ ite["content"] }} </div> <div class="comments-input-box flexacenter" v-if="ite['childState']"> <div class="comments-input flexflex"> <textarea class="flex1" placeholder="回复" v-model="ite['commentInput']"></textarea> <div class="comments-btn flexcenter">发送</div> </div> <img class="forkfork" src="/img/cross-icon.png" /> </div> </div> </div> <div class="comments-also flexacenter" v-if="item['childnum'] > item['child'].length" @click="alsoCommentsData(index, ind)"> <div class>还有{{ item["childnum"] - 1 }}条回复</div> <img class="also-icon" src="/img/arrow-circular-gray.png" /> </div> </div> </div> </div> </div> </template> <script setup> const props = defineProps({ token: String, }) onMounted(() => { getComment() }) const $ajax = inject("$ajax") const $ajaxGET = inject("$ajaxGET") const detailsToken = inject("detailsToken") const handleDate = inject("handleDate") const isNeedLogin = inject("isNeedLogin") const handleMsg = inject("handleMsg") const uploadImg = inject("uploadImg") const commentList = ref([]) let commentCount = ref(0) let commentTotalCount = ref(0) let commentPage = ref(1) let isgetCommentSate = false // 请求评论状态 let alreadyCommentIdList = [] const getComment = () => { if (commentPage.value == 0 || isgetCommentSate) return isgetCommentSate = true $ajax("/api/comment/lists", { token: detailsToken.value, page: commentPage.value, limit: 1000, }) .then(res => { if (res.code != 200) return let data = res.data data.data.forEach((element, index) => { element["isReplyBoxShow"] = 0 element["isPlaceholderVisible"] = true // element.timestamp = util.timeformat(element.timestamp, 2) if (element.child.length > 0) { element.child.forEach(el => { // el.timestamp = util.timeformat(el.timestamp, 2) el["isReplyBoxShow"] = 0 el["isPlaceholderVisible"] = true }) } }) if (commentPage.value > 1) { let alreadyCommentIdList = alreadyCommentIdList for (let index = 0; index < data.data.length; index++) { if (alreadyCommentIdList.includes(data.data[index].id)) { data.data.splice(index, 1) index-- } } } commentList.value = commentList.value.concat(data.data) console.log("commentList", commentList.value) commentCount.value = data.count commentTotalCount.value = data.comments commentPage.value = data.count > commentList.length ? commentPage.value + 1 : 0 // console.log("commentList", commentList) // this.setData({ // commentList, // commentCount: data.count, // commentTotalCount: data.comments, // commentPage: data.count > commentList.length ? this.data.commentPage + 1 : 0, // }) }) .finally(() => { isgetCommentSate = false }) } let picture = ref({}) const handleFileUpload = (event, index, i) => { closeEmoji() const file = event.target.files[0] // 获取选择的文件 if (file) { const reader = new FileReader() reader.onload = e => { const base64 = e.target.result uploadImg(base64).then(res => { console.log("base64", base64) const obj = { base64, ...res, } if (i != undefined) commentList.value[index].child[i]["picture"] = obj else if (index != undefined) commentList.value[index]["picture"] = obj else picture.value = obj handleMsg("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 = {} } // let emojiState = ref(false) let emojiMaskState = ref(false) const emojiData = ["😀", "😁", "😆", "😅", "😂", "😉", "😍", "🥰", "😋", "😜", "🤪", "😎", "🤩", "🥳", "😔", "🙁", "😭", "😡", "😳", "🤗", "🤔", "🤭", "🤫", "😯", "😵", "🙄", "🥴", "🤢", "🤑", "🤠", "👌", "✌️", "🤟", "🤘", "🤙", "👍", "👎", "✊", "👏", "🤝", "🙏", "💪", "❤️", "💔", "🌹", "🥀", "🎉", "🎁", "🧧", "🌙", "⭐", "🌍", "💌", "📬", "🚗", "🚕", "🚲", "🛵", "🚀", "🚁", "⛵", "🚢", "🍎", "🍐", "🍊", "🍉", "🍓", "🍑", "🍔", "🍟", "🍕", "🥪", "🍜", "🍡", "🍨", "🍦", "🎂", "🍰", "🍭", "🍿", "🍩", "🧃", "🍹"] // 打开 Emoji const openEmoji = (index, i) => { if (i != undefined) commentList.value[index].child[i]["emojiState"] = true else if (index != undefined) commentList.value[index]["emojiState"] = true else 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 } // 选择 Emoji const selectEmoji = (key, index, i) => { closeEmoji() console.log("key", key) if (i != undefined) { commentList.value[index].child[i]["isPlaceholderVisible"] = false } else if (index != undefined) { commentList.value[index]["isPlaceholderVisible"] = false } else { inputTextareaRef.value.innerHTML += key isPlaceholderVisible.value = false } } let isPlaceholderVisible = ref(true) const clearPlaceholder = (index, i) => { if (i != undefined) commentList.value[index].child[i]["isPlaceholderVisible"] = false else if (index != undefined) commentList.value[index]["isPlaceholderVisible"] = false else isPlaceholderVisible.value = false } const setPlaceholder = (event, index, i) => { if (event.target.innerHTML == "<br>") event.target.innerHTML = "" const html = event.target.innerHTML if (!html) { if (i != undefined) commentList.value[index].child[i]["isPlaceholderVisible"] = true else if (index != undefined) commentList.value[index]["isPlaceholderVisible"] = true else isPlaceholderVisible.value = true } } const handleInputPaste = (event, index, i) => { 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() // 获取文件 const reader = new FileReader() reader.onload = e => { const base64 = e.target.result uploadImg(base64).then(res => { console.log(index) const obj = { base64, ...res, } if (i != undefined) commentList.value[index].child[i]["picture"] = {} else if (index != undefined) commentList.value[index]["picture"] = {} else picture.value = obj handleMsg("success", "上传成功") }) } reader.readAsDataURL(file) } } } let inputTextareaRef = ref(null) // 提交回答-评论 const submitAnswerComments = (index, i) => { if (isNeedLogin.value) { goLogin() return } let content = "" let parentid = null let token = detailsToken.value let image = [] if (i != null) { const commentInput = document.querySelector(".comments-box .input-textarea") content = commentInput.innerText parentid = commentList.value[index]["child"][i]["id"] } else if (index != null) { const commentInput = document.querySelector(".comments-box .input-textarea") content = commentInput.innerText parentid = commentList.value[index]["id"] } else { content = inputTextareaRef.value.innerText image = picture.value } console.log("content", content) $ajax("/api/comment/submit", { content, token, parentid, image, }).then(res => { if (res.code != 200) return let data = res.data if (i != null) { let targetData = { id: data["commentid"], content, isauthor: 1, islike: 0, likenum: 0, reply: { nickname: commentList.value[index]["child"][i]["nickname"], }, ...data, isPlaceholderVisible: true, image, } commentList.value[index]["child"].unshift(targetData) commentList.value[index]["childnum"]++ } else if (index != null) { let targetData = { id: data["commentid"], content, isauthor: 1, islike: 0, likenum: 0, reply: [], ...data, isPlaceholderVisible: true, image, } commentList.value[index]["child"].unshift(targetData) commentList.value[index]["childnum"]++ } else { let targetData = { id: data["commentid"], content, isauthor: 1, islike: 0, likenum: 0, ...data, child: [], isPlaceholderVisible: true, image, } commentList.value.unshift(targetData) commentCount.value++ inputTextareaRef.value.innerHTML = "" picture.value = {} } // targetAnswerList[index]["commentnum"] = data["count"] closeAnswerCommentsChild() handleMsg("success", res["message"] || "操作成功") }) } // 回答-评论 点赞 const operateAnswerCommentsLike = (token, index, i) => { if (isNeedLogin.value) { goLogin() return } $ajax("/api/comment/like", { token, }).then(res => { if (res.code != 200) return let data = res.data console.log("i",i) if (i != undefined) { commentList.value[index].child[i]["islike"] = data["status"] commentList.value[index].child[i]["likenum"] = data["likenum"] } else { commentList.value[index]["islike"] = data["status"] commentList.value[index]["likenum"] = data["likenum"] } handleMsg("success", res["message"] || "操作成功") }) } // 打开 回答-评论 的子评论 const openAnswerCommentsChild = (index, i) => { console.log("isNeedLogin", isNeedLogin) if (isNeedLogin.value) { goLogin() return } closeAnswerCommentsChild() if (i == null) commentList.value[index]["childState"] = true else commentList.value[index]["child"][i]["childState"] = true } // 关闭 回答-评论 的子评论 const closeAnswerCommentsChild = () => { commentList.value.forEach(ele => { ele["childState"] = false if (ele["child"] && ele["child"].length != 0) { ele["child"].forEach(el => { el["childState"] = false }) } }) } // 获取剩下的子评论 const alsoCommentsData = (index, ind) => { return const targetAnswerList = [...answerList.value] const parentid = targetAnswerList[index]["commentList"][ind]["id"] const token = targetAnswerList[index]["token"] $ajax("/api/comment/childrenList", { token, parentid, limit: 20, page: 1, childlimit: 1, }).then(res => { if (res.code != 200) return let data = res.data let merged1 = [...targetAnswerList[index]["commentList"][ind]["child"], ...data.data.filter(item2 => !targetAnswerList[index]["commentList"][ind]["child"].find(item1 => item1.id == item2.id))] targetAnswerList[index]["commentList"][ind]["child"] = merged1 answerList.value = targetAnswerList }) } const handleMenuState = inject("handleMenuState") const openMenuState = (index, i) => { if (isNeedLogin.value) { goLogin() return } let reportToken = "" if (i === undefined) reportToken = commentList.value[index]["token"] else reportToken = commentList.value[index]["child"][i]["token"] handleMenuState(reportToken) } </script> <style scoped></style>