后台主页模块接口
首页轮播图接口
一张表写一个app, 创建一个app,写轮播图
基表
from django.db import models
# Create your models here.
# 轮播图表
# class Banner(models.Model):
# 将其只作为基表使用
class BaseModel(models.Model):
# 是否删除,展示,创建时间
create_time = models.DateTimeField(auto_now_add=True,verbose_name='创建时间')
update_time = models.DateTimeField(auto_now=True,verbose_name='最后更新时间')
is_delete = models.BooleanField(default=False,verbose_name='是否删除')
is_display = models.BooleanField(default=True,verbose_name='是否上架展示')
privilege = models.IntegerField(verbose_name='优先级')
class Meta:
# 如果表迁移,这个表就会生成, 为了不让表生成苦于加这句话
abstract = True # 虚拟表,只用来做继承,不在数据库生成表
但是上述字段,在别的表中可能也会用到, 于是我们将它抽出来,成为一个基表, 后续有需要使用这些字段的表直接继承它就可以了, 减少代码的编写。 将其放在utils/models.py中【utils文件夹内存放公共的数据和资源】
轮播图表
from utils.models import BaseModel
from django.db import models
# 轮播图表
class Banner(BaseModel):
# 图片地址,名字,图片名字,link地址【前端点击,会跳到图片】
title = models.CharField(max_length=32,unique=True,verbose_name='名字')
image = models.ImageField(upload_to='banner',verbose_name='图片')
link = models.CharField(max_length=64,verbose_name='跳转链接') # course
desc = models.TextField(verbose_name='详情') # 也可以用详情表
class Meta:
db_table = 'luffy_banner'
verbose_name_plural = '轮播表图'
def __str__(self):
return self.title
执行迁移命令
轮播图接口
路由分发
urls.py 总路由
from django.contrib import admin
from django.urls import path,include
from home import views
from django.views.static import serve
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
# path('test/', views.TestView.as_view()),
# 路由分发
path('api/v1/home/',include('home.urls')),
# 开启media访问
path('media/<path:path>',serve,kwargs={'document_root':settings.MEDIA_ROOT}),
]
home/urls.py app下的路由
from home import views
from rest_framework.routers import SimpleRouter
router = SimpleRouter()
router.register('banner',views.BannerView,'banner')
urlpatterns = [
]
urlpatterns+=router.urls
views.py
from django.shortcuts import render
# Create your views here.
from .models import Banner
from .serializer import BannerSerializer
# 自动生成路由
from rest_framework.viewsets import GenericViewSet
# 获取所有的
from rest_framework.mixins import ListModelMixin
class BannerView(GenericViewSet,ListModelMixin):
queryset = Banner.objects.all().filter(is_delete=False,is_display=True).order_by('privilege')
serializer_class = BannerSerializer
serializer.py
from rest_framework import serializers
from .models import Banner
class BannerSerializer(serializers.ModelSerializer):
class Meta:
model = Banner
fields = ['title','image','link']
测试访问一下
重写list方法,获取所有。 返回统一的格式。
utils/views.py 封装一下获取所有数据方法,方便后续,少写代码。
from rest_framework.mixins import ListModelMixin
from utils.response import APIResponse
class CommonListModelMixin(ListModelMixin):
def list(self, request, *args, **kwargs):
res = super().list(request,*args,**kwargs)
return APIResponse(result=res.data)
home/view.py
from django.shortcuts import render
# Create your views here.
from .models import Banner
from .serializer import BannerSerializer
# 自动生成路由
from rest_framework.viewsets import GenericViewSet
# 获取所有的
from utils.view import CommonListModelMixin
class BannerView(GenericViewSet,CommonListModelMixin):
queryset = Banner.objects.all().filter(is_delete=False,is_display=True).order_by('privilege')
serializer_class = BannerSerializer
再次访问就可以看到已经统一了数据返回格式
接下来去后台填充数据
先做汉化处理
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False
创建超级用户 [密码不展示】
python manage.py createsuperuser
admin里注册
from .models import Banner
admin.site.register(Banner)
后台管理采用simpleui
下载安装
app中注册
INSTALLED_APPS = [
'simpleui',]
添加数据
后端完成后我们来接通前端
settings.js 以后访问的路径都是api/v1/...
export default {
BASE_URL:'http://127.0.0.1:8000/api/v1/'
}
HomeVue.vue
export default {
name: 'HomeView',
data(){
return {}
},
created() {
// 联通前后端, 注意斜杠得添加, 前端不会追加斜杠
this.$axios.get(this.$settings.BASE_URL+'/home/banner/').then(res=>{
console.log(res)
})
}
}
然后我们发现前后端交互的时候出现了跨域问题
具体成因及解决方案参考此篇博文
django中的跨域问题以及解决策略_Yietong309的博客-CSDN博客
前端首页
HomeView.vue 页面组件
Top.vue 头部组件
Banner.vue 轮播图组件
Bottom.vue 尾部组件
前端切图
我们需要写头部top,轮播图banner和尾部bottom组件
组件写进componetnts里
Top.vue
<template>
<div class="header">
<div class="slogan">
<p>老男孩IT教育 | 帮助有志向的年轻人通过努力学习获得体面的工作和生活</p>
</div>
<div class="nav">
<ul class="left-part">
<li class="logo">
<router-link to="/">
<img src="../assets/img/head-logo.svg" alt="">
</router-link>
</li>
<li class="ele">
<span @click="goPage('/free-course')" :class="{active: url_path === '/free-course'}">免费课</span>
</li>
<li class="ele">
<span @click="goPage('/actual-course')" :class="{active: url_path === '/actual-course'}">实战课</span>
</li>
<li class="ele">
<span @click="goPage('/light-course')" :class="{active: url_path === '/light-course'}">轻课</span>
</li>
</ul>
<div class="right-part">
<div>
<span>登录</span>
<span class="line">|</span>
<span>注册</span>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: "Top",
data(){
return {
url_path:sessionStorage.url_path || '/',
}
},
methods:{
goPage(url_path){
// 已经是当前路由就不用重新跳转
if (this.url_path !== url_path){
//传入的参数,如果不等同于当前路径就跳转
this.$router.push(url_path)
}
sessionStorage.url_path = url_path;
},
},
created(){
sessionStorage.url_path = this.$route.path
this.url_path = this.$route.path
}
}
</script>
<style scoped>
.header {
background-color: white;
box-shadow: 0 0 5px 0 #aaa;
}
.header:after {
content: "";
display: block;
clear: both;
}
.slogan {
background-color: #eee;
height: 40px;
}
.slogan p {
width: 1200px;
margin: 0 auto;
color: #aaa;
font-size: 13px;
line-height: 40px;
}
.nav {
background-color: white;
user-select: none;
width: 1200px;
margin: 0 auto;
}
.nav ul {
padding: 15px 0;
float: left;
}
.nav ul:after {
clear: both;
content: '';
display: block;
}
.nav ul li {
float: left;
}
.logo {
margin-right: 20px;
}
.ele {
margin: 0 20px;
}
.ele span {
display: block;
font: 15px/36px '微软雅黑';
border-bottom: 2px solid transparent;
cursor: pointer;
}
.ele span:hover {
border-bottom-color: orange;
}
.ele span.active {
color: orange;
border-bottom-color: orange;
}
.right-part {
float: right;
}
.right-part .line {
margin: 0 10px;
}
.right-part span {
line-height: 68px;
cursor: pointer;
}
</style>
Banner.vue
<template>
<div class="banner">
<el-carousel :interval="5000" arrow="always" height="400px">
<el-carousel-item v-for="item in bannerList" :key="item.title">
<div v-if="item.image.indexOf('http')==-1">
<router-link :to="item.link"><img :src="item.image" alt=""></router-link>
</div>
<div v-else>
<a :href="item.link"><img :src="item.image" alt=""></a>
</div>
</el-carousel-item>
</el-carousel>
</div>
</template>
<script>
export default {
name: "Banner",
data() {
return {
bannerList: []
}
},
created() {
this.$axios.get(this.$settings.BASE_URL + 'home/banner/').then(res => {
this.bannerList = res.data.result
})
}
}
</script>
<style scoped>
.el-carousel__item {
height: 400px;
min-width: 1200px;
}
.el-carousel__item img {
height: 400px;
margin-left: calc(50% - 1920px / 2);
}
.el-carousel__item h3 {
color: #475669;
font-size: 18px;
opacity: 0.75;
line-height: 300px;
margin: 0;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n+1) {
background-color: #d3dce6;
}
</style>
Bottom.vue
<template>
<div class="footer">
<ul>
<li>关于我们</li>
<li>联系我们</li>
<li>商务合作</li>
<li>帮助中心</li>
<li>意见反馈</li>
<li>新手指南</li>
</ul>
<p>Copyright © luffycity.com版权所有 | 京ICP备17072161号-1</p>
</div>
</template>
<script>
export default {
name: "Bottom"
}
</script>
<style scoped>
.footer {
width: 100%;
height: 128px;
background: #25292e;
color: #fff;
}
.footer ul {
margin: 0 auto 16px;
padding-top: 38px;
width: 810px;
}
.footer ul li {
float: left;
width: 112px;
margin: 0 10px;
text-align: center;
font-size: 14px;
}
.footer ul::after {
content: "";
display: block;
clear: both;
}
.footer p {
text-align: center;
font-size: 12px;
}
</style>
HomeVue.vue
<template>
<div class="home">
<Header></Header>
<Banner></Banner>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<Footer></Footer>
</div>
</template>
<script>
import Top from "@/components/Top";
import Banner from "@/components/Banner";
import Bottom from "@/components/Bottom";
export default {
name: 'HomeView',
data() {
return {}
},
components: {
Top, Banner, Bottom
}
}
</script>
首页中间部分样式
<div class="course">
<el-row>
<el-col :span="6" v-for="(o, index) in 8" :key="o" class="course_detail">
<el-card :body-style="{ padding: '0px' }">
<img src="https://tva1.sinaimg.cn/large/e6c9d24egy1h1g0zd133mj20l20a875i.jpg"
class="image">
<div style="padding: 14px;">
<span>推荐课程</span>
<div class="bottom clearfix">
<time class="time">价格:999</time>
<el-button type="text" class="button">查看详情</el-button>
</div>
</div>
</el-card>
</el-col>
</el-row>
</div>
<img src="https://tva1.sinaimg.cn/large/e6c9d24egy1h1g112oiclj224l0u0jxl.jpg" alt="" width="100%" height="500px">
<style scoped>
.time {
font-size: 13px;
color: #999;
}
.bottom {
margin-top: 13px;
line-height: 12px;
}
.button {
padding: 0;
float: right;
}
.image {
width: 100%;
display: block;
}
.clearfix:before,
.clearfix:after {
display: table;
content: "";
}
.clearfix:after {
clear: both
}
.course_detail {
padding: 50px;
}
</style>
首页效果图
登录注册功能设计
后端接口
1 账号/手机号/邮箱+密码登录接口
2 手机号+验证码登录接口
3 发送手机验证码接口 (第三方发送短信)
4 注册接口--》手机号,验证码,密码
5 判断手机号是否存在接口