| | |
| | | <view class="video-container"> |
| | | <!-- 视频加载 --> |
| | | <zero-loading v-show="videoLoading" type="circle" color="#0ebd57" text=""></zero-loading> |
| | | <view class="showLeft" @click="showDrawer('showLeft')" v-if="!showLeft"> |
| | | <view class="showLeft" @click="showDrawer('showLeft')" v-if="!showLeft" :style="{top: buttonHeight+'px'}"> |
| | | <uni-icons type="right" size="30"></uni-icons> |
| | | </view> |
| | | <!-- 视频列表 --> |
| | |
| | | @touchstart.stop="handleTouchStart" |
| | | @touchmove.stop="handleTouchMove" |
| | | @touchend.stop="handleTouchEnd" |
| | | :style="{bottom: marginBottom + 'px'}" |
| | | class="container"> |
| | | <!-- 进度条 - 整个区域可拖动 --> |
| | | <view class="process-warp" :style="{ opacity: showProcess ? 1 : 0 }"> |
| | |
| | | |
| | | |
| | | <!-- 视频信息层 --> |
| | | <view class="video-info"> |
| | | <view class="video-info" :style="{bottom: marginBottom + 20 + 'px'}"> |
| | | <view> |
| | | <text class="video-author">@{{item.authorName}}</text> |
| | | </view> |
| | |
| | | |
| | | <!-- 右侧互动按钮 --> |
| | | <view class="action-buttons" v-if="false"> |
| | | <view class="avatar-container"> |
| | | <image class="avatar" @click="jumpToHomePage(item.authorId)" :src="item.authorAvatar" mode="aspectFill"></image> |
| | | <!-- 关注图标 - 使用绝对定位 --> |
| | | <view v-if="!item.subscribeThisAuthor" class="follow-icon" @click="subscribeAuth(index, item.authorId)"> |
| | | <text class="iconfont"></text> |
| | | </view> |
| | | </view> |
| | | <view class="action-item" @click="toggleCollect(item, index)"> |
| | | <text class="iconfont" v-if="item.collected"></text> |
| | | <text class="iconfont" v-else></text> |
| | | <text style="font-size: 10px;font-weight: lighter;">{{item.collectNum}}</text> |
| | | </view> |
| | | <view class="avatar-container"> |
| | | <image class="avatar" @click="jumpToHomePage(item.authorId)" :src="item.authorAvatar" mode="aspectFill"></image> |
| | | <!-- 关注图标 - 使用绝对定位 --> |
| | | <view v-if="!item.subscribeThisAuthor" class="follow-icon" @click="subscribeAuth(index, item.authorId)"> |
| | | <text class="iconfont"></text> |
| | | </view> |
| | | </view> |
| | | <view class="action-item" @click="toggleThumbsUp(item, index)"> |
| | | <text class="iconfont" v-if="item.thumbsUp"></text> |
| | | <text class="iconfont" v-else></text> |
| | | <text style="font-size: 10px;font-weight: lighter;">{{item.thumbsUpNum}}</text> |
| | | </view> |
| | | <view class="action-item" @click="showComments(item)"> |
| | | <text class="iconfont"></text> |
| | | <text style="font-size: 10px;font-weight: lighter;">{{item.commentNum}}</text> |
| | | </view> |
| | | <view class="action-item"> |
| | | <button open-type="share" class="custom-share-btn" :data-obj="item"> |
| | | <text class="iconfont"></text> |
| | | </button> |
| | | |
| | | </view> |
| | | </view> |
| | | <text class="iconfont"></text> |
| | | <text style="font-size: 10px;font-weight: lighter;">{{item.commentNum}}</text> |
| | | </view> |
| | | <view class="action-item" @click="toggleCollect(item, index)"> |
| | | <text class="iconfont" v-if="item.collected"></text> |
| | | <text class="iconfont" v-else></text> |
| | | <text style="font-size: 10px;font-weight: lighter;">{{item.collectNum}}</text> |
| | | </view> |
| | | <view class="action-item"> |
| | | <button open-type="share" class="custom-share-btn" :data-obj="item"> |
| | | <text class="iconfont"></text> |
| | | </button> |
| | | </view> |
| | | </view> |
| | | |
| | | </swiper-item> |
| | | </swiper> |
| | |
| | | </view> |
| | | </view> |
| | | </uni-popup> |
| | | <uni-drawer ref="showLeft" mode="left" width="120" :cus-style="true" height="93vh" |
| | | @change="change($event,'showLeft')" class="navigationLeft"> |
| | | <uni-drawer ref="showLeft" mode="left" width="120" :cus-style="true" height="80vh" |
| | | @change="change($event,'showLeft')" class="navigationLeft" :drawerTop="buttonHeight"> |
| | | <scroll-view class="typeNavigation" :scroll-y="true" :show-scrollbar="false"> |
| | | <view class="typeNavigationItem" :class="{typeNavigationItemCheck:currentCategort ==item.id}" |
| | | @click="chooseCategory(item.id)" v-for="item in categoryList" :key="item.id"> |
| | |
| | | </scroll-view> |
| | | </uni-drawer> |
| | | <view v-if="videoList.length===0" style="display: flex;align-items: center;justify-content: center;background-color: #fff;height: 100%">当前没有可以查看的视频</view> |
| | | |
| | | |
| | | <custom-tabbar bgColor="#ffffff" selected="kitchen"></custom-tabbar> |
| | | </view> |
| | | </template> |
| | |
| | | getVideoComments, |
| | | addVideoComment, |
| | | thubmsUpComment, |
| | | cancelThubmsUpComment |
| | | cancelThubmsUpComment, |
| | | changeThumbsUp |
| | | } from "@/api/video.js"; |
| | | import { |
| | | changeCollect |
| | |
| | | }, |
| | | data() { |
| | | return { |
| | | buttonHeight:0, |
| | | showLeft:false, |
| | | currentCategort: '', |
| | | categoryList: [], |
| | |
| | | startY: 0, |
| | | endY: 0 |
| | | }, |
| | | |
| | | videoContexts: [], // 视频上下文对象集合 |
| | | loading: false, // 是否正在加载 |
| | | videoQuery: { |
| | | pageNumber: 1, |
| | | pageSize: 6, |
| | | videoFrom: 'recommend', |
| | | videoType: 'cook' |
| | | } |
| | | }, |
| | | marginBottom: 0 // 底部安全区域 |
| | | } |
| | | }, |
| | | onShow() { |
| | |
| | | this.startHidenTime = Date.now() |
| | | }, |
| | | onLoad() { |
| | | this.getKitchenTypeList(); |
| | | this.loadVideos(); |
| | | }, |
| | | onReady() { |
| | | // 初始化视频上下文 |
| | | this.initVideoContexts(); |
| | | this.marginBottom = uni.getSystemInfoSync().safeAreaInsets.bottom |
| | | // 获取状态栏高度 |
| | | const systemInfo = uni.getSystemInfoSync(); |
| | | this.buttonHeight = systemInfo.statusBarHeight; |
| | | this.getKitchenTypeList(); |
| | | this.loadVideos(); |
| | | }, |
| | | methods: { |
| | | async chooseCategory(id) { |
| | |
| | | this.videoQuery.kitchenTypeId = id; |
| | | this.videoLoading = false; |
| | | this.currentIndex = 0; |
| | | |
| | | |
| | | await this.loadVideos() |
| | | }, |
| | | getKitchenTypeList() { |
| | |
| | | } |
| | | }) |
| | | }, |
| | | // 初始化视频上下文 |
| | | initVideoContexts() { |
| | | this.videoContexts = this.videoList.map((_, index) => { |
| | | let videoContent = uni.createVideoContext(`video${index}`, this); |
| | | return videoContent; |
| | | }); |
| | | }, |
| | | |
| | | // 加载视频数据 |
| | | async loadVideos() { |
| | | console.log(this.loading, this.videoNoMore,this.videoQuery) |
| | |
| | | ), |
| | | ]; |
| | | } |
| | | this.$nextTick(() => { |
| | | this.initVideoContexts(); |
| | | }); |
| | | this.loading = false; |
| | | if (res.data.data.length < this.videoQuery.pageSize) { |
| | | this.videoNoMore = true; |
| | |
| | | |
| | | // 滑动切换视频 |
| | | onSwiperChange(e) { |
| | | this.videoLoading = false |
| | | // 如果视频处于暂停状态往下刷视频,那么需要再计算一次暂停时间 |
| | | if (!this.currentVideoIsPlaying) { |
| | | if (this.startPauseTime !== 0) { |
| | | const duration = Date.now() - this.startPauseTime |
| | | this.totalPauseTime += duration |
| | | } |
| | | if(!this.currentVideoIsPlaying) { |
| | | if(this.startPauseTime !== 0) { |
| | | const duration = Date.now() - this.startPauseTime |
| | | this.totalPauseTime += duration |
| | | } |
| | | } |
| | | // 保存上一个视频的播放记录 |
| | | this.savePlayRecord() |
| | | const oldIndex = this.currentIndex; |
| | | this.currentIndex = e.detail.current; |
| | | |
| | | const videoContext = uni.createVideoContext(`video${oldIndex}`, this); |
| | | // 暂停上一个视频 |
| | | if (this.videoContexts[oldIndex]) { |
| | | this.videoContexts[oldIndex].pause(); |
| | | } |
| | | |
| | | videoContext.pause(); |
| | | this.startPauseTime = 0; |
| | | |
| | | // 设置当前播放视频的总时长 |
| | | this.duration = this.videoList[this.currentIndex].videoDuration; |
| | | this.formartDuration = this.sliderFormatTime(this.duration); |
| | | |
| | | // 播放当前视频 |
| | | if (this.videoContexts[this.currentIndex]) { |
| | | this.videoContexts[this.currentIndex].play(); |
| | | } |
| | | const videoContext1 = uni.createVideoContext(`video${this.currentIndex}`, this); |
| | | videoContext1.play() |
| | | // 如果剩余视频不足,触发请求获取更多视频 |
| | | if (this.videoList.length - 1 < this.currentIndex + this.videoLiveOffset) { |
| | | this.loadVideos() |
| | | } |
| | | }, |
| | | |
| | | // 开始触摸 |
| | | handleSwiperStart(e) { |
| | | console.log("开始触摸", e); |
| | | this.touchXY.startX = e.touches[0].pageX |
| | | this.touchXY.startY = e.touches[0].pageY |
| | | }, |
| | | // 触摸中 |
| | | handleSwiperMove(e) { |
| | | this.touchXY.endX = e.touches[0].pageX |
| | | this.touchXY.endY = e.touches[0].pageY |
| | | }, |
| | | // 结束触摸 |
| | | handleSwiperEnd(item) { |
| | | // 防止滑动滚动条也触发跳转 |
| | | if (this.showProcess) { |
| | | return |
| | | } |
| | | const diffX = this.touchXY.endX - this.touchXY.startX |
| | | const diffY = this.touchXY.endY - this.touchXY.startY |
| | | |
| | | // 判断是否是横向滑动(X轴变化大于Y轴变化) |
| | | if (Math.abs(diffX) > Math.abs(diffY)) { |
| | | if (diffX > 0) { |
| | | console.log('右滑') |
| | | if (item.goodsList && item.goodsList.length > 0) { |
| | | this.jumpToPay(item.id) |
| | | } |
| | | } else { |
| | | console.log('左滑') |
| | | } |
| | | } |
| | | // 重置坐标 |
| | | this.touchXY = { |
| | | startX: 0, |
| | | endX: 0, |
| | | startY: 0, |
| | | endY: 0 |
| | | } |
| | | }, |
| | | |
| | | // 收藏/取消收藏 |
| | | toggleCollect(item, index) { |
| | |
| | | } |
| | | }) |
| | | }, |
| | | // 点赞/取消点赞 |
| | | toggleThumbsUp(item, index) { |
| | | let data = { |
| | | refId: item.id, |
| | | thumbsUpType: 'video' |
| | | } |
| | | const beforeThumbsUp = item.thumbsUp |
| | | const beforeThumbsUpNum = item.thumbsUpNum |
| | | if(item.thumbsUp) { |
| | | this.videoList[index].thumbsUp = false |
| | | this.videoList[index].thumbsUpNum -= 1 |
| | | } else { |
| | | this.videoList[index].thumbsUp = true |
| | | this.videoList[index].thumbsUpNum += 1 |
| | | } |
| | | changeThumbsUp(data).then(res => { |
| | | if(res.data.code !== 200) { |
| | | this.videoList[index].thumbsUp = beforeThumbsUp |
| | | this.videoList[index].thumbsUpNum = beforeThumbsUpNum |
| | | } |
| | | }) |
| | | }, |
| | | // 单击屏幕:暂停或继续播放 |
| | | togglePlay(index) { |
| | | console.log("单击视频", index); |
| | |
| | | onTimeUpdate(e) { |
| | | this.videoLoading = false |
| | | this.playRecord.playAt = e.detail.currentTime; |
| | | |
| | | |
| | | this.currentTime = e.detail.currentTime; |
| | | this.progress = (e.detail.currentTime / this.duration) * 100 |
| | | }, |
| | |
| | | this.startProgress = this.progress; // 记录开始时的进度 |
| | | this.startX = e.touches[0].pageX; |
| | | console.log("记录开始时的进度", this.startProgress); |
| | | this.videoContexts[this.currentIndex].pause() |
| | | const videoContext = uni.createVideoContext(`video${this.currentIndex}`, this); |
| | | videoContext.pause() |
| | | // this.updateProgress(e); |
| | | }, |
| | | // 开始触摸 |
| | | handleSwiperStart(e) { |
| | | console.log("开始触摸", e); |
| | | this.touchXY.startX = e.touches[0].pageX |
| | | this.touchXY.startY = e.touches[0].pageY |
| | | }, |
| | | // 触摸中 |
| | | handleSwiperMove(e) { |
| | | this.touchXY.endX = e.touches[0].pageX |
| | | this.touchXY.endY = e.touches[0].pageY |
| | | }, |
| | | // 结束触摸 |
| | | handleSwiperEnd(item) { |
| | | // 防止滑动滚动条也触发跳转 |
| | | if (this.showProcess) { |
| | | return |
| | | } |
| | | const diffX = this.touchXY.endX - this.touchXY.startX |
| | | const diffY = this.touchXY.endY - this.touchXY.startY |
| | | |
| | | // 判断是否是横向滑动(X轴变化大于Y轴变化) |
| | | if (Math.abs(diffX) > Math.abs(diffY)) { |
| | | if (diffX > 0) { |
| | | console.log('右滑') |
| | | if (item.goodsList && item.goodsList.length > 0) { |
| | | this.jumpToPay(item.id) |
| | | } |
| | | } else { |
| | | console.log('左滑') |
| | | } |
| | | } |
| | | // 重置坐标 |
| | | this.touchXY = { |
| | | startX: 0, |
| | | endX: 0, |
| | | startY: 0, |
| | | endY: 0 |
| | | } |
| | | }, |
| | | // 触摸移动 |
| | | handleTouchMove(e) { |
| | | if (!this.isDragging || !this.barWidth) return; |
| | | clearTimeout(this.processHidenTimer) |
| | | this.videoContexts[this.currentIndex].pause() |
| | | this.updateProgress(e); |
| | | }, |
| | | |
| | |
| | | handleTouchEnd() { |
| | | this.isDragging = false; |
| | | console.log("滑动结束", this.duration * this.progress); |
| | | this.videoContexts[this.currentIndex].seek(this.duration * this.progress / 100) |
| | | this.videoContexts[this.currentIndex].play() |
| | | const videoContext = uni.createVideoContext(`video${this.currentIndex}`, this); |
| | | videoContext.seek(this.duration * this.progress / 100) |
| | | videoContext.play() |
| | | this.processHidenTimer = setTimeout(() => { |
| | | this.showProcess = false; |
| | | }, 1000); |
| | | this.showProcess = false; |
| | | }, 1000); |
| | | }, |
| | | |
| | | // 更新进度 |
| | | updateProgress(e) { |
| | | // 获取当前触摸点X坐标 |
| | | const currentX = e.touches[0].pageX; |
| | | // 获取当前触摸点X坐标 |
| | | const currentX = e.touches[0].pageX; |
| | | |
| | | // 计算滑动距离(像素) |
| | | const deltaX = currentX - this.startX; |
| | | // 计算滑动距离(像素) |
| | | const deltaX = currentX - this.startX; |
| | | |
| | | // 将像素距离转换为进度增量 |
| | | const deltaProgress = (deltaX / this.barWidth) * 100; |
| | | console.log("进度增量", deltaProgress); |
| | | // 计算新进度 = 开始时的进度 + 滑动增量 |
| | | let newProgress = this.startProgress + deltaProgress; |
| | | // 将像素距离转换为进度增量 |
| | | const deltaProgress = (deltaX / this.barWidth) * 100; |
| | | // 计算新进度 = 开始时的进度 + 滑动增量 |
| | | let newProgress = this.startProgress + deltaProgress; |
| | | |
| | | // 限制范围在0-100之间 |
| | | newProgress = Math.max(0, Math.min(100, newProgress)); |
| | | // 限制范围在0-100之间 |
| | | newProgress = Math.max(0, Math.min(100, newProgress)); |
| | | |
| | | this.progress = newProgress; |
| | | this.progress = newProgress; |
| | | }, |
| | | // 视频缓冲 |
| | | videoWaiting(index) { |
| | |
| | | }, |
| | | // 获取视频总时长 |
| | | onLoadedMetadata(e) { |
| | | this.duration = e.detail.duration; |
| | | this.formartDuration.push(this.sliderFormatTime(this.duration)); |
| | | console.log("视频总时长", this.duration); |
| | | // this.duration = e.detail.duration; |
| | | // this.formartDuration.push(this.sliderFormatTime(this.duration)); |
| | | // console.log("视频总时长", this.duration); |
| | | }, |
| | | // 保存播放记录 |
| | | async savePlayRecord() { |
| | |
| | | |
| | | .showLeft { |
| | | display: flex; |
| | | top: 50rpx; |
| | | left: 0; |
| | | align-items: center; |
| | | justify-content: flex-start; |
| | |
| | | .video-item { |
| | | width: 100%; |
| | | height: 100%; |
| | | object-fit: cover; |
| | | } |
| | | |
| | | .play-icon { |
| | |
| | | .progress-text { |
| | | margin-top: 10px; |
| | | font-size: 14px; |
| | | color: #666; |
| | | color: #fff; |
| | | } |
| | | |
| | | .swiper-box { |
| | |
| | | } |
| | | |
| | | .typeNavigationItem { |
| | | padding: 24rpx; |
| | | font-size: 28rpx; |
| | | color: black; |
| | | margin-top: 10rpx; |
| | | border-radius: 12rpx; |
| | | border: 1rpx solid gray; |
| | | padding: 24rpx; |
| | | font-size: 28rpx; |
| | | color: black; |
| | | margin-top: 10rpx; |
| | | } |
| | | |
| | | |
| | | .typeNavigationItemCheck { |
| | | background-color: #42b993; |
| | | color: #fff; |
| | | border: 0; |
| | | color: #ef321e; |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | border: 0; |
| | | } |
| | | |
| | | .container ::v-deep .navigationLeft .uni-drawer__content { |
| | |
| | | background-color: lightpink !important; |
| | | |
| | | } |
| | | </style> |
| | | |
| | | .custom-share-btn { |
| | | font-size: unset; |
| | | background: none; |
| | | padding: 0; |
| | | margin: 0; |
| | | line-height: normal; |
| | | border: none; |
| | | } |
| | | .custom-share-btn::after { |
| | | border: none; |
| | | } |
| | | </style> |