| | |
| | | :ref="'video'+index" |
| | | :src="item.videoUrl" |
| | | :autoplay="index === currentIndex" |
| | | :controls="false" |
| | | :controls="isControls" |
| | | :loop="true" |
| | | :object-fit="item.videoFit" |
| | | :enable-progress-gesture="false" |
| | | :show-center-play-btn="false" |
| | | :show-progress="false" |
| | | :show-fullscreen-btn="isControls" |
| | | :show-play-btn="isControls" |
| | | :show-mute-btn="false" |
| | | class="video-item" |
| | | |
| | | @fullscreenchange="onFullscreenChange" |
| | | @play="onPlay(item.id, index)" |
| | | @pause="onPause(index)" |
| | | @ended="onEnded(index)" |
| | |
| | | @loadedmetadata="onLoadedMetadata($event)" |
| | | @waiting="videoWaiting(index)" |
| | | ></video> |
| | | <view class="fullscreen-btn"> |
| | | <image |
| | | class="fullscreen-icon" |
| | | :src="isFullScreen ? off_screen : on_screen" |
| | | mode="aspectFit" |
| | | @click="requestFullScreen('video'+index,item)" |
| | | ></image> |
| | | </view> |
| | | <!-- 自定义控制条 --> |
| | | <view |
| | | @touchstart.stop="handleTouchStart" |
| | |
| | | :style="{bottom: marginBottom + 'px'}" |
| | | class="container"> |
| | | <!-- 进度条 - 整个区域可拖动 --> |
| | | <view class="process-warp" :style="{ opacity: showProcess ? 1 : 0 }"> |
| | | <!-- <view class="process-warp" :style="{ opacity: showProcess ? 1 : 0 }"> --> |
| | | <view class="process-warp"> |
| | | <!-- 显示当前进度 --> |
| | | <view class="progress-text">{{ hasPlayTime }}/{{formartDuration}}</view> |
| | | <view v-show="isTouch" class="progress-text">{{ hasPlayTime }}/{{formartDuration}}</view> |
| | | <view |
| | | class="progress-bar" |
| | | id="progressBar" |
| | |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view style="width: 100%; height: 100%;" v-else-if="item.videoContentType === 'img'"> |
| | | <uni-swiper-dot |
| | | :info="item.imgs" |
| | |
| | | <view class="video-info" :style="{bottom: marginBottom + 20 + 'px'}"> |
| | | <view style="width: 100%; position: relative;"> |
| | | <text class="video-author">@{{item.authorName}}</text> |
| | | <text class="iconfont" @click="jumpToSearch" style="position: absolute;right: 45px;"></text> |
| | | <text class="iconfont" @click="jumpToSearch" style="position: absolute;right: 42px;bottom: 50rpx;"></text> |
| | | </view> |
| | | <view style="width: 100%;word-wrap: break-word;white-space: normal;overflow-wrap: break-word;"> |
| | | <text class="video-title">{{item.title}}</text> |
| | |
| | | <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="nickname">{{comment.userNickname}} <text v-if="userId===comment.userId">(我)</text> </text> |
| | | <text class="content">{{comment.commentContent}}</text> |
| | | <view style="position: relative;"> |
| | | <text class="time">{{formatTime(comment.createTime)}}</text> |
| | |
| | | <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> |
| | | <text class="nickname">{{reply.userNickname}}<text v-if="userId===comment.userId">(我)</text></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 v-if="userId===comment.userId">(我)</text></text> |
| | | </view> |
| | | <text class="content">{{reply.commentContent}}</text> |
| | | <view class="reply-footer"> |
| | |
| | | import { getUserInfo } from "@/api/members"; |
| | | import storage from "@/utils/storage.js"; |
| | | import TopBar from "@/components/TopBar.vue"; |
| | | import { nextTick } from "vue"; |
| | | |
| | | export default { |
| | | components: {TopBar}, |
| | |
| | | }, |
| | | data() { |
| | | return { |
| | | isControls:false, |
| | | on_screen: require('@/pages/tabbar/index/static/on_screen.png'), |
| | | off_screen: require('@/pages/tabbar/index/static/off_screen.png'), |
| | | isFullScreen:false, |
| | | isTouch:false, |
| | | userId :'', |
| | | currentImgIndex: 0, // 播放到第几张图--索引 |
| | | currentGoodsIndex: 0, // 播放到第几个商品--索引 |
| | | currentTime: 0, |
| | |
| | | barWidth: 0, // 进度条宽度 |
| | | isDragging: false, // 是否正在拖动 |
| | | processHidenTimer: null, // 进度条隐藏定时器 |
| | | showProcess: false, // 是否显示进度条 |
| | | showProcess: true, // 是否显示进度条 |
| | | videoNoMore: false, // 是否还有更多视频 |
| | | commentNoMore: false, // 是否还有更多评论 |
| | | commentQuery: { |
| | |
| | | } |
| | | }, |
| | | onShow() { |
| | | if(!this.userId){ |
| | | this.getUserId() |
| | | } |
| | | // const token = storage.getAccessToken(); |
| | | // if (! token) { |
| | | // this.wxSilentLogin(() => { |
| | |
| | | // this.loadVideos(); |
| | | // } |
| | | if (this.videoList.length < 1) { |
| | | this.loading = false; |
| | | this.videoNoMore = false; |
| | | console.log('触发数据加载') |
| | | this.loadVideos(); |
| | | } |
| | | // 如果视频按下暂停后切换页面再回到页面时,只算暂停时间(因为暂停时间和离开页面时间是重复的,只算一个) |
| | |
| | | this.startHidenTime = Date.now() |
| | | }, |
| | | onLoad(option) { |
| | | |
| | | console.log('-----------分享出的数据---------->',option) |
| | | //处理扫码出来的视频 |
| | | this.marginBottom = uni.getSystemInfoSync().safeAreaInsets.bottom |
| | |
| | | path: `/pages/tabbar/index/home?videoId=${videoInfo.id}&userId=${userInfo.id}`, |
| | | } |
| | | }, |
| | | methods: { |
| | | methods: { |
| | | requestFullScreen(id,item){ |
| | | console.log(item) |
| | | |
| | | const videoContext = uni.createVideoContext(id, this) |
| | | // 根据视频方向决定全屏方向 |
| | | const direction = this.shouldUseLandscape(item) ? 90 : 0; |
| | | |
| | | // 先暂停视频(避免切换时的声音问题) |
| | | // videoContext.pause(); |
| | | |
| | | // 请求全屏 |
| | | videoContext.requestFullScreen({ |
| | | direction: direction, |
| | | }); |
| | | |
| | | |
| | | }, |
| | | shouldUseLandscape(item) { |
| | | if(item.videoFit === 'cover'){ |
| | | return false |
| | | } |
| | | // 默认横屏(根据业务需求调整) |
| | | return true; |
| | | }, |
| | | // 全屏状态变化事件 |
| | | onFullscreenChange(e) { |
| | | console.log('全屏状态变化:', e.detail.fullScreen) |
| | | if(e.detail.fullScreen){ |
| | | this.isControls = true; |
| | | }else{ |
| | | this.isControls = false; |
| | | } |
| | | }, |
| | | getUserId(){ |
| | | const {id} = uni.getStorageSync('user_info_obj_dev') |
| | | this.userId = id; |
| | | }, |
| | | // 解析URL参数 |
| | | parseUrlParams(url) { |
| | | const params = {}; |
| | |
| | | getVideoComments(this.commentQuery).then(res => { |
| | | this.commentsTotal = res.data.total; |
| | | this.comments = res.data.data; |
| | | console.log('------------------------>',this.comments) |
| | | this.commentQuery.pageNumber += 2; |
| | | this.commentQuery.pageSize /= 2; |
| | | }).catch(() => { |
| | |
| | | handleTouchStart(e) { |
| | | this.isDragging = true; |
| | | this.showProcess = true; |
| | | this.isTouch = true; |
| | | this.startProgress = this.progress; // 记录开始时的进度 |
| | | this.startX = e.touches[0].pageX; |
| | | console.log("记录开始时的进度", this.startProgress); |
| | |
| | | videoContext.seek(this.duration * this.progress / 100) |
| | | videoContext.play() |
| | | this.processHidenTimer = setTimeout(() => { |
| | | this.showProcess = false; |
| | | // this.showProcess = true; |
| | | this.isTouch = false; |
| | | }, 1000); |
| | | }, |
| | | |
| | |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .fullscreen-btn { |
| | | position: absolute; |
| | | right: 45rpx; |
| | | bottom: 70rpx; |
| | | width: 60rpx; |
| | | height: 60rpx; |
| | | border-radius: 50%; |
| | | background-color: rgba(0, 0, 0, 0.4); |
| | | display: flex; |
| | | justify-content: center; |
| | | align-items: center; |
| | | z-index: 999; |
| | | backdrop-filter: blur(10rpx); |
| | | border: 1rpx solid rgba(255, 255, 255, 0.2); |
| | | |
| | | } |
| | | /* 图标样式 */ |
| | | .fullscreen-icon { |
| | | width: 36rpx; |
| | | height: 36rpx; |
| | | opacity: 0.9; |
| | | } |
| | | ::v-deep .custom-tabbar { |
| | | border-top: none !important; |
| | | } |
| | |
| | | .progress-bar { |
| | | position: relative; |
| | | width: 100%; |
| | | height: 16px; |
| | | background-color: #eee; |
| | | height: 5px; |
| | | background-color: rgba(255, 255, 255, 0.2); /* 半透明背景 */ |
| | | overflow: hidden; |
| | | border-radius: 1.5px; |
| | | cursor: pointer; |
| | | transition: height 0.2s; |
| | | } |
| | | |
| | | .progress-fill { |
| | | |
| | | position: absolute; |
| | | left: 0; |
| | | top: 0; |
| | | height: 100%; |
| | | border-radius: 2px; |
| | | background-color: lightgray; |
| | | transition: width 0.1s; |
| | | } |