欢迎访问我的博客专题
源码可访问 Github 查看
通过顶部导航显示的左侧分类,和通过搜索显示的左侧分类,整个格式是相同的,所以它们用的同一个Vue组件。但是它们还是有一定的区别:
- 点击顶部导航,左侧的分类就是显示该一级分类下的所有子分类(包括所有二级分类和二级分类对应的三级分类)
- 搜索显示的分类,只显示了二级分类和一级分类,也就是说,搜索某一个商品,可以获取该商品对应的分类和对应的子分类
获取所有数据getAllData()
list/list.vue 中getAllData()
获取所有数据
getAllData() {
console.log('list/list.vue 获取左侧菜单');
console.log(this.$route.params);
var curloc_id = ''; //当前点击分类的id
if (this.$route.params.id) {
//如果能获取到id值,那么就是点击分类
this.top_category = this.$route.params.id;
this.pageType = 'list';
this.getMenu(this.top_category); // 获取左侧菜单列表
curloc_id = this.$route.params.id;
} else {
this.getMenu(null); // 获取左侧菜单列表
this.pageType = 'search';
this.searchWord = this.$route.params.keyword;
curloc_id = ''
}
this.getCurLoc(curloc_id); // 获取当前位置
this.getListData(); //获取产品列表
this.getPriceRange(); // 获取价格区间
},
这个函数由于判断是点击分类还是搜索进入。
区别导航和搜索显示左侧分类getMenu(id)
list/list.vue 获取菜单
getMenu(id) {
if (id != null) {
getCategory({
id: this.$route.params.id
}).then((response) => {
this.cateMenu = response.data.sub_category;
console.log('list/list.vue 获取分类数据:');
console.log(response.data);
this.currentCategoryName = response.data.name; //获取当前分类的名称
this.currentCategoryID = response.data.id; //获取请求的一级分类的ID
this.isObject = true
//console.log(response.data)
}).catch(function (error) {
console.log(error);
});
} else {
getCategory({}).then((response) => {
this.cateMenu = response.data;
console.log('list/list.vue 获取分类数据:');
console.log(response.data);
this.isObject = false
}).catch(function (error) {
console.log(error);
});
}
},
传递某个一级分类id,通过getCategory
方法获取该分类详情(二、三级分类)显示
在 list/list.vue 引入list/listNav.vue 组件
<div class="cate-menu" id="cate-menu">
<!--<h3 v-if="isObject"><a href=""><strong>{{currentCategoryName}}</strong><i id="total_count">商品共{{proNum}}件</i></a></h3>-->
<h3 v-if="isObject">
<a @click="changeMenu(currentCategoryID)"><strong>{{currentCategoryName}}</strong><i id="total_count">商品共{{proNum}}件</i></a>
</h3>
<h3 v-else><a @click="changeMenu('')"><strong>全部分类</strong><i id="total_count">商品共{{proNum}}件</i></a></h3>
<dl>
<template v-for="item in cateMenu">
<dt><a @click="changeMenu(item.id)" style="color: #888">{{ item.name}}</a></dt>
<dd v-for="subItem in item.sub_category">
<a @click="changeMenu(subItem.id)">{{ subItem.name}}</a>
</dd>
</template>
</dl>
</div>
通过导航点击进入,此时链接为: http://127.0.0.1:8080/#/app/home/list/121
image.png通过搜索进入,此时链接为: http://127.0.0.1:8080/#/app/home/search/%E7%89%9B%E8%82%89
image.png跳转不同页面显示面包屑
获取当前点击分类的id,然后根据这个分类获取父级分类的详情。
父级分类序列化器ParentCategorySerializer
修改 goods/serializers.py ,添加序列化类
# 获取父级分类
class ParentCategorySerializer3(serializers.ModelSerializer):
class Meta:
model = GoodsCategory
fields = '__all__'
class ParentCategorySerializer2(serializers.ModelSerializer):
parent_category = ParentCategorySerializer3()
class Meta:
model = GoodsCategory
fields = '__all__'
class ParentCategorySerializer(serializers.ModelSerializer):
parent_category = ParentCategorySerializer2()
class Meta:
model = GoodsCategory
fields = '__all__'
父级分类视图ParentCategoryViewSet
修改 goods/views.py 增加视图
class ParentCategoryViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
"""
list:
根据子类别查询父类别
"""
queryset = GoodsCategory.objects.all()
serializer_class = ParentCategorySerializer
父级分类URL
修改主 urls.py 注册父级分类的url
# 创建一个路由器并注册我们的视图集
router = DefaultRouter()
router.register(r'goods', GoodsListViewSet, base_name='goods') # 配置goods的url
router.register(r'categories', CategoryViewSet, base_name='categories') # 配置分类的url
router.register(r'parent_categories', ParentCategoryViewSet, base_name='parent_categories') # 配置分类的url
比如获取分类“牛肉”的父级分类,只需要请求该API: http://127.0.0.1:8000/parent_categories/126/
image.png获取当前请求位置getCurLoc(id)
该函数用于提供点击的分类id:this.getCurLoc(curloc_id); // 获取当前位置
,请求后组合分类面包屑。
list/list.vue
//获取当前位置
getCurLoc(id) { // 当前位置
getParentCategory({
id: id //传递指定分类的id
}).then((response) => {
console.log('list/list.vue 获取当前位置:');
console.log(response.data);
var dt = response.data;
var curLoc;
//组合类别
var index_p = {'id': 0, 'name': '首页'};
var first = {'id': dt.id, 'name': dt.name};
curLoc = [index_p, first];
if (dt.parent_category != null) {
var second = {'id': dt.parent_category.id, 'name': dt.parent_category.name};
curLoc = [index_p, second, first];
if (dt.parent_category.parent_category != null) {
var third = {'id': dt.parent_category.parent_category.id, 'name': dt.parent_category.parent_category.name};
curLoc = [index_p, third, second, first];
}
}
this.curLoc = curLoc
}).catch(function (error) {
console.log(error);
});
},
image.png
显示价格区间组件getPriceRange()
将 list/list.vue 中的数据填充到 list/price-range/priceRange.vue 中
getPriceRange() {
//价格区间显示,不使用上方mock.js中的内容
this.priceRange = [
{
min: 1,
max: 30,
},
{
min: 31,
max: 80,
},
{
min: 81,
max: 150,
},
{
min: 151,
max: 300,
},
]
},
changePrice(data) {
this.pricemin = data.min;
this.pricemax = data.max;
this.getListData();
},
数据显示到 priceRange.vue
<div class="bd">
<dl>
<dt>价格:</dt>
<dd class="dd-price" style="height: 48px;">
<div class="items cle w500">
<div class="link" @click="changePriceRange('','')"><a class="item">全部</a></div>
<div class="link" v-for="item in priceRange" @click="changePriceRange(item.min,item.max)"><a class="item">{{item.min}} - {{item.max}} 元</a></div>
</div>
<div class="priceform" id="priceform" v-clickoutside="handleClickOutside">
<div class="form-bg">
<form id="freepriceform">
<span class="rmb"></span>
<input type="text" value="" name="price_min" id="pricemin" v-model="price_min" @focus="focus=true">
<span class="rmb rmb2"></span>
<input type="text" value="" name="price_max" id="pricemax" v-model="price_max" @focus="focus=true">
<input v-show="focus" value="确定" @click="changePriceRange(price_min, price_max)" class="submit">
</form>
</div>
</div>
</dd>
</dl>
</div>
image.png
商品排序
changeSort(type) {
this.ordering = type;
this.getListData();
},
数据显示到 list/list-sort/listSort.vue
<div class="sort">
<div class="bd">
<form name="listform">
<a title="销量" class="curr" rel="nofollow" @click="sortType('-sold_num')"><span :class="{search_DESC: cur==='-sold_num'}">销量</span></a>
<a title="价格" class="curr" rel="nofollow" @click="sortType('-shop_price')"><span :class="{search_DESC: cur==='-shop_price'}">价格</span></a>
</form>
</div>
<div class="search_num">共<b>{{proNum}}</b>个商品 <span id="search_btn" class="search_btn"></span> </div>
</div>
image.png
商品列表分页
修改分页参数
将goods列表分页参数p
修改为page
,修改 goods/views.py 中的GoodsPagination
类,且每一页显示12个数据
class GoodsPagination(PageNumberPagination):
page_size = 12 # 每一页个数,由于前段
page_size_query_param = 'page_size'
page_query_param = 'page' # 参数?p=xx,将其修改为page,适应前端,也方便识别
max_page_size = 36 # 最大指定每页个数
商品分页
list/list.vue
pagefn(value) {//点击分页
this.curPage = value.page;
this.getListData()
}
传递分页值到<Page pre-text="上一页" next-text="下一页" end-show="false" :page="curPage" :total-page='totalPage' @pagefn="pagefn"></Page>
获取商品列表
自定义过滤分类top_category_filter
用于过滤某个分类下的所有商品。例如获取一级分类下的所有商品,就需要同时获取二三级分类的商品。
自定义过滤,按照分类的进行过滤商品,修改 goods/filters.py 中商品过滤类GoodsFilter
class GoodsFilter(filters.FilterSet):
"""
商品的过滤类
"""
name = filters.CharFilter(field_name='name', lookup_expr='contains') # 包含关系,模糊匹配
goods_desc = filters.CharFilter(field_name='name', lookup_expr='contains')
min_price = filters.NumberFilter(field_name="shop_price", lookup_expr='gte') # 自定义字段
max_price = filters.NumberFilter(field_name="shop_price", lookup_expr='lte')
top_category = filters.NumberFilter(method='top_category_filter', field_name='category_id', lookup_expr='=') # 自定义过滤,过滤某个一级分类
def top_category_filter(self, queryset, field_name, value):
"""
自定义过滤内容
这儿是传递一个分类的id,在已有商品查询集基础上获取分类id,一级一级往上找,直到将三级类别找完
:param queryset:
:param field_name:
:param value: 需要过滤的值
:return:
"""
queryset = queryset.filter(Q(category_id=value) | Q(category__parent_category_id=value) | Q(category__parent_category__parent_category_id=value))
return queryset
class Meta:
model = Goods
fields = ['name', 'goods_desc', 'min_price', 'max_price']
不管是点击一级分类、二级分类、三级分类,都能被找到。
image.png这个函数的意思就是对已经过滤后的查询集,进行分类id过滤,比如
>>> from goods.models import Goods
>>> from django.db.models import Q
>>> all_goods = Goods.objects.all()
>>> all_goods1 = all_goods.filter(shop_price__gte=150)
>>> all_goods1 # 获取到价格高于150的所有商品
<QuerySet [<Goods: 新鲜水果甜蜜香脆单果约800克>, <Goods: 酣畅家庭菲力牛排10片澳洲生鲜牛肉团购套餐>, <Goods: 潮香村澳洲进口牛排家庭团购套餐20片>, <Goods: 爱食派内蒙古呼伦贝尔冷冻生鲜牛腱子肉1000g>, <Goods: 澳洲进口牛尾巴300g新鲜肥牛肉>, <Goods: 53度茅台仁酒500ml>, <Goods: 新西兰进口全脂奶粉900g>, <Goods: 维纳斯橄榄菜籽油5L/桶>]>
>>> all_goods1.filter(category_id=122) # 这是个二级分类
<QuerySet [<Goods: 澳洲进口牛尾巴300g新鲜肥牛肉>]>
>>> all_goods1.filter(category__parent_category_id=122)
<QuerySet []>
>>> all_goods1.filter(Q(category_id=122)|Q(category__parent_category_id=122))
<QuerySet [<Goods: 澳洲进口牛尾巴300g新鲜肥牛肉>]>
在所有级别的分类id中进行查找,并获取所有的商品。
获取商品列表getListData()
//获取商品列表
getListData() {
console.log('list/list.vue 获取商品获取方式(list列出、search搜索):' + this.pageType);
if (this.pageType === 'search') {
getGoods({
search: this.searchWord, //搜索关键词
top_category: this.top_category, //商品类型,一级类别的id
ordering: this.ordering, //排序类型
min_price: this.pricemin, //价格最低 默认为‘’ 即为不选价格区间
max_price: this.pricemax // 价格最高 默认为‘’
}).then((response) => {
this.listData = response.data.results;
console.log('list/list.vue 获取商品列表:');
console.log(response.data);
this.proNum = response.data.count;
}).catch(function (error) {
console.log(error);
});
} else {
getGoods({
//以下是url中的过滤参数
page: this.curPage, //当前页码
top_category: this.top_category, //商品类型,一级类别的id
ordering: this.ordering, //排序类型
min_price: this.pricemin, //价格最低 默认为‘’ 即为不选价格区间
max_price: this.pricemax // 价格最高 默认为‘’
}).then((response) => {
this.listData = response.data.results;
console.log('list/list.vue 获取商品列表:');
console.log(response.data);
this.proNum = response.data.count;
}).catch(function (error) {
console.log(error);
});
}
},
list/list.vue 中将数据通过 <product-list :listData="listData"></product-list>
显示到 list/productList.vue
<div class="productlist">
<ul class="cle">
<li v-for="item in listData" >
<router-link :to="'/app/home/productDetail/'+item.id" target="_blank" class="productitem">
<span class="productimg">
<img width="150" height="150" :title="item.productname" :alt="item.productname" :src="item.goods_front_image" style="display: block;">
</span>
<span class="nalaprice xszk">
<b>
¥{{item.shop_price}}元
</b>
</span>
<span class="productname">{{item.name}}</span>
<span class="description">{{item.goods_brief}}</span>
<!-- <span class="price">{{item.price}}</span> -->
<span class="salerow">销量:<span class="sales">{{item.sold_num}}</span>件 </span>
</router-link>
<!--<a class="addcart" target="_blank" rel="nofollow" @click="addShoppingCart">加入购物车</a>-->
</li>
</ul>
<br clear="all">
image.png
网友评论