Uni-app 详情页 播放视频功能
Posted YoloAva
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Uni-app 详情页 播放视频功能相关的知识,希望对你有一定的参考价值。
逻辑:
1.课程详情页加载后,用token判断用户是否登录,登录状态则调用checkAuth的接口(只传入courseId),后端会返回hasAuth为true/false
2.点击某一章节时,登录状态则调用checkAuth的接口(传入courseId & chapterId),后端会返回hasAuth为true/false和videoId,如果这俩都有,则跳页面到视频播放页
3.跳转到视频播放页,加载完毕:请求课程详情接口、getPlay接口(传入courseId & chapterId)——后端返回formt,videoURL,duration等
功能:
4.自动跳转至上次播放时间:
加载完视频播放页,调checkHistory得到上次的记录——接着使用视频控制进度的组件uni.createVideoContext的seek方法,将checkHistory返回的lastTime传入
5.记录播放历史:
video标签上绑定@timeupdate方法,这个方法在播放进度变化时触发,也就是只要在播放就在不停的触发,所以每次进入方法都this.timer++,当它等于25时也就是每隔6s,调用recordHistory记录播放进度的接口,并使this.timer变回为1
6.倍速播放:
下载f-drop-menu插件,绑定videoList,以及changeSpeed事件,使用视频控制进度的组件uni.createVideoContext的playbackRate方法
7.下一节/重学一次
在data中,分别用new Map来new空的实例对象 ,Map字典数据结构是为了方便以[键,值]的形式存储数据,给每个小节做标记。
在初次进入视频页,调课程详情接口时,返回的数据是章嵌套节的结构。所以先循环章,再循环节,将这个实例对象使用set方法,将小节信息设置成键值对的数组,目的是为了给每个小节做标记。
给video绑定了ended事件——在播放结束时用这个实例对象的get方法查找读取key对应的键值,找到下一个章节对应的信息。
判断——如果下个章节有内容,就提示下一节/重学的选项。
8.视频防盗
在video标签中加上视频来源,否则后端会判断为为非法入侵。
<template>
<view class="course-play">
<u-navbar title="视频" class="header"></u-navbar>
<video id="myVideo" :src="videoUrl" controls :header="'Referer':'http://testapp.cn'" :poster="cover"
@timeupdate="timeupdate" @ended="playEnded" class="video-play"></video>
<view class="video-control">
<view class="" @tap="dropSpeed">倍速:speed</view>
<f-drop-menu :list="videoList" v-model="show" @click="changeSpeed"></f-drop-menu>
</view>
<view class="tabs">
<u-tabs-swiper ref="uTabs" :list="list" :current="current" @change="tabsChange" :is-scroll="false"
swiperWidth="750"></u-tabs-swiper>
</view>
<swiper class="swiper" :current="swiperCurrent">
<swiper-item class="swiper-item">
<scroll-view scroll-y style="height: 800rpx;width: 100%;">
<u-collapse accordion class="ui-collapse" :border="false" event-type="close">
<u-collapse-item :title="item.chapterName" :open="index === 0" v-for="(item,index) in catalogueList"
:key="item.id">
<view class="child-item" v-for="child in item.children" :key="child.id"
:class="child.id === current ? 'collapse-active':''" @tap="goPlay(child)">
<view class="video-box">视频</view>
<view class="content-box">child.chapterName</view>
</view>
</u-collapse-item>
</u-collapse>
</scroll-view>
</swiper-item>
<swiper-item class="swiper-item">
<scroll-view scroll-y style="height: 800rpx;width: 100%;">
<Download :downloadList="downloadList"></Download>
</scroll-view>
</swiper-item>
</swiper>
<u-toast ref="uToast" />
</view>
</template>
<script>
import
mapState
from 'vuex'
import Download from '@/common/course-detail/download/index.vue'
import courseApi from '@/service/coursedetail.js'
import playApi from '@/service/play.js'
export default
data()
return
speed: 'X1.0',
show: false, //默认不显示
videoList: [
name: '0.5',
style:
,
name: '1.0',
style:
,
name: '1.25',
style:
,
name: '1.5',
style:
],
courseId: null,
chapterId: null,
swiperCurrent: 0,
current: 0,
list: [
name: '目录'
,
name: '下载笔记代码'
],
downloadList: [],
catalogueList: [],
clientHeight: 0,
cover: null, // 封面图
videoUrl: null, // 视频
totalTime: null, // 总时长
timer: 1, // 控制
indexMap: new Map(), //保存
maps: new Map(),
videoContext: null, // 视频控制组件
,
computed:
...mapState(
userInfo: state => state.user.userInfo
),
curId()
// curId
if (this.chapterId !== 0)
return this.chapterId
else
// 第一个章节里面第一个小节
return this.catalogueList[0].children[0].id
,
,
components:
Download
,
onReady()
//视频进度控制组件
this.videoContext = uni.createVideoContext('myVideo')
// 动态获取头部高
const query = uni.createSelectorQuery().in(this);
query.select('.header').boundingClientRect(data =>
uni.getSystemInfo(
success: (res) =>
this.clientHeight = res.screenHeight - data.height
);
).exec();
// video
query.select('.video-play').boundingClientRect(data =>
this.clientHeight = this.clientHeight - data.height
).exec();
// video-control
query.select('.video-control').boundingClientRect(data =>
this.clientHeight = this.clientHeight - data.height
).exec();
// 切换
query.select('.tabs').boundingClientRect(data =>
this.clientHeight = this.clientHeight - data.height
).exec();
,
onLoad(params)
this.courseId = params.courseId
this.chapterId = params.chapterId
// 如果是点击了重学跳过来的 就有restudy
let restudy = params.restudy
this.__init()
if (!restudy)
this.checkHistory()
,
methods:
//点击倍速
dropSpeed()
this.show = !this.show
,
//切换倍速
changeSpeed(item)
this.speed = 'X' + item.name
this.videoContext.playbackRate(Number(item.name))
this.show = false
,
__init()
// 获取课程 详情
//获取课程详情
courseApi.getClassDetail(
classId: this.courseId
).then(res =>
if (res.meta.code === '200')
//当前课程的目录
this.catalogueList = res.data.data.bizCourseChapters
//笔记代码
this.downloadList = res.data.data.bizCourseAttachments
let idx = 0;
//先循环章
res.data.data.bizCourseChapters.map((item, index) =>
//再循环节
item.children.map((ele) =>
//本课程所有小节 组成一个不重复的数组 给每个小节做标记
this.indexMap.set(ele.id, idx) // 节 ['节ID',0,'节ID',1]
this.maps.set('chapter_' + idx, ele) //['chapter_0',节信息,'chapter_1',节信息]
idx++
)
)
else //非200
).catch(err =>
console.log(err)
)
// 视频播放
playApi.getPlay(
courseId: this.courseId,
chapterId: this.chapterId
).then(res =>
if (res.meta.code === '200')
let playInfoList = res.data.playInfo.playInfoList
let chapterInfo = res.data.chapterInfo //封面图
let arr = playInfoList.filter(item =>
if (item.format === 'mp4')
return item
)
console.log(arr) //[]
let obj = arr[0] //含videoUrl,format,duration的对象
this.videoUrl = obj.playURL
this.totalTime = obj.duration
this.cover = chapterInfo.chapterLitpic
).catch(err =>
console.log(err)
)
,
//查询上一次播放的进度
checkHistory()
playApi.checkHistory(
memberId: this.userInfo.id,
courseId: this.courseId,
chapterId: this.chapterId
).then(res =>
if (res.meta.code === '200') //查询到进度 并且用控件调整进度
this.videoContext.seek(Number(res.data.data.lastTime))
).catch(err =>
console.log(err)
)
,
//历史记录
recordHistory(lastTime)
playApi.recordHistory(
chapterId: this.chapterId,
courseId: this.courseId,
memberId: this.userInfo.id,
lastTime: lastTime //上次观看时间
).then(res =>
if (res.meta.code === '200') //记录成功
this.timer = 1
else
//记录失败
).catch(err =>
console.log(err)
)
,
//播放进度变化时触发 历史记录 存储播放进度
timeupdate(e)
let curTime = e.detail.currentTime
console.log(curTime)
if (this.timer === 25)
//调一次记录历史记录的接口
this.recordHistory(curTime)
console.log('记录')
this.timer++ //每次变化+1
,
//播放结束
playEnded(e)
this.recordHistory(this.totalTime)
// this.indexMap.set(ele.id,idx)// 节 ['节ID',0,'节ID',1]
// this.maps.set('chapter_'+idx,ele)//['chapter_0',节信息,'chapter_1',节信息]
//查找对应项
let index = this.indexMap.get(this.chapterId); //idx
let nextChapter = this.maps.get('chapter_' + (index + 1)) //后面一章的节信息
if (nextChapter) //如果后面还有
//下一个小节 视频
uni.showModal(
title: '提示',
content: '继续下一小节',
confirmText: '下一小节',
cancelText: '重学',
success: res =>
if (res.confirm)
//继续学 下一小节
uni.redirectTo(
url: '/pages/course-play/course-play?courseId=' + this.courseId + '&chapterId=' +
nextChapter.id
)
else if (res.cancel)
//重学
uni.redirectTo(
url: '/pages/course-play/course-play?courseId=' + this.courseId + '&chapterId=' + this
.chapterId + '&restudy=' + 'true'
)
);
else
this.$refs.uToast.show(
title: '恭喜你学完本课程',
type: 'success',
icon: false
)
,
//播放另一个章节
goPlay()
this.chapterId = child.id
//关闭当前页 重定向
uni.redirectTo(
url: '/pages/course-play/course-play?courseId=' + this.courseId + '&chapterId=' + child.id
)
,
tabsChange()
</script>
uni-app 视频播放遇到的问题-video
最近开发一个项目,一个视频列表,点击其中一个视频,全屏播放(像大屏预览似的全屏),图上覆盖截图,全屏和回放图标。
本文涉及到的知识点:
1。视频播放video组件使用;
2。图片下载uni.downloadFile;
3。上拉加载,下拉刷新
4。subNVue;
5。父子组件传值
弹出的全屏窗口:
videoTest.vue
<template>
<view style="height: 100%;">
<videoView :srcPath="src" :picPath="picPath"></videoView>
</view>
</template>
<script>
import videoView from './video.nvue'
export default
components:
videoView,
,
data()
return
picPath:'',
src: 'https://img.cdn.aliyun.dcloud.net.cn/guide/uniapp/%E7%AC%AC1%E8%AE%B2%EF%BC%88uni-app%E4%BA%A7%E5%93%81%E4%BB%8B%E7%BB%8D%EF%BC%89-%20DCloud%E5%AE%98%E6%96%B9%E8%A7%86%E9%A2%91%E6%95%99%E7%A8%8B@20181126-lite.m4v',
,
onLoad(params)
this.src = params.path;
this.picPath = params.picPath;
,
methods:
goBack()
uni.navigateBack();//跳转到上一个页面
,
</script>
<style>
</style>
video.nvue
<template>
<view class="video_bg" @click="goBack">
<video id="myVideo" :src="srcPath" class="video_width"
@error="videoErrorCallback" controls="true"
autoplay="true" direction="90" objectFit="fill"
@play="beginPlay" @fullscreenchange="changeFull"
enable-play-gesture="true" :show-center-play-btn="true"
:poster="picPath" @click="">
<cover-image src="../../static/images/check/icon_full.png" class="cover_img_common right_20" @click="fullScreen()"></cover-image>
<cover-image src="../../static/images/check/icon_crop.png" class="cover_img_common right_65"></cover-image>
</video>
</view>
</template>
<script>
export default
data()
return
videoContext:null,
,
mounted()
this.videoContext = uni.createVideoContext('myVideo');
,
props:srcPath:String,picPath:String,
methods:
//返回上一页前先结束播放,并将屏幕方向还是设为默认
goBack()
this.videoContext.stop();
uni.navigateBack();
plus.screen.lockOrientation('default');
,
videoErrorCallback: function(e)
uni.showModal(
content: e.target.errMsg,
showCancel: false
)
,
//监听播放状态
beginPlay(params)
console.log('playStatus',params);
this.playStatus = 1;
,
//只有视频开始播放后,全屏按钮可用
fullScreen(item)
if(this.playStatus == 1)
this.videoContext.requestFullScreen();
,
changeFull(params)
console.log('changeFull',params);
if(params.detail.fullScreen)
//不能动态设置进度条的显示
// this.videoContext.controls = true;
else
plus.screen.lockOrientation('portrait-primary');
//屏幕方向数值: 0 - 竖屏; 90 - 横屏,HOME键在右; 180 - 反向竖屏; -90 - 反向横屏,HOME'键在左
// let orit = plus.navigator.getOrientation();
// if((orit==0)||(orit==180))
// //竖屏做的操作
// else
// //横屏做的操作
//
</script>
<style>
page
height: 100%;
.video_width
width: 100%;
/* z-index: 100; */
.cover_img_common
width: 25px; height: 25px;float: right;position: absolute;bottom: 15px;
.right_20
right: 20px;
.right_65
right: 65px;
.right_110
right: 110px;
.video_bg
height:100%;display: flex;justify-content: center;align-items: center;
</style>
横竖屏方向:
“orientation” : [
//竖屏正方向
“portrait-primary”,
//竖屏反方向
“portrait-secondary”,
//横屏正方向
“landscape-primary”,
//横屏反方向
“landscape-secondary”,
//自然方向
“default”
]
视频列表页:
列表中做了两种处理:
1。有视频时显示视频,并显示封面,但点击视频时,并不触发播放,是弹出全屏页面,在全屏页面进行全屏,截图等操作;这种处理需要用subNVue特殊处理一下弹出框,要不然弹出框会被视频遮挡。
2。另一种处理是不管有没有视频,都显示图片,有视频的图片给它显示播放按钮和截屏按钮,但点击图片时,仍然会弹出全屏页面。被注释掉的代码就是只显示图片的处理。
<template>
<view>
<uni-nav-bar title="远程巡店" backgroundColor="#074498" color="white" fixed="true"
statusBar='true' @clickRight="searchPerson">
<view slot="right" class="flex_vcenter">
<image src="../../static/images/workbeach/icon_search.png" class="imgSize"></image>
</view>
</uni-nav-bar>
<uni-list :border="false">
<UniListItemCustomer v-for="(item,index) in deviceList" :key="index" :border="false" direction="column" class="item" clickable @click="gotoDetail(item.deviceId)">
<template v-slot:header>
<view>
<!-- <view v-if="item.realTimeResults" style="display: flex;align-items: center;justify-content: center;">
<image ref='img' :src="item.realTimeResults[2].picUrl" class="video_width"
@error="handleImageError1" @click="fullPlay(item)"></image>
<image src="../../static/images/check/icon_play.png" class="play_icon"></image>
<image src="../../static/images/check/icon_full.png" class="cover_img_common right_20" @click="fullScreen(item)"></image>
<image src="../../static/images/check/icon_crop.png" class="cover_img_common right_65" @click="cropPic(item.deviceId)"></image>
</view> -->
<video id="myVideo" :src="item.realTimeResults[2].hls" class="video_width"
@error="videoErrorCallback" controls="false" v-if="item.realTimeResults"
autoplay="false" direction="90" objectFit="fill"
@play="beginPlay" @fullscreenchange="changeFull"
enable-play-gesture="true"
:poster="item.realTimeResults[2].picUrl">
<cover-image @click="fullPlay(item)"></cover-image>
<cover-image src="../../static/images/check/icon_full.png" class="cover_img_common right_20" @click="fullScreen(item)"></cover-image>
<cover-image src="../../static/images/check/icon_crop.png" class="cover_img_common right_65" @click="cropPic(item.deviceId)"></cover-image>
</video>
<image src="../../static/images/icon_check_default.png" class="check_img" @error="handleImageError" v-else></image>
</view>
</template>
</UniListItemCustomer>
</uni-list>
<uni-load-more :status="more" :contentText="contentText" @clickLoadMore="more=='error'?getPersonelList():''"></uni-load-more>
</view>
</template>
<script>
import ApiService from './service.js'
import Tips from '../../utils/tips.js'
import UniListItemCustomer from '../../components/uni-list-item/uni-list-item-customer.vue'
export default
components:
UniListItemCustomer
,
data()
return
deviceList:[],
searchCondition:,
more:'more',
pageNum:1,
pageSize:10,
contentText:
contentdown: '上拉加载更多',
contentrefresh: '正在加载中',
contentnomore: '没有更多数据了',
contenterror: '数据异常,请点击此处重新加载'
,
reLoad:false,
videoContext:null,
playStatus:0,
,
created()
this.getVideoInfoList();
console.log('created');
,
mounted()
console.log('mounted');
,
onReady()
this.videoContext = uni.createVideoContext('myVideo')
,
onLoad()
uni.$on('receiveData',(options)=>
console.log('onLoad====receiveData',JSON.stringify(options));
this.searchCondition = options;
this.reLoad = true;
this.pageNum = 1;
this.getVideoInfoList();
)
,
onReachBottom()
console.log('onReachBottom');
if(this.more != 'noMore')
this.more = 'more';
this.getVideoInfoList();
,
onPullDownRefresh()
console.log('onPullDownRefresh');
this.reLoad = true;
this.pageNum = 1;
this.getVideoInfoList();
,
methods:
goBack()
uni.navigateBack();//跳转到上一个页面
,
searchPerson()
const subNvue = uni.getSubNVueById('netDialog');
subNvue.show();
,
async getVideoInfoList()
this.searchCondition['pageSize'] = this.pageSize;
this.searchCondition['pageNum'] = this.pageNum;
this.more = 'loading';
let res = await ApiService.getVideoInfoList(this.searchCondition);
// console.log('getPersonelList',JSON.stringify(res));
if(res.success)
if(res.data.code == 200)
this.deviceList = this.reLoad?res.data.data.rows:this.deviceList.concat(res.data.data.rows);
if(this.deviceList.length != res.data.data.total)
this.pageNum += 1;
this.more = 'more';
else
this.more = 'noMore';
else
if(this.pageNum == 1)
this.deviceList = [];
Tips.toast(res.data.msg);
this.more = 'error';
else
Tips.toast(res.message);
this.more = 'error';
if(this.reLoad)
uni.stopPullDownRefresh();
this.reLoad = false;
,
gotoDetail(item)
console.log(item)
uni.navigateTo(
url: 'personnelDetail?item='+JSON.stringify(item)
)
,
videoErrorCallback: function(e)
uni.showModal(
content: e.target.errMsg,
showCancel: false
)
,
handleImageError1(props)
console.log('handleImageError',props);
this.$refs.img.src = '../../static/images/icon_check_default.png'
,
handleImageError(props)
console.log('handleImageError',props);
this.picPath = '../../static/images/icon_check_default.png'
,
beginPlay(params)
console.log('playStatus',params);
this.playStatus = 1;
,
async cropPic(deviceId)
let res = await ApiService.getSnapshot("deviceId":deviceId);
console.log('cropPic',res);
if(res.success)
if(res.data.code == 200)
let picPath = res.data.data.url;
console.log('cropPic picPath=',picPath);
//下载截图
uni.downloadFile(
url:picPath,
success: (res) =>
console.log('下载成功',res);
if(res.statusCode === 200)
console.log('下载成功');
Tips.toast('下载成功');
,
fail: (error) =>
console.log('下载失败',error);
Tips.toast('下载失败');
)
else
Tips.toast(res.data.msg);
else
Tips.toast(res.message);
,
fullScreen(item)
if(this.playStatus == 1)
this.videoContext.requestFullScreen();
,
changeFull(params)
console.log('changeFull',params);
if(params.detail.fullScreen == true)
this.videoContext.controls = true;
,
fullPlay(item)
console.log('3333');
uni.navigateTo(
url:'./videoTest?path='+item.realTimeResults[2].hls+'&picPath='+item.realTimeResults[2].picUrl
)
</script>
<style lang="scss" scoped>
@import '../../styles/base.scss';
page
background: #f9f9f9;
overflow-x: hidden;
padding: 0;
.nav-style
font-family: 'PingFangSC-Semibold', 'PingFang SC Semibold', 'PingFang SC';
height: 45px;
.status_style
float: right;position: absolute;right: 35px;
font-size: 16px;
font-family: 'PingFangSC-Regular', 'PingFang SC';
.common_color
color:#074498
.title_size
font-size: 16px;
.status_color_use
color: #73B703;
.status_color_forbidden
color: #D9001B;
.item_text
/* padding-top: 8px; */
font-family: 'PingFangSC-Regular', 'PingFang SC';
color: #333333;
font-size: 14px;
line-height: 25px;
.arrow_right
width: 20px;
height: 25px;
float: right;
right: 20px;
position: absolute;
.item_content
padding: 10px 0px 0px 0px;
display: flex;flex-direction: column;
.divider
height: 0.5px;width: 100%; background-color: #dbdbdb;margin-top: 10px;
.flex_vcenter
display: flex; align-items: center;
.item
border-bottom: #f9f9f9 8px solid;
/* padding: 0px 15px 0px 15px; */
.video_width
width: 100%;
z-index: 100;
background-image: url(../../static/images/icon_check_default.png);
background-size: 100% 100%;
opacity: 0.5;
.cover_img_common
width: 25px; height: 25px;float: right;position: absolute;bottom: 15px;
.play_icon
position: absolute;width: 60px; height: 60px;
.right_20
right: 20px;
.right_65
right: 65px;
.right_110
right: 110px;
.imgSize
width: 25px; height: 25px;
.check_img
width: 100%;opacity: 0.5;
</style>
video层级过高,导致弹窗显示在底层,解决方法:
"path": "pages/netcheck/NetCheckList",
"style":
"navigationBarTitleText": "远程巡店",
"enablePullDownRefresh":true,//下拉刷新配置
"app-plus":
"pullToRefresh":
"style":"circle",
"offset": "88px"
,
"subNVues":[
"id": "netDialog", // 唯一标识
"path": "pages/netcheck/subNVue/DialogNet", // 页面路径
"type": "popup", //原生子窗口内置样式,可取值:'popup',弹出层;"navigationBar",导航栏
"style":
"position":"fixed",
// "height": "60px",
// "top":"40px",
// "top":"40%",
"background":"transparent"
]
,
DialogNet.vue
<template>
<view class="my-confirm-notice" id="netDialog">
<view class="confirm-content-wrap1" @click="">
<!-- 标题 -->
<div class="unipop__ui_tit">
<text class="unipop__ui_tit_text">查询条件</text>
<image src="../../../static/images/workbeach/icon_close.png" class="close_image" @click="close()"></image>
</div>
<!-- 内容 -->
<div class="content_margin">
<div class="flex_row_center label_text label_margin">
<text style="font-size: 16px;">实体名称:</text>
<input type="text" class="inputStyle" placeholder="实体名称" @input="onKeyInput" :value="userName" >
</div>
</div>
<div v-if="btns" class="unipop__ui_btns">
<text v-for="(item,index) in btns" :key="index" class="btn" :style="item.style" @tap="btnTaped(item)">item.text</text>
</div>
</view>
</view>
</template>
<script>
export default
data()
return
btns:[
text: '重置',
style: 'color: #ffffff',
,
text: '确定',
style: 'color: #ffffff',
],
selectCondition:,
userName:'',
,
mounted()
console.log('mounted==================1111111111111111')
,
methods:
close()
this.userName = '';
this.selectCondition = ;
const subNvue = uni.getSubNVueById('netDialog');
subNvue.hide();
,
btnTaped(item)
console.log(item);
if(item.text == '重置')
this.userName = '';
this.selectCondition = ;
else
uni.$emit('receiveData',this.selectCondition);
this.close();
,
onKeyInput(event)
this.userName = event.detail.value;
this.selectCondition['storeName'] = this.userName;
,
</script>
<style lang="scss">
.my-confirm-notice
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.5);
// z-index: 19998;
/* 这里防止当用户长按屏幕,出现的黑色背景色块,以及 iPhone 横平时字体的缩放问题 */
// -webkit-text-size-adjust: 100%;
// -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
display: flex;
align-items: center;
justify-content: center;
.confirm-content-wrap1
position: relative;
left: 0;
right: 0;
width: 675rpx;
/* #ifndef APP-PLUS-NVUE */
height: auto;
margin: 0 auto;
user-select: none;
/* #endif */
background-color: white;
border-radius: 10px;
// z-index: 999;
.unipop__ui_tit
background-color: #074498;
width: 675rpx;
border-top-left-radius: 10px;
border-top-right-radius: 10px;
height: 44px;
display: flex;
align-items: center;
justify-content: center;
.unipop__ui_tit_text
color: white;
text-align: center;
line-height: 44px;
width: 675rpx;
font-size: 16px;
.close_image
z-index: 100;
position: absolute;
/* #ifndef APP-PLUS-NVUE */
float: right;
/* #endif */
right: 10px;width:30px;height: 30px;
.unipop__ui_btns
height: 80px;
justify-content: space-around;
display: flex;
flex-direction: row;
padding-left: 20px;
padding-right: 20px;
align-items: center;
.btn
width: 80px;
height: 34px;
background-color: #074498;
line-height: 34px;
text-align: center;
font-size: 13px;
border-radius: 6rpx;
.content_margin
padding: 15px 0px 5px 0px;
.flex_row_center
display: flex; flex-direction: row; justify-content: center; align-items: center;
.label_text
font-size: 28rpx;color: #555555;
.label_margin
margin-top: 36rpx;
.inputStyle
border: #bababa 0.5px solid; width: 169px;height: 40px;
/* #ifndef APP-PLUS-NVUE */
box-sizing: border-box;
/* #endif */
font-size: 14px; color: #aaaaaa; padding: 3px 5px;
.top_level
position: fixed;z-index: 100;width: 675rpx;
</style>
注意:
(1)subNVue弹窗的显示,只能使用js的show方法,无法携带参数。
(2)参数传递使用uni.$emit和uni.$on
以上是关于Uni-app 详情页 播放视频功能的主要内容,如果未能解决你的问题,请参考以下文章