Luffy之课程详情页

提前写好课程详情的template,并放入到Vue中

注册路由

import CourseDetail from "../components/CourseDetail"

    {
name:"CourseDetail",
path: "/detail",
component: CourseDetail,
}

在页面中引入vue-video组件实现视频播放

# 1. 安装依赖
npm install vue-video-player --save # 2. 在main.js中注册加载组件
require('video.js/dist/video-js.css');
require('vue-video-player/src/custom-theme.css');
import VideoPlayer from 'vue-video-player'
Vue.use(VideoPlayer);

在课程详情页中的script标签里面加入以下代码:

import {videoPlayer} from 'vue-video-player';

export default {
data () {
return {
playerOptions: {
playbackRates: [0.7, 1.0, 1.5, 2.0], // 播放速度
autoplay: false, //如果true,则自动播放
muted: false, // 默认情况下将会消除任何音频。
loop: false, // 循环播放
preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
language: 'zh-CN',
aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
sources: [{
type: "video/mp4",
src: "http://img.ksbbs.com/asset/Mon_1703/05cacb4e02f9d9e.mp4" //你的视频地址(必填)
}],
poster: "../static/courses/675076.jpeg", //视频封面图
width: document.documentElement.clientWidth,
notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖Video.js无法播放媒体源时显示的默认信息。
}
}
},
components: {
videoPlayer
},
methods: {
onPlayerPlay(player) {
alert("play");
},
onPlayerPause(player){
alert("pause");
},
},
computed: {
player() {
return this.$refs.videoPlayer.player
}
}
}

最后前端向后端发送数据请求就可以了,后端提供数据接口

完整代码:

前端(coursedetail),包括倒计时等的js实现

 <template>
<div class="detail">
<Header></Header>
<div class="warp">
<div class="course-info">
<div class="warp-left" style="width: 690px;height: 388px;background-color: #000;">
<video-player class="video-player vjs-custom-skin"
ref="videoPlayer"
:playsinline="true"
:options="playerOptions"
@play="onPlayerPlay($event)"
@pause="onPlayerPause($event)"
>
</video-player>
</div>
<div class="warp-right">
<h3 class="course-title">{{course.name}}</h3>
<p class="course-data">{{course.students}}人在学&nbsp;&nbsp;&nbsp;&nbsp;课程总时长:{{course.lessons}}课时/{{course.pub_lessons}}小时&nbsp;&nbsp;&nbsp;&nbsp;难度:{{course.level_name}}</p>
<div class="preferential">
<p class="price-service">{{course.price_service_type.name}}</p>
<p class="timer">距离结束:仅剩 {{Math.floor(sale_time/86400)}}天 {{Math.floor(sale_time%86400/3600)}}小时 {{Math.floor(sale_time%3600/60)}}分 <span>{{Math.floor(sale_time%60)}}</span> 秒</p>
</div>
<p class="course-price">
<span>活动价</span>
<span class="real-price">¥{{course.real_price}}</span>
<span class="old-price">¥{{course.price}}</span>
</p>
<div class="buy-course">
<p class="buy-btn">
<span class="btn1">立即购买</span>
<span class="btn2">免费试学</span>
</p>
<p class="add-cart">
<img src="../../static/images/cart.svg" alt="">加入购物车
</p>
</div>
</div>
</div>
<div class="course-tab">
<ul>
<li class="active">详情介绍</li>
<li>课程章节 <span>(试学)</span></li>
<li>用户评论 (83)</li>
<li>常见问题</li>
</ul>
</div>
<div class="course-section">
<section class="course-section-left">
<img src="../../static/images/21天01_1547098127.6672518.jpeg" alt="">
</section> </div>
</div>
<Footer></Footer>
</div>
</template> <script>
import Header from "./common/Header"
import Footer from "./common/Footer" import {videoPlayer} from 'vue-video-player'; export default {
name: 'CourseDetail',
data(){
return {
course_id:sessionStorage.course_id,
course:{},
sale_time: 0,
// vue-video的配置选项
playerOptions: {
playbackRates: [0.7, 1.0, 1.5, 2.0], // 播放速度
autoplay: false, //如果true,则打开页面以后自动播放
muted: false, // 默认情况下将会消除任何音频。
loop: false, // 循环播放
preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持)
language: 'zh-CN',
aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3")
fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。
sources: [{ // 播放资源类型和地址
type: "video/mp4",
src: "http://img.ksbbs.com/asset/Mon_1703/05cacb4e02f9d9e.mp4" //你的视频地址(必填)
}],
poster: "../static/courses/675076.jpeg", //视频封面图
width: document.documentElement.clientWidth,
notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖Video.js无法播放媒体源时显示的默认信息。
}
}
},
components:{
Header,
Footer,
videoPlayer, // 引入vue-viedeo播放器组件
},
methods:{
intervaltimer(){
// 课程优惠倒计时
if(this.sale_time > 0 ){
let timer = setInterval(()=>{
if( this.sale_time < 0 ){
clearInterval(timer)
}else{
--this.sale_time;
}
},1000);
}
}
},
computed: {
player() {
return this.$refs.videoPlayer.player
}
},
created(){
this.$axios.get("http://127.0.0.1:8000/courses/"+this.course_id).
then(response=>{
this.course = response.data;
// this.playerOptions.poster = response.data.course_img;
this.sale_time = response.data.price_service_type.priceservices[0].endtime_stamp
this.intervaltimer()
}).catch(error=>{
console.log(error.response);
})
},
};
</script> <style scoped>
.detail{
margin-top: 80px;
}
.course-info{
padding-top: 30px;
width:1200px;
height: 388px;
margin: auto;
}
.warp-left,.warp-right{
float: left;
}
.warp-right{
height: 388px;
position: relative;
}
.course-title{
font-size: 20px;
color: #333;
padding: 10px 23px;
letter-spacing: .45px;
font-weight: normal;
}
.course-data{
padding-left: 23px;
padding-right: 23px;
padding-bottom: 16px;
font-size: 14px;
color: #9b9b9b;
}
.preferential{
width: 100%;
height: auto;
background: #fa6240;
font-size: 14px;
color: #4a4a4a;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
-ms-flex-pack: justify;
justify-content: space-between;
padding: 10px 23px;
}
.price-service{
font-size: 16px;
color: #fff;
letter-spacing: .36px;
}
.timer{
font-size: 14px;
color: #fff;
}
.course-price{
width: 100%;
background: #fff;
height: auto;
font-size: 14px;
color: #4a4a4a;
display: -ms-flexbox;
display: flex;
-ms-flex-align: end;
align-items: flex-end;
padding: 5px 23px;
}
.real-price{
font-size: 26px;
color: #fa6240;
margin-left: 10px;
display: inline-block;
margin-bottom: -5px;
}
.old-price{
font-size: 14px;
color: #9b9b9b;
margin-left: 10px;
text-decoration: line-through;
}
.buy-course{
position: absolute;
left: 0;
bottom: 20px;
width: 100%;
height: auto;
-ms-flex-pack: justify;
justify-content: space-between;
padding-left: 23px;
padding-right: 23px;
}
.buy-btn{
float: left;
}
.buy-btn .btn1{
display: inline-block;
width: 125px;
height: 40px;
background: #ffc210;
border-radius: 4px;
color: #fff;
cursor: pointer;
margin-right: 15px;
text-align: center;
vertical-align: middle;
line-height: 40px;
}
.buy-btn .btn2{
width: 125px;
height: 40px;
border-radius: 4px;
cursor: pointer;
margin-right: 15px;
display: inline-block;
background: #fff;
color: #ffc210;
border: 1px solid #ffc210;
text-align: center;
vertical-align: middle;
line-height: 40px;
}
.add-cart{
font-size: 14px;
color: #ffc210;
text-align: center;
cursor: pointer;
float: right;
margin-top: 10px;
}
.add-cart img{
width: 20px;
height: auto;
margin-right: 7px;
}
.course-tab{
width: 1200px;
margin: auto;
height: auto;
background: #fff;
margin-bottom: 30px;
box-shadow: 0 2px 4px 0 #f0f0f0;
}
.course-tab>ul{
padding: 0;
margin: 0;
list-style: none;
width: 1200px;
height: auto;
display: -ms-flexbox;
display: flex;
-ms-flex-align: center;
align-items: center;
color: #4a4a4a;
}
.course-tab>ul>li{
margin-right: 15px;
padding: 26px 20px 16px;
font-size: 17px;
cursor: pointer;
}
.course-tab>ul>.active{
color: #ffc210;
border-bottom: 2px solid #ffc210;
}
.course-section{
background: #FAFAFA;
overflow: hidden;
padding-bottom: 40px;
width: 1200px;
height: auto;
margin: 0 auto;
}
.course-section-left{
width: 880px;
height: auto;
padding: 20px;
background: #fff;
float: left;
box-sizing: border-box;
overflow: hidden;
position: relative;
box-shadow: 0 2px 4px 0 #f0f0f0;
}
</style>

后端数据接口 views:

class CourseDetailAPIView(RetrieveAPIView):
queryset = models.Course.objects.all()
serializer_class = CourseDetailSerializer

serlizer,序列化器:

 from rest_framework import serializers

 from luffy.apps.courses import models

 class CourseCateserializer(serializers.ModelSerializer):
class Meta:
model = models.CourseCategory
fields = ('id','name','orders') class CourseChaptersSerializer(serializers.ModelSerializer):
class Meta:
model= models.CourseChapter
fields = ("id","chapter","name") class Teacherserializer(serializers.ModelSerializer):
class Meta:
model = models.Teacher
fields = ("id","name","title",) class PriceServiceserializer(serializers.ModelSerializer):
class Meta:
model = models.PriceService
fields = ("condition","sale","start_time","end_time","endtime_stamp") class PriceServiceTypeserializer(serializers.ModelSerializer):
priceservices = PriceServiceserializer(many=True)
class Meta:
model = models.PriceServiceType
fields =("id","name","priceservices") class Courseserializer(serializers.ModelSerializer):
teacher = Teacherserializer()
price_service_type = PriceServiceTypeserializer()
class Meta:
model = models.Course
fields = ("id","name","course_img","students","lessons","pub_lessons","price","teacher","course_category","price_service_type") class CourseDetailSerializer(serializers.ModelSerializer):
coursechapters = CourseChaptersSerializer(many=True)
price_service_type = PriceServiceTypeserializer()
teacher = Teacherserializer()
class Meta:
model = models.Course
fields = (
"id", "name", "course_img", "students",
"lessons", "brief", "level_name", "pub_lessons","price",
"teacher", "real_price", "price_service_type","coursechapters",)

Luffy之课程详情页的更多相关文章

  1. mxonline实战11,课程详情页2,课程章节页

    对应github地址:第11天   一. 课程详情页2   1. 课程详情页第2块中的课程介绍中,修改course-detail.html中代码,搜索课程详情,找到如下代码

  2. mxonline实战10,课程列表页,课程详情页1

    对应github地址:第10天   一. 课程列表页   1. 拷贝course-list.html到templates目录中 2. 编写url和view 在courses/views.py中新加

  3. 20、Django实战第20天:课程详情页

    1.把course-detail.html复制到templates目录下 2.编辑course-detail.html,分析页面,继承base.html 3.编辑courses.views .... ...

  4. day82:luffy:课程详情页面显示&章节和课时显示&视频播放组件&CKEditor富文本编辑器

    目录 1.初始课程详情页面 2.视频播放组件 3.课程详情页面后端接口实现 4.课程详情页面-前端 5.CKEditor富文本编辑器 6.课程章节和课时显示-后端接口 7.课程章节和课时显示-前端 1 ...

  5. mxonline实战12, 课程评论,相关课程推荐,课程视频页

    对应github地址:第12天   一. 课程评论   1. 创建URL, VIEW courses/views.py -> Course

  6. java亿级流量电商详情页系统的大型高并发与高可用缓存架构实战视频教程

    亿级流量电商详情页系统的大型高并发与高可用缓存架构实战 完整高清含源码,需要课程的联系QQ:2608609000 1[免费观看]课程介绍以及高并发高可用复杂系统中的缓存架构有哪些东西2[免费观看]基于 ...

  7. mxonline实战13,授课讲师列表页,详情页,index页面全局导航

    对应github地址:第13天   把teacher-list.html和teacher-detail.html拷贝过来   一. 授课讲师列表页   1. 修改html文件 把org-list.ht ...

  8. 25、Django实战第25天:讲师详情页

    1.复制teacher-detail.html到templates目录下 2.编辑teacher-detail.html,继承base.html 3.编辑organization.view.py cl ...

  9. 【音乐App】—— Vue-music 项目学习笔记:歌手详情页开发

    前言:以下内容均为学习慕课网高级实战课程的实践爬坑笔记. 项目github地址:https://github.com/66Web/ljq_vue_music,欢迎Star. 歌曲列表 歌曲播放 一.子 ...

随机推荐

  1. Java学习:注解,反射,动态编译

    狂神声明 : 文章均为自己的学习笔记 , 转载一定注明出处 ; 编辑不易 , 防君子不防小人~共勉 ! Java学习:注解,反射,动态编译 Annotation 注解  什么是注解 ? Annotat ...

  2. oracle日期格式化

    TO_CHAR(t.CAMERA_CREAT_TIME, 'YYYY-MM-DD HH24:MI:SS') as point_registerdate,TO_CHAR(t.CAMERA_MODIFY_ ...

  3. python框架之Flask(4)-上下文管理

    知识储备 偏函数 作用 偏函数,帮助开发者自动传递参数. 使用 import functools def index(a1, a2): return a1 + a2 # 原来的调用方式 # ret = ...

  4. 十一、无事勿扰,有事通知(2)——KVO

    概述 Key-Value-Observe,简称KVO,和上节介绍的Notification师出同门,主要目的都是为了实现观察者模式. 虽说是同门师兄弟,但是各自精通的技艺却是各不相同的. 不像Noti ...

  5. 《More Accurate Question Answering on Freebase》文献笔记

    bast-2015-CIKM CIKM全称是International Conference on Information and Knowledge Management 这篇文章主要采用采用lea ...

  6. SQLite multiple threads

    const int loops = 1000; public void DatabaseThreadSafetyTest() { var backgroundThread = new Thread(n ...

  7. 强迫症犯了,忍不住赞一下slf4j包Logger.java的优雅代码

    如下是slf4j-api包下的Logger接口类里其中几个方法的声明: package org.slf4j; public interface Logger { /** * Log a message ...

  8. linux挂载概念简述:

    挂载概念简述: 根文件系统之外的其他文件要想能够被访问,都必须通过“关联”至根文件系统上的某个目录来实现,此关联操作即为“挂载”,此目录即为“挂载点”,解除此关联关系的过程称之为“卸载” 1.挂载:根 ...

  9. SpringMvc HandlerMethodResolver 的 handlerMethods & ServletHandlerMethodResolver 的 mappings 在哪里初始化的 ?

    HandlerMethodResolver 的 handlerMethods & ServletHandlerMethodResolver 的 mappings 在哪里初始化的 ? 如下图:

  10. 记录 用tiny6410 j-link eclipse 在线调试裸机代码leds

    1.nand flash烧写uboot 并且启动nandflash uboot,用来初始化6410,进入uboot命令行界面 2.在terminal中输入JLinkGDBServer -device ...