vue ----04课程列表的展示

课程列表页

前端显示课程列表页面

把课程列表页面组件Course.vue放到项目中.

<template>
  <div class="courses">
      <Header :current_page="current_page"/>
      <div class="main">
          <div class="filter">
            <el-row class="filter-el-row1">
              <el-col :span="2" class="filter-text">课程分类:</el-col>
              <el-col :span="2" class="current">全部</el-col>
              <el-col :span="2">Python</el-col>
              <el-col :span="2">Linux运维</el-col>
              <el-col :span="2">Python进阶</el-col>
              <el-col :span="2">开发工具</el-col>
              <el-col :span="2">Go语言</el-col>
              <el-col :span="2">机器学习</el-col>
              <el-col :span="2">技术生涯</el-col>
            </el-row>
            <el-row class="filter-el-row2">
              <el-col :span="2" class="filter-text filter-text2">筛&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;选:</el-col>
              <el-col :span="2" class="current">默认</el-col>
              <el-col :span="2">人气</el-col>
              <el-col :span="2" class="">
                <span @click.stop="filter_price=!filter_price">价格</span>
                <div class="filter-price">
                  <span class="up" @click.stop="filter_price=true" :class="filter_price==true?'active':''"><i class="el-icon-caret-top"></i></span>
                  <span class="down" @click.stop="filter_price=false"  :class="filter_price==false?'active':''"><i class="el-icon-caret-bottom"></i></span>
                </div>
              </el-col>
            </el-row>
          </div>
          <div class="courses_list">
              <el-row class="course-item">
                <el-col :span="24" class="course-item-box">
                  <el-row>
                    <el-col :span="12" class="course-item-left"><img src="/static/courses/675076.jpeg" alt=""></el-col>
                    <el-col :span="12" class="course-item-right">
                        <div class="course-title">
                          <p class="box-title">Go编程33天从入门到放弃系列</p>
                          <p class="box-number">30074人已加入学习</p>
                        </div>
                        <div class="author">
                          <p class="box-author">Alex 金角大王 老男孩Python教学总监</p>
                          <p class="lession">共154课时<span>/更新完成</span></p>
                        </div>
                        <el-row class="course-content">
                            <el-col :span="12"><i class="el-icon-caret-right"></i>01 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>02 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>03 | 三元运算符... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>04 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                        </el-row>
                        <div class="course-price">
                            <p class="course-price-left">
                              <span class="discount">限时免费</span>
                              <span class="count">¥0.00</span>
                              <span class="old_count">原价: ¥8.00元</span>
                            </p>
                            <button class="buy">立即购买</button>
                        </div>
                    </el-col>
                  </el-row>
                </el-col>
              </el-row>
              <el-row class="course-item">
                <el-col :span="24" class="course-item-box">
                  <el-row>
                    <el-col :span="12" class="course-item-left"><img src="/static/courses/675076.jpeg" alt=""></el-col>
                    <el-col :span="12" class="course-item-right">
                        <div class="course-title">
                          <p class="box-title">Go编程33天从入门到放弃系列</p>
                          <p class="box-number">30074人已加入学习</p>
                        </div>
                        <div class="author">
                          <p class="box-author">Alex 金角大王 老男孩Python教学总监</p>
                          <p class="lession">共154课时<span>/更新完成</span></p>
                        </div>
                        <el-row class="course-content">
                            <el-col :span="12"><i class="el-icon-caret-right"></i>01 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>02 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>03 | 三元运算符... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>04 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                        </el-row>
                        <div class="course-price">
                            <p class="course-price-left">
                              <span class="discount">限时免费</span>
                              <span class="count">¥0.00</span>
                              <span class="old_count">原价: ¥8.00元</span>
                            </p>
                            <button class="buy">立即购买</button>
                        </div>
                    </el-col>
                  </el-row>
                </el-col>
              </el-row>
              <el-row class="course-item">
                <el-col :span="24" class="course-item-box">
                  <el-row>
                    <el-col :span="12" class="course-item-left"><img src="/static/courses/675076.jpeg" alt=""></el-col>
                    <el-col :span="12" class="course-item-right">
                        <div class="course-title">
                          <p class="box-title">Go编程33天从入门到放弃系列</p>
                          <p class="box-number">30074人已加入学习</p>
                        </div>
                        <div class="author">
                          <p class="box-author">Alex 金角大王 老男孩Python教学总监</p>
                          <p class="lession">共154课时<span>/更新完成</span></p>
                        </div>
                        <el-row class="course-content">
                            <el-col :span="12"><i class="el-icon-caret-right"></i>01 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>02 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>03 | 三元运算符... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>04 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                        </el-row>
                        <div class="course-price">
                            <p class="course-price-left">
                              <span class="discount">限时免费</span>
                              <span class="count">¥0.00</span>
                              <span class="old_count">原价: ¥8.00元</span>
                            </p>
                            <button class="buy">立即购买</button>
                        </div>
                    </el-col>
                  </el-row>
                </el-col>
              </el-row>
              <el-row class="course-item">
                <el-col :span="24" class="course-item-box">
                  <el-row>
                    <el-col :span="12" class="course-item-left"><img src="/static/courses/675076.jpeg" alt=""></el-col>
                    <el-col :span="12" class="course-item-right">
                        <div class="course-title">
                          <p class="box-title">Go编程33天从入门到放弃系列</p>
                          <p class="box-number">30074人已加入学习</p>
                        </div>
                        <div class="author">
                          <p class="box-author">Alex 金角大王 老男孩Python教学总监</p>
                          <p class="lession">共154课时<span>/更新完成</span></p>
                        </div>
                        <el-row class="course-content">
                            <el-col :span="12"><i class="el-icon-caret-right"></i>01 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>02 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>03 | 三元运算符... <span class="free">免费</span> </el-col>
                            <el-col :span="12"><i class="el-icon-caret-right"></i>04 | 常用模块学习-模块的种类和... <span class="free">免费</span> </el-col>
                        </el-row>
                        <div class="course-price">
                            <p class="course-price-left">
                              <span class="discount">限时免费</span>
                              <span class="count">¥0.00</span>
                              <span class="old_count">原价: ¥8.00元</span>
                            </p>
                            <button class="buy">立即购买</button>
                        </div>
                    </el-col>
                  </el-row>
                </el-col>
              </el-row>
          </div>
      </div>
      <Footer/>
  </div>
</template>

<script>
  import Header from "./common/Header"
  import Footer from "./common/Footer"
  export default {
    name:"Courses",
    data(){
      return {
        current_page:1,
        filter_price:false,
      }
    },
    components:{
      Header,
      Footer,
    },
    methods:{
    }
  }
</script>


<style scoped>
.courses{
  padding-top: 80px;
}
.main{
     1100px;
    height: auto;
    margin: 0 auto;
    padding-top: 35px;
}
.main .filter{
     100%;
    height: auto;
    margin-bottom: 35px;
    padding: 25px 0px 25px 0px;
    background: #fff;
    border-radius: 4px;
    box-shadow: 0 2px 4px 0 #f0f0f0;
}
.filter .el-col{
  text-align: center;
  padding: 6px 0px;
  line-height: 16px;
  margin-left: 14px;
  position: relative;
  transition: all .3s ease;
  cursor: pointer;
  color: #4a4a4a;
}
.filter-el-row1{
  padding-bottom: 18px;
  margin-bottom: 17px;
}
.filter .filter-text{
  text-align: right;
  font-size: 16px;
  color: #888;
}
.filter .filter-text2{
}
.filter .filter-el-row1 .current{
    color: #ffc210;
    border: 1px solid #ffc210!important;
    border-radius: 30px;
}
.filter .filter-el-row2 .current{
    color: #ffc210;
}
.filter-price{
  display:inline-block;
  vertical-align: middle;
}
.filter-price .up, .filter-price .down{
  display: block;
  line-height: 8px;
  font-size: 13px;
  margin: -4px;
  color: #d8d8d8;
}
.current .filter-price .active{
  color: #ffc210;
}
.course-item{
  margin-bottom: 35px;
}
.course-item .course-item-box{
   padding: 20px 30px 20px 20px;
}
.course-item{
     box-shadow: 2px 3px 16px rgba(0,0,0,.1);
    transition: all .2s ease;
}
.course-item .course-item-left{
     423px;
    height: 210px;
    margin-right: 30px;
}
.course-title{
  overflow: hidden;/* 在父元素中使用可以清除子元素的浮动影响 */
}
.course-title .box-title{
  font-size: 26px;
  color: #333333;
  float: left;
  margin-bottom: 8px;
}
.course-title .box-number{
    font-size: 14px;
    color: #9b9b9b;
    font-family: PingFangSC-Light;
    float: right;
    padding-top: 12px;
}
.course-item-right{
   56.6%;
}
.author{
    font-size: 14px;
    color: #9b9b9b;
    margin-bottom: 14px;
    padding-bottom: 14px;
    overflow: hidden;
}
.author .box-author{
  float:left;
}
.author .lession{
  float: right;
}
.course-content .el-icon-caret-right{
  border: 1px solid #000;
  border-radius: 50%;
  margin-right: 6px;
}
.course-content .el-col{
  font-size: 14px;
  color: #666;
   50%;
  margin-bottom: 15px;
  cursor: pointer;
}
.course-content .el-col:hover{
  color: #ffc210;
}
.course-content .el-col:hover .el-icon-caret-right,.course-content .el-col:hover .free{
  border-color: #ffc210;
  color: #ffc210;
}
.course-content .el-col .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;
}
.course-price{
  overflow: hidden;
}
.course-price .course-price-left{
  float: left;
}
.course-price .discount{
    padding: 6px 10px;
    display: inline-block;
    font-size: 16px;
    color: #fff;
    text-align: center;
    margin-right: 8px;
    background: #fa6240;
    border: 1px solid #fa6240;
    border-radius: 10px 0 10px 0;
}
.course-price .course-price-left{
  line-height: 22px;
}
.course-price .count{
    font-size: 24px;
    color: #fa6240;
}
.course-price .old_count{
    font-size: 14px;
    color: #9b9b9b;
    text-decoration: line-through;
    margin-left: 10px;
}
.course-price .buy{
  float: right;
   120px;
  height: 38px;
  font-size: 16px;
  border-radius: 3px;
  border: 1px solid #fd7b4d;
  background: transparent;/* 透明 */
  color: #fa6240;
  cursor: pointer;
  transition: all .2s ease-in-out;/* css3新版本的样式中支持支持 jQuery里面的动画预设效果 */
  /* all表示当前元素的所有样式  .2s表示改变样式完成的时间  ease-in-out */
}
.course-price .buy:hover{
    color: #fff;
    background: #ffc210;
    border: 1px solid #ffc210;
}
</style>
View Code

上面是写死的模板,我们需要在后端开接口获取动态数据

(前端)注册路由

import Courses from "../components/Courses"

 ....
    {
      name:"Courses",
      path: "/courses",
      component: Courses,
    }

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

课程分类:
课程表:
课程章节:
老师表:
价格服务表:(限时免费限时折扣限时满减)

课程子应用创建(后端)

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

注册子应用(settings/dev.py)

INSTALLED_APPS = [
        ...
    'courses',
]

数据模型创建

from django.db import models

# Create your models here.
class CourseCategory(models.Model):
    """
    课程分类
    """
    name = models.CharField(max_length=64, unique=True, verbose_name="分类名称")
    orders = models.IntegerField(verbose_name='显示顺序')
    is_show = models.BooleanField(default=True, verbose_name="是否上线")
    is_delete = models.BooleanField(default=False, verbose_name="逻辑删除")
    class Meta:
        db_table = "ly_course_category"
        verbose_name = "课程分类"
        verbose_name_plural = "课程分类"


    def __str__(self):
        return "%s" % self.name


class Course(models.Model):
    """
    专题课程
    """
    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(max_length=255, verbose_name="封面图片", blank=True, null=True)
    course_type = models.SmallIntegerField(choices=course_type, 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)
    orders = models.IntegerField(verbose_name="课程排序")
    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(models.Model):
    """讲师、导师表"""
    role_choices = (
        (0, '讲师'),
        (1, '导师'),
    )
    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, help_text="导师签名", blank=True, null=True)
    image = models.CharField(max_length=128,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(models.Model):
    """课程章节"""
    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(models.Model):
    """课程课时"""
    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)
View Code

执行数据迁移

python manage.py makemigrations
python manage.py migrate

把当前新增的模型注册到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)
View Code

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

创建序列化器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

注册路由(urls.py)

from django.urls import path
from . import views
urlpatterns = [
    path(r"cate/",views.CourseCategoryAPIView.as_view()),
]



# 总路由
    path('course/', include("courses.urls")),

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

创建序列化器,代码

# 开发中一个序列化器 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 .models import Course
from .serializers import CourseSerializer
class CourseAPIView(ListAPIView):
    queryset = Course.objects.filter(status=0).order_by("-orders","-id")
    serializer_class = CourseSerializer

客户端的课程列表显示当前课程的章节信息(courses/serializers.py)

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")
View Code

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

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

pip install django-filter

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

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

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

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,]
    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===catetory.id?'current':''"
              @click="query_params.course_category=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" :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="{path: '/detail',query:{id:course.id}}" 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 class="pagination">
        <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>
  .main {
    width: 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: "";
    width: 0;
    border: 5px solid transparent;
    border-top-color: #d8d8d8;
    position: absolute;
    right: 0;
    bottom: 2.5px;
  }

  .condition .price2:before {
    content: "";
    width: 0;
    border: 5px solid transparent;
    position: absolute;
    right: 0;
    bottom: 2.5px;
    border-top-color: #ffc210;
  }

  .condition .price2:after {
    content: "";
    width: 0;
    border: 5px solid transparent;
    position: absolute;
    right: 0;
    top: 2.5px;
    border-bottom-color: #d8d8d8;
  }

  .condition .price:after {
    content: "";
    width: 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 {
    width: 423px;
    height: 210px;
    margin-right: 30px;
    float: left;
  }

  .course-info {
    width: 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 {
    width: 49%;
    margin-bottom: 15px;
    cursor: pointer;
    float: left;
    margin-right: 1%;
  }

  .lesson-list li .player {
    width: 16px;
    height: 16px;
    vertical-align: text-bottom;
  }

  .lesson-list li .lesson-title {
    display: inline-block;
    max-width: 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 {
    width: 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 {
    width: 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;
  }

  .pagination {
    text-align: center;
    margin: 20px 0px 50px 0px;
  }
</style>
View Code
原文地址:https://www.cnblogs.com/yang950718/p/10879301.html