导航
后台导航初模型:home/models.py
from django.db import models
class Banner(models.Model):
"""轮播图"""
# upload_to 存储子目录,真实存放地址会使用配置中的MADIE_ROOT+upload_to
image = models.ImageField(upload_to='banner', verbose_name='轮播图', null=True, blank=True)
name = models.CharField(max_length=150, verbose_name='轮播图名称')
note = models.CharField(max_length=150, verbose_name='备注信息')
link = models.CharField(max_length=150, verbose_name='轮播图广告地址')
# 共有部分
orders = models.IntegerField(verbose_name='显示顺序')
is_show=models.BooleanField(verbose_name="是否上架",default=False)
is_delete=models.BooleanField(verbose_name="逻辑删除",default=False)
class Meta:
db_table = 'ly_banner'
verbose_name = '轮播图'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class Nav(models.Model):
"""导航"""
NAV_POSITION = (
(0, '顶部导航'),
(1, '底部导航')
)
name = models.CharField(max_length=50, verbose_name='导航名称')
link = models.CharField(max_length=250, verbose_name='导航地址')
opt = models.SmallIntegerField(choices=NAV_POSITION, default=0, verbose_name='位置')
# 共有部分
orders = models.IntegerField(verbose_name='显示顺序')
is_show=models.BooleanField(verbose_name="是否上架",default=False)
is_delete=models.BooleanField(verbose_name="逻辑删除",default=False)
class Meta:
db_table = 'luffy_nav'
verbose_name = '导航'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
改造model:home/models.py => luffyapi/utils/models.py
# home/models.py
# 轮播图模型
from django.db import models
from luffyapi.utils.models import BaseModel
class Banner(models.Model):
"""轮播图"""
# upload_to 存储子目录,真实存放地址会使用配置中的MADIE_ROOT+upload_to
image = models.ImageField(upload_to='banner', verbose_name='轮播图', null=True, blank=True)
name = models.CharField(max_length=150, verbose_name='轮播图名称')
note = models.CharField(max_length=150, verbose_name='备注信息')
link = models.CharField(max_length=150, verbose_name='轮播图广告地址')
# 共有部分
orders = models.IntegerField(verbose_name='显示顺序')
is_show=models.BooleanField(verbose_name="是否上架",default=False)
is_delete=models.BooleanField(verbose_name="逻辑删除",default=False)
class Meta:
db_table = 'ly_banner'
verbose_name = '轮播图'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class Nav(models.Model):
"""导航"""
NAV_POSITION = (
(0, '顶部导航'),
(1, '底部导航')
)
name = models.CharField(max_length=50, verbose_name='导航名称')
link = models.CharField(max_length=250, verbose_name='导航地址')
opt = models.SmallIntegerField(choices=NAV_POSITION, default=0, verbose_name='位置')
# 共有部分
orders = models.IntegerField(verbose_name='显示顺序')
is_show=models.BooleanField(verbose_name="是否上架",default=False)
is_delete=models.BooleanField(verbose_name="逻辑删除",default=False)
class Meta:
db_table = 'ly_nav'
verbose_name = '导航'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
# luffyapi/utils/models.py
from django.db import models
class BaseModel(models.Model):
"""公共模型"""
orders = models.IntegerField(verbose_name='显示顺序')
is_show = models.BooleanField(verbose_name="是否上架", default=False)
is_delete = models.BooleanField(verbose_name="逻辑删除", default=False)
created_time = models.DateTimeField(verbose_name="创建时间", auto_now_add=True, null=True, blank=True)
updated_time = models.DateTimeField(verbose_name="更新时间", auto_now=True, null=True, blank=True)
class Meta:
# 抽象模型,一般用于设置公共模型字段的,一旦设置这个相关以后,那么dajngo在数据迁移的时候就不会为这个模型单独创建一个数据表了
abstract=True
# 在项目根目录下的终端
python manage.py makemigrations
python manage.py migrate
序列化:home/serializers.py
from .models import Nav
class NavModelSerializer(serializers.ModelSerializer):
"""导航菜单序列化器"""
class Meta:
model = Nav
fields = ["name", "link", "opt"]
视图:home/views.py
from .models import Nav
from .serializers import NavModelSerializer
class NavListAPIView(ListAPIView):
queryset = Nav.objects.filter(is_show=True, is_delete=False).order_by("orders")[:constant.NAV_LENGTH]
serializer_class = NavModelSerializer
配置常量:settings/constant.py
# 导航的显示数量
NAV_LENGTH = 7
子路由:home/nav
# home/nav
path("nav/",views.NavListAPIView.as_view()),
xadmin注册model
from .models import Nav
xadmin.site.register(Nav)
录入数据
"""
1 上架 免费课 /course 顶部导航
2 上架 轻客 /light-course 顶部导航
10 上架 关于我们 /about 底部导航
...
"""
前端组件改造
<template>
<div class="header-box">
<div class="header">
<div class="content">
<div class="logo full-left">
<router-link to="/"><img @click="jump('/')" src="@/assets/image/logo.svg" alt=""></router-link>
</div>
<ul class="nav full-left">
<li v-for="nav in nav_list"><span @click="jump(nav.link)" :class="this_nav==nav.link?'this':''">{{nav.name}}</span></li>
</ul>
<div class="login-bar full-right">
<div class="shop-cart full-left">
<img src="@/assets/image/cart.svg" alt="">
<span><router-link to="/cart">购物车</router-link></span>
</div>
<div class="login-box full-left">
<span>登录</span>
|
<span>注册</span>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Header",
data() {
return {
this_nav: "",
nav_list: [],
}
},
created() {
this.this_nav = localStorage.this_nav;
let _this = this
// 获取服务端的导航信息
this.$axios({
url: this.$settings.Host + "/home/nav/"
}).then(function (response) {
_this.nav_list = response.data
}).catch(function(error) {
console.log(error)
});
},
methods: {
jump(location) {
localStorage.this_nav = location;
// vue-router除了提供router-link标签跳转页面以外,还提供了 js跳转的方式
this.$router.push(location);
}
}
}
</script>
<style scoped>
.header-box {
height: 80px;
}
.header {
width: 100%;
height: 80px;
box-shadow: 0 0.5px 0.5px 0 #c9c9c9;
position: fixed;
top: 0;
left: 0;
right: 0;
margin: auto;
z-index: 99;
background: #fff;
}
.header .content {
max-width: 1200px;
width: 100%;
margin: 0 auto;
}
.header .content .logo {
height: 80px;
line-height: 80px;
margin-right: 50px;
cursor: pointer; /* 设置光标的形状为爪子 */
}
.header .content .logo img {
vertical-align: middle;
}
.header .nav li {
float: left;
height: 80px;
line-height: 80px;
margin-right: 30px;
font-size: 16px;
color: #4a4a4a;
cursor: pointer;
}
.header .nav li span {
padding-bottom: 16px;
padding-left: 5px;
padding-right: 5px;
}
.header .nav li span a {
display: inline-block;
}
.header .nav li .this {
color: #4a4a4a;
border-bottom: 4px solid #ffc210;
}
.header .nav li:hover span {
color: #000;
}
.header .login-bar {
height: 80px;
}
.header .login-bar .shop-cart {
margin-right: 20px;
border-radius: 17px;
background: #f7f7f7;
cursor: pointer;
font-size: 14px;
height: 28px;
width: 88px;
margin-top: 30px;
line-height: 32px;
text-align: center;
}
.header .login-bar .shop-cart:hover {
background: #f0f0f0;
}
.header .login-bar .shop-cart img {
width: 15px;
margin-right: 4px;
margin-left: 6px;
}
.header .login-bar .shop-cart span {
margin-right: 6px;
}
.header .login-bar .login-box {
margin-top: 33px;
}
.header .login-bar .login-box span {
color: #4a4a4a;
cursor: pointer;
}
.header .login-bar .login-box span:hover {
color: #000000;
}
</style>
底部导航
视图改造
from .models import Nav
from .serializers import NavModelSerializer
class NavHeaderListAPIView(ListAPIView):
queryset = Nav.objects.filter(opt=1,is_show=True, is_delete=False).order_by("orders")[:constant.NAV_LENGTH]
serializer_class = NavModelSerializer
class NavFooterListAPIView(ListAPIView):
queryset = Nav.objects.filter(opt=2,is_show=True, is_delete=False).order_by("orders")[:constant.NAV_LENGTH]
serializer_class = NavModelSerializer
子路由改造
path("nav/header/",views.NavHeaderListAPIView.as_view()),
path("nav/footer/",views.NavFooterListAPIView.as_view()),
xadmin注册model改造
from .models import Nav
xadmin.site.register(Nav)
前端组件改造
""" Header.vue
<ul class="nav full-left">
<li v-for="nav in nav_list">
<span @click="jump(nav.link)" :class="this_nav==nav.link?'this':''">{{nav.name}}</span>
</li>
</ul>
created() {
this.this_nav = localStorage.this_nav;
let _this = this
// 获取服务端的导航信息
this.$axios({
url: this.$settings.Host + "/home/nav/header/"
}).then(function (response) {
_this.nav_list = response.data
}).catch(function(error) {
console.log(error)
});
},
"""
""" Footer.vue
<ul>
<li v-for="nav in nav_list">
<a :href="nav.link">{{nav.name}}</a>
</li>
</ul>
data: function () {
return {
nav_list:[]
}
}
created: function () {
let _this = this
// 获取服务端的导航信息
this.$axios({
url: this.$settings.Host + "/home/nav/footer/"
}).then(function (response) {
_this.nav_list = response.data
}).catch(function(error) {
console.log(error)
});
},
.footer a {
color: white;
}
"""