jiuyiUniapp/jiuyi/pages/index/index.nvue

854 lines
20 KiB
Plaintext

<script setup>
/**
* 首页 视频页
*/
import {
ref,
reactive,
getCurrentInstance,
computed,
nextTick,
} from 'vue';
import {
onLoad,
onReady,
onHide,
onShow,
onUnload,
} from '@dcloudio/uni-app'
// 工具库
import util from '@/common/js/util';
// api
import api from '@/api/index.js'
// 顶部状态栏
import statusBar from '@/components/header/statusBar.vue'
// 引入视频
import indexVideo from '@/components/index/indexVideo.vue'
// 底部菜单
import footerMneu from '@/components/footerMenu/footerMenu.vue'
// 计时闹钟弹窗
import timeAlt from '@/components/index/time.vue'
// 评论弹窗
import commentAlt from '@/components/index/commentArea.vue'
// 收藏弹窗
import collectAlt from '@/components/index/collect.vue'
// 分享到好友弹窗
import shareFirendAlt from '@/components/index/shareFirend.vue'
// 左侧菜单弹窗
import leftMenuAlt from "@/components/index/leftMenu.vue"
// 有效读秒唱片
import disc from '@/components/index/disc.vue'
// 长按更多菜单
import moreMenu from '@/components/index/moreMenu.vue'
// 快捷收藏
import fastCollect from '@/components/index/fastCollect.vue';
// 青少年模式
import teen from '@/components/index/teen.vue'
const {
proxy
} = getCurrentInstance()
const dom = uni.requireNativePlugin('dom')
// 钟表提示弹窗
const oclockWindow = ref(false)
// 读秒
const readSecond = reactive({
// 单个视频最大
max: 20,
// 累计
count: 0,
// 总数
total: 0,
// 总数最大
totalMax: 300,
// 定时器
timer: null,
})
// tab选项
const tab = reactive([
// {
// name: '同城',
// },
{
name: '关注',
load: false,
listData: () => attList.data,
getList: () => getAttList(),
getMoreList: () => getMoreAttList(),
refreshList: () => refreshAttList(),
},
{
name: '推荐',
load: false,
listData: () => recList.data,
getList: () => getRecList(),
getMoreList: () => getMoreRecList(),
refreshList: () => refreshRecList(),
},
])
// tab下标
const tabIndex = ref(1)
// 起始值
const startY = ref(0)
// 上一个播放索引
const currentLast = reactive([0, 0])
// 当前播放索引
const current = reactive([0, 0])
// 列表
const recList = reactive({
data: [],
pageNum: 1,
total: 0,
homePageSize: 7,
shopPageSize: 2,
otherPageSize: 1,
})
// 关注的视频列表
const attList = reactive({
data: [],
pageSize: 10,
pageNum: 1,
total: 0,
timer: null,
})
// 容器高度 用来控制视频、遮罩等高度 用来解决nvue模式下没有100%高度的问题
const viewHeight = ref(0)
// 容器宽度
const viewWidth = ref(0)
// 唱片顶部位置
const discOffsetTop = ref(0)
// 用户信息
const userinfo = computed(() => {
let result = uni.$store.state.userinfo || {}
return result
})
// 当前tab选中
const tabCurrent = computed(() => {
let result = tab[tabIndex.value]
return result
})
// 当前视频
const currentVideoRef = computed(() => {
let result = proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]]
return result
})
// 加载完成之后
onLoad(() => {
// 设备信息
const systemInfo = uni.getSystemInfoSync()
discOffsetTop.value = systemInfo.safeAreaInsets.top + 44 + 10
// 判断是否提醒过闹铃
if (!uni.getStorageSync('alarmAlt')) {
setTimeout(() => {
oclockWindow.value = true
}, 1000)
}
// 获取列表
tabCurrent.value.getList()
// 登录
util.isLogin().then(rs => {
// 获取今日观看任务
getTask()
// 如果没开启青少年模式
if (userinfo.value.youth != 1) {
setTimeout(() => {
proxy.$refs.teenRef.open()
}, 2000)
}
})
// 监听登录
uni.$on('login', () => {
// 获取列表
tabCurrent.value.refreshList()
// 获取今日观看任务
getTask()
})
// 监听登录
uni.$on('logout', () => {
// 获取列表
tabCurrent.value.refreshList()
})
// 视频数据被修改
uni.$on('updateVideo', (item) => {
// 校验
if (!item && !item.videoId) return
const list = tabCurrent.value.listData()
const findIndex = list.findIndex(node => node.videoId == item.videoId)
if (findIndex >= 0) list.splice(findIndex, 1, {
...tabCurrent.value.listData()[findIndex],
...item,
})
})
// 视频用户关注
uni.$on('focusUser', (param) => {
if (!param.userId) return
// 重载关注列表
refreshAttList()
// 切换推荐列表的关注状态
for (var index = 0; index < recList.data.length; i++) {
const item = recList.data[index]
if (item.userId == param.userId) {
item.isAttention = param.result
recList.data.splice(index, 1, item)
}
}
})
})
onReady(() => {
// 获取视频容器节点信息
dom.getComponentRect(proxy.$refs.containerRef[0], (option) => {
viewHeight.value = option.size.height
viewWidth.value = option.size.width
})
})
onShow(() => {
// 触发自定义tabbar函数
uni.$emit('changeMine', 'default')
})
onHide(() => {
// 暂停视频
proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]].pause()
})
onUnload(() => {
uni.$off('login')
uni.$off('logout')
uni.$off('updateVideo')
uni.$off('focusUser')
})
// 获取今日观看任务
function getTask() {
api.video.viewingTasks().then(rs => {
if (rs.code == 200) {
const result = rs.data
if (!result) return
if (result) readSecond.total = Number(result.seconds) || 0
return
}
})
}
// 有效读秒增加
function readSecondAdd() {
clearInterval(readSecond.timer)
// 如果今天已达成 则继不继续计算有效读秒
if (readSecond.total >= readSecond.totalMax) return
readSecond.timer = setInterval(() => {
// 判断当前视频是否达到最大有效读秒
if (readSecond.count > readSecond.max) {
// 增加计数
readSecond.total += readSecond.count
// 重置数量
readSecond.count = 0
// 如果达成记录
if (readSecond.total >= readSecond.totalMax) {
// util.alert('您已完成今日观看任务')
return
}
clearInterval(readSecond.timer)
} else readSecond.count++
}, 1000)
}
// 有效读秒暂停
function readSecondPause() {
clearInterval(readSecond.timer)
}
// 重载关注列表
function refreshAttList() {
attList.pageNum = 1
attList.total = 0
getAttList()
}
// 获取更多关注列表
function getMoreAttList() {
if (attList.total <= attList.data.length) return
attList.pageNum++
getAttList()
}
// 获取关注列表
function getAttList() {
//
api.video.followVideo({
query: {
pageSize: attList.pageSize,
pageNum: attList.pageNum,
},
}).then(rs => {
// console.log('followVideo', rs)
if (rs.code == 200) {
// 合并
attList.data.push(...rs.rows.map(item => {
item.format_videoUrl = util.format_url(item.videoUrl, 'video')
item.format_header = util.format_url(item.header, 'img')
return item
}))
// 总数
attList.total = rs.total
return
}
util.alert({
content: rs.msg,
showCancel: false,
})
})
}
// 重载推荐列表
function refreshRecList() {
recList.pageNum = 1
recList.total = 0
getRecList()
}
// 获取更多推荐视频
function getMoreRecList() {
if (recList.total <= recList.data.length) return
recList.pageNum++
getRecList()
}
// 获取推荐视频
function getRecList() {
Promise.all([getHomeVideo(), getShopVideo(), getOtherVideo()]).then(rs => {
// 列表
const list = rs.rows.sort(() => Math.random() - 0.5)
// 总数
recList.total = rs.reduce((last, now) => last + now.total, 0)
// 第一页
if (recList.pageNum == 1) recList.data.length = 0
// 合并
recList.data.push(...list.map(item => {
item.format_videoUrl = util.format_url(item.videoUrl, 'video')
item.format_imageUrl = util.format_url(item.imageUrl, 'img')
item.format_header = util.format_url(item.header, 'img')
// 播放倍速
item.speed = 1
return item
}))
console.log('result', recList.data, rs)
// 如果有多的视频 并且当前数据为一条
if (recList.total > 1 && recList.data.length <= 1) getMoreRecList()
setTimeout(() => {
const pages = getCurrentPages()
// 判断是否当前页
if (pages[pages.length - 1].route == 'pages/index/index') {
proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]].playState.value =
true
proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]].play()
}
}, 50)
}).catch(rs => {
console.log('index catch', rs)
})
}
// 获取首页推荐视频
function getHomeVideo() {
return new Promise((resolve, reject) => {
// 获取首页分页视频
api.video.homeVideo({
query: {
userId: userinfo.value.userId || '',
pageNum: recList.pageNum,
pageSize: recList.homePageSize,
}
}).then(rs => {
if (rs.code == 200) {
resolve(rs)
return
}
reject(rs)
util.alert({
content: rs.msg,
showCancel: false,
})
})
})
}
// 商家视频分页
function getShopVideo() {
return new Promise((resolve, reject) => {
// 获取首页分页视频
api.video.businessHomeVideo({
query: {
userId: userinfo.value.userId || '',
pageNum: recList.pageNum,
pageSize: recList.shopPageSize,
}
}).then(rs => {
if (rs.code == 200) {
resolve(rs)
return
}
reject(rs)
util.alert({
content: rs.msg,
showCancel: false,
})
})
})
}
// 获取首页其他视频
function getOtherVideo() {
return new Promise((resolve, reject) => {
// 获取首页分页视频
api.video.otherHomeVideo({
query: {
userId: userinfo.value.userId || '',
pageNum: recList.pageNum,
pageSize: recList.otherPageSize,
}
}).then(rs => {
if (rs.code == 200) {
resolve(rs)
return
}
reject(rs)
util.alert({
content: rs.msg,
showCancel: false,
})
})
})
}
/**
* 触摸事件完成 滚动到当前位置
* @param {Number} target 滚动到下标的元素
*/
function scrollTo(target) {
// tab下标
const tab_index = tabIndex.value
// 当前cell对象
const element = proxy.$refs[`cellRef${tab_index}`][target]
// 上一个视频组件
const lastVideoRef = proxy.$refs[`videoRef${tab_index}`][currentLast[tab_index]]
// 滚动到对应位置
dom.scrollToElement(element, {
animated: true
})
// 如果视频切换
if (current[tab_index] != currentLast[tab_index]) {
nextTick(() => {
// 暂停上一个视频
lastVideoRef.playState.value = false
//
lastVideoRef.pause()
// 播放当前视频
proxy.$refs[`videoRef${tab_index}`][current[tab_index]].playState.value = true
proxy.$refs[`videoRef${tab_index}`][current[tab_index]].play()
})
// 停止当前有效读秒统计
readSecondPause()
// 浏览记录
browseLog(lastVideoRef)
// 清空累计继续计时
readSecond.total += readSecond.count
//
if (readSecond.total < readSecond.totalMax) readSecondAdd()
}
}
/**
* 触摸开始
* @param {Object} ev 默认事件
* @param {Number} index 所在swiper滑块
*/
function onTouchstart(ev, index) {
Object.assign(currentLast, current)
startY.value = ev.changedTouches[0].screenY
}
/**
* 触摸结束
* @param {Object} ev 默认事件
* @param {Number} index 所在swiper滑块
*/
function onTouchend(ev, index) {
// 结束
const endY = ev.changedTouches[0].screenY
// 列表
const list = tabCurrent.value.listData()
if (!list[0]) return
// 根据滑动距离
if (endY - startY.value < -50) {
if (current[index] < list.length - 1) current[index]++
scrollTo(current[index])
} else if (endY - startY.value > 50) {
if (current[index] > 0) current[index]--
scrollTo(current[index])
} else if (endY - startY.value == 0) {
//
} else {
scrollTo(current[index])
}
}
/**
* 切换tab下标
* @param {Number} index 点击的item
*/
function handle_tab(index) {
if (tabIndex.value === index) return
// 如果是关注 但是没有用户id
if (tab[index].name == '关注' && !userinfo.value.userId) {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
return
}
// 如果有
if (proxy.$refs[`videoRef${tabIndex.value}`]) {
// 上一个视频对象
const lastVideoRef = proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]]
// 暂停当前播放的视频
lastVideoRef.pause()
// 停止当前有效读秒统计
readSecondPause()
// 浏览记录
browseLog(lastVideoRef)
// 清空累计继续计时
readSecond.total += readSecond.count
//
if (readSecond.total < readSecond.totalMax) readSecondAdd()
}
tabIndex.value = index
// 根据是否加载过判断 播放还是获取
if (tabCurrent.value.load && proxy.$refs[`videoRef${index}`]) proxy.$refs[`videoRef${index}`][current[index]]
.play()
else tabCurrent.value.getList()
// 已加载
tab[tabIndex.value].load = true
}
// 打开计时闹钟弹窗
function handleShowTime() {
proxy.$refs.timeRef.open()
}
/**
* 打开评论弹窗
* @param {Object} item 当前列表项
*/
function handleShowCommentAlt(item) {
proxy.$refs.commentRef.open(item)
// 暂停当前视频
currentVideoRef.value.pause()
}
/**
* 打开收藏弹窗
* @param {Object} item 视频对象
*/
function handleShowCollectAlt(item) {
proxy.$refs.collectRef.open(item)
}
/**
* 打开快速收藏弹窗
* @param {Object} ev 视频对象和节点位置信息
*/
function handleShowFastCollect(ev) {
proxy.$refs.fastCollectRef.open(ev)
}
/**
* 打开分享给好友弹窗
* @param {Object} item 当前列表项
*/
function handleShowShareFirend(item) {
proxy.$refs.shareFirendRef.open(item)
}
// 视频播放
function handleVideoOnPlay() {
if (proxy.$refs.discRef) proxy.$refs.discRef.play()
//
if (readSecond.total < readSecond.totalMax) readSecondAdd()
}
// 视频暂停
function handleVideoOnPause() {
if (proxy.$refs.discRef) proxy.$refs.discRef.pause()
readSecondPause()
}
// 观看视频记录
function browseLog(element) {
util.isLogin().then(rs => {
if (readSecond.count == 0) return
//
api.video.browseLog({
query: {
videoId: element.item.videoId,
seconds: readSecond.count,
}
}).then(rs => {
if (rs.code != 200) {
console.log('browseLog err', rs)
}
})
})
}
/**
* 视频点赞
* @param {Object} param 见下
* @param {Number} param.index 操作的视频下标
* @param {Number|String} param.isLike 点赞操作
*/
function videoLike(param) {
const {
index,
isLike
} = param
const item = tabCurrent.value.listData()[index]
// 操作状态
let type = 1
// 0未点赞 1已点赞 3私密赞
if (item.isLike == 0) type = isLike
//
api.video.videoLike({
query: {
// 0赞 1取消赞 3私密赞
type,
// 视频id
videoId: item.videoId,
}
}).then(rs => {
if (rs.code == 200) {
// 同步点赞状态
item.isLike = {
0: 1,
1: 0,
3: 3,
} [type]
// 取消减数量 否则增加
type == 1 ? item.likes-- : item.likes++
uni.$emit('updateVideo', item)
return
}
util.alert({
content: rs.msg,
showCancel: false,
})
})
}
// 设置闹铃
function setAlarm() {
oclockWindow.value = false
// 设置闹铃弹窗不再弹出
uni.setStorageSync('alarmAlt', true)
}
// 打开左侧菜单
function showLeftMenu() {
util.isLogin().then(rs => {
proxy.$refs.leftMenuRef.open()
}).catch(() => {
uni.navigateTo({
url: '/pages/login/loginPhone'
})
})
}
/**
* 修改当前视频播放速度
* @param {Object} item
*/
function handleSpeed(item) {
// 速度
const speed = item.value
//
const tab_index = tabIndex.value
// 修改视频倍速
proxy.$refs[`videoRef${tab_index}`][current[tab_index]].videoCtx().playbackRate(speed)
// 修改视频数据对象
tabCurrent.value.listData()[current[tab_index]].speed = speed
}
/**
* 视频播放进度更新
* @param {Object} event
*/
function onTimeUpdate(event) {
// console.log('event', event)
// 视频时间
videoTime.value = {
...event.videoTime,
videoCurrentTime: event.videoCurrentTime,
}
}
// 唤起闹铃
function showAlarm() {
proxy.$refs.timeRef.open()
}
</script>
<template>
<!-- 页面内容 -->
<view class="page pr f1">
<!-- 顶部内容 -->
<view class="top pf t0 l0 r0">
<statusBar />
<view class="menu head fdr jcsa aic plr40">
<view class="sider" @click="showLeftMenu">
<image class="wh40" src="@/static/indexList.png" mode="aspectFit" />
</view>
<view class="f1">
<view class="tab fdr jcc" :key="tabIndex">
<view class="list" v-for="(item,index) in tab" :key="index"
:class="[{'active': index === tabIndex}]" @click.stop="handle_tab(index)">
<view class="txt">
<text class="text">{{item.name}}</text>
</view>
<view class="line"></view>
</view>
</view>
</view>
<navigator url="/pages/index/search" class="search">
<image class="wh65" src="@/static/indexSearch.png" mode="aspectFit" />
</navigator>
</view>
</view>
<!-- 有效读秒唱片 -->
<view class="disc pf r0" :style="{top: discOffsetTop+'px'}" v-if="userinfo.userId">
<disc ref="discRef" />
</view>
<template v-for="(item, index) in tab" :key="index">
<view class="f1" v-if="tabIndex == index" ref="containerRef">
<!-- 主要内容区域 -->
<list class="container f1" :show-scrollbar="false" @touchstart="onTouchstart($event,index)"
@touchend="onTouchend($event,index)" @loadmore="item.getMoreList">
<cell class="cell" :style="[{height: viewHeight + 'px'}]" :ref="`cellRef` + index"
v-for="(secItem,secIndex) in item.listData()" :key="secIndex" @click.stop>
<!-- 视频 -->
<indexVideo :ref="'videoRef' + index" :tabIndex="index" :current="current[index]"
:item="secItem" :index="secIndex" @showTime="handleShowTime"
@showComment="handleShowCommentAlt" @showCollect="handleShowCollectAlt"
@showShareFirend="handleShowShareFirend" @onPlay="handleVideoOnPlay"
@onPause="handleVideoOnPause" @like="videoLike" @longtap="$refs.moreMenuRef.open(secItem)"
@showFastCollect="handleShowFastCollect" :viewWidth="viewWidth" />
</cell>
</list>
</view>
</template>
<!-- 计时提示 -->
<view class="oclockHint pa pfull fmid" @touchstart.stop="" @click.stop="setAlarm" v-if="oclockWindow">
<image class="image" src="/static/indexOclock.png" mode="widthFix" />
</view>
<!-- 底部导航 -->
<footerMenu ref="footerMenuRef" page="index" subject="dark" />
</view>
<!-- 快捷收藏 -->
<fastCollect ref="fastCollectRef" />
<!-- 长按菜单 -->
<moreMenu ref="moreMenuRef" @changeSpeed="handleSpeed" />
<!-- 闹钟弹窗 -->
<timeAlt ref="timeRef" />
<!-- 评论弹窗 -->
<commentAlt ref="commentRef" />
<!-- 收藏弹窗 -->
<collectAlt ref="collectRef" />
<!-- 分享到好友弹窗 -->
<shareFirendAlt ref="shareFirendRef" />
<!-- 左侧菜单弹窗 -->
<leftMenuAlt ref="leftMenuRef" />
<!-- 青少年模式 -->
<teen ref="teenRef" @setting="showAlarm" />
</template>
<style lang="scss" scoped>
//
.page {
background-color: #161616;
}
// 顶部导航
.top {
z-index: 10;
}
// 分栏
.tab {
.list {
margin: 0 20rpx;
&.active {
.text {
color: #fff;
}
.line {
opacity: 1;
}
}
.text {
color: rgba(255, 255, 255, .7);
}
.line {
margin: 0 10rpx;
height: 5rpx;
background-color: #fff;
opacity: 0;
}
}
}
// 钟表提示
.oclockHint {
background-color: rgba(0, 0, 0, .3);
.image {
margin-top: 300rpx;
width: 750rpx;
}
}
// 唱片盒子
.disc {
margin-right: 20rpx;
}
</style>