You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
zhishifufei_uniapp/pages/special/task_info.vue

726 lines
18 KiB

10 months ago
<template>
<BaseContainer class="task-info">
<NavBar :title="title" />
<!-- 视频窗口 -->
<view :style="{ height: playerHeight + 'px' }" class="player">
<!-- <view :hidden="taskInfo.type == 2" id="J_prismPlayer"></view> -->
<image v-if="taskInfo.type == 2" :src="taskInfo.image" />
<video id="player" @loadedmetadata="handleVideoLoaded" @timeupdate="onPlayerTimeupdate" @play="play"
class="player-self" v-if="taskInfo.type == 3" @ended="onPlayerEnded" :poster="taskInfo.image"
:src="taskInfo.link" object-fit="cover"></video>
</view>
<view class="task-info-body">
<view>{{ taskInfo.title }}</view>
<view>已播放{{ taskInfo.play_count }}</view>
</view>
<!-- 音频播放 -->
<view v-if="taskInfo.type == 2" class="audio">
<view class="control flex flex-center">
<view class="iconfont " @click="audioTab(0)">
<image style="width:44rpx; height:48rpx;" :src=" getImgPath(`/wap/first/zsff/images/c2.png`)" mode="" />
</view>
<view @click="audioPlay" class="play-btn">
<i class="iconfont icon" >
<image style="width:100rpx; height:100rpx;" :src="audioPaused ?getImgPath(`/wap/first/zsff/images/plays.png`) : getImgPath(`/wap/first/zsff/images/paue.png`)" mode="" />
</i>
</view>
<view class="iconfont " type="button" @click="audioTab(1)">
<image style="width:44rpx; height:48rpx;" :src="getImgPath(`/wap/first/zsff/images/c1.png`)" mode="" />
</view>
</view>
<view class="progress">
<view class="time">{{ currentTime | format(duration) }}</view>
<view class="bar" @click="audioSeek">
<view :style="{ width: audioRange + '%' }" class="range" data-id="range">
<view class="dot" data-id="dot" @touchmove="audioMove" @touchend="audioMoveEnd"></view>
</view>
</view>
<view class="time">{{ duration | format }}</view>
</view>
</view>
<!-- 目录详情 -->
<view class="tabbar">
<view :class="{ on: tabIndex == 0 }" @click="tabIndex = 0">目录</view>
<view :class="{ on: tabIndex == 1 }" @click="tabIndex = 1">详情</view>
</view>
<view v-if="tabIndex == 0">
<view class="catalogue">
<view class="catalogue-item" v-for="(item, index) in taskList" :key="item.id" :class="{ on: item.id == taskId }"
@click="playTask(item)">
<view>
<image v-if="item.type == 1" class="img" :src="getImgPath('/wap/first/zsff/images/media1.png')" />
<image v-else-if="item.type == 2" class="img" :src="getImgPath('/wap/first/zsff/images/media2.png')" />
<image v-else-if="item.type == 3" class="img" :src="getImgPath('/wap/first/zsff/images/media3.png')" />
</view>
<view class="text">
<view class="title">
{{ index >= 9 ? index + 1 : "0" + (index + 1) }} | {{ item.title }}
</view>
<view class="try-progress">
<view v-if="item.watch && item.watch.percentage" class="progress">
已学习{{ item.watch.percentage }}%
</view>
<view v-else class="progress no">未学习</view>
</view>
</view>
<view class="status">
<i v-if="item.is_free" :class="[item.pay_status ? 'iconsuozi' : 'iconziyuan2', 'iconfont']"></i>
<span v-else class="free">免费</span>
</view>
</view>
</view>
</view>
8 months ago
<view v-else-if="tabIndex == 1" style="background: #fff;">
<mp-html class="content" container-style="padding: 30rpx; background: #ffffff;"
:content="taskInfo.content"></mp-html>
</view>
10 months ago
</BaseContainer>
</template>
<script>
import {
getCourseList,
getTaskInfo,
getVideoPlayCredentials,
getVideoPlayDuration,
saveSpecialViewing,
} from "@/api/special";
import dayjs from "dayjs";
import mpHtml from "mp-html/dist/uni-app/components/mp-html/mp-html.vue";
export default {
components: {
mpHtml,
},
filters: {
format(time, sibling) {
let duration = dayjs.duration(time * 1000);
let hours = duration.hours();
let siblingHours = sibling ? dayjs.duration(sibling * 1000).hours() : 0;
return dayjs({
h: hours,
m: duration.minutes(),
s: duration.seconds(),
}).format((hours || siblingHours ? "HH:" : "") + "mm:ss");
},
},
computed: {
title() {
if (!this.taskInfo.id) return " ";
return this.taskInfo.type === 2 ? "音频素材" : "视频素材";
},
},
data() {
return {
try_num: 0,
taskId: 0,
specialId: 0,
playerHeight: 0,
taskInfo: {
play_count: 0,
},
taskList: [],
tabIndex: 0,
duration: 0,
currentTime: 0,
audioRange: 0,
audioPaused: true,
page: 1,
limit: 10,
loading: false,
finished: false,
player: null,
viewing_time: 0,
isPay: false,
player: null,
videoContext: null,
audioBarPos: {
width: 0,
left: 0,
},
};
},
onLoad({ id, specialId, viewing_time = 0 }) {
this.taskId = id;
this.specialId = specialId;
this.viewing_time = viewing_time / 1e3;
this.currentTime = Math.floor(this.viewing_time);
console.log(this.viewing_time)
const { screenWidth } = this.$util.getSystemInfo();
this.playerHeight = Math.floor((screenWidth * 9) / 16);
this.getTaskList();
},
onReachBottom() {
this.getTaskList();
},
mounted() {
this.getTaskInfo().then(() => {
this.taskInfo.type === 2 &&
this.$util.getClientRect(".bar").then(({ width, left }) => {
this.audioBarPos.width = width;
this.audioBarPos.left = left;
});
});
},
methods: {
play() {
// #ifdef APP-PLUS
this.player.seek(parseInt(this.currentTime));
// #endif
},
// 获取播放信息
getTaskInfo() {
return getTaskInfo(this.specialId, this.taskId).then(({ data }) => {
this.isPay = data.isPay;
this.specialInfo = data.specialInfo;
this.taskInfo = data.taskInfo;
/* if (this.taskInfo.videoId) {
this.getPlayAuth();
} else */ if (this.taskInfo.type === 3) {
this.createPlayer();
} else {
this.createAudioPlayer();
}
});
},
createAudioPlayer() {
if (this.player) {
this.player.stop();
this.player = null;
this.audioPaused = true;
}
const innerAudioContext = uni.createInnerAudioContext();
innerAudioContext.autoplay = false;
innerAudioContext.src = this.taskInfo.link;
innerAudioContext.onCanplay(() => {
let intervalID = setInterval(() => {
if (innerAudioContext.duration !== 0) {
clearInterval(intervalID);
this.duration = innerAudioContext.duration;
// #ifdef APP
this.player.seek(this.viewing_time);
//#endif
this.audioRange = Math.floor((this.viewing_time / this.duration) * 100)
}
}, 100);
});
innerAudioContext.onEnded(() => {
this.onPlayerEnded();
});
innerAudioContext.onTimeUpdate(() => {
this.onPlayerTimeupdate();
});
this.player = innerAudioContext;
if (this.viewing_time) {
this.player.seek(this.viewing_time);
}
},
onUnload() {
if (!this.player) return;
this.player.pause()
},
// 获取palyauth
getPlayAuth() {
getVideoPlayCredentials(this.taskInfo.videoId, 2).then(({ msg }) => {
getVideoPlayDuration(msg).then((res) => {
try {
var data = JSON.parse(res.responseText);
if (request.status === 200) {
this.duration = data.VideoMeta.Duration;
this.createPlayer(data.PlayAuth);
} else {
this.$util.showMsg(data.Message);
}
} catch (error) {
this.$util.showMsg(error);
}
});
});
},
// 创建播放器
createPlayer(playauth) {
if (this.player) {
this.player.stop();
this.player = null;
}
this.player = uni.createVideoContext("player");
},
// 初始播放位置
handleVideoLoaded(e) {
this.duration = e.target.duration;
if (!this.isPay && this.taskInfo.is_try) {
// this.player.seek(this.taskInfo.try_time * 60);
} else {
this.player.seek(this.viewing_time);
}
},
onPlayerTimeupdate(e) {
if (this.duration == 0 && this.taskInfo.type == 3) {
this.duration = e.target.duration;
}
if (parseInt(this.currentTime) == parseInt(this.duration)) {
this.player.seek(0);
}
this.currentTime = this.taskInfo.type == 3 ? e.detail.currentTime : this.player.currentTime;
this.audioRange = Math.floor((this.currentTime / this.duration) * 100);
if (!this.isPay) {
if (this.taskInfo.is_try && this.currentTime >= this.taskInfo.try_time * 60) {
this.audioPaused = true;
this.player.pause()
this.player.seek(0)
this.onPlayerEnded();
}
return;
} else {
// this.player.seek(parseInt(this.currentTime));
// this.player.seek(this.currentTime)
}
var floorTime = Math.floor(this.currentTime);
if (floorTime && floorTime != this.floorTime && !(floorTime % 10)) {
this.floorTime = floorTime;
this.setViewing(this.currentTime);
}
},
// 播放结束
onPlayerEnded() {
if (!this.isPay) {
this.currentTime = 0
uni.showModal({
title: "提示",
content: "购买后可看全部内容,是否购买?",
confirmText: "购买",
success: ({ confirm }) => {
if (!confirm) return;
uni.navigateBack({})
},
});
} else {
this.setViewing(this.duration);
}
},
// 获取专栏列表
getTaskList() {
if (this.finished) return;
getCourseList({
special_id: this.specialId,
page: this.page,
limit: this.limit,
}).then(({ data }) => {
let list = Array.isArray(data) ? [] : data.list;
this.taskList = this.taskList.concat(list);
this.finished = this.limit > list.length;
if (!this.finished) {
this.page++;
}
});
},
// 点击目录
playTask(item) {
if (item.id == this.taskId) return;
if (item.pay_status && !this.isPay && this.specialInfo.pay_type) {
this.$util.showMsg("请先去购买");
return;
}
this.taskId = item.id;
this.viewing_time = (item.watch && item.watch.viewing_time / 1e3) || 0;
this.currentTime = Math.floor(this.viewing_time);
this.seeked = false;
this.getTaskInfo();
},
// 更新播放进度
setViewing(currentTime) {
saveSpecialViewing({
special_id: this.specialId,
task_id: this.taskId,
total: this.duration * 1e3,
viewing_time: currentTime * 1e3,
percentage: Math.floor((currentTime / this.duration) * 100),
}).then(res => {
console.log(res)
});
},
// 播放/暂停音频
audioPlay() {
if (this.player) {
this.audioPaused = !this.audioPaused;
this.audioPaused ? this.player.pause() : this.player.play();
}
},
// 设置音频进度
setAudioRange() {
this.audioRange = Math.floor((this.currentTime / this.duration) * 100);
},
// 跳到音频的新位置
audioSeek(event) {
const barWidth = this.audioBarPos.width;
const barLeft = this.audioBarPos.left;
const range = (event.touches[0].pageX - barLeft) / barWidth;
this.audioRange = range * 100;
this.currentTime = this.duration * range;
this.player.seek(this.currentTime);
},
// 上一个音频/下一个音频
audioTab(state) {
if (!this.player) return;
if (!this.player.paused) {
this.audioPaused = true;
this.player.pause();
}
let index = 0;
for (let i = this.taskList.length; i--;) {
if (this.taskList[i].id === this.taskId) {
index = i;
break;
}
}
if (index === this.taskList.length - 1 && state) {
return this.$util.showMsg("已经是最后一个");
}
if (!index && !state) {
return this.$util.showMsg("已经是第一个");
}
let task = state ? this.taskList[index + 1] : this.taskList[index - 1];
if (task.pay_status) {
return this.$util.showMsg("请先去购买");
}
this.playTask(task);
},
// 滑动音频
audioMove(event) {
if (!this.player || !this.player.paused) {
this.audioPaused = true;
this.player.pause();
}
this.$util.getClientRect(".bar").then(({ left, width }) => {
let range = Math.floor(((event.touches[0].pageX - left) / width) * 100);
if (range > 100) {
range = 100;
}
this.audioRange = range;
});
},
// 滑动音频停止
audioMoveEnd() {
if (!this.player) return;
this.player.seek((this.duration * this.audioRange) / 100);
if (this.player.paused) {
this.audioPaused = false;
this.player.play();
}
},
},
};
</script>
<style>
page{
background: #F7F8F9;
}
</style>
<style scoped lang="scss">
.task-info .tabbar {
display: flex;
border-top: 16rpx solid #f5f5f5;
background: #ffffff;
box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.03);
}
.task-info .tabbar view {
position: relative;
flex: 1;
height: 96rpx;
font-size: 30rpx;
line-height: 96rpx;
text-align: center;
color: #333;
cursor: pointer;
}
.task-info .tabbar view.on {
font-weight: 500;
font-size: 32rpx;
color: #2c8eff;
}
.task-info .tabbar view.on::after {
content: " ";
position: absolute;
bottom: 15rpx;
left: 50%;
width: 30rpx;
height: 8rpx;
border-radius: 10rpx;
background-color: #2c8eff;
transform: translateX(-50%);
}
.catalogue{
background: #ffffff;
}
.catalogue .catalogue-item.on{
background: #ffffff !important;
.text{
.title{
color: #1D8DFF ;
font-size: 30rpx;
font-weight: 500;
color: #1D8DFF;
line-height: 48rpx;
}
}
}
.task-info .prism-player .prism-big-play-btn {
left: 50% !important;
bottom: 50% !important;
transform: translate(-50%, 50%);
}
.task-info .player>image {
display: block;
width: 100%;
height: 100%;
object-fit: cover;
-webkit-touch-callout: none;
pointer-events: none;
}
.task-info .content {
background-color: #ffffff;
font-size: 30rpx;
line-height: 44rpx;
color: #333;
word-break: break-all;
}
.task-info .content image {
display: block;
width: 100%;
pointer-events: none;
-webkit-touch-callout: none;
}
.task-info .catalogue .catalogue-item {
position: relative;
display: flex;
align-items: center;
height: 152rpx;
}
.task-info .catalogue .catalogue-item::before {
content: "";
position: absolute;
top: 0;
right: 0;
left: 91rpx;
border-top: 1px solid #f5f5f5;
}
.task-info .catalogue .catalogue-item:first-child::before {
display: none;
}
.task-info .catalogue .catalogue-item.on {
background-color: #f9fafc;
}
.task-info .catalogue image {
display: block;
width: 38rpx;
height: 27rpx;
margin: 0 33rpx 0 20rpx;
}
.task-info .catalogue .text {
flex: 1;
min-width: 0;
}
.task-info .catalogue .title {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
font-size: 28rpx;
line-height: 40rpx;
color: #333333;
}
.task-info .catalogue .try-progress {
margin-top: 15rpx;
font-size: 0;
}
.task-info .catalogue .try,
.task-info .catalogue .progress {
display: inline-block;
padding: 2.5rpx 7rpx;
border: 1px solid transparent;
border-radius: 3rpx;
font-size: 18rpx;
line-height: 1;
}
.task-info .catalogue .try {
border-color: #2c8eff;
color: #2c8eff;
}
.task-info .catalogue .progress {
padding-left: 9rpx;
border-color: #fff0e5;
background-color: #fff0e5;
color: #ff6b00;
}
.task-info .catalogue .progress.no {
border-color: #dddddd;
background-color: #dddddd;
color: #999999;
}
.task-info .catalogue .try+.progress {
margin-left: 15rpx;
}
.task-info .catalogue .status {
width: 130rpx;
font-size: 0;
text-align: center;
}
.task-info .catalogue .free {
display: inline-block;
width: 90rpx;
height: 40rpx;
border-radius: 20rpx;
background-color: #fff0e5;
font-size: 22rpx;
line-height: 40rpx;
text-align: center;
color: #ff6b00;
}
.task-info .catalogue .iconfont {
font-size: 34rpx;
color: #cccccc;
}
.task-info-body {
padding: 30rpx;
background-color: #ffffff;
font-size: 24rpx;
color: #999999;
border-radius: 32rpx 32rpx 0rpx 0rpx;
position: relative;
margin-top: -20rpx;
z-index: 9;
}
.task-info-body view:first-child {
margin-bottom: 10rpx;
font-size: 36rpx;
color: #333;
}
.task-info .audio {
padding-bottom: 49rpx;
background: #ffffff;
}
.task-info .audio .progress {
display: flex;
align-items: center;
margin-right: 30rpx;
margin-left: 30rpx;
}
.task-info .audio .progress .time {
width: 75rpx;
font-size: 28rpx;
color: #2c8eff;
}
.task-info .audio .progress .bar {
flex: 1;
border-radius: 2rpx;
background-color: rgba(44, 142, 255, 0.3);
margin-right: 18rpx;
margin-left: 18rpx;
}
.task-info .audio .progress .range {
position: relative;
height: 4rpx;
border-radius: 2rpx;
background-color: rgba(44, 142, 255, 0.8);
transition: 0.2s linear;
}
.task-info .audio .progress .dot {
position: absolute;
top: 50%;
left: 100%;
width: 16rpx;
height: 16rpx;
border-radius: 50%;
background-color: #2c8eff;
transform: translate(-50%, -50%);
}
.task-info .control {
margin-top: 34rpx;
}
.task-info .control .iconfont {
font-size: 36rpx;
color: #46505b;
}
.task-info .control .play-btn {
margin-right: 88rpx;
margin-left: 88rpx;
}
.task-info .control .icon {
font-size: 110rpx;
color: #2c8eff;
}
.player-self {
width: 100%;
height: 100%;
}
.icon {
/* #ifdef MP-WEIXIN */
height: auto !important;
/* #endif */
}
</style>