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">筛 选:</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)
添加测试数据
后端实现课程分类列表接口
创建序列化器
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{
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>
客户端的课程列表显示当前课程的章节信息
可以通过序列化器嵌套来完成,但是查询过程的数量不好控制。以下代码仅供参考:
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')
前端根据排序字段对应的课程顺序
组件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">筛 选:</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">筛 选:</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>