课程列表页

    xiaoxiao2023-11-21  150

    课程列表页

    前端显示课程列表页面

    在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">筛        选:</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{    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;    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 .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; } </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)

    添加测试数据

     

     

     

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

    创建序列化器

    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">筛        选:</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">筛        选:</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">筛        选:</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{    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 .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; } </style>

     

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

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

    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">筛        选:</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')

     

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

    组件html代码

    <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">筛        选:</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 {      catetory_list:[],      course_list:[],      query_params:{        course_category: 0,        ordering:"-id",     }   } },  watch:{    // 每次点击不同课程时,要重新获取课程列表    "query_params.course_category":function(){       this.get_course_list();   },    "query_params.ordering":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}; ​      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{    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; } </style>
    最新回复(0)