实现了一个包括添加表情和回复评论功能的评论区
html部分:
<v-sheet ref="comment_area" color="rgba(255,255,255,0.5)">
<v-container>
<span style="font-weight: bold; font-family: 微软雅黑,serif; font-size: 22px">评论</span>
<!-- 评论输入框-->
<div style="padding-top: 1vh">
<a-textarea
id="comm"
style="background-color: rgba(255,255,255,0.5)"
v-model="comment_content"
placeholder='请发表有价值的评论'
:auto-size="{ minRows: 4, maxRows: 6 }"
@onchange="word_limit($event)"
@onkeydown="word_limit($event)"
:onkeyup="word_limit()"
/>
<v-row no-gutters justify-lg="end">
<v-col cols="12" lg="9" style="text-align: left">
<v-subheader>
还可输入 <span style="color: #2C2E2D">{{input_length}}</span> 个字符
</v-subheader>
</v-col>
<v-col cols="12" lg="1" style="text-align: right">
<v-subheader>
<v-menu scale offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" icon @click="picker_selected = 'comm'">
<v-icon>mdi-emoticon</v-icon>
</v-btn>
</template>
<!-- 评论输入框的表情选择器-->
<picker
style="height: 25vh"
:include="['people','Smileys']"
:showSearch="false"
:showPreview="false"
:showCategories="false"
@select="addEmoji"
/>
</v-menu>
</v-subheader>
</v-col>
<v-col cols="12" lg="2" style="text-align: center">
<v-subheader>
<v-btn rounded outlined color="grey" width="8vw" @click="send_comment">
<span style="color: #2C2E2D">发表评论</span>
</v-btn>
</v-subheader>
</v-col>
</v-row>
</div>
<v-list three-line :key="children_show" style="background-color: #F9F9F3">
<template v-for="(item, index) in comment_list">
<v-divider></v-divider>
<v-list-item :key="index">
<v-list-item-avatar size="32" @click="visitUser(item.userId)" style="cursor: pointer">
<v-img :src="item.profilepic"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-subtitle style="padding-bottom: 1vh">
<v-row no-gutters>
<v-col cols="12" lg="6" style="text-align: left">
<span @click="visitUser(item.userId)" style="cursor: pointer">{{item.nickname}}</span>
<span>{{item.relativeTime}}</span>
</v-col>
<v-col cols="12" lg="4"></v-col>
<!-- 评论删除按钮-->
<v-col cols="12" lg="1">
<v-dialog
max-width="370"
>
<template v-slot:activator="{ on, attrs }">
<a type="text" style="color: #2C2E2D"
v-bind="attrs"
v-on="on"
v-if="isMine || item.mine">
<v-icon left size="18px">mdi-delete-empty-outline</v-icon>
<span>删除</span>
</a>
</template>
<template v-slot:default="dialog">
<v-card>
<v-card-title>
删除评论
</v-card-title>
<v-card-text>确认删除“{{item.nickname}}”的评论吗?</v-card-text>
<br><br>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text @click="dialog.value = false">
取消
</v-btn>
<v-btn text @click="delete_comment(item,dialog)" color="#A94600">
确认
</v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</v-col>
<!-- 顶级评论的回复和收起按钮-->
<v-col cols="12" lg="1">
<a type="text" style="color: #2C2E2D" @click="show_reply(item)" v-if="!item.show_input">
<v-icon left size="18px">mdi-comment-outline</v-icon>
<span>回复</span>
</a>
<a type="text" style="color: #2C2E2D" @click="hide_reply(item)" v-else>
<v-icon left size="18px">mdi-comment-outline</v-icon>
<span>收起</span>
</a>
</v-col>
</v-row>
</v-list-item-subtitle>
<p style="line-height: 20px; white-space: pre-line">{{item.content}}</p>
<!-- 顶级评论的回复框-->
<div style="padding-top: 1vh" v-if="item.show_input">
<a-textarea
:id="item.id"
v-model="children_comment_content"
style="background-color: rgba(255,255,255,0.5)"
placeholder='回复'
:auto-size="{ minRows: 3, maxRows: 5 }"
@onchange="reply_word_limit($event)"
@onkeydown="reply_word_limit($event)"
:onkeyup="reply_word_limit()"
/>
<v-row no-gutters justify-lg="end">
<v-col cols="12" lg="9" style="text-align: left">
<v-subheader>
还可输入 <span style="color: #2C2E2D">{{reply_length}}</span> 个字符
</v-subheader>
</v-col>
<v-col cols="12" lg="1" style="text-align: right">
<v-subheader>
<v-menu scale offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" icon @click="picker_selected = item.id">
<v-icon>mdi-emoticon</v-icon>
</v-btn>
</template>
<!-- 顶级评论的回复框的表情选择器-->
<picker
style="height: 25vh"
:include="['people','Smileys']"
:showSearch="false"
:showPreview="false"
:showCategories="false"
@select="addEmoji"
/>
</v-menu>
</v-subheader>
</v-col>
<v-col cols="12" lg="2" style="text-align: center">
<v-subheader>
<v-btn rounded outlined color="grey" width="6vw" @click="reply_comment(item.id,item.id)">
<span style="color: #2C2E2D">回复</span>
</v-btn>
</v-subheader>
</v-col>
</v-row>
</div>
<!-- 子评论列表-->
<div v-if="item.children.length!==0">
<el-button
v-if="item.show_children===false"
type="text"
@click="item.show_children=true;children_show += 1"
>
<span style="color: #89B8CA; font-family: 微软雅黑,serif; letter-spacing: 1px">
查看全部{{item.children.length}}条回复
</span>
</el-button>
<v-list v-else style="background-color: #F9F9F3">
<template v-for="(child_item, index) in item.children">
<v-divider></v-divider>
<v-list-item :key="index">
<v-list-item-avatar size="28" @click="visitUser(child_item.userId)" style="cursor: pointer">
<v-img :src="child_item.profilepic"></v-img>
</v-list-item-avatar>
<v-list-item-content>
<v-list-item-subtitle style="padding-bottom: 1vh">
<v-row no-gutters>
<v-col cols="12" lg="6" style="text-align: left">
<span @click="visitUser(child_item.userId)" style="cursor: pointer">{{child_item.nickname}}</span>
<span style="color: #89B8CA"> 回复 </span>
<span @click="visitUser(child_item.puserId)" style="cursor: pointer">{{child_item.pnickname}}</span>
{{child_item.relativeTime}}
</v-col>
<v-col cols="12" lg="4"></v-col>
<v-col cols="12" lg="1">
<v-dialog
max-width="370"
>
<template v-slot:activator="{ on, attrs }">
<a type="text" style="color: #2C2E2D"
v-bind="attrs"
v-on="on"
v-if="isMine || child_item.mine">
<v-icon left size="18px">mdi-delete-empty-outline</v-icon>
<span>删除</span>
</a>
</template>
<template v-slot:default="dialog">
<v-card>
<v-card-title>
删除评论
</v-card-title>
<v-card-text>确认删除“{{child_item.nickname}}”的评论吗?</v-card-text>
<br><br>
<v-card-actions>
<v-spacer></v-spacer>
<v-btn text @click="dialog.value = false">
取消
</v-btn>
<v-btn text @click="delete_comment(child_item,dialog)" color="#A94600">
确认
</v-btn>
</v-card-actions>
</v-card>
</template>
</v-dialog>
</v-col>
<v-col cols="12" lg="1">
<a style="color: #2C2E2D" @click="show_reply(child_item)" v-if="!child_item.show_input">
<v-icon left size="18px">mdi-comment-outline</v-icon>
回复
</a>
<a type="text" style="color: #2C2E2D" @click="hide_reply(child_item)" v-else>
<v-icon left size="18px">mdi-comment-outline</v-icon>
<span>收起</span>
</a>
</v-col>
</v-row>
</v-list-item-subtitle>
<p style="line-height: 20px; white-space: pre-line">{{child_item.content}}</p>
<!-- 子评论的回复框-->
<div style="padding-top: 1vh" v-if="child_item.show_input">
<a-textarea
:id="child_item.id"
v-model="children_comment_content"
placeholder='回复'
style="background-color: rgba(255,255,255,0.5)"
:auto-size="{ minRows: 3, maxRows: 5 }"
@onchange="reply_word_limit($event)"
@onkeydown="reply_word_limit($event)"
:onkeyup="reply_word_limit()"
/>
<v-row no-gutters justify-lg="end">
<v-col cols="12" lg="9" style="text-align: left">
<v-subheader>
还可输入 <span style="color: #2C2E2D">{{reply_length}}</span> 个字符
</v-subheader>
</v-col>
<v-col cols="12" lg="1" style="text-align: right">
<v-subheader>
<v-menu scale offset-y>
<template v-slot:activator="{ on, attrs }">
<v-btn v-bind="attrs" v-on="on" icon @click="picker_selected = child_item.id">
<v-icon>mdi-emoticon</v-icon>
</v-btn>
</template>
<!-- 子评论的回复框的表情选择器-->
<picker
style="height: 25vh"
:include="['people','Smileys']"
:showSearch="false"
:showPreview="false"
:showCategories="false"
@select="addEmoji"
/>
</v-menu>
</v-subheader>
</v-col>
<v-col cols="12" lg="2" style="text-align: center">
<v-subheader>
<v-btn rounded outlined color="grey" width="6vw"
@click="reply_comment(child_item.id,item.id)">
<span style="color: #2C2E2D">回复</span>
</v-btn>
</v-subheader>
</v-col>
</v-row>
</div>
</v-list-item-content>
</v-list-item>
</template>
</v-list>
</div>
</v-list-item-content>
</v-list-item>
</template>
</v-list>
<v-pagination
v-if="total!==0&&total!==1"
v-model="page"
:length="total"
@input="change_page(page)"
circle
></v-pagination>
</v-container>
</v-sheet>
js部分:
data: () => ({
comment_list: [], //评论列表
comment_count: 0, //总计评论数
comment_content: '', //评论框输入内容——主评论框
children_comment_content: '', //回复评论框输入内容(所有评论共享)
input_length: 200, //评论框剩余可输入字数
reply_length: 200, //回复框剩余可输入字数
page: 1, //评论当前页数
total: 0, //评论总页数
children_show: 0, //控制子评论列表重新渲染的key
picker_selected: 'comm', //当前选中的表情选择器的id
delete_confirm: false, //删除确认对话框
}),
methods: {
async get_comment(PageNum) { //获取评论
await this.checkFollowState()
request.get("/s-comment/getBySolution",{
params: {
PageNum: PageNum,
PageSize: 5,
solution_id: this.parseJSONCapture(this.decodeCapture(this.$route.query.solution))
}
}).then(res => {
if (res.code === '200'){
if (res.data === null){
this.comment_list = []
this.comment_count = 0
this.total = 0
}
else {
this.comment_list = res.data.list
this.comment_count = res.data.total
this.total = Math.ceil(res.data.originCount / 5)
for(let comment of this.comment_list) {
comment.show_input = false
if(comment.children.length !== 0) {
comment.show_children = false
for(let c of comment.children) {
c.show_input = false
}
}
}
this.children_show += 1
}
}
})
},
change_page(pageNum) { //评论翻页
this.get_comment(pageNum)
},
word_limit() { //修改评论编辑框事件——统计和限制字数
this.comment_content = this.comment_content.substring(0, 200)
this.input_length = 200 - this.comment_content.length
},
reply_word_limit() { //修改回复编辑框事件——统计和限制字数
this.children_comment_content = this.children_comment_content.substring(0, 200)
this.reply_length = 200 - this.children_comment_content.length
},
show_reply(item) { //点击回复按钮事件
this.children_comment_content='' //清空回复框内容
for(let comment of this.comment_list) { //遍历所有评论,设置回复框不可见
comment.show_input = false
if(comment.children.length !== 0) {
for(let c of comment.children) {
c.show_input = false
}
}
}
this.picker_selected = item.id //设置当前选中的表情选择器的id
item.show_input = true //设置当前回复框可见
this.children_show += 1 //重新渲染
},
hide_reply(item) { //收起回复框按钮事件
item.show_input = false //设置当前回复框不可见
this.children_show += 1 //重新渲染
},
send_comment() { //发布评论
if(this.comment_content.split('\n').join('').split(' ').join('').length === 0) {
this.sendWarnMessage('请输入评论内容')
} else {
let s_comment = {}
s_comment.userId = -1
s_comment.solutionId = this.solutionId
s_comment.content = this.comment_content
request.post("/s-comment",s_comment).then(res => {
if(res.code === '200') {
this.comment_content = ''
this.page = 1
this.get_comment(this.page)
this.sendSuccessMessage('评论成功')
}
else {
this.sendWarnMessage(res.msg)
this.get_comment(this.page)
}
})
}
},
reply_comment(pid,originId) { //回复评论
if(this.children_comment_content.split('\n').join('').split(' ').join('').length === 0) {
this.sendWarnMessage('请输入评论内容')
} else {
let s_comment = {}
s_comment.userId = -1
s_comment.solutionId = this.solutionId
s_comment.content = this.children_comment_content
s_comment.pid = pid
s_comment.originId = originId
request.post("/s-comment",s_comment).then(res => {
if(res.code === '200') {
this.children_comment_content = ''
this.get_comment(this.page)
this.sendSuccessMessage('评论成功')
} else {
this.sendWarnMessage('评论失败')
this.get_comment(this.page)
}
})
}
},
addEmoji(emotion){ //添加表情
if(this.input_length < 2) {
this.$message.warning("超过最大字数,无法插入表情")
} else {
let textarea = document.getElementById(this.picker_selected)
if (window.getSelection) {
// 非IE浏览器
textarea.setRangeText(emotion.native);
// 在未选中文本的情况下,重新设置光标位置
textarea.selectionStart += emotion.native.length;
textarea.focus()
if(this.picker_selected === 'comm') {
this.comment_content = textarea.value
} else {
this.children_comment_content = textarea.value
}
} else if (document.selection) {
// IE浏览器
this.comment_content.focus();
let sel = document.selection.createRange();
sel.text = emotion.native;
}
}
},
delete_comment(item,dialog) { //删除评论
request.delete("/s-comment/"+item.id).then(res => {
if(res.code==='200') {
this.sendSuccessMessage('删除评论成功')
this.get_comment(this.page)
} else {
this.sendWarnMessage('删除评论失败')
this.get_comment(this.page)
}
})
dialog.value = false
},
goto_comment() { //滚动到评论区
this.$vuetify.goTo(this.$refs.comment_area, {
duration: 500,
offset: 50,
easing: 'easeInOutCubic',
})
},
}