您现在的位置是: 网站首页 >Django >Vue+Django REST framework前后端分离生鲜电商 Django

【Vue+DRF生鲜电商】11.Vue展示左侧分类、面包屑、排序、商品列表、分页

admin2019年4月26日 17:46 Django | Python | Vue 432人已围观

Vue+Django REST framework前后端分离生鲜电商简介 Vue+Django REST framework 打造前后端分离的生鲜电商项目(慕课网视频)。 Github地址:https://github.com/xyliurui/DjangoOnlineFreshSupermarket ; Django版本:2.2、djangorestframework:3.9.2。 前端Vue模板可以直接联系我拿。

通过顶部导航显示的左侧分类,和通过搜索显示的左侧分类,整个格式是相同的,所以它们用的同一个Vue组件。但是它们还是有一定的区别: - 点击顶部导航,左侧的分类就是显示该一级分类下的所有子分类(包括所有二级分类和二级分类对应的三级分类) - 搜索显示的分类,只显示了二级分类和一级分类,也就是说,搜索某一个商品,可以获取该商品对应的分类和对应的子分类 ### 获取所有数据getAllData() list/list.vue 中`getAllData()`获取所有数据 ```JavaScript 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 获取菜单 ```JavaScript 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 组件 ```html <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 ![BLOG_20190426_175244_15](/media/blog/images/2019/04/BLOG_20190426_175244_15.png "博客图集BLOG_20190426_175244_15.png") 通过搜索进入,此时链接为: http://127.0.0.1:8080/#/app/home/search/%E7%89%9B%E8%82%89 ![BLOG_20190426_175238_13](/media/blog/images/2019/04/BLOG_20190426_175238_13.png "博客图集BLOG_20190426_175238_13.png") ### 跳转不同页面显示面包屑 获取当前点击分类的id,然后根据这个分类获取父级分类的详情。 #### 父级分类序列化器ParentCategorySerializer 修改 goods/serializers.py ,添加序列化类 ```python # 获取父级分类 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 增加视图 ```python class ParentCategoryViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): """ list: 根据子类别查询父类别 """ queryset = GoodsCategory.objects.all() serializer_class = ParentCategorySerializer ``` #### 父级分类URL 修改主 urls.py 注册父级分类的url ```python # 创建一个路由器并注册我们的视图集 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/ ![BLOG_20190426_175214_24](/media/blog/images/2019/04/BLOG_20190426_175214_24.png "博客图集BLOG_20190426_175214_24.png") #### 获取当前请求位置getCurLoc(id) 该函数用于提供点击的分类id:`this.getCurLoc(curloc_id); // 获取当前位置`,请求后组合分类面包屑。 list/list.vue ```JavaScript //获取当前位置 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); }); }, ``` ![BLOG_20190426_175203_67](/media/blog/images/2019/04/BLOG_20190426_175203_67.png "博客图集BLOG_20190426_175203_67.png") ### 显示价格区间组件getPriceRange() 将 list/list.vue 中的数据填充到 list/price-range/priceRange.vue 中 ```JavaScript 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 ```html <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}}&nbsp;-&nbsp;{{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> ``` ![BLOG_20190426_175138_71](/media/blog/images/2019/04/BLOG_20190426_175138_71.png "博客图集BLOG_20190426_175138_71.png") ### 商品排序 ```JavaScript changeSort(type) { this.ordering = type; this.getListData(); }, ``` 数据显示到 list/list-sort/listSort.vue ```html <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> ``` ![BLOG_20190426_175130_42](/media/blog/images/2019/04/BLOG_20190426_175130_42.png "博客图集BLOG_20190426_175130_42.png") ### 商品列表分页 #### 修改分页参数 将goods列表分页参数`p`修改为`page`,修改 goods/views.py 中的`GoodsPagination`类,且每一页显示12个数据 ```python 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 ```JavaScript pagefn(value) {//点击分页 this.curPage = value.page; this.getListData() } ``` 传递分页值到`<Page pre-text="上一页" next-text="下一页" end-show="false" :page="curPage" :total-page='totalPage' @pagefn="pagefn"></Page>` ![BLOG_20190426_175113_83](/media/blog/images/2019/04/BLOG_20190426_175113_83.png "博客图集BLOG_20190426_175113_83.png") ### 获取商品列表 #### 自定义过滤分类top_category_filter 用于过滤某个分类下的所有商品。例如获取一级分类下的所有商品,就需要同时获取二三级分类的商品。 自定义过滤,按照分类的进行过滤商品,修改 goods/filters.py 中商品过滤类`GoodsFilter` ```python 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'] ``` 不管是点击一级分类、二级分类、三级分类,都能被找到。 ![BLOG_20190426_174745_22](/media/blog/images/2019/04/BLOG_20190426_174745_22.png "博客图集BLOG_20190426_174745_22.png") 这个函数的意思就是对已经过滤后的查询集,进行分类id过滤,比如 ```python >>> 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() ```JavaScript //获取商品列表 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 ```html <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"> ``` ![BLOG_20190426_175056_98](/media/blog/images/2019/04/BLOG_20190426_175056_98.png "博客图集BLOG_20190426_175056_98.png")

很赞哦! (1)

文章交流

  • emoji
2人参与,2条评论
None 2019年12月23日 14:00
vue源码在哪,可以提供一下嘛?
None 2019年12月23日 14:00
留言正在审核中...

当前用户

未登录,点击   登录

站点信息

  • 建站时间:网站已运行683天
  • 系统信息:Linux
  • 后台程序:Python: 3.6.6
  • 网站框架:Django: 2.2.12
  • 文章统计:235 篇
  • 文章评论:40 条
  • 腾讯分析网站概况-腾讯分析
  • 百度统计网站概况-百度统计
  • 公众号:微信扫描二维码,关注我们
  • QQ群:QQ加群,下载网站的学习源码
返回
顶部
标题 换行 登录
网站