| | |
| | | <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> |
| | |
| | | <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"></text> |
| | | </view> |
| | | |
| | |
| | | 暂无评论,快来发表第一条评论吧~ |
| | | </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"></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"></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"> </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"></text></view> |
| | | <view @click="retractReplyComment(index)" class="reply-op-item" style="margin-left: 50rpx;">收起<text class="iconfont textSideIcon"></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"></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> |
| | | |
| | |
| | | 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, // 评论总条数 |
| | |
| | | playAt: 0 ,// 这个视频播放到哪了 |
| | | startPlayTime: 0 // 这个视频从什么时候开始播放的 |
| | | }, |
| | | currentVideoIsPlaying: null, // 当前视频是否正在播放 |
| | | currentVideoIsPlaying: true, // 当前视频是否正在播放 |
| | | isFullScreen: false, |
| | | windowHeight: 0, |
| | | currentIndex: 0, // 当前播放的视频索引 |
| | |
| | | } |
| | | }, |
| | | onShow() { |
| | | this.loadVideos() |
| | | // 如果视频按下暂停后切换页面再回到页面时,只算暂停时间(因为暂停时间和离开页面时间是重复的,只算一个) |
| | | if(this.startHidenTime !== 0 && this.currentVideoIsPlaying) { |
| | | const duration = Date.now() - this.startHidenTime |
| | |
| | | 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); |
| | |
| | | // 发表评论 |
| | | 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: '评论成功' |
| | |
| | | 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; |
| | | }, |
| | |
| | | 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 => { |
| | |
| | | // 保存上一个视频的播放记录 |
| | | 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]) { |
| | |
| | | }, |
| | | // 视频播放事件 |
| | | 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) { |
| | |
| | | |
| | | // 视频暂停事件 |
| | | onPause(index) { |
| | | this.currentVideoIsPlaying = false; |
| | | console.log(index, "触发暂停"); |
| | | if(index === this.currentIndex) { |
| | | this.currentVideoIsPlaying = false; |
| | | } else { |
| | | this.currentVideoIsPlaying = true; |
| | | return |
| | | } |
| | | this.startPauseTime = Date.now() |
| | | }, |
| | | |
| | |
| | | } |
| | | |
| | | .original-price { |
| | | font-size: 24rpx; |
| | | font-size: 28rpx; |
| | | color: #999; |
| | | text-decoration: line-through; |
| | | } |
| | |
| | | .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; |
| | |
| | | |
| | | .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 { |
| | |
| | | } |
| | | |
| | | .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; |
| | | } |
| | | |
| | |
| | | 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> |