jiuyiUniapp/jiuyi/components/index/indexVideo.vue

642 lines
14 KiB
Vue
Raw Normal View History

2024-12-18 15:46:27 +08:00
<script setup>
/**
* 首页视频组件
* @property {Object} item 视频数据对象
* @property {Number} index 当前视频列表索引
* @property {Number} current 当前预览索引
*/
import {
onMounted,
ref,
reactive,
getCurrentInstance,
watch,
defineEmits,
computed,
nextTick
} from 'vue';
const {
proxy
} = getCurrentInstance()
// 顶部状态栏
import statusBar from '@/components/header/statusBar.vue'
// 工具库
import util from '@/common/js/util';
//
import api from '@/api/index.js';
// 视频进度条
import videoProgress from '@/components/index/videoProgress';
const props = defineProps({
// 当前视频对象
item: {
type: Object,
},
// 列表中的视频下标
index: {
type: Number,
},
// 当前列表的index
current: {
type: Number,
},
// 当前tab的index
tabIndex: {
type: Number,
},
// 模式 list列表 detail详情
mode: {
type: String,
default: 'list'
},
// 是否我自己 0不是 1是
isMine: {
type: [String, Number],
default: 0,
},
viewWidth: {
type: Number,
},
})
//
const emit = defineEmits(['showTime', 'showComment', 'showCollect', 'showFastCollect', 'showShareFirend', 'onPlay', 'onPause', 'like', 'detailMenu', 'onEnd', 'longtap'])
// 视频上下文对象
const videoCtx = ref(null)
// 是否播放
const playState = ref(true)
// 是否显示快速收藏列表
const collectFirst = ref(false)
// 视频时间
const videoTime = ref({
// 总长
duration: 0,
// 当前时间
currentTime: 0,
})
// 记录点击的数组
const tapList = reactive([])
// 点击时间戳
const tapTimer = ref(null)
// 是否在计数
const isTap = ref(false)
// 收藏按钮判定
const collectBtnActive = ref(false)
// 设置时间
const alarmTime = computed(() => {
let result = uni.$store.state.alarmTime
return result
})
// 挂载后调用
onMounted(() => {
// 视频上下文对象
videoCtx.value = uni.createVideoContext(`video${props.tabIndex}${props.index}`)
})
watch(() => props.current, (nV) => {
if (nV == props.index) play()
else pause()
})
// 手指触摸视频容器
function onTouchStart() {
//
if (isTap.value) {
tapList.length = 0
changeVideoPlay()
} else {
isTap.value = true
// 时间
let time = new Date().getTime()
tapList.push(time)
}
clearTimeout(tapTimer.value)
}
// 手指离开视频容器
function onTouchEnd() {
//
if (!isTap.value) return
isTap.value = false
let time = new Date().getTime()
let diff = time - tapList[tapList.length - 1]
// 判断长按
if (diff > 350) return
tapTimer.value = setTimeout(() => {
// 是否点赞
const isLike = props.item.isLike
let a = tapList.length
tapList.length = 0
switch (a) {
case 1:
changeVideoPlay()
break;
case 2:
console.log('公开赞')
emit('like', {
index: props.index,
isLike: isLike == 0 ? 0 : 1
})
break;
case 3:
console.log('隐私赞')
emit('like', {
index: props.index,
isLike: isLike == 0 ? 3 : 1
})
break;
}
}, 200)
}
// 切换视频播放
function changeVideoPlay() {
// 根据播放状态切换播放暂停
if (playState.value) pause()
else play()
}
// 视频播放
function play() {
let pages = getCurrentPages();
let page = pages[pages.length - 1];
if (props.index != props.current || !['pages/index/index', 'pages/index/videoDetail'].includes(page.route)) return
videoCtx.value.play()
}
// 视频暂停
function pause() {
videoCtx.value.pause()
}
// 视频播放回调
function onVideoPlay() {
playState.value = true
emit('onPlay')
}
// 视频暂停回调
function onVideoPause() {
playState.value = false
emit('onPause')
}
// 打开评论
function handleComment() {
emit('showComment', props.item)
}
// 打开计时闹钟
function handleTime() {
emit('showTime')
}
// 打开收藏弹窗
function showCollect() {
util.isLogin().then(rs => {
emit('showCollect', props.item)
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
// 切换快速收藏
function handleCollectFirst(ev) {
// 获取当前元素的宽高
const changedTouches = ev.changedTouches[0]
let x = getNumber(props.viewWidth) - getNumber(changedTouches.screenX) + getNumber(changedTouches.pageX)
let y = getNumber(changedTouches.screenY) - getNumber(changedTouches.pageY)
emit('showFastCollect', {
item: props.item,
position: {
x,
y,
}
})
return
}
/**
* 手指触摸收藏
* @param {Object} ev
*/
function handleCollectStar(ev) {
collectBtnActive.value = true
util.isLogin().then(rs => {
//
setTimeout(() => {
// 判断是否抬起
if (!collectBtnActive.value) {
// 收藏状态
if (!props.item.isCollect) {
// 单击出菜单
handleCollectFirst(ev)
return
} else cancelCollect()
} else {
setTimeout(() => {
// 长按出弹窗
showCollect()
}, 350)
}
}, 350)
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 手指触摸结束
* @param {Object} str
*/
function handleCollectEnd() {
collectBtnActive.value = false
//
}
// 获取数字
function getNumber(str) {
let result = Math.floor(Number(str))
return result
}
// 取消收藏
function cancelCollect() {
const detail = {
...props.item
}
// 取消收藏
api.video.cancelCollect({
query: {
// 视频id
videoId: detail.videoId,
},
}).then(rs => {
if (rs.code == 200) {
detail.isCollect = false
detail.collect--
uni.$emit('updateVideo', detail)
return
}
//
util.alert({
content: rs.msg,
showCancel: false,
})
})
}
// 分享到好友
function handleShareFirend() {
util.isLogin().then(rs => {
emit('showShareFirend', props.item)
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 点赞
* @param {Number} index 操作的视频下标
* @param {Number|String} isLike 点赞操作
*/
function handleLike(index, isLike) {
util.isLogin().then(rs => {
emit('like', {
index,
isLike,
})
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 私密赞
* @param {Object} index
*/
function handlePrivateLike(index) {
util.isLogin().then(rs => {
util.alert({
title: '提示',
content: '请确认,是否为隐私赞(隐私赞仅自己和作者可见)?',
confirmText: '隐私赞',
cancelText: '公开赞',
}).then(rs => {
if (rs.confirm) emit('like', {
index,
isLike: 3,
})
else emit('like', {
index,
isLike: 0
})
})
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 详情菜单
* @param {Object} item
*/
function handleDetailMenu(item) {
emit('detailMenu')
}
// 播放变化
function handleTimeupdate(ev) {
videoTime.value = ev.detail
// console.log('videoTime.value', videoTime.value)
}
// 进度条拖拽结束
function onProgressEnd(ev) {
videoCtx.value.seek(parseInt(ev.time))
}
/**
* 查看用户主页
* @param {Object} item
*/
function handleUser(item) {
uni.navigateTo({
url: util.setUrl('/pages/index/videoHome', {
userId: item.userId
})
})
}
// 视频出现缓冲
function handleWaiting(ev) {
if (props.index == props.current) play()
else pause()
}
// 长按
function longtap(ev) {
emit('longtap')
}
//
defineExpose({
play,
pause,
videoTime,
item: props.item,
playState,
videoCtx: () => videoCtx.value,
})
</script>
<template>
<view class="container f1 pr" ref="videoBoxRef">
<!-- 视频层 -->
<view class="main f1">
<view class="videoBox f1" @longpress="longtap" @touchmove.stop="" @touchstart="onTouchStart" @touchend="onTouchEnd" @touchcancel="onTouchEnd">
<statusBar />
<video class="video f1" :id="'video' + tabIndex + index" :src="item.format_videoUrl" :poster="item.format_imageUrl" :http-cache="false" :show-fullscreen-btn="false" :enable-progress-gesture="false" :controls="false" @play="onVideoPlay" @pause="onVideoPause" :show-center-play-btn="false" @timeupdate="handleTimeupdate" @waiting="handleWaiting" :play-strategy="2" :initial-time="0" :loop="true" />
</view>
<!-- 视频进度条 -->
<view class="videoProgress" @touchmove.stop="" @touchstart.stop="" @touchend.stop="">
<videoProgress :time="videoTime" @change="onProgressEnd" :viewWidth="viewWidth" />
</view>
</view>
<!-- 暂停蒙版 -->
<view class="pausePanel pfull fmid" v-if="!playState">
<!-- 暂停按钮 -->
<image class="pauseImg" src="@/static/pause.png" mode="aspectFit" />
</view>
<!-- 右侧操作区 -->
<view class="panelRight pa t0 b0 r0">
<statusBar />
<view class="head"></view>
<view class="f1 jcr pl5 pt40 pr20">
<!-- 操作台 -->
<view class="operate f1">
<!-- 用户头像 -->
<navigator :url="util.setUrl('/pages/index/videoHome',{userId:item.userId})" class="item pr mb10">
<view class="col">
<image class="wh80 cir" :src="item.format_header" mode="aspectFill" />
<view class="focus pa" v-if="!item.isAttention">
<image class="wh40" src="@/static/indexAtt.png" mode="aspectFit" />
</view>
</view>
</navigator>
<!-- 点赞 -->
<view class="item">
<view class="col">
<view class="pr">
<image class="wh50" src="/static/indexLike.png" mode="aspectFit" v-if="item.isLike == 0" @click="handleLike(index, 0)" @longpress="handlePrivateLike(index)" />
<image class="wh50" src="/static/indexLike1.png" mode="aspectFit" v-else-if="item.isLike == 1" @click="handleLike(index, 1)" />
<!-- 私密赞的图标 -->
<image class="wh50" src="/static/privateLike.png" mode="aspectFit" v-else-if="item.isLike == 3" @click="handleLike(index, 1)" />
</view>
<view class="txt mt10">
<text class="text">{{ item.likes }}</text>
</view>
</view>
</view>
<!-- 留言 -->
<view class="item" @click="handleComment">
<view class="col">
<image class="wh50" src="@/static/indexMsg.png" mode="aspectFit" />
<view class="txt mt10">
<text class="text">{{ item.comment }}</text>
</view>
</view>
</view>
<!-- 收藏 -->
<view class="item df fdr">
<view class="col" @touchstart="handleCollectStar" @touchend="handleCollectEnd" ref="collectBtn">
<image class="wh50" src="@/static/indexCollect1.png" mode="aspectFit" v-if="item.isCollect" />
<image class="wh50" src="@/static/indexCollect.png" mode="aspectFit" v-else />
<view class="txt mt10">
<text class="text">{{ item.collect }}</text>
</view>
</view>
</view>
<!-- 分享 -->
<view class="item" @click="handleShareFirend">
<view class="col">
<image class="wh50" src="@/static/indexShare.png" mode="aspectFit" />
<view class="txt mt10">
<text class="text">分享</text>
</view>
</view>
</view>
<!-- 闹钟 -->
<view class="item money" @click="handleTime" v-if="mode == 'list'">
<view class="col">
<image class="wh80" src="/static/indexMoney1.png" mode="aspectFit" v-if="alarmTime" />
<image class="wh80" src="/static/indexMoney.png" mode="aspectFit" v-else />
</view>
</view>
<!-- 详情菜单 -->
<view class="item money" @click="handleTime" v-if="mode == 'detail' && isMine == 1">
<view class="col wh90 fmid tac" @click="handleDetailMenu">
<uni-icons type="more-filled" color="#d8d8d8" size="70rpx" />
</view>
</view>
</view>
</view>
</view>
<!-- 底部用户信息 -->
<view class="panelBottom pa l0 r0 b0 pl40 pb30">
<!-- 商品信息 -->
<view class="goods df fdr mb20 br10" v-if="0">
<!-- 商品图片 -->
<image class="image wh100 mr15 br10" src="/static/openPage.png" mode="aspectFill" />
<view class="df fdc jcsb f1">
<view class="">
<text class="cfff f28">果农大王霹雳美味榴莲果子</text>
</view>
<view class="info df fdr aic mr10">
<text class="price mr10 cfff f28 b">超低价6.66</text>
<text class="cfff f1 f20">已售666</text>
<uni-icons type="close" color="#fff" />
</view>
</view>
</view>
<!-- 用户 -->
<view class="user" @click="handleUser(item)">
<text class="cfff f36">@{{ item.userName }}</text>
</view>
<!-- 简介 -->
<view class="desc mt5">
<text class="t2hd cfff f28">{{ item.title }}</text>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
// 视频盒子
.container {
background-color: #000;
}
// 视频时长
.duration {
margin-top: 120rpx;
width: 750rpx;
background-color: rgba(255, 255, 255, .3);
// 时间时长
.line {
width: 0;
height: 2rpx;
background-color: rgba(255, 255, 255, .8);
transition-duration: .25s;
}
}
// 暂停蒙层
.pausePanel {
background-color: rgba(0, 0, 0, .5);
//
.pauseImg {
width: 140rpx;
height: 140rpx;
}
}
// 右侧屏幕
.panelRight {
align-items: flex-end;
.text {
text-align: center;
color: #fff;
font-size: 24rpx;
}
// 操作台
.operate {
flex: 1;
justify-content: flex-end;
align-items: flex-end;
text-align: center;
padding-bottom: 130rpx;
.item {
margin: 5rpx 0;
padding: 10rpx 5rpx;
&.money {
padding: 0;
}
//
.col {
align-items: center;
justify-content: center;
width: 80rpx;
}
}
}
// 关注的小加号
.focus {
align-items: center;
left: 0;
right: 0;
bottom: 0;
}
}
// 底部
.panelBottom {
padding-right: 150rpx;
//
.goods {
padding: 10rpx;
width: 480rpx;
margin-bottom: 20rpx;
background-color: rgba(0, 0, 0, .3);
}
}
//
.videoProgress {
// background-color: red;
}
</style>