1 电商类别表的项目功能需求
1.1 使用vue.js在前端开发一个电商导航栏项目 demo1
- nodejs的安装
yum install -y nodejs
- 安装淘宝镜像(贼慢。。)
npm install -g cnpm --registry=HTTPS://registry.npm.taobao.org
- 安装Vue.js的脚手架工具
cnpm install --globle vue-cli
- 创建项目
vue init webpack-simple demo1
# 5次回车
- 安装依赖
cd demo1
cnpm install
- 运行
npm run dev
- 访问
http://localhost:8080/
-
修改App.vue
- html部分
<template><div id="app"><div class="all"><div class="one"><div class="oneType" v-for="(item, index) in one" :key="index"><b>{{one[index]}}</b></div></div><div class="twothreefour"><div class="two"><div class="twoType" v-for="(item,index) in two" :key="index" @mouseenter="open(index)"><b>{{two[index]}}</b></div></div><div class="threefour" v-if="flag" @mouseleave="close()"><div class="threefourType" v-for="(item, index) in three" :key="index"><span class="three">{{three[index]}}</span><span class="four" v-for="(item4, index4) in four" :key="index4">{{four[index4]}}</span></div></div></div></div></div> </template>
- js部分
<script> export default {name: 'app',data () {return {one:['一级类目', '一级类目', '一级类目', '一级类目', '一级类目'],two:['二级类目1', '二级类目2', '二级类目3', '二级类目4', '二级类目5'],three:[],four:['四级类目','四级类目','四级类目','四级类目','四级类目'],flag:false}} } </script>
- css部分
<style> *{/* 样式初始化 */box-sizing:border-box;margin:0;padding:0; } .all{/* 将整个导航栏组件做整体设置 *//* 宽度占浏览器80%, 高度400px,背景灰色,上边距50px,左右居中 *//* 设置为flex弹性盒子,并且定义为高度不够自动折行模式,用于横向排列子元素 */width: 80%;height: 400px;background: #eee;margin: 50px auto;display: -webkit-flex;display: flex;flex-wrap: wrap; } .one{/* 设置一级类目所占地区的样式,宽度占满all盒子的100% */width: 100%;height: 50px;background: #FF8888;display: flex;display: -webkit-flex;flex-wrap: wrap;/* 弹性盒子内部的子元素都均匀排列成一横排,并且左右两边都留有一定空隙 */justify-content: space-around; } .oneType{width: 20%;height: 50px;line-height: 50px;text-align: center; } .oneType:hover{background-color:chocolate;color: #eee; } .twothreefour{/* 盛放二、三、四级目录的盒子 */width: 100%;height: 350px;background: #66ff66;display: -webkit-flex;display: flex;flex-wrap: wrap;/* 弹性盒子内部的子元素都均匀排列成一横排,并且左右两边都不留空隙 */justify-content: space-between; } .two{/* 设置盛放二级类目录的弹性盒子 */width: 15%;height: 100%;background: #77FFCC;display: -webkit-flex;display: flex;/* 弹性盒子内部的子元素从上到下排成一列 */flex-direction: column; } .twoType{width: 100%;height: 40px;line-height: 40px;text-align: center;background: #EEFFBB; } .twoType:hover{background-color:black;color: #eee; } .threefour{width: 40%;margin-right: 45%;height: 100%;background: #33FFDD;display: -webkit-flex;display: flex;flex-direction: column; } .threefourType{margin: 10px auto; } .three{font-family: 微软雅黑, 黑体;font-size: 16px;font-weight: 800; } .four{font-family: 宋体;font-size: 12px;font-weight: 400; } </style>
-
重新运行项目访问网址
2 为什么不用传统方式建类别表
2.1 新建后端演示项目demo2
- settings中的注册
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01','rest_framework'
]
- app01/models中新建表
class Type1(models.Model):"""一级目录"""name = models.CharField(max_length=10, default='', verbose_name='类目名')add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')class Meta:verbose_name = '商品类别'verbose_name_plural = verbose_namedef __str__(self):return self.nameclass Type2(models.Model):"""二级类目"""parent = models.ForeignKey(Type1, verbose_name='父级类别', null=True, blank=True, on_delete=models.CASCADE)name = models.CharField(max_length=10, default='', verbose_name='类目名')add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')class Meta:verbose_name = '商品类别2'verbose_name_plural = verbose_namedef __str__(self):return self.nameclass Type3(models.Model):"""三级类目"""parent = models.ForeignKey(Type2, verbose_name='父级类别', null=True, blank=True, on_delete=models.CASCADE)name = models.CharField(max_length=10, default='', verbose_name='类目名')add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')class Meta:verbose_name = '商品类别3'verbose_name_plural = verbose_namedef __str__(self):return self.nameclass Type4(models.Model):"""三级类目"""parent = models.ForeignKey(Type3, verbose_name='父级类别', null=True, blank=True, on_delete=models.CASCADE)name = models.CharField(max_length=10, default='', verbose_name='类目名')add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')class Meta:verbose_name = '商品类别4'verbose_name_plural = verbose_namedef __str__(self):return self.name
- 迁移
python manage.py makemigrations
python manage.py migrate
- 添加表中数据
INSERT INTO "app01_type1" VALUES(1,'京东生鲜',1575302400000);
INSERT INTO "app01_type1" VALUES(2,'亚马逊',1575302400000);
INSERT INTO "app01_type1" VALUES(3,'天狗超市',1575302400000);
INSERT INTO "app01_type1" VALUES(4,'淘气网',1575302400000);
INSERT INTO "app01_type1" VALUES(5,'苏宁难购',1575302400000);
INSERT INTO "app01_type2" VALUES(1,'鞋子箱包',1575302400000,3);
INSERT INTO "app01_type2" VALUES(2,'零食',1575388800000,3);
INSERT INTO "app01_type2" VALUES(3,'美妆丽人',1575388800000,3);
INSERT INTO "app01_type2" VALUES(4,'男装女装',1575388800000,3);
INSERT INTO "app01_type2" VALUES(5,'厨卫',1575388800000,3);
INSERT INTO "app01_type3" VALUES(1,'坚果',1575388800000,2);
INSERT INTO "app01_type3" VALUES(2,'夹克衫',1575388800000,4);
INSERT INTO "app01_type3" VALUES(3,'收纳箱',1575388800000,1);
INSERT INTO "app01_type3" VALUES(4,'纱裙',1575388800000,4);
INSERT INTO "app01_type3" VALUES(5,'长裤',1575388800000,4);
INSERT INTO "app01_type4" VALUES(1,'三只老鼠',1575388800000,1);
INSERT INTO "app01_type4" VALUES(2,'仙女白凤裙',1575388800000,4);
INSERT INTO "app01_type4" VALUES(3,'双鹰同款黑皮风衣',1575388800000,2);
INSERT INTO "app01_type4" VALUES(4,'性感黑纱',1575388800000,4);
INSERT INTO "app01_type4" VALUES(5,'折纸箱',1575388800000,3);
INSERT INTO "app01_type4" VALUES(6,'旅行箱',1575388800000,3);
INSERT INTO "app01_type4" VALUES(7,'朦胧美纱裙',1575388800000,4);
INSERT INTO "app01_type4" VALUES(8,'水洗布单裤',1575388800000,5);
INSERT INTO "app01_type4" VALUES(9,'牛仔裤',1575388800000,5);
INSERT INTO "app01_type4" VALUES(10,'闪亮皮夹克',1575388800000,2);
INSERT INTO "app01_type4" VALUES(11,'黑又亮夹克',1575388800000,2);
2.2 完善demo2的后台逻辑代码
- serializers
from rest_framework import serializers
from .models import Type1, Type2, Type3, Type4class Type1ModelSerializer(serializers.ModelSerializer):class Meta:model = Type1fields = '__all__'class Type2ModelSerializer(serializers.ModelSerializer):class Meta:model = Type2fields = '__all__'class Type3ModelSerializer(serializers.ModelSerializer):class Meta:model = Type3fields = '__all__'class Type4ModelSerializer(serializers.ModelSerializer):class Meta:model = Type4fields = '__all__'
- views
from django.shortcuts import render
from .serializers import Type1ModelSerializer, Type2ModelSerializer, Type3ModelSerializer, Type4ModelSerializer
from .models import Type1, Type4, Type3, Type2
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer, BrowsableAPIRendererclass Type1View(APIView):"""all Type1"""renderer_classes = [JSONRenderer]def get(self, request, format=None):Types = Type1.objects.all()Types_serializer = Type1ModelSerializer(Types, many=True)return Response(Types_serializer.data)class Type2View(APIView):"""all Type2"""renderer_classes = [JSONRenderer]def get(self, request, format=None):Types = Type2.objects.all()Types_serializer = Type2ModelSerializer(Types, many=True)return Response(Types_serializer.data)class Type3View(APIView):"""all Type3"""renderer_classes = [JSONRenderer]def get(self, request, format=None):Types = Type3.objects.all()Types_serializer = Type3ModelSerializer(Types, many=True)return Response(Types_serializer.data)class Type4View(APIView):"""all Type4"""renderer_classes = [JSONRenderer]def get(self, request, format=None):Types = Type4.objects.all()Types_serializer = Type4ModelSerializer(Types, many=True)return Response(Types_serializer.data)
-
url
- 总
urlpatterns = {url(r'^api/', include('app01.urls', namespace='app01')) }
- 子
urlpatterns = [url(r'^Type1/$', views.Type1View.as_view(), name='type1'),url(r'^Type2/$', views.Type2View.as_view(), name='type2'),url(r'^Type3/$', views.Type3View.as_view(), name='type3'),url(r'^Type4/$', views.Type4View.as_view(), name='type4'), ]
2.3 前后端项目联合调试
- 给demo1项目安装axios
cnpm install axios -save
- App.vue的修改
html
<template><div id="app"><div class="all"><div class="one"><div class="oneType" v-for="(item, index) in one" :key="index"><b>{{one[index].name}}</b></div></div><div class="twothreefour"><div class="two"><div class="twoType" v-for="(item,index) in two" :key="index" @mouseenter="open(index)"><b>{{two[index].name}}</b></div></div><div class="threefour" v-if="flag" @mouseleave="close()"><div class="threefourType" v-for="(item, index) in three1" :key="index"><span class="three">{{three1[index]}}</span><span class="four" v-for="(item4, index4) in four1" :key="index4">{{four1[index4]}} </span></div></div></div></div></div>
</template>
js
<script>
import Axios from 'axios';
export default {name: 'app',data () {return {one:[],two:[],three:[],four:[],flag:false,three1:[],four1:[]}},methods: {getData() {const api='http://127.0.0.1:8000/';var api1=api+'api/Type1/';var api2=api+'api/Type2/';var api3=api+'api/Type3/';var api4=api+'api/Type4/';var Type1=[];var Type2=[];var Type3=[];var Type4=[];Axios.get(api1).then(function (response){// console.log(response);for (var i=0;i<response.data.length;i++){// console.log(response.data[i])Type1.push(response.data[i])}// console.log(Type1)}).catch(function (error) {console.log(error);});this.one=Type1;Axios.get(api2).then(function (response){// console.log(response);for (var i=0;i<response.data.length;i++){// console.log(response.data[i])Type2.push(response.data[i])}// console.log(Type2)}).catch(function (error) {console.log(error);});this.two=Type2;Axios.get(api3).then(function (response){// console.log(response);for (var i=0;i<response.data.length;i++){// console.log(response.data[i])Type3.push(response.data[i])}// console.log(Type3)}).catch(function (error) {console.log(error);});this.three=Type3;Axios.get(api4).then(function (response){// console.log(response);for (var i=0;i<response.data.length;i++){// console.log(response.data[i])Type4.push(response.data[i])}// console.log(Type4)}).catch(function (error) {console.log(error);});this.four=Type4;// console.log(this.one)// console.log(this.two)// console.log(this.three)// console.log(this.four)},open(index) {// console.log(this.two[index].id)var temp=[]for (var i=0;i<this.three.length;i++) {if(this.three[i].parent===index+1) {temp.push(this.three[i].name)}}console.log(temp)this.three1=temp;var temp4=[]for (var j=0;j<this.four.length;j++) {temp4.push(this.four[j].name)}this.four1=temp4this.flag=true},close() {this.flag=false}},mounted() {this.getData()},
}
</script>
注:四级类目没有筛选赋值
- 解决跨域问题
pip install Django-cors-headers
settings
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','app01','rest_framework','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
-
前后端同时运行访问即可见结果
-
小结
从本节不难看出,为了获取数据,前端通过不同的4个API发送了4次网络请求,这是因为我们假设一个电商平台只有四级类目。但是现实中者显然是不肯能的,往往一个用户量越庞大的电商平台,商品的种类越齐全、越细分,就意味着发送网络请求的倍数也就越多,需要的带宽也就更多,这个成本是非常庞大的。
3 使用Django的model实现类别表建立
3.1 四表合一
- 新建model
class Type(models.Model):"""商品类别"""CATEGORY_TYPE = ((1, '一级类目'),(2, '二级类目'),(3, '三级类目'),(4, '四级类目'),)name = models.CharField(default='', max_length=30, verbose_name='类别名', help_text='类别名')code = models.CharField(default='', max_length=30, verbose_name='类别code', help_text='类别code')desc = models.CharField(default='', max_length=30, verbose_name='类别描述', help_text='类别描述')category_Type = models.IntegerField(choices=CATEGORY_TYPE, verbose_name='类别描述', help_text='类别描述')parent_category = models.ForeignKey('self', null=True, blank=True, verbose_name='父类目录', help_text='父类别', related_name='sub_cat', on_delete=models.CASCADE)is_tab = models.BooleanField(default=False, verbose_name='是否导航', help_text='是否导航')class Meta:verbose_name = '商品类别'verbose_name_plural = verbose_namedef __str__(self):return self.name
- 迁移
python manage.py makemigrations
python manage.py migrate
3.2 数据导入
- 序列化
class TypeModelSerializer(serializers.ModelSerializer):class Meta:model = Typefields = '__all__'
- 视图
class TypeView(APIView):"""操作类别表"""renderer_classes = [JSONRenderer]def get(self, request, format=None):types = Type.objects.all()types_serializer = TypeModelSerializer(types, many=True)return Response(types_serializer.data)def post(self, request):name = request.data.get('name')category_type = request.data.get('lei')parent_category_id = request.data.get('parent')type1 = Type()type1.name=nametype1.category_Type = category_typeif parent_category_id:parent_category = Type.objects.filter(id=parent_category_id).first()type1.parent_category=parent_categorytype1.save()type_serializer = TypeModelSerializer(type1)return Response(type_serializer.data)
- 路由
url(r'^type/$', views.TypeView.as_view(), name='type'),
- 可通过postman post方式来插入数据
- 这里直接给出导入完成的sql
INSERT INTO "app01_type" VALUES(1,'天狗超市','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(2,'京东生鲜','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(3,'苏宁难购','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(4,'亚马逊','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(5,'淘气网','','',1,0,NULL);
INSERT INTO "app01_type" VALUES(6,'男装女装','','',2,0,1);
INSERT INTO "app01_type" VALUES(7,'鞋子箱包','','',2,0,1);
INSERT INTO "app01_type" VALUES(8,'零食','','',2,0,1);
INSERT INTO "app01_type" VALUES(9,'厨卫','','',2,0,1);
INSERT INTO "app01_type" VALUES(10,'美妆丽人','','',2,0,1);
INSERT INTO "app01_type" VALUES(11,'夹克衫','','',3,0,6);
INSERT INTO "app01_type" VALUES(12,'长裤','','',3,0,6);
INSERT INTO "app01_type" VALUES(13,'纱裙','','',3,0,6);
INSERT INTO "app01_type" VALUES(14,'收纳箱','','',3,0,7);
INSERT INTO "app01_type" VALUES(15,'坚果','','',3,0,8);
INSERT INTO "app01_type" VALUES(16,'双鹰同款黑夹克','','',4,0,11);
INSERT INTO "app01_type" VALUES(17,'闪亮皮夹克','','',4,0,11);
INSERT INTO "app01_type" VALUES(18,'黑又亮夹克','','',4,0,11);
INSERT INTO "app01_type" VALUES(19,'牛仔裤','','',4,0,12);
INSERT INTO "app01_type" VALUES(20,'水洗布单裤','','',4,0,12);
INSERT INTO "app01_type" VALUES(21,'朦胧美纱裙','','',4,0,13);
INSERT INTO "app01_type" VALUES(22,'仙女风白裙','','',4,0,13);
INSERT INTO "app01_type" VALUES(23,'性感黑纱','','',4,0,13);
INSERT INTO "app01_type" VALUES(24,'折纸箱','','',4,0,14);
INSERT INTO "app01_type" VALUES(25,'旅行箱','','',4,0,14);
INSERT INTO "app01_type" VALUES(26,'三只老鼠','','',4,0,15);
3.3 前后端项目联合调试
- 在demo1/src/App.vue原来的基础上,只修改
<script>
标签内的代码
<script>
import Axios from 'axios';
export default {name: 'app',data () {return {type:[],one:[],two:[],flag:false,three1:[],four1:[]}},methods: {getData() {const api='http://127.0.0.1:8000/api/type/';var _this = thisAxios.get(api).then(function (response) {_this.type=response.data;for(var i=0;i<_this.type.length;i++){if(_this.type[i].category_Type===1){_this.one.push(_this.type[i])}}for(var j=0;j<_this.type.length;j++){if(_this.type[j].category_Type===2){_this.two.push(_this.type[j])}}}).catch(function (error) {console.log(error);});},open(index) {this.three1=[]this.four1=[]var parent=this.two[index].idfor(var i=0;i<this.type.length;i++){if(this.type[i].parent_category===parent){this.three1.push(this.type[i].name)}if(this.type[i].category_Type===4){this.four1.push(this.type[i].name)}}this.flag=true}},mounted() {this.getData()}
}
</script>