四、商品列表页
4.1 商品列表页分析
4.1.1. 商品列表页组成结构分析
1.商品频道分类
- 已经提前封装在
contents.utils.py 文件中,直接调用即可。
2.面包屑导航
- 可以使用三级分类ID,查询出该类型商品的三级分类数据。
3.排序和分页
- 无论如何排序和分页,商品的分类不能变。
- 排序时需要知道当前排序方式。
- 分页时需要知道当前分页的页码,且每页五条商品记录。
4.热销排行
- 热销排行中的商品分类要和排序、分页的商品分类一致。
- 热销排行是查询出指定分类商品销量前二的商品。
- 热销排行使用Ajax实现局部刷新的效果。
4.1.2. 商品列表页接口设计和定义
1.请求方式
选项 | 方案 |
---|
请求方法 | GET | 请求地址 | /list/(?P<category_id>\d+)/(?P<page_num>\d+)/?sort=排序方式 |
# 按照商品创建时间排序
http://www.meiduo.site:8000/list/115/1/?sort=default
# 按照商品价格由低到高排序
http://www.meiduo.site:8000/list/115/1/?sort=price
# 按照商品销量由高到低排序
http://www.meiduo.site:8000/list/115/1/?sort=hot
总路由:
?子路由:
from django.conf.urls import url
from . import views
urlpatterns = [
# 商品列表
url(r'^list/(?P<category_id>\d+)/(?P<page_num>\d+)/$', views.ListView.as_view(), name='list'),
]
2.请求参数:路径参数 和 查询参数
参数名 | 类型 | 是否必传 | 说明 |
---|
category_id | string | 是 | 商品分类ID,第三级分类 | page_num | string | 是 | 当前页码 | sort | string | 否 | 排序方式 |
3.响应结果:HTML
list.html
【把list.html和list.js文件复制到项目中】
4.接口定义
goods.views.py
from django.shortcuts import render
from django.views import View
# Create your views here.
class ListView(View):
"""商品列表页"""
def get(self, request, category_id, page_num):
"""提供商品列表页"""
return render(request, 'list.html')
4.2 列表页面包屑导航
重要提示:路径参数category_id是商品第三级分类
4.2.1. 查询列表页面包屑导航数据
提示: 对包屑导航数据的查询进行封装,方便后续直接使用。
goods.utils.py
def get_breadcrumb(category):
"""
获取面包屑导航
:param category: 商品类别
:return: 面包屑导航字典
"""
breadcrumb = dict(
cat1='',
cat2='',
cat3=''
)
if category.parent is None:
# 当前类别为一级类别
breadcrumb['cat1'] = category
elif category.subs.count() == 0: # 此时若没有子集,则为3级
# 当前类别为三级
breadcrumb['cat3'] = category
cat2 = category.parent
breadcrumb['cat2'] = cat2
breadcrumb['cat1'] = cat2.parent
else:
# 当前类别为二级
breadcrumb['cat2'] = category
breadcrumb['cat1'] = category.parent
return breadcrumb
views.py
from goods.models import GoodsCategory, SKU
from django import http
from contents.utils import get_categories
from goods.utils import get_breadcrumb
class ListView(View):
"""商品列表页"""
def get(self, request, category_id, page_num):
"""提供商品列表页"""
# 判断category_id是否正确
try:
category = models.GoodsCategory.objects.get(id=category_id)
except models.GoodsCategory.DoesNotExist:
return http.HttpResponseNotFound('GoodsCategory does not exist')
# 查询商品频道分类
categories = get_categories()
# 查询面包屑导航
breadcrumb = get_breadcrumb(category)
# 渲染页面
context = {
'categories':categories,
'breadcrumb':breadcrumb
}
return render(request, 'list.html', context)
4.2.2. 渲染列表页面包屑导航数据
list.html
<div class="breadcrumb">
<a href="{{ breadcrumb.cat1.url }}">{{ breadcrumb.cat1.name }}</a>
<span>></span>
<a href="javascript:;">{{ breadcrumb.cat2.name }}</a>
<span>></span>
<a href="javascript:;">{{ breadcrumb.cat3.name }}</a>
</div>
4.3 列表页分页和排序
# 按照商品创建时间排序
http://www.meiduo.site:8000/list/115/1/?sort=default
# 按照商品价格由低到高排序
http://www.meiduo.site:8000/list/115/1/?sort=price
# 按照商品销量由高到低排序
http://www.meiduo.site:8000/list/115/1/?sort=hot
4.3.1. 查询列表页分页和排序数据
from django.core.paginator import Paginator, EmptyPage
class ListView(View):
"""商品列表页"""
def get(self, request, category_id, page_num):
"""提供商品列表页"""
# 判断category_id是否正确
try:
category = models.GoodsCategory.objects.get(id=category_id)
except models.GoodsCategory.DoesNotExist:
return http.HttpResponseNotFound('GoodsCategory does not exist')
# 接收sort参数:如果用户不传,就是默认的排序规则
sort = request.GET.get('sort', 'default')
# 查询商品频道分类
categories = get_categories()
# 查询面包屑导航
breadcrumb = get_breadcrumb(category)
# 按照排序规则查询该分类商品SKU信息
if sort == 'price':
# 按照价格由低到高
sort_field = 'price'
elif sort == 'hot':
# 按照销量由高到低
sort_field = '-sales'
else:
# 'price'和'sales'以外的所有排序方式都归为'default'
sort = 'default'
sort_field = 'create_time'
skus = models.SKU.objects.filter(category=category, is_launched=True).order_by(sort_field)
# 创建分页器:每页N条记录
paginator = Paginator(skus, constants.GOODS_LIST_LIMIT)
# 获取每页商品数据
try:
page_skus = paginator.page(page_num)
except EmptyPage:
# 如果page_num不正确,默认给用户404
return http.HttpResponseNotFound('empty page')
# 获取列表页总页数
total_page = paginator.num_pages
# 渲染页面
context = {
'categories': categories, # 频道分类
'breadcrumb': breadcrumb, # 面包屑导航
'sort': sort, # 排序字段
'category': category, # 第三级分类
'page_skus': page_skus, # 分页后数据
'total_page': total_page, # 总页数
'page_num': page_num, # 当前页码
}
return render(request, 'list.html', context)
4.3.2. 渲染列表页分页和排序数据
1.渲染分页和排序数据
<div class="r_wrap fr clearfix">
<div class="sort_bar">
<a href="{{ url('goods:list', args=(category.id, page_num)) }}?sort=default" {% if sort == 'default' %}class="active"{% endif %}>默认</a>
<a href="{{ url('goods:list', args=(category.id, page_num)) }}?sort=price" {% if sort == 'price' %}class="active"{% endif %}>价格</a>
<a href="{{ url('goods:list', args=(category.id, page_num)) }}?sort=hot" {% if sort == 'hot' %}class="active"{% endif %}>人气</a>
</div>
<ul class="goods_type_list clearfix">
{% for sku in page_skus %}
<li>
<a href="detail.html"><img src="{{ sku.default_image.url }}"></a>
<h4><a href="detail.html">{{ sku.name }}</a></h4>
<div class="operate">
<span class="price">¥{{ sku.price }}</span>
<span class="unit">台</span>
<a href="#" class="add_goods" title="加入购物车"></a>
</div>
</li>
{% endfor %}
</ul>
</div>
2.列表页分页器
准备分页器标签
<div class="r_wrap fr clearfix">
......
<div class="pagenation">
<div id="pagination" class="page"></div>
</div>
</div>
# 导入样式时放在最前面导入
<link rel="stylesheet" type="text/css" href="{{ static('css/jquery.pagination.css') }}">
准备分页器交互? 【放在最下方】
<script type="text/javascript" src="{{ static('js/jquery.pagination.min.js') }}"></script>
<script type="text/javascript">
$(function () {
$('#pagination').pagination({
currentPage: {{ page_num }},
totalPage: {{ total_page }},
callback:function (current) {
{#location.href = '/list/115/1/?sort=default';#}
location.href = '/list/{{ category.id }}/' + current + '/?sort={{ sort }}';
}
})
});
</script>
4.4 列表页热销排行
【正在上架的,当前类别分类】【不用登录也会看到】
根据路径参数category_id 查询出该类型商品销量前二的商品。
使用Ajax实现局部刷新的效果。
4.4.1. 查询列表页热销排行数据
1.请求方式
选项 | 方案 |
---|
请求方法 | GET | 请求地址 | /hot/(?P<category_id>\d+)/ |
# 热销排行
url(r'^hot/(?P<category_id>\d+)/$', views.HotGoodsView.as_view()),
2.请求参数:路径参数
参数名 | 类型 | 是否必传 | 说明 |
---|
category_id | string | 是 | 商品分类ID,第三级分类 |
3.响应结果:JSON
字段 | 说明 |
---|
code | 状态码 | errmsg | 错误信息 | hot_skus[ ] | 热销SKU列表 | id | SKU编号 | default_image_url | 商品默认图片 | name | 商品名称 | price | 商品价格 |
{
"code":"0",
"errmsg":"OK",
"hot_skus":[
{
"id":6,
"default_image_url":"http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRbI2ARekNAAFZsBqChgk3141998",
"name":"Apple iPhone 8 Plus (A1864) 256GB 深空灰色 移动联通电信4G手机",
"price":"7988.00"
},
{
"id":14,
"default_image_url":"http://image.meiduo.site:8888/group1/M00/00/02/CtM3BVrRdMSAaDUtAAVslh9vkK04466364",
"name":"华为 HUAWEI P10 Plus 6GB+128GB 玫瑰金 移动联通电信4G手机 双卡双待",
"price":"3788.00"
}
]
}
4.接口定义和实现
from meiduo_mall.utils.response_code import RETCODE
# Create your views here.
class HotGoodsView(View):
"""商品热销排行"""
def get(self, request, category_id):
"""提供商品热销排行JSON数据"""
# 根据销量倒序
skus = SKU.objects.filter(category_id=category_id, is_launched=True).order_by('-sales')[:2]
# 序列化
hot_skus = []
for sku in skus:
hot_skus.append({
'id': sku.id,
'default_image_url': sku.default_image.url,
'name': sku.name,
'price': sku.price
})
return http.JsonResponse({'code': RETCODE.OK, 'errmsg': 'OK', 'hot_skus': hot_skus})
4.4.2. 渲染列表页热销排行数据
1.模板数据category_id 传递到Vue.js
<script type="text/javascript">
let category_id = "{{ category.id }}";
</script>
data: {
category_id: category_id,
},
2.Ajax请求商品热销排行JSON数据
get_hot_skus(){
if (this.category_id) {
let url = '/hot/'+ this.category_id +'/';
axios.get(url, {
responseType: 'json'
})
.then(response => {
this.hot_skus = response.data.hot_skus;
for(let i=0; i<this.hot_skus.length; i++){
this.hot_skus[i].url = '/detail/' + this.hot_skus[i].id + '/';
}
})
.catch(error => {
console.log(error.response);
})
}
},
3.渲染商品热销排行界面
<div class="new_goods" v-cloak>
<h3>热销排行</h3>
<ul>
<li v-for="sku in hot_skus">
<a :href="sku.url"><img :src="sku.default_image_url"></a>
<h4><a :href="sku.url">[[ sku.name ]]</a></h4>
<div class="price">¥[[ sku.price ]]</div>
</li>
</ul>
</div>
上面代码写好后,运行可能会报下面的错误:
?解决办法:
|