zoukankan      html  css  js  c++  java
  • luffy_04

    04课程列表页

    前端显示课程列表页面

    在xadmin中增加课程列表的链接导航。然后把课程列表页面组件Course.vue放到项目中.

    <template>
      <div class="course">
        <Header/>
        <div class="main">
          <!-- 筛选功能 -->
          <div class="top">
            <ul class="condition condition1">
              <li class="cate-condition">课程分类:</li>
              <li class="item current">全部</li>
              <li class="item">Python</li>
              <li class="item">Linux运维</li>
              <li class="item">Python进阶</li>
              <li class="item">开发工具</li>
              <li class="item">Go语言</li>
              <li class="item">机器学习</li>
              <li class="item">技术生涯</li>
            </ul>
            <ul class="condition condition2">
              <li class="cate-condition">筛&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;选:</li>
              <li class="item current">默认</li>
              <li class="item">人气</li>
              <li class="item price">价格</li>
              <li class="course-length">共21个课程</li>
            </ul>
          </div>
          <!-- 课程列表 --->
          <div class="list">
            <ul>
              <li class="course-item">
                <router-link to="/detail" class="course-link">
                  <div class="course-cover">
                    <img src="../../static/course/1544059695.jpeg" alt="">
                  </div>
                  <div class="course-info">
                    <div class="course-title">
                      <h3>Python开发21天入门</h3>
                      <span>46520人已加入学习</span>
                    </div>
                    <p class="teacher">
                      <span class="info">Alex 金角大王 老男孩Python教学总监</span>
                      <span class="lesson">共154课时/更新完成</span>
                    </p>
                    <ul class="lesson-list">
                      <li>
                        <p class="lesson-title">01 | 常用模块学习-模块的种类和导入方法</p>
                        <span class="free">免费</span>
                      </li>
                      <li>
                        <p class="lesson-title">02 | 编程语言介绍(三)高级语言</p>
                        <span class="free">免费</span>
                      </li>
                      <li>
                        <p class="lesson-title">03 | 编程语言介绍(一)</p>
                        <span class="free">免费</span>
                      </li>
                      <li>
                        <p class="lesson-title">04 | 课程介绍(二)-Python与其他语言的区别</p>
                        <span class="free">免费</span>
                      </li>
                    </ul>
                    <div class="buy-info">
                      <span class="discount">限时免费</span>
                      <span class="present-price">¥0.00元</span>
                      <span class="original-price">原价:9.00元</span>
                      <button class="buy-now">立即购买</button>
                    </div>
                  </div>
                </router-link>
              </li>
              <li class="course-item">
                <div class="course-cover">
                  <img src="../../static/course/1544059695.jpeg" alt="">
                </div>
                <div class="course-info">
                  <div class="course-title">
                    <h3>Python开发21天入门</h3>
                    <span>46520人已加入学习</span>
                  </div>
                  <p class="teacher">
                    <span class="info">Alex 金角大王 老男孩Python教学总监</span>
                    <span class="lesson">共154课时/更新完成</span>
                  </p>
                  <ul class="lesson-list">
                    <li>
                      <p class="lesson-title">01 | 常用模块学习-模块的种类和导入方法</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">02 | 编程语言介绍(三)高级语言</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">03 | 编程语言介绍(一)</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">04 | 课程介绍(二)-Python与其他语言的区别</p>
                      <span class="free">免费</span>
                    </li>
                  </ul>
                  <div class="buy-info">
                    <span class="discount">限时免费</span>
                    <span class="present-price">¥0.00元</span>
                    <span class="original-price">原价:9.00元</span>
                    <button class="buy-now">立即购买</button>
                  </div>
                </div>
              </li>
              <li class="course-item">
                <div class="course-cover">
                  <img src="../../static/course/1544059695.jpeg" alt="">
                </div>
                <div class="course-info">
                  <div class="course-title">
                    <h3>Python开发21天入门</h3>
                    <span>46520人已加入学习</span>
                  </div>
                  <p class="teacher">
                    <span class="info">Alex 金角大王 老男孩Python教学总监</span>
                    <span class="lesson">共154课时/更新完成</span>
                  </p>
                  <ul class="lesson-list">
                    <li>
                      <p class="lesson-title">01 | 常用模块学习-模块的种类和导入方法</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">02 | 编程语言介绍(三)高级语言</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">03 | 编程语言介绍(一)</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">04 | 课程介绍(二)-Python与其他语言的区别</p>
                      <span class="free">免费</span>
                    </li>
                  </ul>
                  <div class="buy-info">
                    <span class="discount">限时免费</span>
                    <span class="present-price">¥0.00元</span>
                    <span class="original-price">原价:9.00元</span>
                    <button class="buy-now">立即购买</button>
                  </div>
                </div>
              </li>
              <li class="course-item">
                <div class="course-cover">
                  <img src="../../static/course/1544059695.jpeg" alt="">
                </div>
                <div class="course-info">
                  <div class="course-title">
                    <h3>Python开发21天入门</h3>
                    <span>46520人已加入学习</span>
                  </div>
                  <p class="teacher">
                    <span class="info">Alex 金角大王 老男孩Python教学总监</span>
                    <span class="lesson">共154课时/更新完成</span>
                  </p>
                  <ul class="lesson-list">
                    <li>
                      <p class="lesson-title">01 | 常用模块学习-模块的种类和导入方法</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">02 | 编程语言介绍(三)高级语言</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">03 | 编程语言介绍(一)</p>
                      <span class="free">免费</span>
                    </li>
                    <li>
                      <p class="lesson-title">04 | 课程介绍(二)-Python与其他语言的区别</p>
                      <span class="free">免费</span>
                    </li>
                  </ul>
                  <div class="buy-info">
                    <span class="discount">限时免费</span>
                    <span class="present-price">¥0.00元</span>
                    <span class="original-price">原价:9.00元</span>
                    <button class="buy-now">立即购买</button>
                  </div>
                </div>
              </li>
            </ul>
          </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
    import Header from "./common/Header"
    import Footer from "./common/Footer"
    export default {
      name: "Course",
      data(){
        return {
        }
      },
    
      components:{Header,Footer}
    }
    </script>
    
    <style scoped>
    .main{
         1100px;
        height: auto;
        margin: 0 auto;
        padding-top: 35px;
    }
    .main .top{
        margin-bottom: 35px;
        padding: 25px 30px 25px 20px;
        background: #fff;
        border-radius: 4px;
        box-shadow: 0 2px 4px 0 #f0f0f0;
    }
    .condition{
        border-bottom: 1px solid #333;
        border-bottom-color: rgba(51,51,51,.05);
        padding-bottom: 18px;
        margin-bottom: 17px;
        overflow: hidden;
    }
    .condition li{
        float: left;
    }
    .condition .cate-condition{
        color: #888;
        font-size: 16px;
    }
    .condition .item{
        padding: 6px 16px;
        line-height: 16px;
        margin-left: 14px;
        position: relative;
        transition: all .3s ease;
        cursor: pointer;
        color: #4a4a4a;
    }
    .condition1 .current{
        color: #ffc210;
        border: 1px solid #ffc210!important;
        border-radius: 30px;
    }
    .condition2 .current{
      color: #ffc210;
    }
    .condition .price:before{
        content: "";
         0;
        border: 5px solid transparent;
        border-top-color: #d8d8d8;
        position: absolute;
        right: 0;
        bottom: 2.5px;
    }
    .condition .price:after{
        content: "";
         0;
        border: 5px solid transparent;
        border-bottom-color: #ffc210;
        position: absolute;
        right: 0;
        top: 2.5px;
    }
    .condition2 .course-length{
        float: right;
        font-size: 14px;
        color: #9b9b9b;
    }
    .course-item{
        background: #fff;
        padding: 20px 30px 20px 20px;
        margin-bottom: 35px;
        border-radius: 2px;
        cursor: pointer;
        box-shadow: 2px 3px 16px rgba(0,0,0,.1);
        transition: all .2s ease;
        overflow: hidden;
        cursor:pointer;
    }
    .course-link{
      overflow: hidden;
    }
    .course-cover {
         423px;
        height: 210px;
        margin-right: 30px;
        float: left;
    }
    .course-info{
         597px;
        float: left;
    }
    .course-title{
      margin-bottom: 8px;
      overflow: hidden;
    
    }
    .course-title h3{
      font-size: 26px;
      color: #333;
      float: left;
    }
    .course-title span {
      float: right;
      font-size: 14px;
      color: #9b9b9b;
      margin-top: 12px;
      text-indent: 1em; /* 缩进 2字符宽度 */
      background: url("../assets/people.svg") no-repeat 0px 3px;
    }
    .teacher{
        justify-content: space-between;
        font-size: 14px;
        color: #9b9b9b;
        margin-bottom: 14px;
        padding-bottom: 14px;
        border-bottom: 1px solid #333;
        border-bottom-color: rgba(51,51,51,.05);
    }
    .teacher .lesson{
        float: right;
    }
    .lesson-list{
      overflow: hidden;
    }
    .lesson-list li{
         49%;
        margin-bottom: 15px;
        cursor: pointer;
        float: left;
        margin-right:1%;
    }
    .lesson-list li .player{
         16px;
        height: 16px;
        vertical-align: text-bottom;
    }
    .lesson-list li .lesson-title {
      display: inline-block;
      max- 227px;
      text-overflow: ellipsis; /* 如果字体太多超出元素的宽度,则添加省略符号 */
      color: #666;
      overflow: hidden;
      white-space: nowrap;
      font-size: 14px;
      vertical-align: text-bottom; /* 文本的垂直对齐方式: text-botton 文本底部对齐 */
      text-indent: 1.5em;
      background: url(../../static/player.svg) no-repeat 0px 3px;
    }
    
    .lesson-list .free{
         34px;
        height: 20px;
        color: #fd7b4d;
        margin-left: 10px;
        border: 1px solid #fd7b4d;
        border-radius: 2px;
        text-align: center;
        font-size: 13px;
        white-space: nowrap;
    }
    .lesson-list li:hover .lesson-title{
        color: #ffc210;
        background-image: url(../../static/player2.svg);
    }
    .lesson-list li:hover .free{
        border-color: #ffc210;
        color: #ffc210;
    }
    
    .buy-info .discount{
        padding: 0px 10px;
        font-size: 16px;
        color: #fff;
        display: inline-block;
        height: 36px;
        text-align: center;
        margin-right: 8px;
        background: #fa6240;
        border: 1px solid #fa6240;
        border-radius: 10px 0 10px 0;
        line-height: 36px;
    }
    .present-price{
        font-size: 24px;
        color: #fa6240;
    }
    .original-price{
        text-decoration: line-through;
        font-size: 14px;
        color: #9b9b9b;
        margin-left: 10px;
    }
    .buy-now{
         120px;
        height: 38px;
        background: transparent;
        color: #fa6240;
        font-size: 16px;
        border: 1px solid #fd7b4d;
        border-radius: 3px;
        transition: all .2s ease-in-out; /* 过渡动画 */
        float: right;
        margin-top: 5px;
    }
    .buy-now:hover{
        color: #fff;
        background: #ffc210;
        border: 1px solid #ffc210;
        cursor: pointer;
    }
    </style>
    

    注册路由

    import Course from "../components/Course"
    
     ....
        {
          name:"Course",
          path: "/course",
          component: Course,
        }
    
    
    

    分析课程列表页面中的出现的数据之间的关系

    课程分类: 
    课程信息:
    课程章节:
    课时信息:
    老师信息:
    价格策略:(限时免费限时折扣限时满减原价)
    

    课程子应用创建

    python ../../manage.py startapp courses
    

    注册子应用

    INSTALLED_APPS = [
    		...
        'courses',
    ]
    

    数据模型创建

    from django.db import models
    from luffy.utils.models import BaseModel
    # Create your models here.
    class CourseCategory(BaseModel):
        """
        课程分类
        """
        name = models.CharField(max_length=64, unique=True, verbose_name="分类名称")
        class Meta:
            db_table = "ly_course_category"
            verbose_name = "课程分类"
            verbose_name_plural = "课程分类"
    
    
        def __str__(self):
            return "%s" % self.name
    
    
    
    class Course(BaseModel):
        """
        专题课程
        """
        course_type = (
            (0, '付费'),
            (1, 'VIP专享'),
            (2, '学位课程')
        )
        level_choices = (
            (0, '初级'),
            (1, '中级'),
            (2, '高级'),
        )
        status_choices = (
            (0, '上线'),
            (1, '下线'),
            (2, '预上线'),
        )
        name = models.CharField(max_length=128, verbose_name="课程名称")
        course_img = models.ImageField(upload_to="course", max_length=255, verbose_name="封面图片", blank=True, null=True)
        course_type = models.SmallIntegerField(choices=course_type,default=0, verbose_name="付费类型")
        # 使用这个字段的原因
        brief = models.TextField(max_length=2048, verbose_name="详情介绍", null=True, blank=True)
        level = models.SmallIntegerField(choices=level_choices, default=1, verbose_name="难度等级")
        pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
        period = models.IntegerField(verbose_name="建议学习周期(day)", default=7)
        attachment_path = models.FileField(max_length=128, verbose_name="课件路径", blank=True, null=True)
        status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="课程状态")
        course_category = models.ForeignKey("CourseCategory", on_delete=models.CASCADE, null=True, blank=True,verbose_name="课程分类")
        students = models.IntegerField(verbose_name="学习人数",default = 0)
        lessons = models.IntegerField(verbose_name="总课时数量",default = 0)
        pub_lessons = models.IntegerField(verbose_name="课时更新数量",default = 0)
        price = models.DecimalField(max_digits=6,decimal_places=2, verbose_name="课程原价",default=0)
        teacher = models.ForeignKey("Teacher",on_delete=models.DO_NOTHING, null=True, blank=True,verbose_name="授课老师")
        class Meta:
            db_table = "ly_course"
            verbose_name = "专题课程"
            verbose_name_plural = "专题课程"
    
        def __str__(self):
            return "%s" % self.name
    
    
    
    class Teacher(BaseModel):
        """讲师、导师表"""
        role_choices = (
            (0, '讲师'),
            (1, '导师'),
            (2, '班主任'),
        )
        name = models.CharField(max_length=32, verbose_name="讲师title")
        role = models.SmallIntegerField(choices=role_choices, default=0, verbose_name="讲师身份")
        title = models.CharField(max_length=64, verbose_name="职位、职称")
        signature = models.CharField(max_length=255, verbose_name="导师签名", help_text="导师签名", blank=True, null=True)
        image = models.ImageField(upload_to="teacher", null=True, verbose_name = "讲师封面")
        brief = models.TextField(max_length=1024, verbose_name="讲师描述")
    
        class Meta:
            db_table = "ly_teacher"
            verbose_name = "讲师导师"
            verbose_name_plural = "讲师导师"
    
        def __str__(self):
            return "%s" % self.name
    
    
    class CourseChapter(BaseModel):
        """课程章节"""
        course = models.ForeignKey("Course", related_name='coursechapters', on_delete=models.CASCADE, verbose_name="课程名称")
        chapter = models.SmallIntegerField(verbose_name="第几章", default=1)
        name = models.CharField(max_length=128, verbose_name="章节标题")
        summary = models.TextField(verbose_name="章节介绍", blank=True, null=True)
        pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
    
        class Meta:
            db_table = "ly_course_chapter"
            verbose_name = "课程章节"
            verbose_name_plural = "课程章节"
    
        def __str__(self):
            return "%s:(第%s章)%s" % (self.course, self.chapter, self.name)
    
    
    class CourseLesson(BaseModel):
        """课程课时"""
        section_type_choices = (
            (0, '文档'),
            (1, '练习'),
            (2, '视频')
        )
        chapter = models.ForeignKey("CourseChapter", related_name='coursesections', on_delete=models.CASCADE,verbose_name="课程章节")
        name = models.CharField(max_length=128,verbose_name = "课时标题")
        orders = models.PositiveSmallIntegerField(verbose_name="课时排序")
        section_type = models.SmallIntegerField(default=2, choices=section_type_choices, verbose_name="课时种类")
        section_link = models.CharField(max_length=255, blank=True, null=True, verbose_name="课时链接", help_text = "若是video,填vid,若是文档,填link")
        duration = models.CharField(verbose_name="视频时长", blank=True, null=True, max_length=32)  # 仅在前端展示使用
        pub_date = models.DateTimeField(verbose_name="发布时间", auto_now_add=True)
        free_trail = models.BooleanField(verbose_name="是否可试看", default=False)
    
        class Meta:
            db_table = "ly_course_lesson"
            verbose_name = "课程课时"
            verbose_name_plural = "课程课时"
    
        def __str__(self):
            return "%s-%s" % (self.chapter, self.name)
    

    执行数据迁移

    python manage.py makemigrations
    python manage.py migrate
    

    执行数据库时,如果发生以下错误

    则到 django/db/backends/utils.py : 237,修改代码如下:

        if value is None:
            return None
        if isinstance(value, decimal.Decimal):
            context = decimal.getcontext().copy()
            if max_digits is not None:
                # 增加转型类型转换
                context.prec = int(max_digits)
            if decimal_places is not None:
                # 增加转型类型转换
                value = value.quantize(decimal.Decimal(1).scaleb(-int(decimal_places)), context=context)
            else:
                context.traps[decimal.Rounded] = 1
                value = context.create_decimal(value)
            return "{:f}".format(value)
        if decimal_places is not None:
            return "%.*f" % (decimal_places, value)
        return "{:f}".format(value)
    

    把当前新增的课程模型注册到xadmin里面.

    coursers/adminx.py,代码:

    import xadmin
    
    from .models import CourseCategory
    class CourseCategoryModelAdmin(object):
        """课程分类模型管理类"""
        pass
    xadmin.site.register(CourseCategory, CourseCategoryModelAdmin)
    
    
    from .models import Course
    class CourseModelAdmin(object):
        """课程模型管理类"""
        pass
    xadmin.site.register(Course, CourseModelAdmin)
    
    
    from .models import Teacher
    class TeacherModelAdmin(object):
        """老师模型管理类"""
        pass
    xadmin.site.register(Teacher, TeacherModelAdmin)
    
    
    from .models import CourseChapter
    class CourseChapterModelAdmin(object):
        """课程章节模型管理类"""
        pass
    xadmin.site.register(CourseChapter, CourseChapterModelAdmin)
    
    
    
    from .models import CourseLesson
    class CourseLessonModelAdmin(object):
        """课程课时模型管理类"""
        pass
    xadmin.site.register(CourseLesson, CourseLessonModelAdmin)
    
    
    

    添加测试数据

    1557805132070

    后端实现课程分类列表接口

    创建序列化器

    courses/serializers.py

    from rest_framework import serializers
    from .models import CourseCategory
    class CourseCategoryModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = CourseCategory
            fields = ("id","name")
    
    
    

    创建视图

    courses/views.py

    # Create your views here.
    from rest_framework.generics import ListAPIView
    from .models import CourseCategory
    from .serializers import CourseCategoryModelSerializer
    
    class CourseCategoryAPIView(ListAPIView):
        """课程分类列表"""
        queryset = CourseCategory.objects.filter( is_delete=False, is_show=True ).order_by("-orders")
        serializer_class = CourseCategoryModelSerializer
    
    
    

    注册路由

    from django.urls import path
    from . import views
    urlpatterns = [
        path(r"cate/",views.CourseCategoryAPIView.as_view()),
    ]
    
    
    
    # 总路由
        path('courses/', include("courses.urls")),
    
    
    

    客户端发送请求获取课程分类信息

    <template>
      <div class="course">
        <Header/>
        <div class="main">
          <!-- 筛选功能 -->
          <div class="top">
            <ul class="condition condition1">
              <li class="cate-condition">课程分类:</li>
              <li class="item current">全部</li>
              <li v-for="catetory in catetory_list" :data-key="catetory.id" class="item">{{catetory.name}}</li>
            </ul>
            <ul class="condition condition2">
              <li class="cate-condition">筛&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;选:</li>
              <li class="item current">默认</li>
              <li class="item">人气</li>
              <li class="item price">价格</li>
              <li class="course-length">共21个课程</li>
            </ul>
          </div>
          。。。。
      </div>
    </template>
    
    <script>
    import Header from "./common/Header"
    import Footer from "./common/Footer"
    export default {
      name: "Course",
      data(){
        return {
          catetory_list:[],
        }
      },
      components: {Header, Footer},
      created(){
        // 获取课程分类
        this.$axios.get(this.$settings.Host+"/courses/cate/").then(response=>{
          this.catetory_list = response.data
        }).catch(error=>{
          console.log(error.response)
        });
      },
    }
    </script>
    
    
    

    完善课程分类筛选的前端特效

    <template>
      <div class="course">
        <Header/>
        <div class="main">
          <!-- 筛选功能 -->
          <div class="top">
            <ul class="condition condition1">
              <li class="cate-condition">课程分类:</li>
              <li class="item" :class="current_cate==0?'current':''" @click="current_cate=0">全部</li>
              <li :class="current_cate==catetory.id?'current':''" @click="current_cate=catetory.id" v-for="catetory in catetory_list" :data-key="catetory.id" class="item">{{catetory.name}}</li>
            </ul>
            <ul class="condition condition2">
              <li class="cate-condition">筛&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;选:</li>
              <li class="item current">默认</li>
              <li class="item">人气</li>
              <li class="item price">价格</li>
              <li class="course-length">共21个课程</li>
            </ul>
          </div>
          ....
      </div>
    </template>
    
    <script>
    import Header from "./common/Header"
    import Footer from "./common/Footer"
    export default {
      name: "Course",
      data(){
        return {
          catetory_list:[],
          current_cate:0,
        }
      },
      components: {Header, Footer},
      created(){
        // 获取课程分类
        this.$axios.get(this.$settings.Host+"/courses/cate/").then(response=>{
          this.catetory_list = response.data
        }).catch(error=>{
          console.log(error.response)
        });
      },
      methods:{
    
      }
    }
    </script>
    
    <style scoped>
    
    .condition .item{
        padding: 6px 16px;
        line-height: 16px;
        margin-left: 14px;
        position: relative;
        transition: all .3s ease;
        border:1px solid transparent; /*  transparent 透明 */
        cursor: pointer;
        color: #4a4a4a;
    }
        
    ...
    </style>
    
    
    

    后端实现课程信息列表接口

    创建序列化器,代码

    # 开发中一个序列化器 A 中需要同时序列化其他模型 B 的数据返回给客户端,那么直接通过外键默认只会返回主键ID
    # 所以我们可以通过再创建一个模型B的序列化器,对模型B的数据进行序列化
    # 在序列化器A中直接把模型B的序列化器调用作为字段值来声明即可.
    
    from .models import Teacher
    class TeacherSerializer(serializers.ModelSerializer):
        class Meta:
            model = Teacher
            fields = ("id","name","title")
    
    from .models import Course
    class CourseSerializer(serializers.ModelSerializer):
        # 这里调用的序列化器,必须事先在前面已经声明好的,否则报错
        teacher = TeacherSerializer()
        class Meta:
            model= Course
            fields = ("id","name","course_img","students","lessons","pub_lessons","price","teacher")
    
    
    
    

    视图代码:

    from .serializers import CourseModelSerializer
    class CourseAPIView(ListAPIView):
        queryset = Course.objects.filter(is_delete=False, is_show=True).order_by("orders")
        serializer_class = CourseModelSerializer
    
    
    

    路由代码:

    path(r"list/",views.CourseAPIView.as_view()),
    
    
    

    客户端发送请求获取课程列表信息

    <template>
      <div class="course">
        <Header/>
        <div class="main">
          <!-- 筛选功能 -->
          <div class="top">
            <ul class="condition condition1">
              <li class="cate-condition">课程分类:</li>
              <li class="item" :class="current_cate==0?'current':''" @click="current_cate=0">全部</li>
              <li :class="current_cate==catetory.id?'current':''" @click="current_cate=catetory.id" v-for="catetory in catetory_list" :data-key="catetory.id" class="item">{{catetory.name}}</li>
            </ul>
            <ul class="condition condition2">
              <li class="cate-condition">筛&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;选:</li>
              <li class="item current">默认</li>
              <li class="item">人气</li>
              <li class="item price">价格</li>
              <li class="course-length">共21个课程</li>
            </ul>
          </div>
          <!-- 课程列表 --->
          <div class="list">
            <ul>
              <li class="course-item" v-for="course in course_list">
                <router-link to="/detail" class="course-link">
                  <div class="course-cover">
                    <img :src="course.course_img" alt="">
                  </div>
                  <div class="course-info">
                    <div class="course-title">
                      <h3>{{course.name}}</h3>
                      <span>{{course.students}}人已加入学习</span>
                    </div>
                    <p class="teacher">
                      <span class="info">{{course.teacher.name}} {{course.teacher.title}}</span>
                      <span class="lesson">共{{course.lessons}}课时/{{course.lessons==course.pub_lessons?'更新完成':('已更新'+course.pub_lessons+"课时")}}</span>
                    </p>
                    <ul class="lesson-list">
                      <li>
                        <p class="lesson-title">01 | 常用模块学习-模块的种类和导入方法</p>
                        <span class="free">免费</span>
                      </li>
                      <li>
                        <p class="lesson-title">02 | 编程语言介绍(三)高级语言</p>
                        <span class="free">免费</span>
                      </li>
                      <li>
                        <p class="lesson-title">03 | 编程语言介绍(一)</p>
                        <span class="free">免费</span>
                      </li>
                      <li>
                        <p class="lesson-title">04 | 课程介绍(二)-Python与其他语言的区别</p>
                        <span class="free">免费</span>
                      </li>
                    </ul>
                    <div class="buy-info">
                      <span class="discount">限时免费</span>
                      <span class="present-price">¥0.00元</span>
                      <span class="original-price">原价:{{course.price}}元</span>
                      <button class="buy-now">立即购买</button>
                    </div>
                  </div>
                </router-link>
              </li>
    <!--          <li class="course-item">-->
    <!--            <div class="course-cover">-->
    <!--              <img src="../../static/course/1544059695.jpeg" alt="">-->
    <!--            </div>-->
    <!--            <div class="course-info">-->
    <!--              <div class="course-title">-->
    <!--                <h3>Python开发21天入门</h3>-->
    <!--                <span>46520人已加入学习</span>-->
    <!--              </div>-->
    <!--              <p class="teacher">-->
    <!--                <span class="info">Alex 金角大王 老男孩Python教学总监</span>-->
    <!--                <span class="lesson">共154课时/更新完成</span>-->
    <!--              </p>-->
    <!--              <ul class="lesson-list">-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">01 | 常用模块学习-模块的种类和导入方法</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">02 | 编程语言介绍(三)高级语言</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">03 | 编程语言介绍(一)</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">04 | 课程介绍(二)-Python与其他语言的区别</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--              </ul>-->
    <!--              <div class="buy-info">-->
    <!--                <span class="discount">限时免费</span>-->
    <!--                <span class="present-price">¥0.00元</span>-->
    <!--                <span class="original-price">原价:9.00元</span>-->
    <!--                <button class="buy-now">立即购买</button>-->
    <!--              </div>-->
    <!--            </div>-->
    <!--          </li>-->
    <!--          <li class="course-item">-->
    <!--            <div class="course-cover">-->
    <!--              <img src="../../static/course/1544059695.jpeg" alt="">-->
    <!--            </div>-->
    <!--            <div class="course-info">-->
    <!--              <div class="course-title">-->
    <!--                <h3>Python开发21天入门</h3>-->
    <!--                <span>46520人已加入学习</span>-->
    <!--              </div>-->
    <!--              <p class="teacher">-->
    <!--                <span class="info">Alex 金角大王 老男孩Python教学总监</span>-->
    <!--                <span class="lesson">共154课时/更新完成</span>-->
    <!--              </p>-->
    <!--              <ul class="lesson-list">-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">01 | 常用模块学习-模块的种类和导入方法</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">02 | 编程语言介绍(三)高级语言</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">03 | 编程语言介绍(一)</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">04 | 课程介绍(二)-Python与其他语言的区别</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--              </ul>-->
    <!--              <div class="buy-info">-->
    <!--                <span class="discount">限时免费</span>-->
    <!--                <span class="present-price">¥0.00元</span>-->
    <!--                <span class="original-price">原价:9.00元</span>-->
    <!--                <button class="buy-now">立即购买</button>-->
    <!--              </div>-->
    <!--            </div>-->
    <!--          </li>-->
    <!--          <li class="course-item">-->
    <!--            <div class="course-cover">-->
    <!--              <img src="../../static/course/1544059695.jpeg" alt="">-->
    <!--            </div>-->
    <!--            <div class="course-info">-->
    <!--              <div class="course-title">-->
    <!--                <h3>Python开发21天入门</h3>-->
    <!--                <span>46520人已加入学习</span>-->
    <!--              </div>-->
    <!--              <p class="teacher">-->
    <!--                <span class="info">Alex 金角大王 老男孩Python教学总监</span>-->
    <!--                <span class="lesson">共154课时/更新完成</span>-->
    <!--              </p>-->
    <!--              <ul class="lesson-list">-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">01 | 常用模块学习-模块的种类和导入方法</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">02 | 编程语言介绍(三)高级语言</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">03 | 编程语言介绍(一)</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--                <li>-->
    <!--                  <p class="lesson-title">04 | 课程介绍(二)-Python与其他语言的区别</p>-->
    <!--                  <span class="free">免费</span>-->
    <!--                </li>-->
    <!--              </ul>-->
    <!--              <div class="buy-info">-->
    <!--                <span class="discount">限时免费</span>-->
    <!--                <span class="present-price">¥0.00元</span>-->
    <!--                <span class="original-price">原价:9.00元</span>-->
    <!--                <button class="buy-now">立即购买</button>-->
    <!--              </div>-->
    <!--            </div>-->
    <!--          </li>-->
            </ul>
          </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
    import Header from "./common/Header"
    import Footer from "./common/Footer"
    export default {
      name: "Course",
      data(){
        return {
          catetory_list:[],
          course_list:[],
          current_cate:0,
        }
      },
      components: {Header, Footer},
      created(){
        // 获取课程分类
        this.$axios.get(this.$settings.Host+"/courses/cate/").then(response=>{
          this.catetory_list = response.data
        }).catch(error=>{
          console.log(error.response)
        });
    
        // 获取课程信息
        this.$axios.get(this.$settings.Host+"/courses/list/").then(response=>{
          this.course_list = response.data
        }).catch(error=>{
          console.log(error.response)
        });
    
      },
      methods:{
    
      }
    }
    </script>
    
    <style scoped>
    .main{
         1100px;
        height: auto;
        margin: 0 auto;
        padding-top: 35px;
    }
    .main .top{
        margin-bottom: 35px;
        padding: 25px 30px 25px 20px;
        background: #fff;
        border-radius: 4px;
        box-shadow: 0 2px 4px 0 #f0f0f0;
    }
    .condition{
        border-bottom: 1px solid #333;
        border-bottom-color: rgba(51,51,51,.05);
        padding-bottom: 18px;
        margin-bottom: 17px;
        overflow: hidden;
    }
    .condition li{
        float: left;
    }
    .condition .cate-condition{
        color: #888;
        font-size: 16px;
    }
    .condition .item{
        padding: 6px 16px;
        line-height: 16px;
        margin-left: 14px;
        position: relative;
        transition: all .3s ease;
        border:1px solid transparent; /*  transparent 透明 */
        cursor: pointer;
        color: #4a4a4a;
    }
    .condition1 .current{
        color: #ffc210;
        border: 1px solid #ffc210!important;
        border-radius: 30px;
    }
    .condition2 .current{
      color: #ffc210;
    }
    .condition .price:before{
        content: "";
         0;
        border: 5px solid transparent;
        border-top-color: #d8d8d8;
        position: absolute;
        right: 0;
        bottom: 2.5px;
    }
    .condition .price:after{
        content: "";
         0;
        border: 5px solid transparent;
        border-bottom-color: #ffc210;
        position: absolute;
        right: 0;
        top: 2.5px;
    }
    .condition2 .course-length{
        float: right;
        font-size: 14px;
        color: #9b9b9b;
    }
    .course-item{
        background: #fff;
        padding: 20px 30px 20px 20px;
        margin-bottom: 35px;
        border-radius: 2px;
        cursor: pointer;
        box-shadow: 2px 3px 16px rgba(0,0,0,.1);
        transition: all .2s ease;
        overflow: hidden;
        cursor:pointer;
    }
    .course-link{
      overflow: hidden;
    }
    .course-cover {
         423px;
        height: 210px;
        margin-right: 30px;
        float: left;
    }
    .course-info{
         597px;
        float: left;
    }
    .course-title{
      margin-bottom: 8px;
      overflow: hidden;
    
    }
    .course-title h3{
      font-size: 26px;
      color: #333;
      float: left;
    }
    .course-title span {
      float: right;
      font-size: 14px;
      color: #9b9b9b;
      margin-top: 12px;
      text-indent: 1em; /* 缩进 2字符宽度 */
      background: url("../assets/people.svg") no-repeat 0px 3px;
    }
    .teacher{
        justify-content: space-between;
        font-size: 14px;
        color: #9b9b9b;
        margin-bottom: 14px;
        padding-bottom: 14px;
        border-bottom: 1px solid #333;
        border-bottom-color: rgba(51,51,51,.05);
    }
    .teacher .lesson{
        float: right;
    }
    .lesson-list{
      overflow: hidden;
    }
    .lesson-list li{
         49%;
        margin-bottom: 15px;
        cursor: pointer;
        float: left;
        margin-right:1%;
    }
    .lesson-list li .player{
         16px;
        height: 16px;
        vertical-align: text-bottom;
    }
    .lesson-list li .lesson-title {
      display: inline-block;
      max- 227px;
      text-overflow: ellipsis; /* 如果字体太多超出元素的宽度,则添加省略符号 */
      color: #666;
      overflow: hidden;
      white-space: nowrap;
      font-size: 14px;
      vertical-align: text-bottom; /* 文本的垂直对齐方式: text-botton 文本底部对齐 */
      text-indent: 1.5em;
      background: url(../../static/player.svg) no-repeat 0px 3px;
    }
    
    .lesson-list .free{
         34px;
        height: 20px;
        color: #fd7b4d;
        margin-left: 10px;
        border: 1px solid #fd7b4d;
        border-radius: 2px;
        text-align: center;
        font-size: 13px;
        white-space: nowrap;
    }
    .lesson-list li:hover .lesson-title{
        color: #ffc210;
        background-image: url(../../static/player2.svg);
    }
    .lesson-list li:hover .free{
        border-color: #ffc210;
        color: #ffc210;
    }
    
    .buy-info .discount{
        padding: 0px 10px;
        font-size: 16px;
        color: #fff;
        display: inline-block;
        height: 36px;
        text-align: center;
        margin-right: 8px;
        background: #fa6240;
        border: 1px solid #fa6240;
        border-radius: 10px 0 10px 0;
        line-height: 36px;
    }
    .present-price{
        font-size: 24px;
        color: #fa6240;
    }
    .original-price{
        text-decoration: line-through;
        font-size: 14px;
        color: #9b9b9b;
        margin-left: 10px;
    }
    .buy-now{
         120px;
        height: 38px;
        background: transparent;
        color: #fa6240;
        font-size: 16px;
        border: 1px solid #fd7b4d;
        border-radius: 3px;
        transition: all .2s ease-in-out; /* 过渡动画 */
        float: right;
        margin-top: 5px;
    }
    .buy-now:hover{
        color: #fff;
        background: #ffc210;
        border: 1px solid #ffc210;
        cursor: pointer;
    }
    </style>
    
    
    

    1557818465550

    客户端的课程列表显示当前课程的章节信息

    可以通过序列化器嵌套来完成,但是查询过程的数量不好控制。以下代码仅供参考:

    from .models import CourseLesson
    class CourseLessonModelSerializer(serializers.ModelSerializer):
        class Meta:
            model = CourseLesson
            fields = ("id","name","free_trail")
    
    from .models import CourseChapter
    class CourseChapterModelSerializer(serializers.ModelSerializer):
        coursesections = CourseLessonModelSerializer(many=True)
        class Meta:
            model = CourseChapter
            fields = ("id", "name", "coursesections")
    
    from .models import Course
    class CourseModelSerializer(serializers.ModelSerializer):
        teacher = TeacherModelSerializer()
        coursechapters = CourseChapterModelSerializer(many=True)  # 课程章节多个,所以需要声明 many=True
        class Meta:
            model = Course
            fields = ("id", "name", "course_img", "students", "lessons", "pub_lessons", "price", "teacher","coursechapters")
    
    
    

    我们可以通过在models模型中给数据模型增加自定义字段来完成获取课时功能。

    class Course(BaseModel):
        """
        专题课程
        """
        course_type = (
            (0, '付费'),
            (1, 'VIP专享'),
            (2, '学位课程')
        )
        level_choices = (
            (0, '初级'),
            (1, '中级'),
            (2, '高级'),
        )
        status_choices = (
            (0, '上线'),
            (1, '下线'),
            (2, '预上线'),
        )
        name = models.CharField(max_length=128, verbose_name="课程名称")
        course_img = models.ImageField(upload_to="course", max_length=255, verbose_name="封面图片", blank=True, null=True)
        course_type = models.SmallIntegerField(choices=course_type,default=0, verbose_name="付费类型")
        # 使用这个字段的原因
        brief = models.TextField(max_length=2048, verbose_name="详情介绍", null=True, blank=True)
        level = models.SmallIntegerField(choices=level_choices, default=1, verbose_name="难度等级")
        pub_date = models.DateField(verbose_name="发布日期", auto_now_add=True)
        period = models.IntegerField(verbose_name="建议学习周期(day)", default=7)
        attachment_path = models.FileField(max_length=128, verbose_name="课件路径", blank=True, null=True)
        status = models.SmallIntegerField(choices=status_choices, default=0, verbose_name="课程状态")
        course_category = models.ForeignKey("CourseCategory", on_delete=models.CASCADE, null=True, blank=True,verbose_name="课程分类")
        students = models.IntegerField(verbose_name="学习人数",default = 0)
        lessons = models.IntegerField(verbose_name="总课时数量",default = 0)
        pub_lessons = models.IntegerField(verbose_name="课时更新数量",default = 0)
        price = models.DecimalField(max_digits=6,decimal_places=2, verbose_name="课程原价",default=0)
        teacher = models.ForeignKey("Teacher",on_delete=models.DO_NOTHING, null=True, blank=True,verbose_name="授课老师")
        class Meta:
            db_table = "ly_course"
            verbose_name = "专题课程"
            verbose_name_plural = "专题课程"
    
        def __str__(self):
            return "%s" % self.name
    
        def lesson_list(self):
            """获取当前课程的前8个课时展示到列表中"""
    
            # 获取所有章节
            chapters_list = self.coursechapters.filter(is_delete=False,is_show=True)
            lesson_list = []
            if chapters_list:
                for chapter in chapters_list:
                    lessons = chapter.coursesections.filter(is_delete=False,is_show=True)[:4]
                    if lessons:
                        for lesson in lessons:
                            lesson_list.append({
                                "id":lesson.id,
                                "name":lesson.name,
                                "free_trail":lesson.free_trail
                            })
    
            return lesson_list[:4]
    
    
    

    序列化器字段增加自定义的模型字段,

    class CourseModelSerializer(serializers.ModelSerializer):
        # 默认情况,序列化器转换模型数据时,默认会把外键直接转成主键ID值
        # 所以我们需要重新设置在序列化器中针对外键的序列化
        # 这种操作就是一个序列器里面调用另一个序列化器了.叫"序列化器嵌套"
        teacher = TeacherModelSerializer()
        # coursechapters = CourseChapterModelSerializer(many=True)
        class Meta:
            model = Course
            fields = ("id","name","course_img","students","lessons","pub_lessons","price","teacher","lesson_list")
    
    
    

    前端显示课程章节信息

    <template>
      <div class="course">
        <Header/>
        <div class="main">
          <!-- 筛选功能 -->
          <div class="top">
            <ul class="condition condition1">
              <li class="cate-condition">课程分类:</li>
              <li class="item" :class="current_cate==0?'current':''" @click="current_cate=0">全部</li>
              <li :class="current_cate==catetory.id?'current':''" @click="current_cate=catetory.id" v-for="catetory in catetory_list" :data-key="catetory.id" class="item">{{catetory.name}}</li>
            </ul>
            <ul class="condition condition2">
              <li class="cate-condition">筛&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;选:</li>
              <li class="item current">默认</li>
              <li class="item">人气</li>
              <li class="item price">价格</li>
              <li class="course-length">共21个课程</li>
            </ul>
          </div>
          <!-- 课程列表 --->
          <div class="list">
            <ul>
              <li class="course-item" v-for="course in course_list">
                <router-link to="/detail" class="course-link">
                  <div class="course-cover">
                    <img :src="course.course_img" alt="">
                  </div>
                  <div class="course-info">
                    <div class="course-title">
                      <h3>{{course.name}}</h3>
                      <span>{{course.students}}人已加入学习</span>
                    </div>
                    <p class="teacher">
                      <span class="info">{{course.teacher.name}} {{course.teacher.title}}</span>
                      <span class="lesson">共{{course.lessons}}课时/{{course.lessons==course.pub_lessons?'更新完成':('已更新'+course.pub_lessons+"课时")}}</span>
                    </p>
                    <ul class="lesson-list">
                      <li v-for="lesson,key in course.lesson_list">
                        <p class="lesson-title">0{{key+1}} | {{lesson.name}}</p>
                        <span v-if="lesson.free_trail" class="free">免费</span>
                      </li>
    
                    </ul>
                    <div class="buy-info">
                      <span class="discount">限时免费</span>
                      <span class="present-price">¥0.00元</span>
                      <span class="original-price">原价:{{course.price}}元</span>
                      <button class="buy-now">立即购买</button>
                    </div>
                  </div>
                </router-link>
              </li>
            </ul>
          </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
    import Header from "./common/Header"
    import Footer from "./common/Footer"
    export default {
      name: "Course",
      data(){
        return {
          catetory_list:[],
          course_list:[],
          current_cate:0,
        }
      },
      components: {Header, Footer},
      created(){
        // 获取课程分类
        this.$axios.get(this.$settings.Host+"/courses/cate/").then(response=>{
          this.catetory_list = response.data
        }).catch(error=>{
          console.log(error.response)
        });
    
        // 获取课程信息
        this.$axios.get(this.$settings.Host+"/courses/list/").then(response=>{
          this.course_list = response.data
        }).catch(error=>{
          console.log(error.response)
        });
    
      },
      methods:{
    
      }
    }
    </script>
    
    

    按照指定分类显示课程信息

    在当前项目中安装 字段过滤排序

    pip install django-filter
    
    

    在settings/dev.py配置文件中增加过滤后端的设置:

    INSTALLED_APPS = [
        ...
        'django_filters',  # 需要注册应用,
    ]
    
    
    

    在视图中设置允许过滤的字段名和引入过滤字段核心类

    from .serializers import CourseModelSerializer
    from django_filters.rest_framework import DjangoFilterBackend
    class CourseAPIView(ListAPIView):
        queryset = Course.objects.filter(is_delete=False, is_show=True).order_by("orders")
        serializer_class = CourseModelSerializer
        filter_backends = [DjangoFilterBackend, ]
        filter_fields = ('course_category',)
    
    
    

    排序显示课程

    后端提供排序课程的接口

    把原来views.py中的CoursesAPIView新增两句代码:

    from .models import Course
    from .serializers import CourseModelSerializer
    from rest_framework.filters import OrderingFilter
    from django_filters.rest_framework import DjangoFilterBackend
    class CourseAPIView(ListAPIView):
        queryset = Course.objects.filter(status=0).order_by("-orders","-id")
        serializer_class = CourseModelSerializer
        # 设置价格排序
        filter_backends = [DjangoFilterBackend,OrderingFilter]
        filter_fields = ('course_category',)
        ordering_fields = ('id', 'students', 'price')
    
    
    

    前端根据排序字段对应的课程顺序

    组件vue代码

    <template>
      <div class="course">
        <Header/>
        <div class="main">
          <!-- 筛选功能 -->
          <div class="top">
            <ul class="condition condition1">
              <li class="cate-condition">课程分类:</li>
              <li class="item" :class="query_params.course_category==0?'current':''" @click="query_params.course_category=0">全部</li>
              <li :class="query_params.course_category==category.id?'current':''" @click="query_params.course_category=category.id"
                  v-for="category in category_list" :data-key="category.id" class="item">{{category.name}}</li>
            </ul>
    <!--        <ul class="condition condition2">-->
    <!--          <li class="cate-condition">筛&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;选:</li>-->
    <!--          <li class="item" :class="(query_params.ordering=='-id' || query_params.ordering=='id')?'current':''" @click="select_ordering('id')">默认</li>-->
    <!--          <li class="item" :class="(query_params.ordering=='-students' || query_params.ordering=='students')?'current':''" @click="select_ordering('students')">人气</li>-->
    <!--          <li class="item" :class="query_params.ordering=='price'?'current price':(query_params.ordering=='-price'?'current price2':'')" @click="select_ordering('price')">价格</li>-->
    <!--          <li class="course-length">共21个课程</li>-->
    <!--        </ul>-->
             <ul class="condition condition2">
              <li class="cate-condition">筛&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;选:</li>
              <li class="item" :class="(query_params.ordering=='-id' || query_params.ordering=='id')?'current':''"
                  @click="select_ordering('id')">默认</li>
              <li class="item" :class="(query_params.ordering=='-students' || query_params.ordering=='students')?'current':''"
                  @click="select_ordering('students')">人气</li>
              <li class="item" :class="query_params.ordering=='price'?'current price':(query_params.ordering=='-price'?'current price2':'')"
                  @click="select_ordering('price')">价格</li>
              <li class="course-length">共21个课程</li>
            </ul>
          </div>
          <!-- 课程列表 --->
          <div class="list">
            <ul>
              <li class="course-item" v-for="course in course_list">
                <router-link to="/detail" class="course-link">
                  <div class="course-cover">
                    <img :src="course.course_img" alt="">
                  </div>
                  <div class="course-info">
                    <div class="course-title">
                      <h3>{{course.name}}</h3>
                      <span>{{course.students}}人已加入学习</span>
                    </div>
                    <p class="teacher">
                      <span class="info">{{course.teacher.name}} {{course.teacher.title}}</span>
                      <span class="lesson">共{{course.lessons}}课时/{{course.lessons==course.pub_lessons?'更新完成':('已更新'+course.pub_lessons+"课时")}}</span>
                    </p>
                    <ul class="lesson-list">
                      <li v-for="lesson,key in course.lesson_list">
                        <p class="lesson-title">0{{key+1}} | {{lesson.name}}</p>
                        <span v-if="lesson.free_trail" class="free">免费</span>
                      </li>
    
                    </ul>
                    <div class="buy-info">
                      <span class="discount">限时免费</span>
                      <span class="present-price">¥0.00元</span>
                      <span class="original-price">原价:{{course.price}}元</span>
                      <button class="buy-now">立即购买</button>
                    </div>
                  </div>
                </router-link>
              </li>
            </ul>
          </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
    import Header from "./common/Header"
    import Footer from "./common/Footer"
    export default {
      name: "Course",
      data(){
        return {
          category_list:[],
          course_list:[],
          query_params:{
            course_category: 0,
            ordering:"-id",
          }
        }
      },
      components: {Header, Footer},
      watch:{
        // 每次点击不同课程时,要重新获取课程列表
        "query_params.course_category":function(){
           this.get_course_list();
        },
    
        // 点击排序时刷新
        "query_params.ordering":function(){
           this.get_course_list();
        },
      },
      created(){
        // 获取课程分类
        this.$axios.get(this.$settings.Host+"/courses/cate/").then(response=>{
          this.category_list = response.data
        }).catch(error=>{
          console.log(error.response)
        });
    
        // 获取课程信息
        this.get_course_list()
    
      },
      methods:{
        // select_ordering(selector){
        //   // 默认排序
        //   if(this.query_params.ordering==('-'+selector) ){
        //     console.log(this.query_params.ordering);
        //     this.query_params.ordering = selector;
        //   }else{
        //     console.log(this.query_params.ordering);
        //     this.query_params.ordering = '-'+ selector;
        //   }
        // },
        select_ordering(selector){
          if (this.query_params.ordering==('-'+selector)){
            this.query_params.ordering = selector;
          }else{
            this.query_params.ordering = '-' + selector;
          }
        },
        get_course_list(){
          let query_params = {ordering:this.query_params.ordering};
    
          if( this.query_params.course_category != 0 ){
            query_params.course_category = this.query_params.course_category;
          }
    
          this.$axios.get(this.$settings.Host+"/courses/list/",{
            params: query_params
          }).then(response=>{
            this.course_list = response.data
          }).catch(error=>{
            console.log(error.response)
          });
        }
      }
    }
    </script>
    
    <style scoped>
    .main{
         1100px;
        height: auto;
        margin: 0 auto;
        padding-top: 35px;
    }
    .main .top{
        margin-bottom: 35px;
        padding: 25px 30px 25px 20px;
        background: #fff;
        border-radius: 4px;
        box-shadow: 0 2px 4px 0 #f0f0f0;
    }
    .condition{
        border-bottom: 1px solid #333;
        border-bottom-color: rgba(51,51,51,.05);
        padding-bottom: 18px;
        margin-bottom: 17px;
        overflow: hidden;
    }
    .condition li{
        float: left;
    }
    .condition .cate-condition{
        color: #888;
        font-size: 16px;
    }
    .condition .item{
        padding: 6px 16px;
        line-height: 16px;
        margin-left: 14px;
        position: relative;
        transition: all .3s ease;
        border:1px solid transparent; /*  transparent 透明 */
        cursor: pointer;
        color: #4a4a4a;
    }
    .condition1 .current{
        color: #ffc210;
        border: 1px solid #ffc210!important;
        border-radius: 30px;
    }
    .condition2 .current{
      color: #ffc210;
    }
    .condition .price:before{
        content: "";
         0;
        border: 5px solid transparent;
        border-top-color: #d8d8d8;
        position: absolute;
        right: 0;
        bottom: 2.5px;
    }
    .condition .price2:before{
        content: "";
         0;
        border: 5px solid transparent;
        position: absolute;
        right: 0;
        bottom: 2.5px;
        border-top-color: #ffc210;
    }
    .condition .price2:after{
        content: "";
         0;
        border: 5px solid transparent;
        position: absolute;
        right: 0;
        top: 2.5px;
        border-bottom-color: #d8d8d8;
    }
    .condition .price:after{
        content: "";
         0;
        border: 5px solid transparent;
        border-bottom-color: #ffc210;
        position: absolute;
        right: 0;
        top: 2.5px;
    }
    .condition2 .course-length{
        float: right;
        font-size: 14px;
        color: #9b9b9b;
    }
    .course-item{
        background: #fff;
        padding: 20px 30px 20px 20px;
        margin-bottom: 35px;
        border-radius: 2px;
        cursor: pointer;
        box-shadow: 2px 3px 16px rgba(0,0,0,.1);
        transition: all .2s ease;
        overflow: hidden;
        cursor:pointer;
    }
    .course-link{
      overflow: hidden;
    }
    .course-cover {
         423px;
        height: 210px;
        margin-right: 30px;
        float: left;
    }
    .course-info{
         597px;
        float: left;
    }
    .course-title{
      margin-bottom: 8px;
      overflow: hidden;
    
    }
    .course-title h3{
      font-size: 26px;
      color: #333;
      float: left;
    }
    .course-title span {
      float: right;
      font-size: 14px;
      color: #9b9b9b;
      margin-top: 12px;
      text-indent: 1em; /* 缩进 2字符宽度 */
      background: url("../assets/people.svg") no-repeat 0px 3px;
    }
    .teacher{
        justify-content: space-between;
        font-size: 14px;
        color: #9b9b9b;
        margin-bottom: 14px;
        padding-bottom: 14px;
        border-bottom: 1px solid #333;
        border-bottom-color: rgba(51,51,51,.05);
    }
    .teacher .lesson{
        float: right;
    }
    .lesson-list{
      overflow: hidden;
    }
    .lesson-list li{
         49%;
        margin-bottom: 15px;
        cursor: pointer;
        float: left;
        margin-right:1%;
    }
    .lesson-list li .player{
         16px;
        height: 16px;
        vertical-align: text-bottom;
    }
    .lesson-list li .lesson-title {
      display: inline-block;
      max- 227px;
      text-overflow: ellipsis; /* 如果字体太多超出元素的宽度,则添加省略符号 */
      color: #666;
      overflow: hidden;
      white-space: nowrap;
      font-size: 14px;
      vertical-align: text-bottom; /* 文本的垂直对齐方式: text-botton 文本底部对齐 */
      text-indent: 1.5em;
      background: url(../../static/2.svg) no-repeat 0px 3px;
    }
    
    .lesson-list .free{
         34px;
        height: 20px;
        color: #fd7b4d;
        margin-left: 10px;
        border: 1px solid #fd7b4d;
        border-radius: 2px;
        text-align: center;
        font-size: 13px;
        white-space: nowrap;
    }
    .lesson-list li:hover .lesson-title{
        color: #ffc210;
        background-image: url(../../static/2.svg);
    }
    .lesson-list li:hover .free{
        border-color: #ffc210;
        color: #ffc210;
    }
    
    .buy-info .discount{
        padding: 0px 10px;
        font-size: 16px;
        color: #fff;
        display: inline-block;
        height: 36px;
        text-align: center;
        margin-right: 8px;
        background: #fa6240;
        border: 1px solid #fa6240;
        border-radius: 10px 0 10px 0;
        line-height: 36px;
    }
    .present-price{
        font-size: 24px;
        color: #fa6240;
    }
    .original-price{
        text-decoration: line-through;
        font-size: 14px;
        color: #9b9b9b;
        margin-left: 10px;
    }
    .buy-now{
         120px;
        height: 38px;
        background: transparent;
        color: #fa6240;
        font-size: 16px;
        border: 1px solid #fd7b4d;
        border-radius: 3px;
        transition: all .2s ease-in-out; /* 过渡动画 */
        float: right;
        margin-top: 5px;
    }
    .buy-now:hover{
        color: #fff;
        background: #ffc210;
        border: 1px solid #ffc210;
        cursor: pointer;
    }
    </style>
    
    

    分页显示数据

    from rest_framework.pagination import PageNumberPagination
    
    class StandardPageNumberPagination(PageNumberPagination):
        page_size_query_param = 'page_size'
        max_page_size = 1
    
    class CourseAPIView(ListAPIView):
        queryset = Course.objects.filter(status=0).order_by("-orders","-students")
        # 设置过滤的字段
        filter_fields = ('course_category',)
        serializer_class = CourseSerializer
        filter_backends = [OrderingFilter]
        ordering_fields = ('id', 'students', 'price', 'course_category')
        pagination_class = StandardPageNumberPagination
    
    

    客户端请求后端发送数据

    <template>
      <div class="course">
        <Header/>
        <div class="main">
          <!-- 筛选功能 -->
          <div class="top">
            ....
          <!-- 课程列表 --->
          <div class="list">
            。。。。
          </div>
          <div class="pagination">
            <!-- 必须设置page-size属性,total值的改变才会有效果 -->
            <el-pagination
              @current-change="handleCurrentChange"
              :current-page="query_params.current_page"
              background
              layout="prev, pager, next"
              :page-size="course_page_size"
              :total="course_count">
            </el-pagination>
          </div>
        </div>
        <Footer/>
      </div>
    </template>
    
    <script>
    import Header from "./common/Header"
    import Footer from "./common/Footer"
    export default {
      name: "Course",
      data(){
        return {
          catetory_list:[],
          course_list:[],
          course_count: 0,
          course_page_size:1,
          query_params:{
            course_category: 0,
            ordering:"-id",
            current_page: 1,
          }
        }
      },
      watch:{
        // 每次点击不同课程时,要重新获取课程列表
        "query_params.course_category":function(){
           this.get_course_list();
           // 当切换分类的时候,重置页码
           this.query_params.current_page = 1;
        },
        "query_params.ordering":function(){
           // 当切换排序条件的时候,重置页码
           // this.query_params.current_page = 1;
           this.get_course_list();
        },
        "query_params.current_page":function(){
           this.get_course_list();
        }
      },
      components: {Header, Footer},
      created(){
        // 获取课程分类
        this.$axios.get(this.$settings.Host+"/courses/cate/").then(response=>{
          this.catetory_list = response.data
        }).catch(error=>{
          console.log(error.response)
        });
    
        // 获取课程信息
        this.get_course_list()
    
      },
      methods:{
        select_ordering(selector){
          // 默认排序
          if(this.query_params.ordering==('-'+selector) ){
            this.query_params.ordering = selector;
          }else{
            this.query_params.ordering = '-'+selector;
          }
        },
        get_course_list(){
          let query_params = {
            ordering:this.query_params.ordering,
            page:this.query_params.current_page,
          };
    
          if( this.query_params.course_category != 0 ){
            query_params.course_category = this.query_params.course_category;
          }
    
          this.$axios.get(this.$settings.Host+"/courses/list/",{
            params: query_params
          }).then(response=>{
            // 课程列表
            this.course_list = response.data.results;
            // 课程总数量
            this.course_count = response.data.count;
    
          }).catch(error=>{
            console.log(error.response)
          });
        },
        handleCurrentChange(page){
          // 页码发生改变
          this.query_params.current_page = page;
        }
      }
    }
    </script>
    
    <style scoped>
    ...
    .pagination{
      text-align: center;
      margin: 20px 0px 50px 0px;
    }
    </style>
    
    
  • 相关阅读:
    thinkphp分页样式css代码
    thinkphp两表,多表联合查询及分页的连贯操作写法
    ThinkPHP3.2.3新特性之:数据库设置
    ThinkPHP的主从数据库配置
    CentOS7 64位下MySQL5.7安装与配置(YUM)
    sessionid如何产生?由谁产生?保存在哪里?
    GIT使用
    Windows下安装composer
    树莓派debian配置lamp[解决Apache不显示php网页]
    【Linux】Debian 下安装 Apache,MySQL,PHP
  • 原文地址:https://www.cnblogs.com/pankypan/p/11300264.html
Copyright © 2011-2022 走看看