目录
1. 评论区效果
还是话不多说,先上效果图
2. 代码实现
分父级(主评论)和子级(子评论)
自己回复的评论可以进行crud, 对于他人的评论仅仅只能评论
所以我们需要画出大致的布局,先上组件代码Index.vue代码,再上comment.vue
3. 封装组件代码index.vue
<template>
<view class="comment-eg">
<view class="comment-space"></view>
<hb-comment
ref="hbComment"
@share="share"
@fold="fold"
@add="add"
@del="del"
@like="like"
@focusOn="focusOn"
:deleteTip="'确认删除?'"
:cmData="commentData"
v-if="commentData"
></hb-comment>
</view>
</template>
<script>
import hbComment from "./hb-comment.vue";
import { getCommentList } from "../../apis/metaUniverse";
import {
addMessageData,
delMessageData,
likeMessageData,
} from "../../apis/index";
export default {
name: "comment-eg",
data() {
return {
scriptCode: "",
// nickName: '',
userId: "",
commentData: null,
reqFlag: false, // 请求标志,防止重复操作,true表示请求中
foldList: [], // 不需要折叠的id集合
// userInfo: {},
};
},
props: {
articleId: {
type: String,
default: () => {
return null;
},
},
},
watch: {
articleId: {
handler: function (newVal, oldVal) {
if (newVal) {
console.log("触发监听", newVal);
this.scriptCode = newVal;
this.foldList = [];
this.getComment(newVal);
}
},
immediate: true,
},
},
created() {
this.userId = uni.getStorageSync("userId") || "";
},
onShow() {
},
methods: {
// 登录校验
checkLogin() {
// TODO 此处填写登录校验逻辑
if (true) {
return true;
} else {
uni.showModal({
title: "提示",
content: "请先登录",
confirmText: "前往登录",
success: function (res) {
if (res.confirm) {
uni.redirectTo({
url: "/pages/login/login",
});
}
},
});
return false;
}
},
share() {
console.log("点击转发");
this.$emit("shareFriend");
},
// 输入框聚焦
focusOn() {
// this.checkLogin();
},
// 获取评论
getComment(articleId) {
// TODO 接入真实接口
let sData = {
scriptCode: this.scriptCode,
userId: this.userId,
};
getCommentList(sData).then((res) => {
let _this = this;
res.data.forEach((item) => {
item.isFold =
!_this.foldList.includes(item.id) && item.children.length > 1
? true
: false;
});
// console.log('评论结果', res.data)
_this.commentData = {
readNumer: res.data.length || 0,
commentSize: res.data.length || 0,
comment: res.data || [],
};
console.log("commentSize", _this.commentData.commentSize);
});
// 下边假装请求成功
},
fold(id) {
this.foldList.push(id);
console.log("需要展开的id合集", this.foldList);
const foldIndex = this.commentData.comment.findIndex(
(item) => item.id === id
);
console.log("展开评论222", foldIndex);
this.$set(this.commentData.comment[foldIndex], "isFold", false);
console.log("this.commentData", this.commentData.comment);
},
// 新增评论
add(req) {
req.userId = this.userId;
req.scriptCode = this.scriptCode;
console.log("新增评论接收到的结果", req);
if (this.reqFlag) {
return;
}
this.reqFlag = true;
addMessageData(req).then((res) => {
if (res.data) {
uni.showToast({
title: "评论成功",
icon: "none",
});
this.reqFlag = false;
this.$refs.hbComment.addComplete();
this.getComment();
} else {
this.reqFlag = false;
}
});
// TODO 接入真实接口
},
// 点赞评论
like(comment) {
if (this.reqFlag) {
return;
}
this.reqFlag = true;
console.log("点赞的参数", comment);
let sData = {
scriptCode: this.scriptCode,
commentId: comment.commentId,
status: comment.status,
};
let _this = this;
likeMessageData(sData).then((res) => {
if (res.data) {
if (comment.status) {
uni.showToast({
title: "点赞成功",
icon: "none",
});
}
_this.reqFlag = false;
_this.$refs.hbComment.likeComplete(comment.commentId);
} else {
_this.reqFlag = false;
}
});
// TODO 接入真实接口
},
// 删除评论
del(commentId) {
console.log("获取到的id", commentId);
if (this.reqFlag) {
return;
}
this.reqFlag = true;
let sData = {
userId: this.userId,
commentId: commentId,
};
let _this = this;
delMessageData(sData).then((res) => {
console.log("删除", res.data);
if (res.data) {
_this.reqFlag = false;
_this.$refs.hbComment.deleteComplete(commentId);
this.getComment();
} else {
_this.reqFlag = false;
}
});
// TODO 接入真实接口
},
// 列表按照父子转换成树
getTree(data) {
let result = [];
let map = {};
data.forEach((item) => {
map[item.id] = item;
});
data.forEach((item) => {
let parent = map[item.parentId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
result.push(item);
}
});
console.log("父子树", result);
return result;
},
},
components: {
hbComment,
},
};
</script>
<style>
.comment-space {
height: 12rpx;
background: #f8f8f8;
}
</style>
4. 组件代码
comment.vue
<template>
<view class="hb-comment">
<view class="comment-space"></view>
<!-- 评论主体-start -->
<view class="comment-list" v-if="commentData.comment.length != 0">
<view class="comment-num">
<view>评论</view>
</view>
<!-- 评论列表-start -->
<view
class="comment-box"
v-for="(item, index) in commentData.comment"
:key="item.id"
>
<view class="comment-box-item">
<view>
<image
:src="item.headImg || emptyAvatar"
mode="aspectFill"
class="avatar-parent"
></image>
</view>
<view
:class="[
'comment-main',
{ border_bottom: index + 1 !== commentData.commentSize },
]"
>
<!-- 父评论体-start -->
<view class="comment-main-top">
<view class="nick-name-box">
<view class="nick-name">{{ item.nickName }}</view>
</view>
</view>
<view class="comment-main-content">
{{ item.content }}
<view v-if="item.picUrl" @click="prviewImage(item.picUrl)">
<image class="img" :src="item.picUrl" mode="widthFix"></image>
</view>
</view>
<view class="comment-main-foot">
<!-- <view class="foot-time">{{item.createTime}}</view> -->
<view class="foot-time"
>{{ getTimeInfo(item.commentTime)
}}{{ `·${item.location}` }}</view
>
<view
class="foot-btn foot-btn_common p-l40"
@click="reply(item, 'reply')"
>回复</view
>
<view
class="foot-btn foot-btn_common p-l32"
v-if="item.isMyComment"
@click="confirmDelete(item.id)"
>删除</view
>
<view class="zan-box">
<span :class="item.isLike ? 'isLike' : 'notLike'">{{
item.liked > 0 ? item.liked : ""
}}</span>
<!-- <span :class="item.isLike ? 'isLike' : 'notLike'">{{item.liked == 0 ? '抢首赞' : item.liked}}</span> -->
<image
@click.stop="like(item.id, 1)"
v-if="!item.isLike"
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/A0gxSGwrgBvn890d208d4d79baec3a208abafd2096ee.png"
/>
<image
@click.stop="like(item.id, 0)"
v-else
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/LQjAtVKGVRuT47750ea0ce1e2cb081672d92f52508c9.png"
/>
</view>
</view>
<!-- 父评论体-end -->
<!-- 子评论列表-start -->
<view
:class="[
'comment-sub-box',
{ 'comment-sub-box_empty': !item.children.length },
]"
>
<view v-for="(each, index) in item.children" :key="each.id">
<view class="comment-sub-item" v-if="index < 1 && item.isFold">
<view>
<image
:src="each.headImg || emptyAvatar"
mode="aspectFill"
class="avatar"
>
</image>
</view>
<view class="comment-main">
<view class="sub-comment-main-top">
<view class="nick-name">
<view>{{ each.nickName }}</view>
<view v-if="each.pnickName"
><span>回复</span>{{ each.pnickName }}</view
>
</view>
</view>
<view class="comment-main-content">
{{ each.content }}
<view
v-if="each.picUrl"
@click="prviewImage(each.picUrl)"
>
<image
class="img"
:src="each.picUrl"
mode="widthFix"
></image>
</view>
</view>
<view class="comment-main-foot">
<view class="foot-time"
>{{ getTimeInfo(each.commentTime)
}}{{ `·${each.location}` }}</view
>
<view
class="foot-btn foot-btn_common p-l40"
@click="replyChild(each, item.id)"
>
回复</view
>
<view
class="foot-btn foot-btn_common p-l32"
v-if="each.isMyComment"
@click="confirmDelete(each.id)"
>
删除
</view>
<view class="zan-box">
<span :class="each.isLike ? 'isLike' : 'notLike'">{{
each.liked > 0 ? each.liked : ""
}}</span>
<!-- <span :class="each.isLike ? 'isLike' : 'notLike'">{{each.liked == 0 ? '抢首赞' : each.liked}}</span> -->
<image
@click="like(each.id, 1)"
v-if="!each.isLike"
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/A0gxSGwrgBvn890d208d4d79baec3a208abafd2096ee.png"
/>
<image
@click="like(each.id, 0)"
v-else
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/LQjAtVKGVRuT47750ea0ce1e2cb081672d92f52508c9.png"
/>
</view>
</view>
</view>
</view>
<view class="comment-sub-item" v-if="!item.isFold">
<view>
<image
:src="each.headImg || emptyAvatar"
mode="aspectFill"
class="avatar"
>
</image>
</view>
<view class="comment-main">
<view class="sub-comment-main-top">
<view class="nick-name">
<view>{{ each.nickName }}</view>
<view v-if="each.pnickName"
><span>回复</span>{{ each.pnickName }}</view
>
</view>
</view>
<view class="comment-main-content">
{{ each.content }}
<view
v-if="each.picUrl"
@click="prviewImage(each.picUrl)"
>
<image
class="img"
:src="each.picUrl"
mode="widthFix"
></image>
</view>
</view>
<view class="comment-main-foot">
<view class="foot-time"
>{{ getTimeInfo(each.commentTime)
}}{{ `·${each.location}` }}</view
>
<view
class="foot-btn foot-btn_common p-l40"
@click="replyChild(each, item.id)"
>
回复</view
>
<view
class="foot-btn foot-btn_common p-l32"
v-if="each.isMyComment"
@click="confirmDelete(each.id)"
>
删除
</view>
<view class="zan-box">
<span :class="each.isLike ? 'isLike' : 'notLike'">{{
each.liked > 0 ? each.liked : ""
}}</span>
<!-- <span :class="each.isLike ? 'isLike' : 'notLike'">{{each.liked == 0 ? '抢首赞' : each.liked}}</span> -->
<image
@click="like(each.id, 1)"
v-if="!each.isLike"
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/A0gxSGwrgBvn890d208d4d79baec3a208abafd2096ee.png"
/>
<image
@click="like(each.id, 0)"
v-else
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/LQjAtVKGVRuT47750ea0ce1e2cb081672d92f52508c9.png"
/>
</view>
</view>
</view>
</view>
</view>
<view
class="fold-more"
v-if="item.isFold"
@click="foldMsg(item.id)"
>展开更多回复<u-icon size="14" name="arrow-down"></u-icon
></view>
</view>
<!-- 子评论列表-end -->
</view>
</view>
</view>
<!-- 评论列表-end -->
</view>
<!-- 评论主体-end -->
<!-- 无评论-start -->
<view class="comment-none" v-else>
<view class="title">评论</view>
<image
mode="widthFix"
src="https://dby-front-resource.oss-cn-hangzhou.aliyuncs.com/static/images/duke/meta-empty.png"
class="top-bg"
></image>
<view class="tips">快来抢沙发吧</view>
<!-- 暂无评论,<span @click="commentInput" style="color: #007AFF;">抢沙发</span> -->
</view>
<!-- 无评论-end -->
<!-- 发表评论-start -->
<view class="comment-footer">
<view class="comment-left" @click="reply('self')">说说你的想法</view>
<view class="comment-right" @click="click_share">
<image
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/EehiPALe7txe47f84c7f6483b3a3bb455aba32927dee.png"
/>
<view class="label">转发</view>
</view>
</view>
<!-- 评论弹框 -->
<view class="comment-submit-box" v-if="submit" @click="closeInput">
<view
class="comment-add"
@click.stop.prevent="stopPrevent"
:style="'bottom:' + KeyboardHeight + 'px'"
>
<view class="comment-add_top">
<textarea
class="textarea"
v-model="sendConf.content"
:placeholder="placeholderTxt"
:adjust-position="false"
:show-confirm-bar="false"
@blur="blur"
@focus="focusOn"
@input="sumfontnum"
:focus="focus"
maxlength="1000"
auto-height
></textarea>
<view class="pic-box">
<view class="pic_box" v-if="sendConf.picUrl">
<image
class="del"
v-if="upLoad"
@click="delImg"
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/OvHnS9bQSAj5c959ff8678314f225057b163bfb36fe6.png"
mode="aspectFill"
></image>
<image
class="pic"
:src="sendConf.picUrl"
mode="aspectFill"
></image>
<view class="mask_progress" v-if="!upLoad">
<view class="progress">
<view class="label">{{ progress }}%</view>
<view class="long" :style="'width:' + progress + '%'"></view
></view>
</view>
</view>
</view>
</view>
<view
class="comment-submit flex"
:class="{ 'no-p': KeyboardHeight > 0 }"
>
<image
@click="chooseImg"
class="pic-icon"
src="https://duoke-card.oss-cn-hangzhou.aliyuncs.com/nEJ0FD9ERL53978e81ece9b7f00201e14789d8f10ba2.png"
mode="aspectFill"
></image>
<view class="flex">
<view class="num">{{ fontNum }}/1000</view>
<view class="send" :class="{ nosend: permissSend }" @click="addMsg"
>发送</view
>
</view>
</view>
</view>
</view>
<!-- 新增评论-end -->
</view>
</template>
<script>
import { getCommentImg } from "../../apis/metaUniverse";
import ossUpload from "@/utils/ossUpload";
export default {
name: "hb-comment",
props: {
cmData: {
type: Object,
default: () => {
return null;
},
},
deleteTip: {
type: String,
default: () => {
return "确认删除您的评论?";
},
},
scriptCode: {
type: String,
default: "",
},
},
watch: {
cmData: {
handler: function (newVal, oldVal) {
console.log("监听到改变", newVal);
this.init(newVal);
this.$forceUpdate();
},
immediate: true,
},
},
computed: {
permissSend() {
return (!this.sendConf.content.trim() && !this.sendConf.picUrl) ||
!this.upLoad
? true
: false;
},
fontNum() {
return this.sendConf?.content?.length || 0;
},
placeholderTxt() {
return this.sendConf.answerId === 0
? "说说你的想法"
: `回复${this.sendConf.nickName}`;
},
},
data() {
return {
emptyAvatar:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAABF5JREFUWEfFl11oHFUUx//nbmKwxdJ9qFL7YPEhRJC2gljjF60WG0WsoNkWfSnZ7Jy7FVPF0AoKpmBFqyIG3Jk7G1iIQXHbBz+KbbGtRfBbMe2LseKjiVDoiqIhwZ0jV3fb3cnM7kQCuU+7M+ec/+9+nHPPEJZ50DLrY1EApVJp9fz8/BYRuZ2INgDYWJvAWRE5R0RnZmZmPh4ZGZlPOrFEAMVi8e4gCPYSUZ+IXGGFgiCYIaJpKyQi1yql1orIFgAXARxRSvm5XO67diBtAYwxRQCDAE4RUUkpdWxwcNCKLBiu665TSj0kIpqIbgTgMzO3gmgJYIz5CMB9AIaZ+bXGQMVi8RoRuZeI/lZKHc1ms3/U34+Pj6+cnZ3dC+AggGPMfH8cRCyAMUZqThlmPlwPUCgU0qlUyq7Kww1BrbjHzPsahYwx/QDK9hkzR2pFPjTGnAWwIexkjOkRkRIR3Rozo5Miskdr/VMIxE7mHDPXD+2l1wsA6nseBMHmfD7/dSjQOwB2tTlYC/bddd1blFJfARhj5lyjfxNA7bSfitpz3/d3iYgFaDuUUjeHM8AY8zSAV5VS9+RyudP1IE0Axpj3Aaxk5m1hFc/zPiWiO9uq/2dwiJn3h22NMScB/MnMOxYAjI6Orurq6rpgU0hrXYpw/hFAd0KAD5n5wbCt7/t7ROT1ubm5NUNDQ7/b95dWwHXd7Uqp452dnVcPDAxciACwDlclAbAFynGcdRGr2EtEnwdB0JfP5080ARhjDhLRbY7jbI0SMcZUAKxOAgDgN2ZOx8SxGfEiMz/bBOD7/lgQBCu01o/GOH4PYFNCgElmvinK1vf9X2xxchzHVtfLW2CMOQpgipmHYwBeBtBUaOJgRKSgtX48Js63AH5l5geaADzPe1cpddFxnHyM42YAXyZYAXt+epn557iVFJHzWuudYYDnlFJ9juPcESfi+35JRHa3gdjPzIfibIwxNtPe0Fq/EAZ4hIhcZl4T5+y67nql1CcA1kfZENFnrSZQ6ycqItKvtT4SBthIRJMdHR092WzW5nzk8H1/WEReiQHY4TjOB3G+nuf9qyEim7TW9r65fAjL5fKVlUrlPICXmPnNFquwWym1oFBZ+yAItubz+TMtAJ4gon3pdLo7k8nMNgHYP7ZeE5EWkbuYeaYx0NjY2HXValUDeApAV4zIJIADzPxe+H2hULg+lUp9U6sBl3qLprugXC6nKpXKF0R02nGcZ2wQ3/e3ichOEckQ0aoEWQDbsgF4a3p6eqLeH3qeN0FE3el0ujeTyVTrcaKuY1uIjIg8CaCfiLYnEW1hY4WPi8gEgMeY+e1G27iGxHYxtptZynGYmTPhgK1asqWEiBRfcAjDdMaYpYCIFW8LUMuM54nIsb3/YvbDXskiYtuzA6382n4X1CDWAnCSgNSFa98ETakcWbwWMytjzAoAPUEQ3JBKpXrs75r/VLVanVJK/VC7Uf9KGjfRCiQN9n/slh3gHz9i4jC+FVL5AAAAAElFTkSuQmCC",
commentData: null,
// placeholder: "说说你的想法",
// pUser: null, // 标签-回复人
showTag: false, // 标签展示与否
focus: false, // 输入框自动聚焦
submit: false, // 弹出评论
KeyboardHeight: 0, // 键盘高度
replayShow: false, // 展示弹框
sendConf: {
// id: 1,
// nickName: '',
// fileList: [],
userId: null,
content: "",
picUrl: "",
answerId: 0, // 评论id
parentId: 0,
location: "", // 评论的地址所在地
},
specialArea: ["澳门", "香港"],
upLoad: true,
progress: 0,
};
},
onShow() {
},
mounted: function () {
uni.onKeyboardHeightChange((res) => {
this.KeyboardHeight = res.height;
});
},
methods: {
getImgUrl(){
getCommentImg().then(res => {
this.sendConf.picUrl = res.data;
})
},
// 设置进度条
setProgressData() {
const timer = setInterval(() => {
this.progress += 11;
if (this.progress >= 99) {
clearInterval(timer);
}
}, 5);
},
// 计算时间差
getTimeInfo(time) {
// commentTime
let new_date = new Date(); //新建一个日期对象,默认现在的时间
let old_date = new Date(time);
let difftime = new_date - old_date;
let days = parseInt(difftime / 1000 / 60 / 60 / 24); // 天 24*60*60*1000
let hours = parseInt(difftime / 1000 / 60 / 60); // 小时 60*60*1000
let minutes = parseInt(difftime / 1000 / 60); // 分钟 -(day*24) 以60秒为一整份 取余 剩下秒数 秒数/60 就是分钟数
if (hours >= 1 && hours < 24) {
return `${hours}个小时前`;
} else if (days >= 1) {
if (days > 7) {
return time.split(" ")[0];
} else {
return `${days}天前`;
}
} else {
return minutes < 1 ? "刚刚" : `${minutes}分钟前`;
}
},
sumfontnum(e) {
let str = e.detail.value.trim();
if (str.length >= 1000) {
this.sendConf.content = e.detail.value.substring(0, 1000);
} else {
this.sendConf.content = e.detail.value;
}
},
delImg() {
this.sendConf.picUrl = "";
this.progress = 0;
this.upLoad = true;
},
// 选择图片
chooseImg() {
let _this = this;
uni.getSetting({
success(res) {
if (res.authSetting["scope.writePhotosAlbum"]) {
_this.chooseImage();
} else {
uni.authorize({
scope: "scope.writePhotosAlbum",
success() {
_this.chooseImage();
},
fail(res) {
uni.showToast({
title: "请点击右上角“…”打开相册权限设置",
icon: "none",
duration: 2000,
});
},
});
}
},
});
},
chooseImage() {
uni.chooseImage({
count: 1,
sizeType: ["compressed"],
success: async (res) => {
let imageSize = res.tempFiles[0].size;
// 限制图片大小不超过2兆
if (imageSize > 2097152) {
uni.showToast({
title: "图片超过2M!",
icon: "none",
});
return;
}
this.upLoad = false;
this.setProgressData();
this.sendConf.picUrl = res.tempFiles[0].path;
await ossUpload(res.tempFiles[0].path).then((imageResult) => {
this.progress = 100;
this.sendConf.picUrl = imageResult.url;
this.upLoad = true;
uni.showToast({
title: "上传成功",
icon: "none",
});
this.progress = 0;
});
// console.log('选择的图片', this.sendConf.picUrl);
},
fail: () => {
this.upLoad = true;
this.progress = 0;
// uni.showToast({
// title: "未",
// icon: "none",
// });
},
});
},
sendData() {
// 调用接口
},
closePopup() {
this.replayShow = false;
this.resetSendData();
},
// 重置输入框的值
resetSendData() {
let keyNameList = ["content", "picUrl"];
keyNameList.forEach((item) => {
this.sendConf[item] = "";
});
this.sendConf.answerId = 0;
this.sendConf.parentId = 0;
this.progress = 0;
this.upload = true;
},
click_share() {
this.$emit("share");
},
foldMsg(id) {
this.$emit("fold", id);
},
// 初始化评论
init(cmData) {
this.commentData = cmData;
},
// 没用的方法,但不要删
stopPrevent() {},
replyChild(e, parentId) {
console.log("回复子评论");
this.sendConf.nickName = e.nickName;
this.sendConf.answerId = e.id;
this.sendConf.parentId = parentId;
this.showTag = true;
this.commentInput();
},
// 预览图片
prviewImage(imgUrl) {
// console.log('预览图片', imgUrl);
uni.previewImage({
current: 0,
urls: [imgUrl],
});
},
// 回复评论
reply(e, type) {
this.getImgUrl();
// let _this = this;
switch (type) {
case "self":
// this.sendConf.nickName = e.nickName;
this.sendConf.parentId = 0;
break;
case "reply":
this.sendConf.nickName = e.nickName;
this.sendConf.answerId = e.id;
this.sendConf.parentId = e.id;
break;
}
this.showTag = true;
this.commentInput();
},
// 删除评论前确认
confirmDelete(commentId) {
let that = this;
uni.showModal({
title: "提示",
content: "确认删除您的评论?",
confirmText: "确认",
success: function (res) {
if (res.confirm) {
that.$emit("del", commentId);
}
},
});
},
// 新增评论
addMsg() {
if (this.permissSend) {
return;
}
this.$emit("add", this.sendConf);
},
// 点赞评论
like(commentId, status) {
let sData = {
commentId: commentId,
status: status,
};
this.$emit("like", sData);
},
// 新增完成
addComplete() {
// this.sendConf.content = null;
this.tagClose();
this.closeInput();
},
// 点赞完成-本地修改点赞结果
likeComplete(commentId) {
for (var i in this.commentData.comment) {
if (this.commentData.comment[i].id == commentId) {
this.commentData.comment[i].isLike
? this.commentData.comment[i].liked--
: this.commentData.comment[i].liked++;
this.commentData.comment[i].isLike =
!this.commentData.comment[i].isLike;
return;
}
for (var j in this.commentData.comment[i].children) {
if (this.commentData.comment[i].children[j].id == commentId) {
this.commentData.comment[i].children[j].isLike
? this.commentData.comment[i].children[j].liked--
: this.commentData.comment[i].children[j].liked++;
this.commentData.comment[i].children[j].isLike =
!this.commentData.comment[i].children[j].isLike;
return;
}
}
}
},
// 删除完成-本地删除评论
deleteComplete(commentId) {
for (let i in this.commentData.comment) {
for (let j in this.commentData.comment[i].children) {
if (this.commentData.comment[i].children[j].id == commentId) {
this.commentData.comment[i].children.splice(Number(j), 1);
return;
}
}
if (this.commentData.comment[i].id == commentId) {
this.commentData.comment.splice(Number(i), 1);
return;
}
}
},
// 输入框失去焦点
blur() {
this.focus = false;
},
// 输入框聚焦
focusOn() {
this.$emit("focusOn");
},
// 标签关闭
tagClose() {
this.showTag = false;
// this.pUser = null;
// this.commentReq.pId = null;
},
// 输入评论
commentInput() {
// TODO 调起键盘方法
this.submit = true;
setTimeout(() => {
this.focus = true;
}, 50);
},
// 关闭输入评论
closeInput() {
console.log("关闭评论");
this.focus = false;
this.submit = false;
this.resetSendData();
},
},
};
</script>
<style lang="scss" scoped>
.p-l32 {
padding-left: 32rpx;
}
.p-l40 {
padding-left: 40rpx;
}
.flex {
display: flex;
align-items: center;
justify-content: space-between;
}
.fold-more {
display: flex;
font-size: 24rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #666666;
line-height: 32rpx;
padding-bottom: 32rpx;
}
.hb-comment {
// padding: 10rpx;
// margin-top: 12rpx;
padding: 32rpx 0 32rpx 32rpx;
background: #fff;
padding-bottom: calc(
64rpx + constant(safe-area-inset-bottom)
); /** ios < 11.2 */
padding-bottom: calc(64rpx + env(safe-area-inset-bottom)); /** ios >= 11.2 */
}
.top-read {
font-size: 28rpx;
padding-left: 10rpx;
color: #999999;
}
.seg_line_box {
display: flex;
height: 5rpx;
justify-content: space-between;
margin: 5rpx 0;
}
.seg_line {
width: 45%;
border-bottom: 1rpx solid #e1e1e1;
}
.seg_dot {
width: 8%;
border-bottom: 5rpx dotted #dbdbdb;
}
.comment-num {
display: flex;
justify-content: space-between;
align-items: center;
// padding: 20rpx 0;
height: 44rpx;
font-size: 32rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 44rpx;
}
.comment-box {
// padding: 10rpx 0;
width: 714rpx;
&:nth-last-child(1) {
padding-bottom: 200rpx;
}
}
.comment-box-item {
display: flex;
margin-top: 32rpx;
}
.comment-main {
// padding-left: 20rpx;
display: inline-block;
white-space: pre-wrap;
word-break: break-word;
margin-left: 12rpx;
width: 638rpx;
}
.border_bottom {
position: relative;
}
.border_bottom::after {
position: absolute;
content: "";
width: 100%;
left: 0;
bottom: 0;
height: 2rpx;
// padding: 0 28rpx;
box-sizing: border-box;
background-color: #e9e9e9;
// background-color: red;
-webkit-transform: scale(1, 0.5);
transform: scale(1, 0.5);
-webkit-transform-origin: center bottom;
transform-origin: center bottom;
}
.comment-main-top {
width: 600rpx;
// padding-top: 6rpx;
padding-top: 2rpx;
display: flex;
justify-content: space-between;
}
.sub-comment-main-top {
// width: 510rpx;
// padding-top: 6rpx;
display: flex;
justify-content: space-between;
height: 34rpx;
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 34rpx;
}
.avatar {
width: 36rpx;
height: 36rpx;
border-radius: 50%;
}
.avatar-parent {
width: 68rpx;
height: 68rpx;
border-radius: 50%;
}
.nick-name-box {
display: flex;
align-items: center;
}
.comLogo {
margin-right: 18rpx;
font-size: 22rpx;
border-radius: 10rpx;
padding: 5rpx 15rpx;
color: #ffffff;
}
.com1 {
background-color: #d218b1;
}
.com2 {
background-color: #f19c0b;
}
.com3 {
background-color: #c8da85;
}
.com4 {
background-color: #bfd0da;
}
.nick-name {
// color: #2d8cf0;
display: flex;
height: 34rpx;
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 34rpx;
span {
margin: 0 12rpx;
}
}
.isLike {
font-size: 28rpx;
padding-right: 10rpx;
color: #476dff;
}
.notLike {
font-size: 28rpx;
padding-right: 10rpx;
color: #999999;
}
.comment-main-content {
// padding: 10rpx 10rpx 10rpx 0;
display: inline-block;
white-space: pre-wrap;
word-break: break-word;
padding: 12rpx 32rpx 12rpx 0rpx;
font-size: 28rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
line-height: 40rpx;
.img {
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
margin-top: 12rpx;
}
}
.comment-main-foot {
display: flex;
// font-size: 22rpx;
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 32rpx;
padding-bottom: 24rpx;
}
.replayTag {
color: #909399;
margin-bottom: 5px;
border: 1px solid #c8c9cc;
background-color: #f4f4f5;
border-radius: 3px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 16rpx;
padding: 5px 10px;
}
.replyTagClose {
font-size: 20px;
line-height: 12px;
padding: 0 0 2px 5px;
}
.foot-btn {
// padding-left: 10rpx;
color: #007aff;
}
.foot-btn_common {
height: 32rpx;
font-size: 24rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #666666;
line-height: 32rpx;
}
.zan-box {
display: flex;
align-items: center;
// margin-right: 32rpx;
margin-left: auto;
image {
width: 28rpx;
height: 28rpx;
padding-right: 32rpx;
}
}
.comment-sub-box {
padding: 8rpx 0;
}
.comment-sub-box_empty {
padding: 0;
padding-bottom: 8rpx;
}
.comment-sub-item {
display: flex;
}
.comment-none {
// padding: 0 20rpx calc(100rpx + env(safe-area-inset-bottom));
padding-bottom: calc(100rpx + env(safe-area-inset-bottom));
width: 100%;
text-align: center;
color: #999999;
.title {
// padding: 20rpx 0;
height: 44rpx;
text-align: left;
font-size: 32rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 44rpx;
}
.tips {
font-size: 32rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #999999;
line-height: 44rpx;
}
}
.comment-footer {
display: flex;
justify-content: space-between;
box-shadow: 0px -2rpx 0 0 rgba(0, 0, 0, 0.05);
background: #fff;
padding: 12rpx 32rpx 12rpx 24rpx;
width: 750rpx;
box-sizing: border-box;
position: fixed;
bottom: 0;
left: 0;
padding-bottom: calc(
12rpx + constant(safe-area-inset-bottom)
); /** ios < 11.2 */
padding-bottom: calc(12rpx + env(safe-area-inset-bottom));
.comment-left {
box-sizing: border-box;
// width: 622rpx;
flex: 1;
background: #f7f7f7;
height: 80rpx;
line-height: 80rpx;
border-radius: 44rpx;
padding-left: 40rpx;
color: #9999;
font-size: 28rpx;
}
.comment-right {
padding-top: 6rpx;
padding-left: 32rpx;
image {
width: 40rpx;
height: 40rpx;
}
.label {
font-size: 20rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #666666;
line-height: 20rpx;
}
}
}
.comment-submit-box {
position: fixed;
display: flex;
align-items: flex-end;
z-index: 9900;
left: 0;
top: var(--window-top);
bottom: 0;
background-color: rgba($color: #000000, $alpha: 0.5);
width: 100%;
}
.comment-add {
position: fixed;
width: 100%;
box-sizing: border-box;
// padding: 5rpx;
// border: 1px solid #ddd;
// transition: .3s;
// -webkit-transition: .3s;
// padding: 24rpx 32rpx 32rpx 40rpx;
padding-top: 24rpx;
border-radius: 24rpx 24rpx 0 0;
background: #fff;
.comment-add_top {
overflow-y: scroll;
height: 300rpx;
}
textarea {
height: 184rpx;
width: 100%;
padding: 0 40rpx;
box-sizing: border-box;
// padding-right: 8rpx;
}
.pic_box {
position: relative;
width: 224rpx;
margin-top: 54rpx;
padding-left: 40rpx;
.pic {
width: 224rpx;
height: 224rpx;
border-radius: 8rpx;
}
.del {
z-index: 10;
width: 36rpx;
height: 36rpx;
border-radius: 50%;
position: absolute;
top: -18rpx;
right: -18rpx;
}
}
.mask_progress {
width: 224rpx;
height: 224rpx !important;
padding: 108rpx 21rpx;
border-radius: 8rpx;
box-sizing: border-box;
background: #333333;
position: absolute;
right: 0;
bottom: 0;
z-index: 2;
opacity: 0.8;
.progress {
width: 100%;
height: 100%;
border-radius: 1rpx;
.label {
margin-bottom: 2rpx;
text-align: center;
color: #fff;
font-size: 16rpx;
}
.long {
height: 3rpx;
background: white;
}
}
}
}
.btn-click {
color: #007aff;
font-size: 28rpx;
padding: 10rpx;
}
.cancel {
color: #606266;
}
// .textarea {
// height: 100px;
// padding: 16rpx;
// width: 100%;
// }
.comment-submit {
// padding: 5rpx 20rpx 0 20rpx;
border-bottom: 1rpx dashed #ddd;
// width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
// padding: 16rpx 32rpx 16rpx 40rpx;
padding: 16rpx 32rpx calc(16rpx + env(safe-area-inset-bottom)) 40rpx;
box-shadow: 0 -2rpx 0rpx 0rpx rgba(0, 0, 0, 0.05);
.num {
height: 24rpx;
font-size: 24rpx;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #b3b3b3;
line-height: 24rpx;
}
.send {
width: 128rpx;
height: 64rpx;
background: #476dff;
border-radius: 32rpx;
margin-left: 24rpx;
text-align: center;
font-size: 32rpx;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 64rpx;
}
.nosend {
opacity: 0.5;
}
.pic-icon {
width: 46rpx;
height: 42rpx;
margin: 5rpx 2rpx;
}
}
.no-p {
padding-bottom: 16rpx;
}
</style>
5.组件的使用
<comment-eg v-if="!(!detailCollect.content && isNetWork)" :articleId="tempCode" @shareFriend="shareOthers"></comment-eg>
// articleId是文章详情,通过id去调用接口获取评论区详情数据, shareothers是唤起微信的分享操作
无非就是写个评论区页面,将需要的参数通过props传入,将需要抛出的事件通过emit抛出
6. 评论区数据格式
answerContent: null
answerId: "0"
answerPicUrl: null
answerUserId: "0"
children: [{id: "484", userId: "405298514273243136", answerUserId: "417213072755658752",…}]
0: {id: "484", userId: "405298514273243136", answerUserId: "417213072755658752",…}
answerContent: ""
answerId: "468"
answerPicUrl: "https://images.dby.cn/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20230217095836%202023-10-20%2011_15_06.png"
answerUserId: "417213072755658752"
children: null
commentTime: "2024-03-04 17:39:52"
content: "评论组件"
headImg: "https://duoke-card.oss-cn-hangzhou.aliyuncs.com/tmp_5ed263ea895cb9b6510b08375e350bdb0e2d7e915c739fc3.jpeg"
id: "484"
ipAddress: "124.90.42.134"
isLike: false
isMyComment: true
liked: 0
location: "浙江"
nickName: "Mortimer"
parentId: "468"
pheadImg: "https://duoke-card.oss-cn-hangzhou.aliyuncs.com/tmp_7621a56009b34c01cc3c46feadcfbb8e.jpeg"
picUrl: null
pnickName: "你猜我猜你猜猜"
scriptCode: "HJ1689172726976020481"
status: false
userId: "405298514273243136"
commentTime: "2023-10-20 11:34:29"
content: ""
headImg: "https://duoke-card.oss-cn-hangzhou.aliyuncs.com/tmp_7621a56009b34c01cc3c46feadcfbb8e.jpeg"
id: "468"
ipAddress: "124.90.42.134"
isLike: false
isMyComment: false
liked: 0
location: "浙江"
nickName: "你猜我猜你猜猜"
parentId: "0"
pheadImg: null
picUrl: "https://images.dby.cn/%E5%BE%AE%E4%BF%A1%E5%9B%BE%E7%89%87_20230217095836%202023-10-20%2011_15_06.png"
pnickName: null
scriptCode: "HJ1689172726976020481"
status: false
userId: "417213072755658752"
有问题可以一起沟通哦~