zoukankan      html  css  js  c++  java
  • 路飞项目搭建2 luffy前台主页, xadmin后台管理, 前后台分离跨域交互, 自定义配置, 浏览器屏幕缩放bug修复

    前端主页

    图片准备

    将提供的资料中的图片移植到项目的img文件夹下

    页头组件:components/Header.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: "Header",
            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>

    轮播图组件:components/Banner.vue

    <template>
        <div class="banner">
            <el-carousel height="400px">
                <el-carousel-item v-for="item in 4" :key="item">
                    <img src="../assets/img/banner1.png" alt="">
                </el-carousel-item>
            </el-carousel>
        </div>
    </template>
    
    <script>
        export default {
            name: "Banner"
        }
    </script>
    
    <style scoped>
        .el-carousel__item {
            height: 400px;
            min-width: 1200px;    /*屏幕在1200px内不变,超过1200px变大*/
        }
        .el-carousel__item img {
            height: 400px;    /*等比例缩放*/
            margin-left: calc(50% - 1920px / 2);    /*图片放到中央*/
        }
    </style>

    页脚组件:components/Footer.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: "Footer"
        }
    </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>

    主页组件:views/Home.vue

    <template>
        <div class="home">
            <Header />
            <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 />
        </div>
    </template>
    
    <script>
        import Header from '../components/Header'
        import Footer from '../components/Footer'
        import Banner from '../components/Banner'
    
        export default {
            name: "Home",
            components: {
                Header,
                Footer,
                Banner,
            }
        }
    </script>

    后台主页模块设计

    home模块

    创建home模块

    前提:在 luffy 虚拟环境下
    
    1.终端从项目根目录进入apps目录
    >: cd luffyapi & cd apps
    
    2.创建app
    >: python ../../manage.py startapp home

    路由分发

    主路由:luffyapi/urls.py
    from django.urls import path, re_path, include
    urlpatterns = [
        # ...
        path('home/', include('home.urls')),
        # ...
    ]
    子路由:home/urls.py
    from django.urls import path, re_path
    urlpatterns = [
    
    ]

    Banner数据表model设计

    utils/model.py
    from django.db import models
    
    class BaseModel(models.Model):
        created_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
        updated_time = models.DateTimeField(auto_now=True, verbose_name='最后更新时间')
        is_delete = models.BooleanField(default=False, verbose_name='是否删除')
        is_show = models.BooleanField(default=True, verbose_name='是否上架')
        orders = models.IntegerField(verbose_name='优先级')
    
        class Meta:
            abstract = True
    home/models.py
    from django.db import models
    from utils.model import BaseModel
    
    class Banner(BaseModel):
        title = models.CharField(max_length=16, unique=True, verbose_name='名称')
        image = models.ImageField(upload_to='banner', verbose_name='图片')
        link = models.CharField(max_length=64, verbose_name='跳转链接')
        info = models.TextField(verbose_name='详情')  # 也可以用详情表,宽高出处
    
        class Meta:
            db_table = 'luffy_banner'
            verbose_name_plural = '轮播图表'
    
        def __str__(self):
            return self.title
    数据迁移:在大luffyapi路径下的终端
    >: python manage.py makemigrations
    >: python manage.py migrate

    注册home模块:dev.py

    INSTALLED_APPS = [
        # ...
        'rest_framework',
        'home',
    ]

    设计Banner数据接口

    home/serializers.py
    from rest_framework import serializers
    from . import models
    class BannerSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Banner
            fields = ['title', 'image', 'link']
    home/views.py
    from rest_framework.viewsets import ModelViewSet
    from rest_framework import mixins
    from . import models, serializers
    
    class BannerViewSet(ModelViewSet, mixins.ListModelMixin):
        queryset = models.Banner.objects.filter(is_delete=False, is_show=True).all()
        serializer_class = serializers.BannerSerializer
    home/urls.py
    from django.urls import path, include
    from utils.router import router
    from . import views
    # 注册ViewSet的路由
    router.register('banners', views.BannerViewSet, 'banner')
    
    urlpatterns = [
        path('', include(router.urls)),
    ]
    接口
    http://localhost:8000/home/banners/

    xadmin后台管理

    可以用simpleui代替,地址:https://github.com/newpanjing/simpleui    注意django1.11版本安装会升级为3.00版本,慎用

     安装: luffy虚拟环境下    

    >: pip install https://codeload.github.com/sshwsfc/xadmin/zip/django2
    注册app:dev.py
    INSTALLED_APPS = [
        # ...
        # xadmin主体模块
        'xadmin',
        # 渲染表格模块
        'crispy_forms',
        # 为模型通过版本控制,可以回滚数据
        'reversion',
    ]
    xadmin:需要自己的数据库模型类,完成数据库迁移
    python manage.py makemigrations
    python manage.py migrate
    设置主路由替换掉admin:主urls.py
    # xadmin的依赖
    import xadmin
    xadmin.autodiscover()
    # xversion模块自动注册需要版本控制的 Model
    from xadmin.plugins import xversion
    xversion.register_models()
    
    urlpatterns = [
        # ...
        path(r'xadmin/', xadmin.site.urls),
    ]
    创建超级用户:大luffyapi路径终端
    # 在项目根目录下的终端
    python manage.py createsuperuser
    # 账号密码设置:admin | Admin123
    完成xadmin全局配置:新建home/adminx.py
    # home/adminx.py
    # xadmin全局配置
    import xadmin

    # 建议的配置,写在任何一个app的admin文件中都可以
    from xadmin import views class GlobalSettings(object): """xadmin的全局配置""" site_title = "路飞学城" # 设置站点标题 site_footer = "路飞学城有限公司" # 设置站点的页脚 menu_style = "accordion" # 设置菜单折叠 xadmin.site.register(views.CommAdminView, GlobalSettings)
    在adminx.py下方中注册model:home/adminx.py
    from . import models
    # 注册
    xadmin.site.register(models.Banner)

    前后台分离跨域交互

    问题:前后台分离项目交互时,如果出现了CORS,代表什么?如何解决

        1)服务器已经接收到了客户端请求
        2)服务器发现客户端非同域客户端(不是自家的)(ip、port、协议存在不一致)
        3)服务器不对客户端做需要响应,而是一种CORS错误响应(就是在响应头中做文章)
        处理CORS错误响应的本质:处理响应头  Access-Control-Allow-Origin

    后台处理跨域

    安装插件
    >: pip install django-cors-headers
    
    插件参考地址:https://github.com/ottoyiu/django-cors-headers/
    项目配置:dev.py
    # 注册app
    INSTALLED_APPS = [
        ...
        'corsheaders',
    ]
    
    # 添加中间件
    MIDDLEWARE = [
        ...
        'corsheaders.middleware.CorsMiddleware',
    ]
    
    # 允许跨域源
    CORS_ORIGIN_ALLOW_ALL = True
    
    # 允许的请求头
    CORS_ALLOW_HEADERS = (
        "accept",
        "accept-encoding",
        "authorization",
        "content-type",
        "dnt",
        "origin",
        "user-agent",
        "x-csrftoken",
        "x-requested-with",
    
        # 额外允许的请求头
        'token',
    )

    特殊处理(不用)

    views.py中

    from rest_framework.response import Response
    class BannerViewSet(ModelViewSet,mixins.ListModelMixin):
        queryset = models.Banner.objects.filter(is_delete=False,is_show=True).all()
        serializer_class = serializers.BannerSerializer
    
        # 了解CORS本质,处理还是在中间件中用插件,因为可以控制所有接口
        '''
        def list(self,request,*args,**kwargs):
            response = super().list(request,*args,**kwargs)
            # 响应头设置'Access-Control-Allow-Origin' 为'*'就代表该接口可以对任何请求完成响应
            return Response(response.data,headers={'Access-Control-Allow-Origin':'*'})
        '''

    前台请求Banner数据

    修订Banner.vue
    <template>
        <div class="banner">
    <!--        <el-carousel height="400px">-->
    <!--            <el-carousel-item v-for="item in 4" :key="item">-->
    <!--                <img src="../assets/img/banner1.png" alt="">-->
    <!--            </el-carousel-item>-->
    <!--        </el-carousel>-->
            <el-carousel height="400px">
                <el-carousel-item v-for="banner in banner_list" :key="banner.title">
                    <router-link :to="banner.link">
                        <img :src="banner.image" alt="">
                    </router-link>
                </el-carousel-item>
            </el-carousel>
        </div>
    </template>
    
    <script>
        export default {
            name: "Banner",
            data() {
                return {
                    banner_list: []
                }
            },
    
            // 在created钩子中
            created() {
                this.$axios({
                    url: this.$settings.base_url + '/home/banners/',    // 所有请求默认为get发送
                    headers: {  // 测试前台给后台提交请求头
                        // authorization: 'jwt abc.def.xyz',    解决跨域问题,为后台默认请求头
                        // token: 'jwt abc.def.xyz',    自定义请求后,后台也必须设置该自定义请求头,否则跨域问题
                    }
                }).then(response => {
                    console.log(response.data);
                    this.banner_list = response.data;
                }).catch(error => {
                    console.log(">>>", error);
                })
            }
    
        }
    </script>
    
    <style scoped>
        .el-carousel__item {
            height: 400px;
            min-width: 1200px;
        }
        .el-carousel__item img {
            height: 400px;
            margin-left: calc(50% - 1920px / 2);
        }
    </style>

    自定义配置

    自定义常量配置文件:settings/const.py

    # 自定义的常量配置文件,在settings中 from 该文件 import *,将名字全部丢给settings
    BANNER_COUNT = 4

    加载自定义配置名称空间:settings/dev.py

    # 加载自定义配置名称空间
    from .const import *

    在轮播图接口中应用:banner/views.py

    class BannerViewSet(ModelViewSet, mixins.ListModelMixin):
        queryset = models.Banner.objects.filter(is_delete=False, is_show=True).order_by('-orders')[:settings.BANNER_COUNT]    # 反向
        serializer_class = serializers.BannerSerializer

    浏览器屏幕缩放bug修复

    问题:浏览器缩放时,轮播图显示不全,滚动水平滚动条,发现图片缺失

    其实是element-ui插件的问题,也可以换成swiper插件解决

    解决:隐藏水平滚动条,页面都只提供垂直滚动条的需求

    global.css
    /* 水平超出部分默认隐藏 */
    #app {
        overflow: hidden;
    }
  • 相关阅读:
    dup/dup2函数
    read/write函数
    lseek函数
    流程控制
    vim普通模式
    vim实用技巧1
    python源代码解读
    python变量命名规则
    python之字符串2
    Docker系列文章
  • 原文地址:https://www.cnblogs.com/ludingchao/p/12381045.html
Copyright © 2011-2022 走看看