zoukankan      html  css  js  c++  java
  • 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", //视频封面图
             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=" 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            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>
    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
  • 相关阅读:
    nginx获取上游真实IP(ngx_http_realip_module)
    配置NFS固定端口
    elasticsearch 占CPU过高
    jdk集合常用方法分析之HashSet和TreeSet
    SparkSQL使用之如何使用UDF
    SparkSQL使用之JDBC代码访问Thrift JDBC Server
    SparkSQL使用之Thrift JDBC server
    SparkSQL使用之Spark SQL CLI
    jdk分析之String
    jdk集合常用方法分析之ArrayList&LinkedList&以及两者的对比分析
  • 原文地址:https://www.cnblogs.com/Mixtea/p/10679816.html
Copyright © 2011-2022 走看看