import CourseDetail from "../components/CourseDetail" { name:"CourseDetail", path: "/detail", component: CourseDetail, }
# 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", //视频封面图 document.documentElement.clientWidth, notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖Video.js无法播放媒体源时显示的默认信息。 } } }, components: { videoPlayer }, methods: { onPlayerPlay(player) { alert("play"); }, onPlayerPause(player){ alert("pause"); }, }, computed: { player() { return this.$refs.videoPlayer.player } } }
1 <template> 2 <div class="detail"> 3 <Header></Header> 4 <div class="warp"> 5 <div class="course-info"> 6 <div class="warp-left" style=" 690px;height: 388px;background-color: #000;"> 7 <video-player class="video-player vjs-custom-skin" 8 ref="videoPlayer" 9 :playsinline="true" 10 :options="playerOptions" 11 @play="onPlayerPlay($event)" 12 @pause="onPlayerPause($event)" 13 > 14 </video-player> 15 </div> 16 <div class="warp-right"> 17 <h3 class="course-title">{{course.name}}</h3> 18 <p class="course-data">{{course.students}}人在学 课程总时长:{{course.lessons}}课时/{{course.pub_lessons}}小时 难度:{{course.level_name}}</p> 19 <div class="preferential"> 20 <p class="price-service">{{course.price_service_type.name}}</p> 21 <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> 22 </div> 23 <p class="course-price"> 24 <span>活动价</span> 25 <span class="real-price">¥{{course.real_price}}</span> 26 <span class="old-price">¥{{course.price}}</span> 27 </p> 28 <div class="buy-course"> 29 <p class="buy-btn"> 30 <span class="btn1">立即购买</span> 31 <span class="btn2">免费试学</span> 32 </p> 33 <p class="add-cart"> 34 <img src="../../static/images/cart.svg" alt="">加入购物车 35 </p> 36 </div> 37 </div> 38 </div> 39 <div class="course-tab"> 40 <ul> 41 <li class="active">详情介绍</li> 42 <li>课程章节 <span>(试学)</span></li> 43 <li>用户评论 (83)</li> 44 <li>常见问题</li> 45 </ul> 46 </div> 47 <div class="course-section"> 48 <section class="course-section-left"> 49 <img src="../../static/images/21天01_1547098127.6672518.jpeg" alt=""> 50 </section> 51 52 </div> 53 </div> 54 <Footer></Footer> 55 </div> 56 </template> 57 58 <script> 59 import Header from "./common/Header" 60 import Footer from "./common/Footer" 61 62 import {videoPlayer} from 'vue-video-player'; 63 64 export default { 65 name: 'CourseDetail', 66 data(){ 67 return { 68 course_id:sessionStorage.course_id, 69 course:{}, 70 sale_time: 0, 71 // vue-video的配置选项 72 playerOptions: { 73 playbackRates: [0.7, 1.0, 1.5, 2.0], // 播放速度 74 autoplay: false, //如果true,则打开页面以后自动播放 75 muted: false, // 默认情况下将会消除任何音频。 76 loop: false, // 循环播放 77 preload: 'auto', // 建议浏览器在<video>加载元素后是否应该开始下载视频数据。auto浏览器选择最佳行为,立即开始加载视频(如果浏览器支持) 78 language: 'zh-CN', 79 aspectRatio: '16:9', // 将播放器置于流畅模式,并在计算播放器的动态大小时使用该值。值应该代表一个比例 - 用冒号分隔的两个数字(例如"16:9"或"4:3") 80 fluid: true, // 当true时,Video.js player将拥有流体大小。换句话说,它将按比例缩放以适应其容器。 81 sources: [{ // 播放资源类型和地址 82 type: "video/mp4", 83 src: "http://img.ksbbs.com/asset/Mon_1703/05cacb4e02f9d9e.mp4" //你的视频地址(必填) 84 }], 85 poster: "../static/courses/675076.jpeg", //视频封面图 86 document.documentElement.clientWidth, 87 notSupportedMessage: '此视频暂无法播放,请稍后再试', //允许覆盖Video.js无法播放媒体源时显示的默认信息。 88 } 89 } 90 }, 91 components:{ 92 Header, 93 Footer, 94 videoPlayer, // 引入vue-viedeo播放器组件 95 }, 96 methods:{ 97 intervaltimer(){ 98 // 课程优惠倒计时 99 if(this.sale_time > 0 ){ 100 let timer = setInterval(()=>{ 101 if( this.sale_time < 0 ){ 102 clearInterval(timer) 103 }else{ 104 --this.sale_time; 105 } 106 },1000); 107 } 108 } 109 }, 110 computed: { 111 player() { 112 return this.$refs.videoPlayer.player 113 } 114 }, 115 created(){ 116 this.$axios.get("http://127.0.0.1:8000/courses/"+this.course_id). 117 then(response=>{ 118 this.course = response.data; 119 // this.playerOptions.poster = response.data.course_img; 120 this.sale_time = response.data.price_service_type.priceservices[0].endtime_stamp 121 this.intervaltimer() 122 }).catch(error=>{ 123 console.log(error.response); 124 }) 125 }, 126 }; 127 </script> 128 129 <style scoped> 130 .detail{ 131 margin-top: 80px; 132 } 133 .course-info{ 134 padding-top: 30px; 135 1200px; 136 height: 388px; 137 margin: auto; 138 } 139 .warp-left,.warp-right{ 140 float: left; 141 } 142 .warp-right{ 143 height: 388px; 144 position: relative; 145 } 146 .course-title{ 147 font-size: 20px; 148 color: #333; 149 padding: 10px 23px; 150 letter-spacing: .45px; 151 font-weight: normal; 152 } 153 .course-data{ 154 padding-left: 23px; 155 padding-right: 23px; 156 padding-bottom: 16px; 157 font-size: 14px; 158 color: #9b9b9b; 159 } 160 .preferential{ 161 100%; 162 height: auto; 163 background: #fa6240; 164 font-size: 14px; 165 color: #4a4a4a; 166 display: -ms-flexbox; 167 display: flex; 168 -ms-flex-align: center; 169 align-items: center; 170 -ms-flex-pack: justify; 171 justify-content: space-between; 172 padding: 10px 23px; 173 } 174 .price-service{ 175 font-size: 16px; 176 color: #fff; 177 letter-spacing: .36px; 178 } 179 .timer{ 180 font-size: 14px; 181 color: #fff; 182 } 183 .course-price{ 184 100%; 185 background: #fff; 186 height: auto; 187 font-size: 14px; 188 color: #4a4a4a; 189 display: -ms-flexbox; 190 display: flex; 191 -ms-flex-align: end; 192 align-items: flex-end; 193 padding: 5px 23px; 194 } 195 .real-price{ 196 font-size: 26px; 197 color: #fa6240; 198 margin-left: 10px; 199 display: inline-block; 200 margin-bottom: -5px; 201 } 202 .old-price{ 203 font-size: 14px; 204 color: #9b9b9b; 205 margin-left: 10px; 206 text-decoration: line-through; 207 } 208 .buy-course{ 209 position: absolute; 210 left: 0; 211 bottom: 20px; 212 100%; 213 height: auto; 214 -ms-flex-pack: justify; 215 justify-content: space-between; 216 padding-left: 23px; 217 padding-right: 23px; 218 } 219 .buy-btn{ 220 float: left; 221 } 222 .buy-btn .btn1{ 223 display: inline-block; 224 125px; 225 height: 40px; 226 background: #ffc210; 227 border-radius: 4px; 228 color: #fff; 229 cursor: pointer; 230 margin-right: 15px; 231 text-align: center; 232 vertical-align: middle; 233 line-height: 40px; 234 } 235 .buy-btn .btn2{ 236 125px; 237 height: 40px; 238 border-radius: 4px; 239 cursor: pointer; 240 margin-right: 15px; 241 display: inline-block; 242 background: #fff; 243 color: #ffc210; 244 border: 1px solid #ffc210; 245 text-align: center; 246 vertical-align: middle; 247 line-height: 40px; 248 } 249 .add-cart{ 250 font-size: 14px; 251 color: #ffc210; 252 text-align: center; 253 cursor: pointer; 254 float: right; 255 margin-top: 10px; 256 } 257 .add-cart img{ 258 20px; 259 height: auto; 260 margin-right: 7px; 261 } 262 .course-tab{ 263 1200px; 264 margin: auto; 265 height: auto; 266 background: #fff; 267 margin-bottom: 30px; 268 box-shadow: 0 2px 4px 0 #f0f0f0; 269 } 270 .course-tab>ul{ 271 padding: 0; 272 margin: 0; 273 list-style: none; 274 1200px; 275 height: auto; 276 display: -ms-flexbox; 277 display: flex; 278 -ms-flex-align: center; 279 align-items: center; 280 color: #4a4a4a; 281 } 282 .course-tab>ul>li{ 283 margin-right: 15px; 284 padding: 26px 20px 16px; 285 font-size: 17px; 286 cursor: pointer; 287 } 288 .course-tab>ul>.active{ 289 color: #ffc210; 290 border-bottom: 2px solid #ffc210; 291 } 292 .course-section{ 293 background: #FAFAFA; 294 overflow: hidden; 295 padding-bottom: 40px; 296 1200px; 297 height: auto; 298 margin: 0 auto; 299 } 300 .course-section-left{ 301 880px; 302 height: auto; 303 padding: 20px; 304 background: #fff; 305 float: left; 306 box-sizing: border-box; 307 overflow: hidden; 308 position: relative; 309 box-shadow: 0 2px 4px 0 #f0f0f0; 310 } 311 </style>
后端数据接口 views:
class CourseDetailAPIView(RetrieveAPIView): queryset = models.Course.objects.all() serializer_class = CourseDetailSerializer
serlizer,序列化器:
1 from rest_framework import serializers 2 3 from luffy.apps.courses import models 4 5 6 class CourseCateserializer(serializers.ModelSerializer): 7 class Meta: 8 model = models.CourseCategory 9 fields = ('id','name','orders') 10 11 class CourseChaptersSerializer(serializers.ModelSerializer): 12 class Meta: 13 model= models.CourseChapter 14 fields = ("id","chapter","name") 15 16 class Teacherserializer(serializers.ModelSerializer): 17 class Meta: 18 model = models.Teacher 19 fields = ("id","name","title",) 20 21 22 class PriceServiceserializer(serializers.ModelSerializer): 23 class Meta: 24 model = models.PriceService 25 fields = ("condition","sale","start_time","end_time","endtime_stamp") 26 27 28 class PriceServiceTypeserializer(serializers.ModelSerializer): 29 priceservices = PriceServiceserializer(many=True) 30 class Meta: 31 model = models.PriceServiceType 32 fields =("id","name","priceservices") 33 34 class Courseserializer(serializers.ModelSerializer): 35 teacher = Teacherserializer() 36 price_service_type = PriceServiceTypeserializer() 37 class Meta: 38 model = models.Course 39 fields = ("id","name","course_img","students","lessons","pub_lessons","price","teacher","course_category","price_service_type") 40 41 42 class CourseDetailSerializer(serializers.ModelSerializer): 43 coursechapters = CourseChaptersSerializer(many=True) 44 price_service_type = PriceServiceTypeserializer() 45 teacher = Teacherserializer() 46 class Meta: 47 model = models.Course 48 fields = ( 49 "id", "name", "course_img", "students", 50 "lessons", "brief", "level_name", "pub_lessons","price", 51 "teacher", "real_price", "price_service_type","coursechapters",)