1.需求解析
之前在做一个项目时,需要用到评论这个功能模块,第一时间是去网上找现成的,但是看完一圈后,发现都不是很满意,于是索性就自己实现一个
2.准备工作
我是采用组件化思想,将他封装成一个vue组件的,其中也用到了element-ui这个组件、moment.js工具(获取当前的时间)以及nanoid(用于生成唯一的id标识,主要是标识新添加的评论),如果不熟悉的话,这里说明一下:
element-ui
先需要在控制台输入以下命令安装一下element-ui
npm i element-ui -S
再在main.js中引入一下样式以及组件就可以使用了
import Vue from 'vue';
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import App from './App.vue';
Vue.use(ElementUI);
new Vue(
{
el: '#app',
render: h => h(App)
});
moment
直接在控制台输入下面命令
npm install moment --save
nanoid 同样直接在控制台输入下面命令安装
npm i nanoid
3.说明介绍
我这里将整个组件分为了三个模块
1)一个是输入评论功能,我这里命名为 commentHeader.vue
2)在一个就是评论内容展示,并命名为 commentItem.vue
3)最后就是将前面两个组件合理使用,组成整个评论功能的组件,命名为 discuss.vue
4.具体实现
commentHeader.vue
<template>
<div @click.stop>
<div class="header">
<div class="peopleicon">
<el-avatar icon="el-icon-user-solid" :src="selfinfo.headPictureUrl"></el-avatar>
</div>
<div class="input">
<el-input type="textarea" autosize placeholder="请输入评论..." v-model="inputContent">
</el-input>
</div>
</div>
<div class="btn">
<el-button @click.native="submit" type="primary">发表评论</el-button>
</div>
</div>
</template>
<script>
import { getbaseinfo } from '@/network/getbaseinfo'
import moment from 'moment'
// import 'moment/locale/zh-cn';
export default {
name: 'commentHeader',
props: {
index: {
type: String,
default: '-1'
}
},
data() {
return {
inputContent: '',
selfinfo:{}
}
},
created() {
getbaseinfo('/userinfo').then(res => {
this.selfinfo = res.data.userinfo;
})
},
methods: {
submit() {
if (this.inputContent == '') {
this.$message({ message: '评论不能为空' });
} else {
this.$emit('subCommet', {
name: '不过',
prio:1,
to:'不过',
toId:this.index,
headImg:'https://w.wallhaven.cc/full/k7/wallhaven-k7y1x7.png',
comment: this.inputContent,
time: moment().format('YYYY年MM月DD日 HH:mm'),
reply: []
});
this.inputContent = '';
this.$message({
showClose: true,
message: '评论成功',
type: 'success'
});
}
}
}
}
</script>
<style scoped>
.header {
margin: 0 2%;
display: flex;
justify-content: space-around;
}
.peopleicon {
flex: 2;
}
.input {
flex: 20;
}
.btn {
margin: 10px 3% 0 0;
display: flex;
justify-content: right;
}
</style>
commentItem.vue
<template>
<div class="comItem">
<div class="header-img">
<el-avatar :src="itemcomment.headImg" :size="50"></el-avatar>
</div>
<div class="baseinfo">
<div class="author-info">
<span class="author-name">{{ itemcomment.name }}</span>
<span class="author-time">{{ itemcomment.time }}</span>
<li class="el-icon-chat-dot-square" id="icon" @click.stop="showcombtn" > </li>
</div>
<div class="datainfo">
<span> {{ itemcomment.comment }}</span>
</div>
<slot></slot>
</div>
</div>
</template>
<script>
import commentHeader from './commentHeader.vue'
export default {
name: 'commentItem',
components: {
commentHeader
},
props: {
itemcomment: {
type: Object,
default: {}
},
index: {
type: String,
default: '-1'
}
},
methods: {
showcombtn() {
this.$emit('getindex',this.itemcomment)
},
}
}
</script>
<style scoped>
.comItem {
margin: 10px auto;
padding-top: 5px;
display: flex;
border-top: 1px solid rgb(128, 125, 125);
}
.header-img {
margin: 0 1%;
}
.baseinfo {
flex: 18;
}
.author-info {
display: flex;
position: relative;
flex-direction: column;
}
#icon {
font-size: 20px;
position: absolute;
top: 10%;
right: 20%
}
.author-name {
font-size: 20px;
font-weight: bold;
}
.datainfo {
margin: 10px 30px 0 0;
font-size: 18px;
white-space: pre-line;
word-break: break-all;
}
</style>
discuss.vue
<template>
<div @click="seting">
<comment-header @subCommet="addNewComment"></comment-header>
<div v-for="(item,key) in comments" :key="key">
<comment-item :itemcomment="item" :index="key" @getindex="getindex">
<div v-show="item.prio == 1" class="reply">
<div v-for="(singe, k) in item.reply" :key="k">
<comment-item :itemcomment="singe" @getindex="getindex"></comment-item>
</div>
</div>
<div class="commentbtn">
<comment-header v-show="currentId == key" :index="key" @subCommet="subCommet"></comment-header>
</div>
</comment-item>
</div>
<h4 class="combottom">到底了~~</h4>
<back-top></back-top>
</div>
</template>
<script>
import commentHeader from './discussChild/commentHeader.vue'
import commentItem from './discussChild/commentItem.vue'
import backTop from '@/components/common/backtop/backTop'
import { nanoid } from "nanoid";
export default {
name: 'discuss',
components: {
commentHeader,
commentItem,
backTop
},
data() {
return {
currentId: -1,
currentcomment: {},
comments: {
1: {
name: 'Lana',
headImg: 'https://w.wallhaven.cc/full/72/wallhaven-727md3.png',
to: 'Lana',
toId: 1,
comment: '我发布一张新专辑Norman Fucking Rockwell,大家快来听啊',
time: '2019年9月16日 18:43',
prio: 1,
reply: [
{
name: 'Taylor Swift',
headImg: 'https://ae01.alicdn.com/kf/H94c78935ffa64e7e977544d19ecebf06L.jpg',
to: 'Lana',
toId: 1,
comment: '我很喜欢你的新专辑!!',
time: '2019年9月16日 18:43',
prio: 2,
reply: []
},
]
},
2: {
name: 'Taylor Swift',
to: 'Taylor Swift',
toId: 2,
headImg: 'https://ae01.alicdn.com/kf/H94c78935ffa64e7e977544d19ecebf06L.jpg',
comment: '我发行了我的新专辑Lover',
time: '2019年9月16日 18:43',
prio: 1,
reply: [
{
name: 'Lana',
headImg: 'https://w.wallhaven.cc/full/72/wallhaven-727md3.png',
to: 'Taylor Swift',
toId: 2,
comment: '新专辑和speak now 一样棒!',
time: '2019年9月16日 18:43',
prio: 2,
reply: []
}, {
name: 'Rey',
toId: 2,
headImg: 'https://w.wallhaven.cc/full/k7/wallhaven-k7q9m7.png',
to: 'Taylor Swift',
comment: '新专辑和speak now 一样棒!',
time: '2019年9月16日 18:43',
prio: 2,
reply: []
}, {
name: 'Del',
headImg: 'https://w.wallhaven.cc/full/k7/wallhaven-k7yww1.jpg',
to: 'Taylor Swift',
toId: 2,
comment: '新专辑和speak now 一样棒!',
time: '2019年9月16日 18:43',
prio: 2,
reply: []
}
]
},
3: {
name: 'Rockwell',
to: 'Rockwell',
toId: 3,
headImg: 'https://ae01.alicdn.com/kf/Hdd856ae4c81545d2b51fa0c209f7aa28Z.jpg',
comment: 'https://w.wallhaven.cc/full/o3/wallhaven-o3pj1m.jpg',
time: '2019年9月16日 18:43',
prio: 1,
reply: []
},
}
}
},
methods: {
getindex(value) {
this.currentcomment = value;
this.currentId = value.toId;
},
subCommet(value) {
if (this.currentcomment.prio == 2) {
value=this.initcomment(value)
value.comment = `@${this.currentcomment.name}: ${value.comment}`
this.comments[this.currentId].reply.push(value)
}
else {
this.comments[this.currentId].reply.push(this.initcomment(value))
}
this.seting();
},
seting() {
this.currentId = -1;
this.currentcomment = {}
},
addNewComment(value) {
const id=nanoid()
value.toId=id
this.$set(this.comments,id,value);
},
initcomment(value) {
value.prio = 2;
value.to = this.currentcomment.name;
value.toId = this.currentcomment.toId;
return value;
}
},
}
</script>
<style scoped >
.reply {
width: 90%;
margin: auto;
}
.commentbtn {
margin-top: 30px;
}
.combottom {
margin-top: 30px;
text-align: center;
}
</style>
最后在你需要的地方导入使用就行了
实例:
<template>
<discuss></discuss>
</template>
<script>
import discuss from './discuss/discuss';
export default {
name: 'articleContent',
components:{
discuss,
},
}
</script>
<style scoped>
</style>
我这里呈现的效果大致是这样,因为数据的原因大家最后呈现的结果跟我的有些出入
5.总结
其实能够不借助其他组件,自己实现这样的评论功能,还挺有收获的,一定程度上也提高了个人的代码编写能力