Luffy之课程详情页

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实现

  1 <template>
  2     <div class="detail">
  3     <Header></Header>
  4     <div class="warp">
  5         <div class="course-info">
  6           <div class="warp-left" style="width: 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}}人在学&nbsp;&nbsp;&nbsp;&nbsp;课程总时长:{{course.lessons}}课时/{{course.pub_lessons}}小时&nbsp;&nbsp;&nbsp;&nbsp;难度:{{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           width: 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   width: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     width: 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     width: 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     width: 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     width: 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     width: 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     width: 20px;
259     height: auto;
260     margin-right: 7px;
261 }
262 .course-tab{
263     width: 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     width: 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     width: 1200px;
297     height: auto;
298     margin: 0 auto;
299 }
300 .course-section-left{
301     width: 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>
View Code

后端数据接口 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",)
View Code

猜你喜欢

转载自www.cnblogs.com/Mixtea/p/10679816.html