首页和登录注册
前端显示首页
首页组件代码
1 <template> 2 <div id="home"> 3 <Header/> 4 <Banner/> 5 <Footer/> 6 </div> 7 </template> 8 9 <script> 10 import Header from "./common/Header" 11 import Banner from "./common/Banner" 12 import Footer from "./common/Footer" 13 14 export default { 15 name:"Home", 16 data(){ 17 return { 18 19 } 20 }, 21 components:{ 22 Header, 23 Banner, 24 Footer, 25 } 26 } 27 </script> 28 29 <style scoped> 30 31 </style> 32
首页头部子组件
Header.vue
1 <template> 2 <div class="header"> 3 <el-container> 4 <el-header> 5 <el-row> 6 <el-col class="logo" :span="3"> 7 <a href="/"> 8 <img src="@/assets/head-logo.svg" alt=""> 9 </a> 10 </el-col> 11 <el-col class="nav" :span="16"> 12 <el-row> 13 <el-col :span="3"><router-link class="current" to="/course">免费课</router-link></el-col> 14 <el-col :span="3"><router-link to="/">轻课</router-link></el-col> 15 <el-col :span="3"><router-link to="/">学位课</router-link></el-col> 16 <el-col :span="3"><router-link to="/">题库</router-link></el-col> 17 <el-col :span="3"><router-link to="/">教育</router-link></el-col> 18 </el-row> 19 </el-col> 20 <el-col class="login-bar" :span="5"> 21 <el-row v-if="token"> 22 <el-col class="cart-ico" :span="9"> 23 <router-link to=""> 24 <b class="goods-number">0</b> 25 <img class="cart-icon" src="@/assets/cart.svg" alt=""> 26 <span><router-link to="/cart">购物车</router-link></span> 27 </router-link> 28 </el-col> 29 <el-col class="study" :span="8" :offset="2"><router-link to="">学习中心</router-link></el-col> 30 <el-col class="member" :span="5"> 31 <el-menu class="el-menu-demo" mode="horizontal"> 32 <el-submenu index="2"> 33 <template slot="title"><router-link to=""><img src="@/assets/logo@2x.png" alt=""></router-link></template> 34 <el-menu-item index="2-1">我的账户</el-menu-item> 35 <el-menu-item index="2-2">我的订单</el-menu-item> 36 <el-menu-item index="2-3">我的优惠卷</el-menu-item> 37 <el-menu-item index="2-3">退出登录</el-menu-item> 38 </el-submenu> 39 </el-menu> 40 </el-col> 41 </el-row> 42 <el-row v-else> 43 <el-col class="cart-ico" :span="9"> 44 <router-link to=""> 45 <img class="cart-icon" src="@/assets/cart.svg" alt=""> 46 <span><router-link to="/cart">购物车</router-link></span> 47 </router-link> 48 </el-col> 49 <el-col :span="10" :offset="5"> 50 <span class="register"> 51 <router-link to="/login">登录</router-link> 52 | 53 <router-link to="/register">注册</router-link> 54 </span> 55 </el-col> 56 </el-row> 57 </el-col> 58 </el-row> 59 </el-header> 60 </el-container> 61 </div> 62 </template> 63 64 <script> 65 export default { 66 name: "Header", 67 data(){ 68 return { 69 // 设置一个登录标识,表示是否登录 70 token: false, 71 }; 72 } 73 } 74 </script> 75 76 <style scoped> 77 .header{ 78 top:0; 79 left:0; 80 right:0; 81 margin: auto; 82 background-color: #fff; 83 height: 80px; 84 z-index: 1000; 85 position: fixed; 86 box-shadow: 0 0.5px 0.5px 0 #c9c9c9; 87 } 88 .header .el-container{ 89 width: 1200px; 90 margin: 0 auto; 91 } 92 .el-header{ 93 height: 80px!important; 94 padding:0; 95 } 96 .logo{ 97 98 } 99 .logo img{ 100 padding-top: 22px; 101 } 102 103 .nav{ 104 margin-top: 22px; 105 } 106 107 .nav .el-col a{ 108 display: inline-block; 109 text-align: center; 110 padding-bottom: 16px; 111 padding-left: 5px; 112 padding-right: 5px; 113 position: relative; 114 font-size: 16px; 115 margin-left: 20px; 116 } 117 118 .nav .el-col .current{ 119 color: #4a4a4a; 120 border-bottom: 4px solid #ffc210; 121 } 122 123 .login-bar{ 124 margin-top: 22px; 125 } 126 .cart-ico{ 127 position: relative; 128 border-radius: 17px; 129 } 130 .cart-ico:hover{ 131 background: #f0f0f0; 132 } 133 .goods-number{ 134 width: 16px; 135 height: 16px; 136 line-height: 17px; 137 font-size: 12px; 138 color: #fff; 139 text-align: center; 140 background: #fa6240; 141 border-radius: 50%; 142 transform: scale(.8); 143 position: absolute; 144 left: 16px; 145 top: -1px; 146 } 147 .cart-icon{ 148 width: 15px; 149 height: auto; 150 margin-left: 6px; 151 } 152 .cart-ico span{ 153 margin-left: 12px; 154 } 155 .member img{ 156 width: 26px; 157 height: 26px; 158 border-radius: 50%; 159 display: inline-block; 160 } 161 .member img:hover{ 162 border: 1px solid yellow; 163 } 164 165 </style>
脚部子组件
Footer.vue
1 <template> 2 <div class="footer"> 3 <el-container> 4 <el-row> 5 <el-col :span="4"><router-link to="">关于我们</router-link></el-col> 6 <el-col :span="4"><router-link to="">联系我们</router-link></el-col> 7 <el-col :span="4"><router-link to="">商务合作</router-link></el-col> 8 <el-col :span="4"><router-link to="">帮助中心</router-link></el-col> 9 <el-col :span="4"><router-link to="">意见反馈</router-link></el-col> 10 <el-col :span="4"><router-link to="">新手指南</router-link></el-col> 11 <el-col :span="24"><p class="copyright">Copyright © luffycity.com版权所有 | 京ICP备17072161号-1</p></el-col> 12 </el-row> 13 </el-container> 14 </div> 15 </template> 16 17 <script> 18 export default { 19 name:"Footer", 20 data(){ 21 return {} 22 } 23 } 24 </script> 25 26 27 <style scoped> 28 .footer{ 29 width: 100%; 30 height: 128px; 31 background: #25292e; 32 } 33 .footer .el-container{ 34 width: 1200px; 35 margin: auto; 36 } 37 .footer .el-row { 38 align-items: center; 39 padding: 0 200px; 40 padding-bottom: 15px; 41 width: 100%; 42 margin-top: 38px; 43 } 44 .footer .el-row a{ 45 color: #fff; 46 font-size: 14px; 47 } 48 .footer .el-row .copyright{ 49 text-align: center; 50 color: #fff; 51 font-size: 14px; 52 } 53 </style>
轮播图子组件
Banner.vue
1 <template> 2 <div class="banner"> 3 <el-carousel trigger="click" height="473px"> 4 <el-carousel-item v-for="banner in banner_list"> 5 <a :href="banner.link"><img width="100%" :src="banner.img" alt=""></a> 6 </el-carousel-item> 7 </el-carousel> 8 </div> 9 </template> 10 11 <script> 12 export default { 13 name:"Banner", 14 data(){ 15 return { 16 banner_list:[ 17 {link:"http://www.baidu.com",img:"/static/banner/1.png"}, 18 {link:"http://www.baidu.com",img:"/static/banner/2.png"}, 19 {link:"http://www.baidu.com",img:"/static/banner/3.png"}, 20 ] 21 }; 22 } 23 } 24 </script> 25 26 <style> 27 .el-carousel__arrow{ 28 width: 100px!important; 29 height: 100px!important; 30 } 31 .el-icon-arrow-left{ 32 font-size: 35px; 33 margin-left: 50px; 34 } 35 .el-carousel__arrow--left{ 36 left: -50px; 37 } 38 </style>
注册首页路由
1 import Vue from "vue" 2 import Router from "vue-router" 3 4 // 导入需要注册路由的组件 5 import Home from "../components/Home" 6 Vue.use(Router); 7 8 // 配置路由列表 9 export default new Router({ 10 mode:"history", 11 routes:[ 12 // 路由列表 13 { 14 name:"Home", 15 path: "/home", 16 component:Home, 17 }, 18 { 19 name:"Home", 20 path: "/", 21 component:Home, 22 } 23 ] 24 })
轮播图功能实现
安装依赖模块和配置
图片处理模块
前面已经安装了,如果没有安装则需要安装
1 pip install pillow
上传文件相关配置
settings.py
1 # 访问静态文件的url地址前缀 2 STATIC_URL = '/static/' 3 # 设置django的静态文件目录 4 STATICFILES_DIRS = [ 5 os.path.join(BASE_DIR,"luffy/statics") 6 ] 7 8 # 项目中存储上传文件的根目录[暂时配置],注意,static目录需要手动创建否则上传文件时报错 9 MEDIA_ROOT=os.path.join(BASE_DIR,"luffy/statics") 10 # 访问上传文件的url地址前缀 11 MEDIA_URL ="/media/"
在xadmin中输出上传文件的Url地址
总路由urls.py新增代码:
1 from django.urls import re_path 2 from django.conf import settings 3 from django.views.static import serve 4 5 urlpatterns = [ 6 ... 7 re_path(r'media/(?P<path>.*)', serve, {"document_root": settings.MEDIA_ROOT}), 8 ]
创建轮播图的模型
因为当前功能是drf的第一个功能,所以我们先创建一个子应用home,创建在luffy/apps目录下
注册home子应用,因为子应用的位置发生了改变,所以为了原来子应用的注册写法,所以新增一个导包路径:
1 # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 2 BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 3 4 # 新增一个系统导包路径 5 import sys 6 sys.path.insert(0,os.path.join(BASE_DIR,"apps")) 7 8 9 10 INSTALLED_APPS = [ 11 # 注意,加上drf框架的注册 12 'rest_framework', 13 14 # 子应用 15 'home', 16 17 ]
注意,pycharm会出现路径错误的提示。(pycharm在生成项目的时候会将根项目路径当做导包路径)
home/models.py
1 from django.db import models 2 3 # Create your models here. 4 class BannerInfo(models.Model): 5 """ 6 轮播图 7 """ 8 # upload_to 存储子目录,真实存放地址会使用配置中的MADIE_ROOT+upload_to 9 image = models.ImageField(upload_to='banner', verbose_name='轮播图', null=True,blank=True) 10 name = models.CharField(max_length=150, verbose_name='轮播图名称') 11 note = models.CharField(max_length=150, verbose_name='备注信息') 12 link = models.CharField(max_length=150, verbose_name='轮播图广告地址') 13 orders = models.IntegerField(verbose_name='显示顺序') 14 is_show=models.BooleanField(verbose_name="是否上架",default=False) 15 is_delete=models.BooleanField(verbose_name="逻辑删除",default=False) 16 17 class Meta: 18 db_table = 'ly_banner' 19 verbose_name = '轮播图' 20 verbose_name_plural = verbose_name 21 22 def __str__(self): 23 return self.name
数据迁移(django内部会有内置的登录注册功能,扩展的时候会报错,后期再改正)
python manage.py makemigrations python manage.py migrate
序列化器
home/serializers.py
1 from rest_framework.serializers import ModelSerializer 2 from .models import BannerInfo 3 class BannerInfoSerializer(ModelSerializer): 4 """轮播图序列化器""" 5 class Meta: 6 model=BannerInfo 7 fields = ("image","link")
视图代码
views.py
1 from django.db.models import Q 2 from rest_framework.generics import ListAPIView 3 from .models import BannerInfo 4 from .serializers import BannerInfoSerializer 5 class BannerInfoListAPIView(ListAPIView): 6 """ 7 轮播图列表 8 """ 9 queryset = BannerInfo.objects.filter( Q(is_show=True) & Q(is_delete=False) ).order_by("-orders") 10 serializer_class = BannerInfoSerializer
路由代码
home/urls.py
1 urls.py 2 3 from django.urls import path,re_path 4 from . import views 5 urlpatterns = [ 6 path(r"banner/",views.BannerInfoListAPIView.as_view()), 7 ]
把home的路由urls.py注册到总路由
1 from django.urls import path,include 2 3 urlpatterns = [ 4 path('admin/', admin.site.urls), 5 path('', include("home.urls")), 6 ]
访问http://api.luffycity.cn:8000/banner/,效果:
所以我们需要有一个后台提供数据.安装xadmin
pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2
在配置文件中注册如下应用(apps)
1 INSTALLED_APPS = [ 2 ... 3 'xadmin', 4 'crispy_forms', 5 'reversion', 6 ... 7 ] 8 9 # 修改使用中文界面 10 LANGUAGE_CODE = 'zh-Hans' 11 12 # 修改时区 13 TIME_ZONE = 'Asia/Shanghai'
1 # 修改使用中文界面 2 LANGUAGE_CODE = 'zh-Hans' 3 4 # 修改时区 5 TIME_ZONE = 'Asia/Shanghai'
xadmin有建立自己的数据库模型类,需要进行数据库迁移
1 python manage.py makemigrations 2 python manage.py migrate
在总路由中添加xadmin的路由信息
1 import xadmin 2 xadmin.autodiscover() 3 4 # version模块自动注册需要版本控制的 Model 5 from xadmin.plugins import xversion 6 xversion.register_models() 7 8 urlpatterns = [ 9 path(r'xadmin/', xadmin.site.urls), 10 ]
如果之前没有创建超级用户,需要创建,如果有了,则可以直接使用之前的。
python manage.py createsuperuser
给xadmin设置基本站点配置信息
注册模型到xadmin中
在当前子应用中创建adminx.py,添加如下代码
1 import xadmin 2 from xadmin import views 3 4 class BaseSetting(object): 5 """xadmin的基本配置""" 6 enable_themes = True # 开启主题切换功能 7 use_bootswatch = True 8 9 xadmin.site.register(views.BaseAdminView, BaseSetting) 10 11 class GlobalSettings(object): 12 """xadmin的全局配置""" 13 site_title = "路飞学城" # 设置站点标题 14 site_footer = "路飞学城有限公司" # 设置站点的页脚 15 menu_style = "accordion" # 设置菜单折叠 16 17 xadmin.site.register(views.CommAdminView, GlobalSettings)
注册模型到xadmin中
adminx.py
1 # 轮播图 2 from .models import BannerInfo 3 class BannerInfoModelAdmin(object): 4 list_display=["name","orders","is_show"] 5 xadmin.site.register(BannerInfo, BannerInfoModelAdmin)
修改后端xadmin中子应用名称
apps.py
1 class HomeConfig(AppConfig): 2 name = 'home' 3 verbose_name = '我的首页'
__init__.py
default_app_config = "home.apps.HomeConfig"
给轮播图添加测试数据
添加几条测试数据效果:
客户端代码获取数据
Banner.vue代码:
1 <template> 2 <div class="banner"> 3 <el-carousel trigger="click" height="506px"> 4 <el-carousel-item v-for="item in banner_list"> 5 <a :href="item.link"><img :src="item.image"></a> 6 </el-carousel-item> 7 </el-carousel> 8 </div> 9 </template> 10 11 <script> 12 export default { 13 name:"Banner", 14 data(){ 15 return { 16 banner_list:[], 17 }; 18 }, 19 created: function(){ 20 // 获取轮播图 21 this.$axios.get(this.$settings+"/banner/").then(response => { 22 console.log(response.data) 23 this.banner_list = response.data 24 }).catch(error => { 25 console.log(error.response); 26 }); 27 } 28 } 29 </script>
页面空白时间较长解决方法(缓存原因)
导航功能实现
创建模型
引入一个公共模型【抽象模型,不会在数据迁移的时候为它创建表】
1 from django.db import models 2 from luffy.utils.models import BaseModel 3 # Create your models here. 4 class BannerInfo(BaseModel): 5 """ 6 轮播图 7 """ 8 # upload_to 存储子目录,真实存放地址会使用配置中的MADIE_ROOT+upload_to 9 image = models.ImageField(upload_to='banner', verbose_name='轮播图', null=True,blank=True) 10 name = models.CharField(max_length=150, verbose_name='轮播图名称') 11 note = models.CharField(max_length=150, verbose_name='备注信息') 12 link = models.CharField(max_length=150, verbose_name='轮播图广告地址') 13 14 class Meta: 15 db_table = 'ly_banner' 16 verbose_name = '轮播图' 17 verbose_name_plural = verbose_name 18 19 def __str__(self): 20 return self.name 21 22 23 class NavInfo(BaseModel): 24 """ 25 导航 26 """ 27 NAV_POSITION = ( 28 (0, 'top'), 29 (1, 'footer') 30 ) 31 name = models.CharField(max_length=50, verbose_name='导航名称') 32 link = models.CharField(max_length=250, verbose_name='导航地址') 33 opt = models.SmallIntegerField(choices=NAV_POSITION, default=0, verbose_name='位置') 34 35 class Meta: 36 db_table = 'ly_nav' 37 verbose_name = '导航' 38 verbose_name_plural = verbose_name 39 40 def __str__(self): 41 return self.name
公共模型(meta设置为抽象模型),保存项目的公共代码库目录下luffy/utils.py文件中。
1 from django.db import models 2 3 class BaseModel(models.Model): 4 """公共字段模型""" 5 orders = models.IntegerField(verbose_name='显示顺序') 6 is_show=models.BooleanField(verbose_name="是否上架",default=False) 7 is_delete=models.BooleanField(verbose_name="逻辑删除",default=False) 8 create_time = models.DateTimeField(auto_now_add=True,verbose_name="添加时间") 9 update_time = models.DateTimeField(auto_now=True,verbose_name="更新时间") 10 11 class Meta: 12 # 设置当前模型在数据迁移的时候不要为它创建表 13 abstract = True
数据迁移
python manage.py makemigrations python manage.py migrate
序列化器代码
1 from rest_framework.serializers import ModelSerializer 2 from .models import NavInfo 3 class NavInfoSerializer(ModelSerializer): 4 """导航序列化器""" 5 class Meta: 6 model=NavInfo 7 fields = ("name","link")
视图代码
views.py
1 from .models import NavInfo 2 from .serializers import NavInfoSerializer 3 class NavInfoAPIView(ListAPIView): 4 """ 5 导航列表 6 """ 7 queryset = NavInfo.objects.filter( Q(is_show=True) & Q(is_delete=False) & Q(opt=0) ).order_by("-orders") 8 serializer_class = NavInfoSerializer
路由代码
urls.py
1 from django.urls import path,re_path 2 from . import views 3 urlpatterns = [ 4 ... 5 path(r"nav/",views.NavInfoAPIView.as_view()), 6 ]
注册模型到xadmin中
在当前子应用adminx.py,添加如下代码
1 # 导航 2 from home.models import NavInfo 3 class NavInfoInfoModelAdmin(object): 4 list_display=["name","link","is_show"] 5 xadmin.site.register(NavInfo, NavInfoInfoModelAdmin)
添加测试数据
客户端代码获取数据
Header.vue代码:
1 <template> 2 <div class="header"> 3 <el-container> 4 <el-header> 5 <el-row> 6 <el-col class="logo" :span="3"> 7 <a href="/"> 8 <img src="@/assets/head-logo.svg" alt=""> 9 </a> 10 </el-col> 11 <el-col class="nav" :span="16"> 12 <el-row> 13 <el-col v-for="nav in nav_list" :span="3"><a :class="check(nav.link)?'current':''" :href="nav.link">{{nav.name}}</a></el-col> 14 </el-row> 15 </el-col> 16 <el-col class="login-bar" :span="5"> 17 <el-row v-if="token"> 18 <el-col class="cart-ico" :span="9"> 19 <router-link to=""> 20 <b class="goods-number">0</b> 21 <img class="cart-icon" src="@/assets/cart.svg" alt=""> 22 <span><router-link to="/cart">购物车</router-link></span> 23 </router-link> 24 </el-col> 25 <el-col class="study" :span="8" :offset="2"><router-link to="">学习中心</router-link></el-col> 26 <el-col class="member" :span="5"> 27 <el-menu class="el-menu-demo" mode="horizontal"> 28 <el-submenu index="2"> 29 <template slot="title"><router-link to=""><img src="@/assets/logo@2x.png" alt=""></router-link></template> 30 <el-menu-item index="2-1">我的账户</el-menu-item> 31 <el-menu-item index="2-2">我的订单</el-menu-item> 32 <el-menu-item index="2-3">我的优惠卷</el-menu-item> 33 <el-menu-item index="2-3">退出登录</el-menu-item> 34 </el-submenu> 35 </el-menu> 36 </el-col> 37 </el-row> 38 <el-row v-else> 39 <el-col class="cart-ico" :span="9"> 40 <router-link to=""> 41 <img class="cart-icon" src="@/assets/cart.svg" alt=""> 42 <span><router-link to="/cart">购物车</router-link></span> 43 </router-link> 44 </el-col> 45 <el-col :span="10" :offset="5"> 46 <span class="register"> 47 <router-link to="/login">登录</router-link> 48 | 49 <router-link to="/register">注册</router-link> 50 </span> 51 </el-col> 52 </el-row> 53 </el-col> 54 </el-row> 55 </el-header> 56 </el-container> 57 </div> 58 </template> 59 60 <script> 61 export default { 62 name: "Header", 63 data(){ 64 return { 65 // 设置一个登录标识,表示是否登录 66 token: false, 67 nav_list:[], 68 }; 69 }, 70 created() { 71 // 获取导航 72 this.$axios.get(this.$settings.Host+"/nav/").then(response=>{ 73 this.nav_list = response.data 74 console.log(this.nav_list) 75 }).catch(error=>{ 76 console.log(error.response) 77 }) 78 }, 79 methods:{ 80 check(link){ 81 return link==window.location.pathname 82 } 83 } 84 } 85 </script> 86 87 <style scoped> 88 .header{ 89 top:0; 90 left:0; 91 right:0; 92 margin: auto; 93 background-color: #fff; 94 height: 80px; 95 z-index: 1000; 96 position: fixed; 97 box-shadow: 0 0.5px 0.5px 0 #c9c9c9; 98 } 99 .header .el-container{ 100 width: 1200px; 101 margin: 0 auto; 102 } 103 .el-header{ 104 height: 80px!important; 105 padding:0; 106 } 107 .logo{ 108 109 } 110 .logo img{ 111 padding-top: 22px; 112 } 113 114 .nav{ 115 margin-top: 22px; 116 } 117 118 .nav .el-col a{ 119 display: inline-block; 120 text-align: center; 121 padding-bottom: 16px; 122 padding-left: 5px; 123 padding-right: 5px; 124 position: relative; 125 font-size: 16px; 126 margin-left: 20px; 127 } 128 129 .nav .el-col .current{ 130 color: #4a4a4a; 131 border-bottom: 4px solid #ffc210; 132 } 133 134 .login-bar{ 135 margin-top: 22px; 136 } 137 .cart-ico{ 138 position: relative; 139 border-radius: 17px; 140 } 141 .cart-ico:hover{ 142 background: #f0f0f0; 143 } 144 .goods-number{ 145 width: 16px; 146 height: 16px; 147 line-height: 17px; 148 font-size: 12px; 149 color: #fff; 150 text-align: center; 151 background: #fa6240; 152 border-radius: 50%; 153 transform: scale(.8); 154 position: absolute; 155 left: 16px; 156 top: -1px; 157 } 158 .cart-icon{ 159 width: 15px; 160 height: auto; 161 margin-left: 6px; 162 } 163 .cart-ico span{ 164 margin-left: 12px; 165 } 166 .member img{ 167 width: 26px; 168 height: 26px; 169 border-radius: 50%; 170 display: inline-block; 171 } 172 .member img:hover{ 173 border: 1px solid yellow; 174 } 175 176 </style>