uniapp实现一个评论区

目录

1.  评论区效果

2. 代码实现

3. 封装组件代码index.vue

4. 组件代码

5.组件的使用

6. 评论区数据格式


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"

有问题可以一起沟通哦~

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值