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_name
def __str__(self):
return self.name
class 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_name
def __str__(self):
return self.name
class 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_name
def __str__(self):
return self.name
class 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_name
def __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, Type4
class Type1ModelSerializer(serializers.ModelSerializer):
class Meta:
model = Type1
fields = '__all__'
class Type2ModelSerializer(serializers.ModelSerializer):
class Meta:
model = Type2
fields = '__all__'
class Type3ModelSerializer(serializers.ModelSerializer):
class Meta:
model = Type3
fields = '__all__'
class Type4ModelSerializer(serializers.ModelSerializer):
class Meta:
model = Type4
fields = '__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, BrowsableAPIRenderer
class 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=temp4
this.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_name
def __str__(self):
return self.name
- 迁移
python manage.py makemigrations
python manage.py migrate
3.2 数据导入
- 序列化
class TypeModelSerializer(serializers.ModelSerializer):
class Meta:
model = Type
fields = '__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=name
type1.category_Type = category_type
if parent_category_id:
parent_category = Type.objects.filter(id=parent_category_id).first()
type1.parent_category=parent_category
type1.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 = this
Axios.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].id
for(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>