利用Vue+ElementUi實(shí)現(xiàn)評(píng)論功能
前言
這兩天在用vue重構(gòu)之前寫的一個(gè)社區(qū)博客項(xiàng)目,之前評(píng)論的樣式和效果都差強(qiáng)人意,所以仿照掘金重寫了一個(gè)評(píng)論功能(但不是完全照搬模仿)
在寫完這個(gè)功能后,由心覺(jué)得Vue真的非常好用。
話不多說(shuō),先上效果圖



代碼
html代碼:
<template>
<div>
<div v-clickoutside="hideReplyBtn" @click="inputFocus" class="my-reply">
<el-avatar class="header-img" :size="40" :src="myHeader"></el-avatar>
<div class="reply-info" >
<div
tabindex="0"
contenteditable="true"
id="replyInput"
spellcheck="false"
placeholder="輸入評(píng)論..."
class="reply-input"
@focus="showReplyBtn"
@input="onDivInput($event)"
>
</div>
</div>
<div class="reply-btn-box" v-show="btnShow">
<el-button class="reply-btn" size="medium" @click="sendComment" type="primary">發(fā)表評(píng)論</el-button>
</div>
</div>
<div v-for="(item,i) in comments" :key="i" class="author-title reply-father">
<el-avatar class="header-img" :size="40" :src="item.headImg"></el-avatar>
<div class="author-info">
<span class="author-name">{{item.name}}</span>
<span class="author-time">{{item.time}}</span>
</div>
<div class="icon-btn">
<span @click="showReplyInput(i,item.name,item.id)"><i class="iconfont el-icon-s-comment"></i>{{item.commentNum}}</span>
<i class="iconfont el-icon-caret-top"></i>{{item.like}}
</div>
<div class="talk-box">
<p>
<span class="reply">{{item.comment}}</span>
</p>
</div>
<div class="reply-box">
<div v-for="(reply,j) in item.reply" :key="j" class="author-title">
<el-avatar class="header-img" :size="40" :src="reply.fromHeadImg"></el-avatar>
<div class="author-info">
<span class="author-name">{{reply.from}}</span>
<span class="author-time">{{reply.time}}</span>
</div>
<div class="icon-btn">
<span @click="showReplyInput(i,reply.from,reply.id)"><i class="iconfont el-icon-s-comment"></i>{{reply.commentNum}}</span>
<i class="iconfont el-icon-caret-top"></i>{{reply.like}}
</div>
<div class="talk-box">
<p>
<span>回復(fù) {{reply.to}}:</span>
<span class="reply">{{reply.comment}}</span>
</p>
</div>
<div class="reply-box">
</div>
</div>
</div>
<div v-show="_inputShow(i)" class="my-reply my-comment-reply">
<el-avatar class="header-img" :size="40" :src="myHeader"></el-avatar>
<div class="reply-info" >
<div tabindex="0" contenteditable="true" spellcheck="false" placeholder="輸入評(píng)論..." @input="onDivInput($event)" class="reply-input reply-comment-input"></div>
</div>
<div class=" reply-btn-box">
<el-button class="reply-btn" size="medium" @click="sendCommentReply(i,j)" type="primary">發(fā)表評(píng)論</el-button>
</div>
</div>
</div>
</div>
</template>
JS代碼如下
我把模擬的數(shù)據(jù)寫在了data里面,顯得js有點(diǎn)長(zhǎng)。如果要更改數(shù)據(jù)的格式的話,記得也要改Html不然會(huì)出錯(cuò)。
<script>
const clickoutside = {
// 初始化指令
bind(el, binding, vnode) {
function documentHandler(e) {
// 這里判斷點(diǎn)擊的元素是否是本身,是本身,則返回
if (el.contains(e.target)) {
return false;
}
// 判斷指令中是否綁定了函數(shù)
if (binding.expression) {
// 如果綁定了函數(shù) 則調(diào)用那個(gè)函數(shù),此處binding.value就是handleClose方法
binding.value(e);
}
}
// 給當(dāng)前元素綁定個(gè)私有變量,方便在unbind中可以解除事件監(jiān)聽(tīng)
el.vueClickOutside = documentHandler;
document.addEventListener('click', documentHandler);
},
update() {},
unbind(el, binding) {
// 解除事件監(jiān)聽(tīng)
document.removeEventListener('click', el.vueClickOutside);
delete el.vueClickOutside;
},
};
export default {
name:'ArticleComment',
data(){
return{
btnShow: false,
index:'0',
replyComment:'',
myName:'Lana Del Rey',
myHeader:'https://ae01.alicdn.com/kf/Hd60a3f7c06fd47ae85624badd32ce54dv.jpg',
myId:19870621,
to:'',
toId:-1,
comments:[
{
name:'Lana Del Rey',
id:19870621,
headImg:'https://ae01.alicdn.com/kf/Hd60a3f7c06fd47ae85624badd32ce54dv.jpg',
comment:'我發(fā)布一張新專輯Norman Fucking Rockwell,大家快來(lái)聽(tīng)啊',
time:'2019年9月16日 18:43',
commentNum:2,
like:15,
inputShow:false,
reply:[
{
from:'Taylor Swift',
fromId:19891221,
fromHeadImg:'https://ae01.alicdn.com/kf/H94c78935ffa64e7e977544d19ecebf06L.jpg',
to:'Lana Del Rey',
toId:19870621,
comment:'我很喜歡你的新專輯?。?,
time:'2019年9月16日 18:43',
commentNum:1,
like:15,
inputShow:false
},
{
from:'Ariana Grande',
fromId:1123,
fromHeadImg:'https://ae01.alicdn.com/kf/Hf6c0b4a7428b4edf866a9fbab75568e6U.jpg',
to:'Lana Del Rey',
toId:19870621,
comment:'別忘記宣傳我們的合作單曲啊',
time:'2019年9月16日 18:43',
commentNum:0,
like:5,
inputShow:false
}
]
},
{
name:'Taylor Swift',
id:19891221,
headImg:'https://ae01.alicdn.com/kf/H94c78935ffa64e7e977544d19ecebf06L.jpg',
comment:'我發(fā)行了我的新專輯Lover',
time:'2019年9月16日 18:43',
commentNum:1,
like:5,
inputShow:false,
reply:[
{
from:'Lana Del Rey',
fromId:19870621,
fromHeadImg:'https://ae01.alicdn.com/kf/Hd60a3f7c06fd47ae85624badd32ce54dv.jpg',
to:'Taylor Swift',
toId:19891221,
comment:'新專輯和speak now 一樣棒!',
time:'2019年9月16日 18:43',
commentNum:25,
like:5,
inputShow:false
}
]
},
{
name:'Norman Fucking Rockwell',
id:20190830,
headImg:'https://ae01.alicdn.com/kf/Hdd856ae4c81545d2b51fa0c209f7aa28Z.jpg',
comment:'Plz buy Norman Fucking Rockwell on everywhere',
time:'2019年9月16日 18:43',
commentNum:0,
like:5,
inputShow:false,
reply:[]
},
]
}
},
directives: {clickoutside},
methods: {
inputFocus(){
var replyInput = document.getElementById('replyInput');
replyInput.style.padding= "8px 8px"
replyInput.style.border ="2px solid blue"
replyInput.focus()
},
showReplyBtn(){
this.btnShow = true
},
hideReplyBtn(){
this.btnShow = false
replyInput.style.padding= "10px"
replyInput.style.border ="none"
},
showReplyInput(i,name,id){
this.comments[this.index].inputShow = false
this.index =i
this.comments[i].inputShow = true
this.to = name
this.toId = id
},
_inputShow(i){
return this.comments[i].inputShow
},
sendComment(){
if(!this.replyComment){
this.$message({
showClose: true,
type:'warning',
message:'評(píng)論不能為空'
})
}else{
let a ={}
let input = document.getElementById('replyInput')
let timeNow = new Date().getTime();
let time= this.dateStr(timeNow);
a.name= this.myName
a.comment =this.replyComment
a.headImg = this.myHeader
a.time = time
a.commentNum = 0
a.like = 0
this.comments.push(a)
this.replyComment = ''
input.innerHTML = '';
}
},
sendCommentReply(i,j){
if(!this.replyComment){
this.$message({
showClose: true,
type:'warning',
message:'評(píng)論不能為空'
})
}else{
let a ={}
let timeNow = new Date().getTime();
let time= this.dateStr(timeNow);
a.from= this.myName
a.to = this.to
a.fromHeadImg = this.myHeader
a.comment =this.replyComment
a.time = time
a.commentNum = 0
a.like = 0
this.comments[i].reply.push(a)
this.replyComment = ''
document.getElementsByClassName("reply-comment-input")[i].innerHTML = ""
}
},
onDivInput: function(e) {
this.replyComment = e.target.innerHTML;
},
dateStr(date){
//獲取js 時(shí)間戳
var time=new Date().getTime();
//去掉 js 時(shí)間戳后三位,與php 時(shí)間戳保持一致
time=parseInt((time-date)/1000);
//存儲(chǔ)轉(zhuǎn)換值
var s;
if(time<60*10){//十分鐘內(nèi)
return '剛剛';
}else if((time<60*60)&&(time>=60*10)){
//超過(guò)十分鐘少于1小時(shí)
s = Math.floor(time/60);
return s+"分鐘前";
}else if((time<60*60*24)&&(time>=60*60)){
//超過(guò)1小時(shí)少于24小時(shí)
s = Math.floor(time/60/60);
return s+"小時(shí)前";
}else if((time<60*60*24*30)&&(time>=60*60*24)){
//超過(guò)1天少于30天內(nèi)
s = Math.floor(time/60/60/24);
return s+"天前";
}else{
//超過(guò)30天ddd
var date= new Date(parseInt(date));
return date.getFullYear()+"/"+(date.getMonth()+1)+"/"+date.getDate();
}
}
},
}
</script>
css 代碼
<style lang="stylus" scoped>
.my-reply
padding 10px
background-color #fafbfc
.header-img
display inline-block
vertical-align top
.reply-info
display inline-block
margin-left 5px
width 90%
@media screen and (max-width:1200px) {
width 80%
}
.reply-input
min-height 20px
line-height 22px
padding 10px 10px
color #ccc
background-color #fff
border-radius 5px
&:empty:before
content attr(placeholder)
&:focus:before
content none
&:focus
padding 8px 8px
border 2px solid blue
box-shadow none
outline none
.reply-btn-box
height 25px
margin 10px 0
.reply-btn
position relative
float right
margin-right 15px
.my-comment-reply
margin-left 50px
.reply-input
width flex
.author-title:not(:last-child)
border-bottom: 1px solid rgba(178,186,194,.3)
.author-title
padding 10px
.header-img
display inline-block
vertical-align top
.author-info
display inline-block
margin-left 5px
width 60%
height 40px
line-height 20px
>span
display block
cursor pointer
overflow hidden
white-space nowrap
text-overflow ellipsis
.author-name
color #000
font-size 18px
font-weight bold
.author-time
font-size 14px
.icon-btn
width 30%
padding 0 !important
float right
@media screen and (max-width : 1200px){
width 20%
padding 7px
}
>span
cursor pointer
.iconfont
margin 0 5px
.talk-box
margin 0 50px
>p
margin 0
.reply
font-size 16px
color #000
.reply-box
margin 10px 0 0 50px
background-color #efefef
</style>
寫在最后
如果代碼有什么問(wèn)題,或者你有什么意見(jiàn),歡迎大家指出。
到此這篇關(guān)于利用Vue+ElementUi實(shí)現(xiàn)評(píng)論功能的文章就介紹到這了,更多相關(guān)Vue ElementUi評(píng)論內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
vue+elementUI組件table實(shí)現(xiàn)前端分頁(yè)功能
這篇文章主要為大家詳細(xì)介紹了vue+elementUI組件table實(shí)現(xiàn)前端分頁(yè)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12
vue中defineProperty和Proxy的區(qū)別詳解
這篇文章主要介紹了vue中defineProperty和Proxy的區(qū)別詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
vue如何實(shí)現(xiàn)清空this.$route.query的值
這篇文章主要介紹了vue如何實(shí)現(xiàn)清空this.$route.query的值,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-09-09
Vue出現(xiàn)did you register the component 
這篇文章主要介紹了Vue出現(xiàn)did you register the component correctly?解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-03-03
一文帶你深入理解Vue3中Composition API的使用
Composition API 是 Vue 3 中的一項(xiàng)強(qiáng)大功能,它改進(jìn)了代碼組織和重用,使得構(gòu)建組件更加靈活和可維護(hù),本文我們將深入探討 Composition API 的各個(gè)方面,希望對(duì)大家有所幫助2023-10-10

