美文网首页Django+Vue生鲜电商
【Vue+DRF生鲜电商】11.Vue展示左侧分类、面包屑、排序

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

作者: 吾星喵 | 来源:发表于2019-04-26 17:52 被阅读51次

    欢迎访问我的博客专题

    源码可访问 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}}&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>
    
    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>

    image.png

    获取商品列表

    自定义过滤分类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

    相关文章

      网友评论

        本文标题:【Vue+DRF生鲜电商】11.Vue展示左侧分类、面包屑、排序

        本文链接:https://www.haomeiwen.com/subject/levqnqtx.html