绿满眶商城微信小程序-uniapp
2个文件已修改
305 ■■■■ 已修改文件
App.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/tabbar/index/home.vue 299 ●●●● 补丁 | 查看 | 原始文档 | 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_s75bca4srg.woff2?t=1748511781888') format('woff2'),
           url('//at.alicdn.com/t/c/font_4921691_s75bca4srg.woff?t=1748511781888') format('woff'),
           url('//at.alicdn.com/t/c/font_4921691_s75bca4srg.ttf?t=1748511781888') format('truetype');
    }
    .iconfont {
          /* font-family需要和自定义的相同 */
pages/tabbar/index/home.vue
@@ -13,7 +13,7 @@
        <view 
          class="play-icon" 
          @click="togglePlay(index)"
          v-if="currentVideoIsPlaying != null && !currentVideoIsPlaying"
          v-if="!currentVideoIsPlaying"
        >
          <image src="/static/video/play.png" style="width: 45px;height: 45px" mode="aspectFit"></image>
        </view>
@@ -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,25 +114,59 @@
            暂无评论,快来发表第一条评论吧~
          </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 class="comment-content">
              <text class="nickname">{{comment.userNickname}}</text>
              <text class="content">{{comment.commentContent}}</text>
              <text class="time">{{formatTime(comment.createTime)}}</text>
            </view>
          <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;">
                        <image class="comment-reply-avatar" :src="reply.replyUserAvatar || '/static/default-avatar.png'"></image>
                        <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)">
                <text class="line">——</text>展开{{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="line">——</text>展开更多<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="line">——</text>收起<text class="iconfont textSideIcon">&#xeb9b;</text></view>
              </view>
          </view>
        </scroll-view>
        <view class="comment-input-area">
          <input
            class="comment-input"
            v-model="commentForm.commentContent"
            placeholder="写下你的评论..."
            placeholder-class="placeholder"
          />
          <button class="submit-btn" @click="submitComment">发送</button>
        </view>
          <input
            ref="commentInput"
            class="comment-input"
            v-model="commentForm.commentContent"
            :placeholder="commentForm.replyId ? `回复 @${commentForm.replyUserNickname}` : '写下你的评论...'"
            placeholder-class="placeholder"
          />
          <button class="submit-btn" @click="submitComment" :disabled="!commentForm.commentContent.trim()">发送</button>
        </view>
      </view>
    </uni-popup>
    
@@ -150,11 +188,21 @@
            videoId: '',
            masterCommentId: ''
        },
        replyCommentQuery: {
            pageNumber: 1,
            pageSize: 5,
            videoId: '',
            masterCommentId: ''
        },
        commentForm: { // 评论表单数据
            id: null,
            videoId: null,
            id: '',
            videoId: '',
            commentContent: '',
            replyId: null
            replyId: '',
            replyUserId: '',
            replyUserNickname: '',
            replyUserAvatar: '',
            masterCommentId: ''
        },
        comments: [],            // 评论列表
        commentsTotal: 0,            // 评论总条数
@@ -169,7 +217,7 @@
            playAt: 0 ,// 这个视频播放到哪了
            startPlayTime: 0 // 这个视频从什么时候开始播放的
        },
        currentVideoIsPlaying: null, // 当前视频是否正在播放
        currentVideoIsPlaying: true, // 当前视频是否正在播放
        isFullScreen: false,
        windowHeight: 0,
        currentIndex: 0, // 当前播放的视频索引
@@ -183,6 +231,7 @@
    }
  },
  onShow() {
      this.loadVideos()
      // 如果视频按下暂停后切换页面再回到页面时,只算暂停时间(因为暂停时间和离开页面时间是重复的,只算一个)
      if(this.startHidenTime !== 0 && this.currentVideoIsPlaying) {
          const duration = Date.now() - this.startHidenTime
@@ -200,6 +249,65 @@
    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: '',
                commentContent: '',
                replyId: '',
                replyUserId: '',
                replyUserNickname: '',
                replyUserAvatar: '',
                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.commentForm.replyUserAvatar = comment.userAvatar;
          // 自动聚焦输入框
          this.$nextTick(() => {
            const input = this.$refs.commentInput;
            if (input) input.focus();
          });
        },
        // 格式化时间
        formatTime(time) {
          const date = new Date(time);
@@ -224,13 +332,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);
                  }
                  this.comments.unshift(res.data.data);
                  console.log("新增后",this.comments);
                  uni.showToast({
                    title: '评论成功'
@@ -256,12 +371,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;
        },
@@ -294,6 +404,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 => {
@@ -373,14 +484,13 @@
        // 保存上一个视频的播放记录
        this.savePlayRecord()
        const oldIndex = this.currentIndex;
        console.log("视频上下文",this.videoContexts[oldIndex]);
        this.currentIndex = e.detail.current;
        // 暂停上一个视频
        if (this.videoContexts[oldIndex]) {
            this.videoContexts[oldIndex].pause();
        }
        this.currentVideoIsPlaying = true;
        this.startPauseTime = 0;
        // 播放当前视频
        if (this.videoContexts[this.currentIndex]) {
@@ -420,7 +530,13 @@
    },
    // 视频播放事件
    onPlay(id, index) {
        this.currentVideoIsPlaying = true;
        console.log(id, index, "触发播放");
        if(index === this.currentIndex) {
            this.currentVideoIsPlaying = true;
        } else {
            this.currentVideoIsPlaying = false;
            return
        }
        this.playRecord.videoId = id;
        // 没初始化才赋值,因为一个视频重复播放onPlay会重复触发
        if(this.playRecord.startPlayTime === 0) {
@@ -434,7 +550,13 @@
    
    // 视频暂停事件
    onPause(index) {
      this.currentVideoIsPlaying = false;
        console.log(index, "触发暂停");
        if(index === this.currentIndex) {
            this.currentVideoIsPlaying = false;
        } else {
            this.currentVideoIsPlaying = true;
            return
        }
      this.startPauseTime = Date.now()
    },
    
@@ -630,7 +752,7 @@
    }
    
    .original-price {
      font-size: 24rpx;
      font-size: 28rpx;
      color: #999;
      text-decoration: line-through;
    }
@@ -643,7 +765,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;
@@ -685,14 +807,21 @@
    .comment-item {
      display: flex;
      padding: 10rpx 0;
      flex-direction: column;
      padding: 10rpx 0 20rpx 0;
    }
    .avatar {
      width: 80rpx;
      height: 80rpx;
    .comment-avatar {
      width: 70rpx;
      height: 70rpx;
      border-radius: 50%;
      margin-right: 20rpx;
      margin-right: 10rpx;
    }
    .comment-reply-avatar {
        width: 40rpx;
        height: 40rpx;
        border-radius: 50%;
        margin-right: 10rpx;
    }
    .comment-content {
@@ -700,21 +829,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;
    }
@@ -754,4 +883,78 @@
      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;
        height: 40rpx;
    }
    .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;
    }
    .line {
        margin-right: 10rpx;
        color: #cccccc;
    }
</style>