app/components/vlog/videoFollowComp.vue
2025-04-25 18:20:19 +08:00

988 lines
22 KiB
Vue
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view style="flex: 1">
<!-- <uni-list @change="onchange" :num="playerList.length"> -->
<list
ref="list"
:pagingEnabled="true"
:show-scrollbar="false"
@scroll="listScroll"
@scrollend="scroll"
:scrollable="true"
>
<refresh
@pullingdown="onpullingdown"
@refresh="onrefresh"
:display="refreshing ? 'show' : 'hide'"
>
<text class="refresh-info-txt"></text>
<loading-indicator></loading-indicator>
</refresh>
<cell
:recycle="false"
v-if="playerList.length == 0"
:style="{ height: screenHeight + 'px' }"
>
<view
class="my-follow"
:style="{ height: screenHeight + 'px' }"
>
<text class="warn-info">关注列表为空</text>
</view>
</cell>
<cell
:recycle="false"
:class="isIOS ? 'abs-box-ios' : ''"
v-for="(item, index) in playerList"
:key="index"
:data-index="index"
:style="{ height: screenHeight + 'px' }"
>
<video
ref="myFollowVideo"
id="myFollowVideo"
:object-fit="item.width >= item.height ? 'contain' : 'fill'"
:src="item.url"
:controls="false"
:enable-progress-gesture="false"
v-if="playerCur === index"
loop
autoplay
style="width: 750rpx"
:style="{ height: screenHeight + 'px' }"
:http-cache="true"
@click="playOrPause"
@play="onplay"
@error="onerror"
@timeupdate="onTimeUpdate"
></video>
<image
:lazy-load="true"
:fade-show="false"
v-if="!item.play"
:src="item.firstFrameImg"
:mode="item.width >= item.height ? 'aspectFit' : 'scaleToFill'"
style="width: 750rpx; filter: blur(10px)"
:style="{ height: screenHeight + 'px' }"
></image>
<!-- 播放按钮 -->
<view
v-if="!isIOS && !playFollowStatus && item.play"
class="abs-box-ad"
:style="{ height: screenHeight + 'px' }"
>
<image
src="/static/images/playvd.png"
class="play-btn"
></image>
</view>
<image
v-if="isIOS && !playFollowStatus && item.play"
src="/static/images/playvd.png"
class="play-btn"
></image>
<!-- 进度条 -->
<view
v-if="progressFlag"
class="progress-bar"
@touchstart="startDrag"
@touchmove="onDragging"
@panend="endDrag"
>
<view
class="progress-background"
v-if="progressFlag"
></view>
<view
v-if="progressFlag"
class="progress-foreground"
:class="isDragging ? '' : 'anm'"
:style="{ width: progressWidth + 'px' }"
></view>
</view>
<!-- 浮动时间提示 -->
<view
class="float-time"
v-if="isDragging"
:style="{ left: floatLeft + 'px' }"
>
<text style="color: #fff">{{ formatTime(floatTime) }}</text>
</view>
<view class="publish-info-box">
<view class="">
<text class="publish-info-vloger-name">@{{ item.vlogerName }}</text>
<text class="publish-info-content">{{ item.content }}</text>
<view class="publish-info-music-box">
<image
src="/static/images/icon-fire.png"
class="icon-fire"
></image>
<text class="muisc-words">{{ item.vlogerName }}的原声创作</text>
</view>
</view>
<!-- <view
class=""
style="flex-direction: row"
>
<image src="/static/images/cd-play-4.gif"
style="width: 150rpx;height: 150rpx;opacity: 0.8;"></image>
</view> -->
</view>
<!-- 视频展示右侧的操作按钮,头像 - 点赞 - 评论 - 转发 -->
<view class="operation-box">
<view class="operation-face-box">
<image
:src="item.vlogerFace"
class="user-face"
@click="goUserInfoSeeSee(item.vlogerId)"
></image>
</view>
<image
v-if="!item.doIFollowVloger"
src="/static/images/icon-follow.png"
@click="followMe(item.vlogerId)"
class="follow-me"
></image>
<view class="like-box">
<image
v-if="!item.doILikeThisVlog"
src="/static/images/icon-unlike.png"
@click="likeOrDislikeVlog(1)"
class="icon"
></image>
<image
v-if="item.doILikeThisVlog"
src="/static/images/icon-like.png"
@click="likeOrDislikeVlog(0)"
class="icon"
></image>
<text class="some-counts">{{ item.likeCounts }}</text>
</view>
<view class="comment-and-share-box">
<image
src="/static/images/icon-comments.png"
@click="commentToggle"
class="icon"
></image>
<!-- <text class="some-counts">{{item.commentsCounts}}</text> -->
<text class="some-counts">{{ thisVlogTotalComentCounts }}</text>
</view>
<view class="comment-and-share-box">
<image
src="/static/images/icon-share.png"
@click="shareToggle"
class="icon"
></image>
<text class="some-counts">分享</text>
</view>
</view>
</cell>
</list>
<!-- </uni-list> -->
<view v-if="thisVlog != null && thisVlog != {}">
<!-- 底部评论窗口popup -->
<uni-popup
ref="comment"
type="comment"
>
<uni-popup-comments
:thisVlogerId="thisVlogerId"
:thisVlogId="thisVlogId"
></uni-popup-comments>
</uni-popup>
<uni-popup
ref="share"
background-color="#fff"
type="share"
>
<uni-popup-share
:thisVlogerId="thisVlogerId"
:thisVlogId="thisVlogId"
:vlogUrl="thisVlog.url"
:isPrivate="thisVlog.isPrivate"
></uni-popup-share>
</uni-popup>
</view>
</view>
</template>
<script>
let system = uni.getSystemInfoSync();
import storage from '@/utils/storage.js'; //缓存
import api from '@/config/api.js';
import { vlogFollowList, vlogLike, vlogUnLike, vlogComment, vlogFollow, vlogTotalLikedCounts } from '@/api/vlog';
export default {
props: {
screenHeight: {
default: 0
},
src: {
default: false
},
playFollowStatus: {
default: false
},
videoList: {
default: []
},
refreshList: {
default: []
},
pagingList: {
default: []
},
progressFlag: {
default: false
}
},
data() {
return {
thisVlog: {}, // 当前的短视频对象
thisVlogId: '', // 当前的短视频主键id
thisVlogerId: '', // 当前的短视频博主的主键id
refreshing: false,
showRefreshLoading: 'hide',
playerCur: 0,
page: 0,
totalPage: 0,
// playerList: this.videoList,
playerList: [],
thisVlogTotalComentCounts: 0,
videoContext: null,
currentIndex: 0,
contentOffsetY: 0,
times: new Date().getTime(),
objectFit: 'fill',
isIOS: uni.getSystemInfoSync().platform == 'ios' ? true : false,
duration: 0,
currentTime: 0,
progressWidth: 0,
floatTime: 0,
floatLeft: 0,
isDragging: false
// progressFlag: false
};
},
//created
created() {
console.log('flowVd-----------------------------');
this.isIOS = uni.getSystemInfoSync().platform == 'ios' ? true : false;
if (!this.isIOS) {
this.objectFit = 'cover';
}
// 查询首页短视频列表
this.displayVideoPaging(this.page + 1, true);
if (this.playerList.length > 0) {
this.videoContext = uni.createVideoContext('myFollowVideo');
}
},
watch: {
refreshList(value) {
var me = this;
var newList = value;
if (newList != null && newList != undefined && newList.length > 0) {
me.playerList = newList;
}
// 重置
this.playerCur = 0;
this.currentIndex = 0;
this.contentOffsetY = 0;
},
playFollowStatus(val) {
var me = this;
try {
if (!val) {
me.videoContext.pause();
} else {
me.videoContext.play();
}
} catch (error) {
//TODO handle the exception
console.log(error);
}
}
},
methods: {
//进度start
formatTime(sec) {
const m = Math.floor(sec / 60);
const s = Math.floor(sec % 60);
return `${m}:${s < 10 ? '0' : ''}${s}`;
},
onTimeUpdate(e) {
// console.log(e.detail.currentTime);
if (e.detail.currentTime > 0.2) {
this.doplay(e.detail.currentTime);
}
if (this.isDragging) return;
this.currentTime = e.detail.currentTime;
this.duration = e.detail.duration;
this.updateProgress();
},
updateProgress() {
const percent = this.currentTime / this.duration;
this.progressWidth = Math.floor(system.screenWidth * percent);
},
startDrag(e) {
console.log('触发开始拖动');
this.isDragging = true;
},
onDragging(e) {
console.log('拖动中');
const touchX = e.touches[0].pageX;
const max = system.screenWidth;
const clampedX = Math.max(0, Math.min(touchX, max));
this.progressWidth = clampedX;
const percent = clampedX / max;
this.floatTime = percent * this.duration;
// 浮动提示偏移,避免越界
const floatBoxWidth = 60;
this.floatLeft = Math.max(0, Math.min(clampedX - floatBoxWidth / 2, max - floatBoxWidth));
},
endDrag() {
console.log('拖动结束');
const percent = this.progressWidth / system.screenWidth;
const seekTime = percent * this.duration;
if (!this.duration || isNaN(seekTime)) {
console.warn('duration 未就绪或 seekTime 非法');
this.isDragging = false;
return;
}
// 避免重复 seek
if (Math.abs(seekTime - this.currentTime) > 0.2) {
this.videoContext.seek(seekTime);
}
this.isDragging = false;
},
//进度end
//------------addd
setScrollRef(height) {
if (this.$refs['list'].setSpecialEffects) {
this.$refs['list'].setSpecialEffects({
id: this.parentId,
headerHeight: height
});
}
},
loadData() {
// 首次激活时被调用
console.log('loadData');
this.displayVideoPaging(this.page + 1, true);
},
clear() {
// 释放数据时被调用,参考 swiper-list 缓存配置
this.playerList.length = 0;
},
//---------------------------------
async freshCommentCounts() {
var me = this;
if (me.playerList == null || me.playerList == undefined || me.playerList.length == 0) {
return;
}
var userInfo = storage.getVlogUserInfo() || null;
if (userInfo == null) return;
var userId = userInfo.id;
var currentIndex = me.playerCur;
var vlog = me.playerList[currentIndex];
var vlogId = vlog.vlogId;
var res = await vlogComment(vlogId);
if (result.data.status == 200) {
me.thisVlogTotalComentCounts = result.data.data;
} else {
me.thisVlogTotalComentCounts = 0;
}
},
async likeOrDislikeVlog(isLike) {
var me = this;
var currentIndex = me.playerCur;
var vlog = me.playerList[currentIndex];
var myUserInfo = storage.getVlogUserInfo() || null;
if (isLike == 1) {
// 喜欢/点赞视频
if (myUserInfo == null) {
uni.navigateTo({
url: '/pages/passport/login',
animationType: 'slide-in-bottom'
});
return;
}
var userId = myUserInfo.id;
var result = await vlogLike({
userId,
vlogerId: vlog.vlogerId,
vlogId: vlog.vlogId
});
if (result.data.status == 200) {
me.reLikePlayList(vlog.vlogId);
me.refreshVlogCounts();
} else {
uni.showToast({
title: result.data.msg,
icon: 'none',
duration: 3000
});
}
} else if (isLike == 0) {
// 取消喜欢/点赞视频
if (myUserInfo == null) {
uni.navigateTo({
url: '/pages/passport/login',
animationType: 'slide-in-bottom'
});
return;
}
var userId = myUserInfo.id;
var result = await vlogUnLike({
userId,
vlogerId: vlog.vlogerId,
vlogId: vlog.vlogId
});
if (result.data.status == 200) {
me.reDislikePlayList(vlog.vlogId);
me.refreshVlogCounts();
} else {
uni.showToast({
title: result.data.msg,
icon: 'none',
duration: 3000
});
}
}
},
// 喜欢/点赞的list重新设置
reLikePlayList(vlogId) {
var me = this;
var playerList = me.playerList;
// 关注以后循环当前playerList修改对应粉丝关系的doIFollowVloger改为true
for (var i = 0; i < playerList.length; i++) {
var vlog = playerList[i];
if (vlog.vlogId == vlogId) {
vlog.doILikeThisVlog = true;
playerList.splice(i, 1, vlog);
}
}
me.playerList = playerList;
},
reDislikePlayList(vlogId) {
var me = this;
var playerList = me.playerList;
// 关注以后循环当前playerList修改对应粉丝关系的doIFollowVloger改为true
for (var i = 0; i < playerList.length; i++) {
var vlog = playerList[i];
if (vlog.vlogId == vlogId) {
vlog.doILikeThisVlog = false;
playerList.splice(i, 1, vlog);
}
}
me.playerList = playerList;
},
// 关注后的list重新设置
reFollowPlayList(vlogerId) {
var me = this;
var playerList = me.playerList;
// 关注以后循环当前playerList修改对应粉丝关系的doIFollowVloger改为true
for (var i = 0; i < playerList.length; i++) {
var vlog = playerList[i];
if (vlog.vlogerId == vlogerId) {
vlog.doIFollowVloger = true;
playerList.splice(i, 1, vlog);
}
}
me.playerList = playerList;
},
// 取关后的list重新设置
reCancelPlayList(vlogerId) {
var me = this;
var playerList = me.playerList;
// 关注以后循环当前playerList修改对应粉丝关系的doIFollowVloger改为true
for (var i = 0; i < playerList.length; i++) {
var vlog = playerList[i];
if (vlog.vlogerId == vlogerId) {
vlog.doIFollowVloger = false;
playerList.splice(i, 1, vlog);
}
}
me.playerList = playerList;
},
// 关注博主
async followMe(vlogerId) {
var me = this;
var myUserInfo = storage.getVlogUserInfo() || null;
if (myUserInfo == null) {
uni.navigateTo({
url: '/pages/passport/login',
animationType: 'slide-in-bottom'
});
return;
}
var userId = myUserInfo.id;
var result = await vlogFollow(userId, vlogerId);
if (result.data.status == 200) {
me.reFollowPlayList(vlogerId);
} else {
uni.showToast({
title: result.data.msg,
icon: 'none',
duration: 3000
});
}
},
goUserInfoSeeSee(userId) {
uni.setStorageSync('userPageId', userId);
uni.navigateTo({
url: '/pages/me/vlogerInfo?userPageId=' + userId
});
},
listScroll(e) {
this.progressFlag = false;
if (e.contentOffset.y > 0) {
this.$emit('showLoading');
}
},
downloadVlog() {
var me = this;
var serverUrl = app.globalData.serverUrl;
var currentIndex = me.playerCur;
var vlog = me.playerList[currentIndex];
var pendingLength = vlog.url;
},
copyLink() {
var me = me;
},
showQRCode() {
var me = me;
},
changeVlogToPrivate() {
var me = me;
},
// 下拉刷新的过程中改变头部tab显示的字样
onpullingdown(e) {
console.log('下拉中');
},
async onrefresh(e) {
// this.refreshing = true;
// setTimeout(() => {
// this.refreshing = false;
// this.$emit("hideLoading");
// this.refreshText = '↓ Pull To Refresh'
// }, 300)
// // 通过list组件的下拉刷新触发当前所在页面的下拉刷新
// uni.startPullDownRefresh();
console.log('开始');
this.refreshing = true;
// 通过list组件的下拉刷新触发当前所在页面的下拉刷新
uni.startPullDownRefresh();
await this.displayVideoPaging(1, true);
uni.stopPullDownRefresh();
this.refreshing = false;
this.$emit('hideLoading');
},
onplay: function (e) {
// let timer = setTimeout(() => {
// this.progressFlag = true;
// clearTimeout(timer);
// }, 500);
this.progressFlag = true;
if (uni.getSystemInfoSync().platform == 'ios') {
this.doplay(0.1);
}
},
onerror: function (err) {},
// timeupdate: function (e) {
// if (e.detail.currentTime > 0.2) {
// // this.$emit("play", e.detail.currentTime);
// this.doplay(e.detail.currentTime);
// }
// },
playOrPause() {
var me = this;
var playFollowStatus = this.playFollowStatus;
if (!playFollowStatus) {
me.videoContext.pause();
} else {
me.videoContext.play();
}
this.playFollowStatus = !playFollowStatus;
},
scroll: function (e) {
let originalIndex = this.currentIndex;
let isNext = false;
if (e.contentOffset.y < this.contentOffsetY) {
isNext = true;
}
this.contentOffsetY = e.contentOffset.y;
var num = this.playerList.length;
this.currentIndex = Math.round(Math.abs(this.contentOffsetY) / (e.contentSize.height / num));
this.onchange(this.currentIndex);
this.times = new Date().getTime();
// 判断如果视频列表总长度-当前下标少于3个则开始分页查询后续的视频并且追加到现有list中
if (this.playerList.length - this.currentIndex < 3) {
// 如果要分页的page和总数totalPage相等则没有更多
if (this.page == this.totalPage || this.totalPage == 0) {
this.$emit('hideLoading');
return;
}
this.displayVideoPaging(this.page + 1, false);
}
},
// 分页查询新的list并且追加到现有list中
async displayVideoPaging(page, needClearList) {
console.log(111111111111111111111111111111);
// 查询首页短视频列表
var me = this;
var myUserInfo = storage.getVlogUserInfo() || null;
var userId = '';
if (myUserInfo != null) {
userId = myUserInfo.id;
} else {
// return;
}
console.log(myUserInfo);
var result = await vlogFollowList(page, 10, userId);
console.log(result);
if (result.data.status == 200) {
var vlogList = result.data.data.rows;
var totalPage = result.data.data.total;
if (needClearList) {
me.playerList = [];
}
console.log(vlogList);
if (this.videoContext == null && vlogList.length > 0) {
this.videoContext = uni.createVideoContext('myFollowVideo');
}
me.playerList = me.playerList.concat(vlogList);
me.page = page;
me.totalPage = totalPage;
if (needClearList) {
me.setThisVlogInfo();
me.freshCommentCounts();
}
me.doTimer();
} else {
uni.showToast({
title: result.data.msg,
icon: 'none',
duration: 3000
});
}
},
doTimer() {
var me = this;
var t = setTimeout(() => {
if (me.playerList != null && me.playerList != undefined && me.playerList.length > 0) {
me.videoContext.pause();
me.playFollowStatus = false;
clearTimeout(t);
}
}, 3000);
},
doplay: function (time) {
if (time > 0) {
this.playerList[this.playerCur].play = true;
}
},
onchange: function (index) {
if (index != this.playerCur) {
this.playerList[this.playerCur].play = false;
this.playerCur = index;
this.playFollowStatus = true;
this.progressWidth = 0;
}
this.progressFlag = true;
// let timer = setTimeout(() => {
// this.progressFlag = true;
// clearTimeout(timer);
// }, 300);
this.refreshVlogCounts();
this.setThisVlogInfo();
this.freshCommentCounts();
},
// 设置当前vlog的信息
setThisVlogInfo() {
var me = this;
if (me.playerList != null && me.playerList != undefined && me.playerList.length > 0) {
var currentIndex = me.playerCur;
var vlog = me.playerList[currentIndex];
this.thisVlog = vlog;
this.thisVlogId = vlog.vlogId;
this.thisVlogerId = vlog.vlogerId;
}
},
async refreshVlogCounts() {
// 查询当前点赞数,重新赋值给当前视频
var me = this;
var currentIndex = me.playerCur;
var vlog = me.playerList[currentIndex];
var result = await vlogTotalLikedCounts(vlog.vlogId);
if (result.data.status == 200) {
var counts = result.data.data;
me.reChangeVlogLikedCountsInPlayList(vlog.vlogId, counts);
}
},
reChangeVlogLikedCountsInPlayList(vlogId, counts) {
var me = this;
var playerList = me.playerList;
// 关注以后循环当前playerList修改对应粉丝关系的doIFollowVloger改为true
for (var i = 0; i < playerList.length; i++) {
var vlog = playerList[i];
if (vlog.vlogId == vlogId) {
vlog.likeCounts = counts;
playerList.splice(i, 1, vlog);
}
}
me.playerList = playerList;
},
commentToggle() {
this.$refs.comment.open();
uni.hideTabBar({
animation: true
});
},
shareToggle() {
this.$refs.share.open();
uni.hideTabBar({
animation: true
});
}
}
};
</script>
<style scoped>
.progress-bar {
height: 60rpx;
width: 750rpx;
display: flex;
align-items: center;
position: fixed;
bottom: 0;
z-index: 99999;
}
.progress-background {
position: absolute;
bottom: 0;
width: 750rpx;
height: 2px;
background-color: #ccc;
z-index: 3;
}
.progress-foreground {
z-index: 2;
height: 2px;
background-color: #fff;
position: absolute;
bottom: 0;
left: 0;
}
.anm {
transition: width 0.25s linear;
}
.float-time {
position: fixed;
bottom: 2px;
width: 60px;
height: 30px;
background-color: rgba(0, 0, 0, 0.3);
color: white;
text-align: center;
align-items: center;
justify-content: center;
line-height: 30px;
font-size: 12px;
border-radius: 4px;
z-index: 5;
}
.abs-box-ad {
width: 750rpx;
position: absolute;
display: flex;
justify-content: center;
align-items: center;
}
.abs-box-ios {
position: relative;
width: 750rpx;
display: flex;
justify-content: center;
align-items: center;
}
.play-btn {
position: absolute;
width: 120rpx;
height: 120rpx;
}
.my-follow {
background-color: #000000;
align-items: center;
justify-content: center;
}
.warn-info {
color: #ffffff;
font-size: 36rpx;
font-weight: 600;
}
.icon {
width: 80rpx;
height: 80rpx;
opacity: 0.9;
}
.user-face {
width: 100rpx;
height: 100rpx;
border-radius: 100rpx;
}
.play-cd {
width: 150rpx;
height: 150rpx;
opacity: 0.8;
}
.refresh-info-txt {
color: #f1f1f1;
text-align: center;
font-size: 12px;
}
.publish-info-box {
position: absolute;
bottom: 200rpx;
left: 0;
right: 0;
padding-left: 20rpx;
padding-right: 20rpx;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.publish-info-vloger-name {
color: #ffffff;
font-size: 40rpx;
font-weight: 600;
padding: 10rpx;
}
.publish-info-music-box {
flex-direction: row;
align-items: center;
}
.publish-info-content {
color: #ffffff;
font-size: 28rpx;
font-weight: 400;
padding: 10rpx;
lines: 5;
width: 520rpx;
text-overflow: ellipsis;
}
.icon-fire {
width: 36rpx;
height: 36rpx;
}
.muisc-words {
color: #ffffff;
font-size: 28rpx;
padding: 10rpx;
width: 400rpx;
}
.some-counts {
font-size: 24rpx;
font-weight: 500;
text-align: center;
color: #ffffff;
margin-top: 2rpx;
}
.operation-box {
position: absolute;
top: 0;
bottom: 0;
right: 0;
align-items: center;
justify-content: center;
padding-right: 20rpx;
}
.operation-face-box {
border-radius: 100rpx;
border-color: #ffffff;
border-width: 3rpx;
}
.follow-me {
width: 40rpx;
height: 40rpx;
border-radius: 10px;
position: relative;
top: -20rpx;
}
.like-box {
flex-direction: column;
align-items: center;
margin-top: 30rpx;
}
.comment-and-share-box {
flex-direction: column;
align-items: center;
margin-top: 45rpx;
}
</style>