绿满眶商城微信小程序-uniapp
xiangpei
2025-05-29 474781abbec22374faf768856f4c04e9a3a868cd
视频评论的加载显示完成
2个文件已修改
240 ■■■■ 已修改文件
App.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabbar/index/home.vue 234 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
App.vue
@@ -255,9 +255,9 @@
    @font-face {
      font-family: 'iconfont';  /* Project id 4921691 */
      src: 
           url('//at.alicdn.com/t/c/font_4921691_1hkd3qibo87.woff2?t=1748343163548') format('woff2'),
           url('//at.alicdn.com/t/c/font_4921691_1hkd3qibo87.woff?t=1748343163548') format('woff'),
           url('//at.alicdn.com/t/c/font_4921691_1hkd3qibo87.ttf?t=1748343163548') format('truetype');
           url('//at.alicdn.com/t/c/font_4921691_egp12flgzc7.woff2?t=1748508836450') format('woff2'),
           url('//at.alicdn.com/t/c/font_4921691_egp12flgzc7.woff?t=1748508836450') format('woff'),
           url('//at.alicdn.com/t/c/font_4921691_egp12flgzc7.ttf?t=1748508836450') format('truetype');
    }
    .iconfont {
          /* font-family需要和自定义的相同 */
pages/tabbar/index/home.vue
@@ -97,7 +97,11 @@
    <uni-popup ref="commentPopup" type="bottom" :is-mask-click="true" @maskClick="closeCommentPopup">
      <view class="comment-popup">
        <view class="popup-header">
          <text class="popup-title">评论({{commentsTotal}})</text>
          <text class="popup-title" v-if="!commentForm.replyId">评论({{commentsTotal}})</text>
          <view class="reply-title" v-else>
            <text>回复 @{{commentForm.replyUserNickname}}</text>
            <text class="cancel-reply" @click="cancelReply">取消</text>
          </view>
          <text class="iconfont close-icon" @click="closeCommentPopup">&#xe675;</text>
        </view>
        
@@ -110,24 +114,57 @@
            暂无评论,快来发表第一条评论吧~
          </view>
          
          <view v-else class="comment-item" v-for="comment in comments" :key="comment.id">
            <image class="avatar" :src="comment.userAvatar || '/static/default-avatar.png'"></image>
          <view v-else class="comment-item" v-for="(comment, index) in comments" :key="comment.id">
            <view style="display: flex;">
                <image class="comment-avatar" :src="comment.userAvatar || '/static/default-avatar.png'"></image>
            <view class="comment-content">
              <text class="nickname">{{comment.userNickname}}</text>
              <text class="content">{{comment.commentContent}}</text>
                  <view style="position: relative;">
              <text class="time">{{formatTime(comment.createTime)}}</text>
                    <text @click="openReply(comment)" class="reply-btu time">回复</text>
                    <text class="thumbs-up time iconfont">&#xe60b;</text>
                  </view>
                </view>
            </view>
            <!-- 回复列表 -->
              <view class="reply-list" v-if="comment.replies && comment.replies.length > 0">
                <view class="reply-item" v-for="reply in comment.replies" :key="reply.id">
                  <view class="reply-content">
                    <view style="display: flex;">
                        <text class="nickname">{{reply.userNickname}}</text>
                        <text v-if="reply.replyUserId && reply.masterCommentId !== reply.replyId" class="reply-to"><text style="margin-right: 10rpx;font-size: 28rpx;" class="iconfont">&#xe666;</text>{{reply.replyUserNickname}}</text>
                    </view>
                    <text class="content">{{reply.commentContent}}</text>
                    <view class="reply-footer">
                      <text class="time">{{formatTime(reply.createTime)}}</text>
                      <text @click="openReply(comment, reply)" class="reply-btu time">回复</text>
                      <text class="thumbs-up time iconfont">&#xe60b; </text>
                    </view>
                  </view>
                </view>
              </view>
              <view class="view-more-replies" v-if="comment.replyTotalCount > 0 && !comment.expandReply" @click="loadRepliesPage(comment, index)">
                展开{{comment.replyTotalCount}}条回复 ↓
              </view>
              <view class="reply-op" v-if="comment.replyTotalCount > replyCommentQuery.pageNumber * replyCommentQuery.pageSize && comment.expandReply">
                  <view @click="loadNextPageReply(index)" class="reply-op-item">展开更多<text class="iconfont textSideIcon">&#xeb8d;</text></view>
                  <view @click="retractReplyComment(index)" class="reply-op-item" style="margin-left: 50rpx;">收起<text class="iconfont textSideIcon">&#xeb9b;</text></view>
              </view>
              <view class="reply-op" v-else-if="comment.replyTotalCount <= replyCommentQuery.pageNumber * replyCommentQuery.pageSize && comment.expandReply">
                  <view @click="retractReplyComment(index)" class="reply-op-item">收起<text class="iconfont textSideIcon">&#xeb9b;</text></view>
            </view>
          </view>
        </scroll-view>
        <view class="comment-input-area">
          <input 
            ref="commentInput"
            class="comment-input" 
            v-model="commentForm.commentContent" 
            placeholder="写下你的评论..."
            :placeholder="commentForm.replyId ? `回复 @${commentForm.replyUserNickname}` : '写下你的评论...'"
            placeholder-class="placeholder"
          />
          <button class="submit-btn" @click="submitComment">发送</button>
          <button class="submit-btn" @click="submitComment" :disabled="!commentForm.commentContent.trim()">发送</button>
        </view>
      </view>
    </uni-popup>
@@ -150,11 +187,20 @@
            videoId: '',
            masterCommentId: ''
        },
        replyCommentQuery: {
            pageNumber: 1,
            pageSize: 5,
            videoId: '',
            masterCommentId: ''
        },
        commentForm: { // 评论表单数据
            id: null,
            videoId: null,
            id: '',
            videoId: '',
            commentContent: '',
            replyId: null
            replyId: '',
            replyUserId: '',
            replyUserNickname: '',
            masterCommentId: ''
        },
        comments: [],            // 评论列表
        commentsTotal: 0,            // 评论总条数
@@ -201,6 +247,63 @@
    this.initVideoContexts();
  },
  methods: {
        // 加载下一页回复
        loadNextPageReply(index) {
            this.replyCommentQuery.pageNumber++;
            getVideoComments(this.replyCommentQuery).then(res => {
                this.comments[index].replies = [
                  ...this.comments[index].replies,
                  ...res.data.data.filter(
                    (newItem) => !this.comments[index].replies.some((oldItem) => oldItem.id === newItem.id)
                  ),
                ];
            })
        },
        // 收起回复
        retractReplyComment(index) {
            this.comments[index].expandReply = false;
            this.comments[index].replies = [];
        },
        // 加载回复
        loadRepliesPage(comment, index) {
            this.replyCommentQuery.pageNumber = 1;
            this.replyCommentQuery.masterCommentId = comment.id
            getVideoComments(this.replyCommentQuery).then(res => {
                this.comments[index].replies = res.data.data;
                this.comments[index].expandReply = true;
            })
        },
        resetCommentForm() {
            const videoId = this.commentForm.videoId;
            this.commentForm = { // 评论表单数据
                id: '',
                videoId: videoId,
                commentContent: '',
                replyId: '',
                replyUserId: '',
                replyUserNickname: '',
                masterCommentId: ''
            }
        },
        // 取消回复
        cancelReply() {
            this.resetCommentForm()
          },
        // 打开回复框
        openReply(comment, reply = null) {
          if(reply) {
            comment = reply
          }
          this.commentForm.masterCommentId = comment.masterCommentId ? comment.masterCommentId : comment.id;
          this.commentForm.replyId = comment.id;
          this.commentForm.replyUserId = comment.userId;
          this.commentForm.replyUserNickname = comment.userNickname;
          // 自动聚焦输入框
          this.$nextTick(() => {
            const input = this.$refs.commentInput;
            if (input) input.focus();
          });
        },
        // 格式化时间
        formatTime(time) {
          const date = new Date(time);
@@ -225,13 +328,20 @@
          // 发表评论
          addVideoComment(this.commentForm).then(res => {
              if(res.data.code === 200) {
                  this.commentForm = {
                                id: null,
                                videoId: null,
                                commentContent: '',
                                replyId: null
                  this.resetCommentForm()
                  // 如果是评论别人的回复,那么就将这个发布到replies里面
                  if(res.data.data.replyId) {
                      for (const [index, item] of this.comments.entries()) {
                        if (item.id === res.data.data.replyId) {
                          item.replies.unshift(res.data.data);
                          // this.loadRepliesPage(item, index)
                          break; // 跳出循环
                  }
                      }
                  } else {
                  this.comments.unshift(res.data.data);
                  }
                  console.log("新增后",this.comments);
                  uni.showToast({
                    title: '评论成功'
@@ -257,12 +367,7 @@
          this.$refs.commentPopup.close()
          this.showCommentPopup = false;
          this.comments = [];
          this.commentForm = {
              id: null,
              videoId: null,
              commentContent: '',
              replyId: null
          }
          this.resetCommentForm()
          this.commentQuery.pageNumber = 1;
          this.commentNoMore = false;
        },
@@ -295,6 +400,7 @@
          this.$refs.commentPopup.open();
          this.commentLoading = true;
          this.commentQuery.videoId = item.id
          this.replyCommentQuery.videoId = item.id
          // 首次加载评论分页大小增加一倍,以产生滚动条,后续可触发
          this.commentQuery.pageSize *= 2;
          getVideoComments(this.commentQuery).then(res => {
@@ -642,7 +748,7 @@
    }
    
    .original-price {
      font-size: 24rpx;
      font-size: 28rpx;
      color: #999;
      text-decoration: line-through;
    }
@@ -655,7 +761,7 @@
    .buy-button {
      background: linear-gradient(to right, #ff5a5f, #ff2e4d);
      color: white;
      padding: 10rpx 24rpx;
      padding: 10rpx 28rpx;
      border-radius: 20rpx;
      font-size: 26rpx;
      font-weight: bold;
@@ -697,14 +803,15 @@
    .comment-item {
      display: flex;
      flex-direction: column;
      padding: 10rpx 0;
    }
    .avatar {
      width: 80rpx;
      height: 80rpx;
    .comment-avatar {
      width: 70rpx;
      height: 70rpx;
      border-radius: 50%;
      margin-right: 20rpx;
      margin-right: 10rpx;
    }
    .comment-content {
@@ -712,21 +819,21 @@
    }
    .nickname {
      font-size: 24rpx;
      font-size: 28rpx;
      color: #666;
      display: block;
      margin-bottom: 10rpx;
    }
    .content {
      font-size: 24rpx;
      font-size: 28rpx;
      color: #333;
      display: block;
      margin-bottom: 10rpx;
    }
    .time {
      font-size: 24rpx;
      font-size: 28rpx;
      color: #999;
    }
@@ -766,4 +873,73 @@
      text-align: center;
      color: #999;
    }
    .reply-list {
      margin-top: 20rpx;
      padding-left: 80rpx;
    }
    .reply-op {
        margin-top: 10rpx;
        padding-left: 80rpx;
        display: flex;
        font-size: 28rpx;
        color: #333;
    }
    .reply-op-item {
        display: flex;
        align-items: center;
    }
    .reply-item {
      display: flex;
      margin-bottom: 20rpx;
    }
    .reply-content {
      flex: 1;
    }
    .reply-to {
      color: #576b95;
      margin: 0 10rpx;
      font-size: 28rpx;
    }
    .reply-title {
      display: flex;
      align-items: center;
      font-size: 28rpx;
      color: #333;
    }
    .cancel-reply {
      margin-left: 20rpx;
      color: #576b95;
      font-size: 28rpx;
      padding: 6rpx 12rpx;
      background: #f5f5f5;
      border-radius: 20rpx;
    }
    .view-more-replies {
      color: #576b95;
      font-size: 28rpx;
      padding: 10rpx 0;
      padding-left: 80rpx;
    }
    .comment-footer, .reply-footer {
      display: flex;
      align-items: center;
      font-size: 28rpx;
      color: #999;
    }
    .reply-btu {
        margin-left: 30rpx;
    }
    .thumbs-up {
        position: absolute;
        right: 80rpx;
        font-size: 32rpx;
    }
    .textSideIcon {
        font-size: 36rpx;
        margin-left: 5rpx;
    }
</style>