封装组件的原因
在日常开发中总会涉及到评论回复这种需求,最近在做的项目就用到了,去github上面搜索了一部分这种插件,有些还是不错的,都是都不是我想要的,所以自己就动手自己封装一个组件,具体看如下代码 首先在components文件夹下创建一个组件,components存放公共组件的,可以进行复用的组件,然后分别创建两个文件夹,分别叫CommentBox和MainComment,具体代码如下。
CommentBox
这里封装了评论的输入框以及通过props和自定义事件完成父子组件之间的通信。布局采用了element-ui的layout样式布局,其实自己写布局也可以,同时集成了表情输入的功能,表情插件是emoji-picker,使用方法可以去上面地址查看,或者直接复制这个就可以,不过还是编写样式,直接粘贴复制我下面的就可以。
这里介绍一下怎么引入vue-emoji-picker
npm引入
npm i vue-emoji-picker --save
CDN引入
<script src="https://unpkg.com/vue-emoji-picker/dist/vue-emoji-picker.js"></script>
在项目中使用
这个可以在全局使用,也可以在局部使用,这里介绍一下怎么在局部使用
import EmojiPicker from 'vue-emoji-picker'
export default {
// ...
components: {
EmojiPicker,
},
// ...
}
<template>
<div class="comment-content">
<div class="comment-send-box">
<el-row :gutter="8">
<el-col :span="2">
<div class="avatar">
<el-image
:src="userInfo.avatarUrl"
fit="cover"
:title="userInfo.nickname"
></el-image>
</div>
</el-col>
<el-col :span="22">
<div class="comment-input">
<!-- 输入框 -->
<el-input
type="textarea"
:autosize="{ minRows: 3, maxRows: 4 }"
placeholder="输入你的精彩评论"
v-model="commentContent"
>
</el-input>
<div class="comment-tool">
<!-- 表情 -->
<el-col :span="1">
<div class="comment-expression">
<emoji-picker @emoji="insert">
<div
slot="emoji-invoker"
slot-scope="{ events: { click: clickEvent } }"
@click.stop="clickEvent"
>
<i class="iconfont icon-biaoqing"></i>
</div>
<div slot="emoji-picker" slot-scope="{ emojis, insert }">
<div class="emoji-picker">
<div>
<div
v-for="(emojiGroup, category) in emojis"
:key="category"
>
<h5>{{ category }}</h5>
<div class="emojis">
<span
v-for="(emoji, emojiName) in emojiGroup"
:key="emojiName"
@click="insert(emoji)"
:title="emojiName"
>{{ emoji }}</span
>
</div>
</div>
</div>
</div>
</div>
</emoji-picker>
</div>
</el-col>
<el-col :span="18">
<div class="comment-at">
<i class="iconfont icon-aite"></i>
</div>
</el-col>
<el-col :span="3">
<el-button
type="primary"
size="small"
autofocus
@click="commentSubmit"
>{{ buttonText }}</el-button
>
</el-col>
<el-col :span="1">
<el-button size="small" autofocus @click="cancelComment"
>取消</el-button
>
</el-col>
</div>
</div>
</el-col>
</el-row>
</div>
</div>
</template>
<script>
import EmojiPicker from 'vue-emoji-picker'
import { mapGetters } from 'vuex'
export default {
name: 'CommentBox',
data() {
return {
// 评论内容
commentContent: '',
}
},
components: {
EmojiPicker
},
props: {
currentCommentId: {
type: [String, Number]
},
buttonText: {
type: String,
default: '评论'
},
// 是否清空评论框
clearContent: {
type: Boolean
}
},
computed: {
// 获取登录状态和用户信息
...mapGetters(['userInfo', 'loginStatus']),
},
watch: {
// 观察提交评论之后输入框内容的变化
clearContent(val) {
if (val) {
this.commentContent = ''
}
}
},
mounted() {
// console.log('mapGetters', this.currentCommentId)
},
methods: {
// 将表情插入到输入框
insert(emoji) {
this.commentContent += emoji
},
// 评论提交
commentSubmit() {
this.$emit('commentSubmit', this.commentContent)
},
// 取消评论
cancelComment() {
this.$emit('cancelComment')
this.commentContent = ''
}
}
}
</script>
<style lang='less' scoped>
.comment-content {
width: 100%;
.comment-send-box {
.avatar {
width: 50px;
height: 50px;
}
.comment-input {
margin-bottom: 10px;
}
.comment-tool {
margin-top: 10px;
.comment-expression {
.icon-biaoqing {
cursor: pointer;
font-size: 1rem;
&:hover {
color: @color-theme;
}
}
.emoji-picker {
position: absolute;
z-index: 1;
border: 1px solid #ccc;
width: 15rem;
height: 20rem;
overflow: scroll;
padding: 1rem;
box-sizing: border-box;
border-radius: 0.5rem;
background: @color-dark;
box-shadow: 1px 1px 8px #c7dbe6;
h5 {
margin-bottom: 0;
color: @color-blank;
text-transform: uppercase;
font-size: 0.8rem;
cursor: default;
}
.emojis {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
&:after {
content: '';
flex: auto;
}
span {
padding: 0.2rem;
cursor: pointer;
border-radius: 8px;
&:hover {
background: #ececec;
cursor: pointer;
}
}
}
}
}
.comment-at {
.icon-aite {
cursor: pointer;
font-size: 1rem;
&:hover {
color: @color-theme;
}
}
}
}
}
}
</style>
效果图
MainComment
引入上面的评论回复框在评论组件中显示
接再来就是引入上面的评论回复框在评论组件中显示了,具体看代码,相信小伙伴们都是大佬,这个没啥问题的。不过这里需要下载一些iconfont文件,有些图标是在上面使用的,i标签中的带有icon-前缀的都是图标。这里面还集成了点赞的功能,同时引入了vuex状态管理,这块不建议小伙伴直接用,可以参考参考。
<template>
<div class="commen-box container">
<!-- <div class="commen-header">
<div class="comment-header-title">
<span class="comment-text">评论</span>
<span>{{ commentList.length }}条评论</span>
</div>
</div> -->
<div class="comment-list">
<h3>{{ title }}</h3>
<ul>
<li class="item" v-for="item of commentList" :key="item.time">
<div class="avatar">
<el-image
:src="item.user.avatarUrl"
fit="cover"
:alt="item.user.nickname"
:title="item.user.nickname"
></el-image>
</div>
<div class="info">
<h2 class="flex-between">
<span>
{{ item.user.nickname }}
<small>· {{ utils.formatMsgTime(item.time) }}</small>
</span>
<div class="tool flex-row">
<!-- 点赞 -->
<i
class="iconfont icon-dianzan_access icon-zan"
:class="item.liked ? 'active' : ''"
@click="commentLike(item.commentId, item.liked)"
></i>
<span>({{ item.likedCount }})</span>
<!-- 评论 -->
<i
class="iconfont icon-huifu"
@click="commentHandle(item.commentId)"
></i>
</div>
</h2>
<div class="content">
{{ item.content }}
<div
class="beReqlied"
v-for="subItem of item.beReplied"
:key="subItem.beRepliedCommentId"
>
<small>@{{ subItem.user.nickname }}:</small
>{{ subItem.content }}
</div>
</div>
<div>
<CommentBox
@commentSubmit="commentSubmit"
@cancelComment="cancelComment"
:buttonText="buttonText"
v-if="item.commentId === currentCommentId"
></CommentBox>
</div>
</div>
</li>
</ul>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import CommentBox from '../CommentBox'
export default {
name: 'MainComment',
data() {
return {
buttonText: '回复'
}
},
props: {
// 头部标题
title: {
type: String
},
// 评论列表
commentList: {
tyep: Array
},
// 回复者的id
currentCommentId: {
type: [String, Number]
}
},
components: {
CommentBox,
},
computed: {
...mapGetters(['userInfo', 'loginStatu'])
},
mounted() {
},
methods: {
// 评论点赞
commentLike(id, liked) {
this.$emit('commentLike', id, liked)
},
// 取消评论
cancelComment() {
this.$emit('cancelComment')
},
// 提交评论
commentSubmit(content) {
this.$emit('commentSubmit', content)
},
// 打开评论
commentHandle(id) {
this.$emit('commentHandle', id)
}
}
}
</script>
<style lang='less' scoped>
.commen-box {
width: 100%;
.comment-list {
margin-top: 20px;
h3 {
margin-bottom: 12px;
font-size: 1.2rem;
border-bottom: 3px solid @color-theme;
}
ul {
margin: 0;
padding: 0;
list-style: none;
li {
padding: 10px 0;
width: 100%;
display: flex;
.avatar {
width: 45px;
height: 45px;
border-radius: 50%;
margin-right: 12px;
flex-shrink: 0;
.el-image {
width: 100%;
border-radius: 50%;
}
}
.info {
flex: 1;
h2 {
font-size: 1rem;
margin-right: 6px;
margin-bottom: 10px;
small {
color: #a5a5c1;
font-weight: 200;
font-size: 0.8rem;
}
.tool {
i {
font-size: 1.3rem;
font-weight: 100;
margin-left: 20px;
cursor: pointer;
transition: all 0.4s;
&.icon-zan {
&.active {
color: @color-theme;
}
}
}
.icon-huifu {
&:hover {
color: @color-theme;
}
}
span {
font-size: 0.8rem;
margin-top: 2px;
color: #666;
font-weight: 200;
position: relative;
&::after {
content: '';
width: 1px;
height: 13px;
background: #4a4a4a;
opacity: 0.7;
position: absolute;
top: 2px;
right: -12px;
}
}
}
}
.content {
width: 100%;
font-size: 0.9rem;
color: #4a4a4a;
line-height: 1.6;
padding: 8px 10px;
background: #f5f5f5;
margin-top: 5px;
border-radius: 2px;
.beReqlied {
margin-top: 10px;
background: #ffffff;
padding: 8px 10px;
border-radius: 3px;
color: #666666;
small {
color: @color-theme;
}
}
}
}
}
}
}
}
</style>
效果图
在项目中使用
这里我就放了部分的代码。这里涉及到父子组件之间的通信问题以及父子组件之间自定义事件
<template>
<div>
<MainComment
title="最新评论"
:commentList="commentList"
:currentCommentId="currentCommentId"
@commentHandle="commentHandle"
@commentSubmit="commentSubmit"
@cancelComment="cancelComment"
@commentLike="commentLike"
>
</MainComment>
</div>
</template>
<script>
import MainComment from '@/components/MianComponent/MainComment'
export default {
data() {
return {
commentList: [],
currentCommentId: '',
}
}
components: {
MainComment,
},
methods: {
// 给评论点赞
async commentLike(id, liked) {
let timestamp = new Date().getTime()
let params = {
id: this.articleId,
cid: id,
type: 2,
timestamp
}
// 判断是已经点赞了还是取消点赞
if (liked) {
params.t = 0
} else {
params.t = 1
}
try {
let res = await commentLike(params)
if (res.code === this.constants.code_status) {
// 获取歌单点赞的相关数据
this.getCommentList(this.articleId)
}
} catch (error) {
console.log(error);
}
},
// 取消评论
cancelComment() {
this.currentCommentId = ''
},
//评论提交
commentSubmit() {
//...
},
// 点击评论,打开评论框
commentHandle(id) {
// 将父组件id传递到子组件,用于判断是否是同一个账户
this.currentCommentId = id
},
}
}
</script>
写到最后
这只是本人在闲暇之余瞎写的一篇博文,如果有不好的地方请谅解,创作不易,转载需通过联系本人,请勿自动转载!!! 博客地址