jiuyiUniapp/jiuyi2/pages/index/index.nvue

847 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({
// 定时器
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,
pageSize: 10,
})
// 关注的视频列表
const attList = reactive({
data: [],
pageSize: 10,
pageNum: 1,
total: 0,
timer: null,
})
// 容器高度 用来控制视频、遮罩等高度 用来解决nvue模式下没有100%高度的问题
const viewSize = reactive({
height: 0,
width: 0,
})
// 唱片顶部位置
const discOffsetTop = ref(0)
// 有效读秒顶部位置
const complete2Top = ref(0)
// 结束特效配置
const completeConfig = {
'complete1': {
time: 2,
},
'complete2': {
time: 1.5,
},
}
// 结束特效键 complete1优先任务特效 complete2有效读秒特效
const completeKey = ref('')
// 底部菜单高度
const footerMenuHeight = ref(0)
// 当前任务
const task = computed(() => uni.$store.state.task)
// 用户信息
const userinfo = computed(() => uni.$store.state.userinfo || {})
// 当前tab选中
const tabCurrent = computed(() => tab[tabIndex.value])
// 当前视频元素对象
const currentVideoRef = computed(() => proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]])
// 系统配置
const config = computed(() => uni.$store.state.config)
// 加载完成之后
onLoad(() => {
// 设备信息
const systemInfo = uni.getSystemInfoSync()
// 唱片高度
discOffsetTop.value = systemInfo.safeAreaInsets.top + 44 + 30
// 特效完成高度
complete2Top.value = systemInfo.safeAreaInsets.top + 44 + 150
// 获取列表
tabCurrent.value.getList()
// 判断是否提醒过闹铃
if (!uni.getStorageSync('alarmAlt')) {
setTimeout(() => {
oclockWindow.value = true
}, 1000)
}
//
util.getMyTask()
// 监听登录
uni.$on('login', () => {
recList.data.length = 0
attList.data.length = 0
nextTick(() => {
// 获取列表
tabCurrent.value.refreshList()
})
})
// 监听登录
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.id == item.id)
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(() => {
setTimeout(() => {
// 获取视频容器节点信息
dom.getComponentRect(proxy.$refs.containerRef[0], (option) => {
viewSize.height = option.size.height
viewSize.width = option.size.width
})
}, 50)
})
onShow(() => {
// 触发自定义tabbar函数
uni.$emit('changeMine', 'default')
})
onHide(() => {
// 暂停视频
if (proxy.$refs[`videoRef${tabIndex.value}`]) {
proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]].pause()
}
})
onUnload(() => {
uni.$off('login')
uni.$off('logout')
uni.$off('updateVideo')
uni.$off('focusUser')
})
// 重载关注列表
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 => {
handleListData(rs, attList)
})
}
// 重载推荐列表
function refreshRecList() {
console.log('refreshRecList')
recList.pageNum = 1
recList.total = 0
getRecList()
}
// 获取更多推荐视频
function getMoreRecList() {
if (recList.total <= recList.data.length) return
recList.pageNum++
getRecList()
}
// 获取推荐视频
function getRecList() {
console.log('getRecList')
// 获取首页分页视频
api.video.homeVideo({
query: {
userId: userinfo.value.id || 0,
pageNum: recList.pageNum,
pageSize: recList.pageSize,
}
}).then(rs => {
console.log('getRecList then')
handleListData(rs, recList)
})
}
/**
* 数据列表
* @param {Object} rs 接口返回数据
* @param {Object} obj 数据对象
*/
function handleListData(rs, obj) {
if (rs.code == 200) {
// 如果第一页
if (obj.pageNum == 1) obj.data.length = 0
nextTick(() => {
// 总数
obj.total = rs.total
// 合并
obj.data.push(...rs.rows.map(item => {
// 初始播放时间
item.readSecond = 0
return item
}))
// 延时监听播放
setTimeout(() => {
const pages = getCurrentPages()
// 判断是否当前页
if (pages[pages.length - 1].route != 'pages/index/index') {
proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]].playState.value =
false
proxy.$refs[`videoRef${tabIndex.value}`][current[tabIndex.value]].pause()
}
}, 500)
console.log('listdata final', rs, obj)
})
return
}
util.alert({
content: rs.msg,
showCancel: false,
})
}
/**
* 观看视频记录
* @param {Object} element 记录的视频元素对象
*/
function browseLog(element) {
util.isLogin().then(rs => {
// if (readSecond.count == 0) return
const data = {
// 视频id
videoId: element.item.id,
// 有效读秒时间统计
viewingDuration: Math.floor(element.item.readSecond),
// 视频秒数
videoDescription: Math.floor(element.videoTime.currentTime),
//
task: 0,
}
console.log('browseLog data', data)
//
api.video.browseLog({
data,
}).then(rs => {
if (rs.code == 200) {
// 计数
const result = rs.data
// 现在的有效读秒
const taskValue = task.value
console.log('browseLog result', rs, taskValue)
// 如果不是第一次统计
if (taskValue.viewingDuration != 0) {
// 如果原来任务是优先任务 当前任务是有效读秒
if (taskValue.taskType == 0 && result.taskType == 1) {
// 优先任务完成 播放烟花动画
console.log('优先任务完成 播放烟花动画')
handleCompleteMode('complete1')
}
// 如果原来任务任务是有效读秒 并且新返回的数据小于当前的任务进度
else if (result.taskType == 1 && (result.viewingDuration < taskValue
.viewingDuration)) {
console.log('有效读秒完成 播放任务完成动画')
handleCompleteMode('complete2')
}
}
//
uni.$store.commit('setState', {
key: 'task',
value: result,
})
return
} else {
console.log('browseLog err', rs)
}
})
})
}
/**
* 触发完成特效
* @param {Object} mode ['complete1'|'complete2'] 需要触发的动画键
*/
function handleCompleteMode(mode) {
// 开启对应的特效
completeKey.value = mode
setTimeout(() => {
// 关闭特效
completeKey.value = ''
}, completeConfig[mode].time * 1000)
}
// 有效读秒增加
function readSecondAdd() {
clearInterval(readSecond.timer)
// 当前视频对象
const item = tab[tabIndex.value].listData()[current[tabIndex.value]]
// 开启计时器
readSecond.timer = setInterval(() => {
// 当前视频有效读秒 小于等于 最大有效读秒限制(视频最大时长-2s,设置的有效读秒上限)
if (item.readSecond < Math.min(Math.floor(item.videoDuration) - 2, config.value
.EFFECTIVE_VIDEO_TIME)) {
// 增加这条视频的有效读秒
item.readSecond++
} else {
// 暂停有效读秒的统计
readSecondPause()
}
}, 1000)
}
// 暂停有效读秒的统计
function readSecondPause() {
// 暂停唱片
proxy.$refs.discRef && proxy.$refs.discRef.pause()
clearInterval(readSecond.timer)
}
/**
* 触摸事件完成 滚动到当前位置
* @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]) {
// 停止当前有效读秒统计
readSecondPause()
// 浏览记录
browseLog(lastVideoRef)
// 开始记录
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.id) {
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
// 开始记录有效读秒
readSecondAdd()
}
tabIndex.value = index
// 根据是否加载过判断 播放还是获取
if (tabCurrent.value.load && proxy.$refs[`videoRef${index}`]) proxy.$refs[`videoRef${index}`][current[index]]
.play()
else tabCurrent.value.refreshList()
// 已加载
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()
// 开始计时有效读秒
readSecondAdd()
}
// 视频暂停
function handleVideoOnPause() {
if (proxy.$refs.discRef) proxy.$refs.discRef.pause()
readSecondPause()
}
/**
* 视频点赞
* @param {Object} param 见下
* @param {Number} param.index 操作的视频下标
* @param {Number|String} param.isLike 0.点赞 1.取消点赞
* @param {Number|String} param.likeType 点赞类型 0.公开赞 1.隐私赞
*/
function videoLike(param) {
// 当前项
const item = tabCurrent.value.listData()[param.index]
console.log('data', {
// 视频id
videoId: item.id,
// 点赞用户id
likeUserId: userinfo.value.id,
// 被点赞用户id
targetUserId: item.userId,
// 点赞类型 0.公开赞 1.隐私赞
likeType: param.likeType,
// //点赞 0.点赞 1.取消点赞
isLike: param.isLike,
})
//
api.video.videoLike({
data: {
// 视频id
videoId: item.id,
// 点赞用户id
likeUserId: userinfo.value.id,
// 被点赞用户id
targetUserId: item.userId,
// 点赞类型 0.公开赞 1.隐私赞
likeType: param.likeType,
// //点赞 0.点赞 1.取消点赞
isLike: param.isLike,
}
}).then(rs => {
if (rs.code == 200) {
uni.$emit('updateVideo', {
...item,
...rs.data,
})
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
}
// 唤起闹铃
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>
<!-- 优先任务结算 -->
<template v-if="completeKey == 'complete1'">
<image class="complete1" src="/static/complete1.gif" mode="aspectFit" />
</template>
<!-- 有效读秒结算 -->
<view class="complete2" :style="{top: complete2Top + 'px'}" v-if="completeKey == 'complete2'">
<image class="image" src="/static/complete2.gif" mode="aspectFit" />
</view>
<!-- 有效读秒唱片 -->
<view class="disc pf r0" :style="{top: discOffsetTop+'px'}" v-if="userinfo.id">
<disc ref="discRef" />
</view>
<template v-for="(item, index) in tab" :key="index">
<view class="container f1" v-if="tabIndex == index" ref="containerRef">
<!-- 主要内容区域 -->
<list class="listBox f1" :show-scrollbar="false" @touchstart="onTouchstart($event,index)"
@touchend="onTouchend($event,index)" @loadmore="item.getMoreList">
<cell class="cell" :style="[{height: viewSize.height + 'px'}]" :ref="`cellRef` + index"
v-for="(secItem,secIndex) in item.listData()" :key="secItem.id" @click.stop>
<!-- <template v-if="current[tabIndex] < secIndex + 2 && current[tabIndex] > secIndex - 2"> -->
<!-- 视频 -->
<indexVideo :ref="'videoRef' + index" :tabIndex="index" :current="current[tabIndex]"
:width="viewSize.width" :height="viewSize.height" :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" />
<!-- </template> -->
</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" />
<!-- 青少年模式 -->
<teen ref="teenRef" />
<!-- 长按菜单 -->
<moreMenu ref="moreMenuRef" @changeSpeed="handleSpeed" />
<!-- 闹钟弹窗 -->
<timeAlt ref="timeRef" />
<!-- 评论弹窗 -->
<commentAlt ref="commentRef" />
<!-- 收藏弹窗 -->
<collectAlt ref="collectRef" />
<!-- 分享到好友弹窗 -->
<shareFirendAlt ref="shareFirendRef" />
<!-- 左侧菜单弹窗 -->
<leftMenuAlt ref="leftMenuRef" />
</template>
<style lang="scss" scoped>
//
.page {
background-color: #161616;
}
// 优先任务完成
.complete1 {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 10;
transform: translateY(100rpx);
}
// 有效读秒完成
.complete2 {
position: fixed;
justify-content: flex-end;
right: 30rpx;
z-index: 10;
//
.image {
width: 80rpx;
height: 80rpx;
}
}
// 顶部导航
.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>