为了让数据在前端正常显示,还需要过滤类别数据、只显示一级类别,修改views.py如下:
class CategoryViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): ''' List: 显示商品所有分类列表数据 Retrieve: 获取单个商品分类详情 ''' queryset = GoodsCategory.objects.filter(category_type=1) serializer_class = CategorySerializer
此时再访问http://127.0.0.1:8080,显示:
此时再访问,却访问不到商品分类,查看控制台提示很明显,同源策略禁止读取位于 http://127.0.0.1:8000/categorys/ 的远程资源。,即禁止跨域访问,当前端口是 8080,而数据接口端口是8000,因此被浏览器自动拒绝,一种方式是对服务器进行设置,还有一种是通过前端代理解决,这里采用第一种方式:
首先在虚拟环境中执行pip install django-cors-headers命令安装库,然后在后端settings.py中配置Cors:
INSTALLED_APPS = [ 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'apps.users.apps.UsersConfig', 'goods.apps.GoodsConfig', 'trade.apps.TradeConfig', 'user_operation.apps.UserOperationConfig', 'DjangoUeditor', 'xadmin', 'crispy_forms', 'django.contrib.admin', 'rest_framework', 'django_filters', 'corsheaders', ] MIDDLEWARE = [ 'corsheaders.middleware.CorsMiddleware', 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] # 跨域访问设置 CORS_ORIGIN_ALLOW_ALL = True
此时再访问,显示:
此时不再报错,商品的各级分类也显示出来,但是可以看出来,全部分类右侧并未显示商品分类,这是因为一级分类的is_tab属性默认为False,改为True即可,可以在数据库中修改,也可以直接在后台管理系统修改。
head.vue中显示商品分类的逻辑也是通过循环实现,如下:
<template v-for="(item,index) in allMenuLabel"> <li> <div v-if="item.is_tab"> <router-link :to="'/app/home/list/'+item.id" >{{item.name}}</router-link> </div> </li> </template>
后台修改商品分类is_tab属性并刷新页面如下:
此时已经显示出商品分类。
三、Vue展示商品列表页数据和搜索
现在进一步实现点击某一个商品分类下面显示出商品详情,具体包括分类显示、价格筛选、分页和排序等功能。
通过项目可以看到,通过搜索和点击Tab页左侧显示的导航栏是不同的,其数据接口也不一样,head.vue如下:
<div class="head_search_hot"> <span>热搜榜:</span> <router-link v-for="item in hotSearch" :to="'/app/home/search/'+item.keywords" :key="item.keywords"> {{item.keywords}} </router-link> </div> // ... <div class="main_nav_link" @mouseover="overAllmenu" @mouseout="outAllmenu"> <a class="meunAll">全部商品分类 <i class="iconfont"></i> </a> <div class="main_cata" id="J_mainCata" v-show="showAllmenu"> <ul> <li class="first" v-for="(item,index) in allMenuLabel" @mouseover="overChildrenmenu(index)" @mouseout="outChildrenmenu(index)"> <h3 style="background:url(../images/1449088788518670880.png) 20px center no-repeat;"> <router-link :to="'/app/home/list/'+item.id">{{item.name}}</router-link> </h3> <div class="J_subCata" id="J_subCata" v-show="showChildrenMenu ===index" style=" left: 215px; top: 0px;"> <div class="J_subView" > <div v-for="list in item.sub_cat"> <dl> <dt> <router-link :to="'/app/home/list/'+list.id">{{list.name}}</router-link> </dt> <dd> <router-link v-for="childrenList in list.sub_cat" :key="childrenList.id" :to="'/app/home/list/'+childrenList.id">{{childrenList.name}}</router-link> </dd> </dl> <div class="clear"></div> </div> </div> </div> </li> </ul> </div> </div>
可以看到,跳转路由不同,搜索跳转到/app/home/search/
,而点击导航栏跳转到/app/home/list/
。
再查看src/router/index.js如下:
{ path: 'list/:id', name: 'list', component: list, meta: { title: '列表', need_log: false } }, { path: 'search/:keyword', name: 'search', component: list, meta: { title: '搜索', need_log: false } },
可以看到,两个路由绑定的组件是相同的,都是list,而在src/views/list/list.vue中:
getAllData () { console.log(this.$route.params) if(this.$route.params.id){ this.top_category = this.$route.params.id; this.getMenu(this.top_category); // 获取左侧菜单列表 }else{ this.getMenu(null); // 获取左侧菜单列表 this.pageType = 'search' this.searchWord = this.$route.params.keyword; } this.getCurLoc(); // 获取当前位置 this.getListData(); //获取产品列表 this.getPriceRange(); // 获取价格区间 }, getListData() { if(this.pageType=='search'){ getGoods({ search: this.searchWord, //搜索关键词 }).then((response)=> { this.listData = response.data.results; this.proNum = response.data.count; }).catch(function (error) { console.log(error); }); }else { getGoods({ page: this.curPage, //当前页码 top_category: this.top_category, //商品类型 ordering: this.ordering, //排序类型 pricemin: this.pricemin, //价格最低 默认为‘’ 即为不选价格区间 pricemax: this.pricemax // 价格最高 默认为‘’ }).then((response)=> { this.listData = response.data.results; this.proNum = response.data.count; }).catch(function (error) { console.log(error); }); } }, getMenu(id) { if(id != null){ getCategory({ id:this.$route.params.id }).then((response)=> { this.cateMenu = response.data.sub_cat; this.currentCategoryName = response.data.name }).catch(function (error) { console.log(error); }); }else { getCategory({}).then((response)=> { this.cateMenu = response.data; this.isObject = false }).catch(function (error) { console.log(error); }); } },
可以看到,针对不同的参数有不同的请求方法和参数。
请求商品列表数据使用的是getListData()
方法,调用了getGoods()
方法,为了测试获取商品,将getGoods
API进行修改如下:
//获取商品列表 export const getGoods = params => { return axios.get(`${local_host}/goods/`, { params: params }) }
同时,向后端请求的参数有一个为top_category,即表示一级类别,请求该参数则返回这一类别下的所有类别,需要在后端定义一个过滤器,需要找到该一级分类下的所有二级分及其对应的商品,后端apps/goods/filters.py如下:
import django_filters from django.db.models import Q from .models import Goods class GoodsFilter(django_filters.rest_framework.FilterSet): '''商品过滤类''' name = django_filters.CharFilter(field_name="name", lookup_expr='contains') pricemin = django_filters.NumberFilter(field_name="market_price", lookup_expr='gte') pricemax = django_filters.NumberFilter(field_name="market_price", lookup_expr='lte') top_category = django_filters.NumberFilter(method='top_category_filter') def top_category_filter(self, queryset, name, value): '''自定义过滤''' return queryset.filter(Q(category_id=value)|Q(category__parent_category_id=value)|Q(category__parent_category__parent_category_id=value)) class Meta: model = Goods fields = ['name', 'pricemin', 'pricemax']
显示:
此时,可以根据top_category进行筛选,再查看前端:
可以看到,已经实现了价格筛选、排序、分页等功能。
从之前的代码还可以看到搜索功能的实现:
if(this.pageType=='search'){ getGoods({ search: this.searchWord, //搜索关键词 }).then((response)=> { this.listData = response.data.results; this.proNum = response.data.count; }).catch(function (error) { console.log(error); }); }
再测试搜索功能如下:
显然,已经实现了搜索功能。