2.学城项目 头部底部组件&首页轮播图

1. 下载框架/组件/库

* 1. 下载项目中需要使用的框架个组件.
1. axion 网络请求的第三方框架 (之前测试前后端通信已经安装过了)
cnpm install axios -S 

2. vue操作cookies 的组件
cnpm install vue-cookies -S

3. element-ui ui库
cnpm install element-ui -S

4. jquery框架(bootstrap需要使用)
cnpm install jquery -S (不适合 vue.js 开发的项目, 不在推荐去使了)

5. bootstrap框架
cnpm install bootstrap@3 -S

2.学城项目 头部底部组件&首页轮播图_第1张图片

2.学城项目 头部底部组件&首页轮播图_第2张图片

* 2. 在main.js导入下载的框架/组件/
// 导入axios模块
import axios from 'axios'
// 通过Vue的原型将值存放在Vue中, 以后Vue对象通过.属性取到axios, Vue的属性一般都在属性名前加上$标识
Vue.prototype.$axios = axios


// vue-cookies配置
import cookies from 'vue-cookies'
Vue.prototype.$cookies = cookies;

// ElementUI的配置
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(ElementUI);

// bootstrap配置
import 'bootstrap'
import 'bootstrap/dist/css/bootstrap.min.css'

2.学城项目 头部底部组件&首页轮播图_第3张图片

* 3. juery的带入需要单独配置, 在项目根目录下新建vue.config.js文件
     如果文件已经创建了, 在后面将代码添加即可.
const webpack = require("webpack");

module.exports = {
    configureWebpack: {
        plugins: [
            new webpack.ProvidePlugin({
                $: "jquery",
                jQuery: "jquery",
                "window.jQuery": "jquery",
                "window.$": "jquery",
                Popper: ["popper.js", "default"]
            })
        ]
    }
};

2.学城项目 头部底部组件&首页轮播图_第4张图片

2. vue路由跳转方式

方式1: html中路由跳转
<router-link to="/">
</router-link>

方式2: js点击事件中控制路由跳转
 @click='func()'
    this.$router.push('/');

3. Load storage 与 Session Storage

Load storage: 数据存放在本地, 可以永久存储.
Session Storage: 数据临时存储,页面关闭, 数据清除
可以在开发者工作中的Application中查看Load storage  Session Storage 都是windows对象, 可以通过
对象.属性 =  为对象创建属性 
对象.属性 取值

2.学城项目 头部底部组件&首页轮播图_第5张图片

4. 头部组件

4.1 组件代码
将img目录复制到在assets文件下
在components小组件中新建Head.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 {
                // 当sessionStorage.url_path有值, 就将值赋值给url_path, 否则就将/根路径赋值给url_path
                url_path: sessionStorage.url_path || '/',
            }
        },
        methods: {
            goPage(url_path) {
                // 已经是当前路由就没有必要重新跳转
                if (this.url_path !== url_path) {
                    this.$router.push(url_path);
                }
                // 更新sessionStorage对的url_path属性值
                sessionStorage.url_path = url_path;
            },
        },
        created() {
            // $route属性是内置的属性, $route.path获取当前页面的路径,
            sessionStorage.url_path = this.$route.path;
            // 将当前路径分别赋值给 Vue对象 与 sessionStorage 对象 的 url_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>
4.2 使用组件
在home页面组件中使用, (页面组件之前的测试代码删除掉!)
使用组件可以是 <Head></Head> 也可以是 <Head/>


image-20220515140740788

5. 全局css样式/全局配置文件

5.1 全局css样式
全局css样式将一些标签的默认样式清除.
* 1. 在assets新建css目录, cs目录中新建global.css
/* 声明全局样式和项目的初始化样式 */
/* 清除默认边距 字体大小*/
body, h1, h2, h3, h4, h5, h6, p, table, tr, td, ul, li, a, form, input, select, option, textarea {

    margin: 0;
    padding: 0;
    font-size: 15px;
}  

/* a标签的下划线 a标签的颜色 */
a {

    text-decoration: none;
    color: #333;  
}

/* li 前面不带点 */
ul {
    list-style: none;  
}

/* 合并边框 */
table {
    border-collapse: collapse; 
}
* 2. 在main.js中配置
// 配置全局样式 @ 符号,代指src路径
import '@/assets/css/global.css'

image-20220515140716001

5.2 全局配置文件
将一些常用的配置写在一个文件中, 最终将这文件中的信息写入到Vue对象的属性中方便调用.
* 1. 在assets下新建js目录, js目录中新建settings.js 文件	
// 这个文件是被导入使用的, 需要先export default导出才能被导入使用
export default {
	// 基本路由(服务器的)
    base_url: 'http://127.0.0.1:8000'
}
* 2. 在main.js配置文件中导入全局配置文件, 并将信息加入到Vue对象中.
// 配置全局自定义设置
import settings from '@/assets/js/settings'
Vue.prototype.$settings = settings;

// 在所有需要与后台交互的组件中:this.$settings.base_url + '再拼接具体后台路由'
// eg: axios.get(this.$settings.base_url + 'xxx')

2.学城项目 头部底部组件&首页轮播图_第6张图片

6. 底部组件

6.1 组件代码
在components小组件中新建Footer.vue





6.2 使用组件
在home页面组件中使用.
使用组件可以是 <Footer></Footer> 也可以是 <Footer/>


2.学城项目 头部底部组件&首页轮播图_第7张图片

7. 轮播图组件

7.1 组件代码
在components小组件中新建Carousel.vue





7.2 使用组件


2.学城项目 头部底部组件&首页轮播图_第8张图片

2.学城项目 头部底部组件&首页轮播图_第9张图片

7.3 轮播图接口
轮播图的所有图片都是向后端发送请求获取.
1. 创建APP
* 1. 新建home app应用
PS F:\synchro\Project\luffy> cd luffy/apps
PS F:\synchro\Project\luffy\luffy\apps> python ../../manage.py startapp home
* 2. 将新建的app添加到引用列表中
INSTALLED_APPS = [
	...
    'home',
]
2. 设置数据表
* 1. 在apps下的utils的目录下新建models.py 数据库的基础字段
	继承这个类之后, 该表拥有以下字段, 只想被继承不想生成表需要定义Meta类中, 类中定义abstract抽象类.
	create_time    创建时间
	update_time    最后一次更新时间
	is_delete      是否删除
	is_show        是否展示
	display_order  显示顺序
from django.db import models


#  表的基础字段
class BaseModel(models.Model):
    # 创建时间 auto_now_add=True(创建数据时自动创建)
    create_time = models.DateTimeField(auto_now_add=True, verbose_name='创建时间')
    # 最后一次更新时间
    update_time = models.DateTimeField(auto_now=True, verbose_name='最后一次更新时间')
    # 是否删除 (默认设置为False 不删除)
    is_delete = models.BooleanField(default=False, verbose_name='是否删除')
    # 是否展示 (默认设置为False 不显示)
    is_show = models.BooleanField(default=False, verbose_name='是否展示')
    # 显示顺序
    display_order = models.IntegerField(verbose_name='显示顺序')

    class Meta:
        # Abstract(抽象)可以修饰类、方法 为True则此类必须被继承使用, 此类不可生成对象, 必须被继承使用.
        # 如果不设置会生成这样表, 我们不需要这个表只想继承它
        abstract = True
* 2. 在home 目录的models.py 中继承继承字段类, 创建轮播图表
	除了基础字段之后额外添加以下字段, 在后台打印对象的时候展示名字需要定义__str__方法.
	在Meta类中定义verbose_name_plural设置表在后台显示的名字
    name   图片的名字
    img    上传图片
    link   点击图片跳转的地址
    info   图片的描述信息
from django.db import models

# 导入model表继承字段
from utils.model import BaseModel


# 创建轮播图表
class CarouselModel(BaseModel):
    # 图片的名字
    name = models.CharField(max_length=32, verbose_name='图片名称')
    # 图片 (upload_to指定图片上传的地址, help_text提示信息, null=True 图片可以为空)
    img = models.ImageField(upload_to='Carousel', verbose_name='轮播图', help_text='图片尺寸必须是: 3840*800', null=True)
    # 点击图片跳转的地址
    link = models.CharField(max_length=128, verbose_name='跳转地址')
    # 图片信息
    info = models.TextField(verbose_name='图片信息')

    # 在后台打印对象的时候展示名字
    def __str__(self):
        return self.name
    
    # 在后台显示的名称
    class Meta:
        # verbose_name = '轮播图表'  # 加s
        verbose_name_plural = '轮播图表'  # 不加s

* 3. 数据库迁移
python manage.py makemigrations
python manage.py migrate
3. 模型序列化器
在项目名目录下的utils目录下新建Serializer.py 序列模块文件
# 导入序列化器模块
from rest_framework import serializers
# 导入home的模型层
from home import models


# 轮播图表模型序列化器
class CarouselModelSerializer(serializers.ModelSerializer):
    # 定义Meta类
    class Meta:
        # 对应的模型表
        model = models.CarouselModel
        # 序列化的字段
        fields = ['name', 'link', 'img']	
4. 视图类
获取图片的接口, 过滤不需要显示的数据, 再排序, 在通过指定获取取返回值.
* 1. 在settings目录下新建const.py 文件 (记录一些常量)
# 一些常量的数据

# 获取轮播图的数量
CAROUSEL_SHOW_QUANTITY = 3
* 2. 在settings的dev.py 配置文件中导入const.py文件
# 导入常量配置(在开头处导入)

from .const import *

CAROUSEL_SHOW_QUANTITY = CAROUSEL_SHOW_QUANTITY  # 这样写的目的是不让编辑飘黄提示 from .const import *没有使用
* 3. 在home apps的视图层中写视图类.
# from django.shortcuts import render

# Create your views here.


# 1. 轮播图接口

# 导入ListAPIView (继承GenericAPIView, ListModelMixin
from rest_framework.generics import ListAPIView
# 导入模型层
from . import models
# 导入序列化器模块, 自定义的响应对象
from utils import Serializer, api_response

# 导入配置文件, 先从dev配置文件中加在数据, 如果没有会去内置的配置文件中找
from django.conf import settings


# 导入模型
class CarouselAPI(ListAPIView):
    # 设置queryset属性值 过滤没有删除 要显示的数据, 查询之后按display_order排序, 在通过索取值取指定的数量
    queryset = models.CarouselModel.objects.filter(is_delete=False, is_show=False).order_by('display_order')[
               :settings.CAROUSEL_SHOW_QUANTITY]
    # 设置使用的模型序列化器
    serializer_class = Serializer.CarouselModelSerializer

    # 写一个静态方法, 源码 return Response(serializer.data) 改为下面这个静态方法, 静态方法调用自定义的数据返回格式
    @staticmethod
    def response(serializer_data):
        return api_response.ResponseDataFormat(data=serializer_data)
ListModelMixin源码中使用在视图类中定义的方法.
class ListModelMixin:
    """
    List a queryset.
    """
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        # return Response(serializer.data)
        return self.response(serializer.data)
另一种方式: 在数图类中定义ListModelMixin的list方法, 先调用父类的ListModelMixin的list(也就是内置的)
得到一个Response对象, 充对象中将数据取出放进自己定义的response对象中返回.
# 导入模型
class CarouselAPI(ListAPIView):
    # 设置queryset属性值 过滤没有删除 要显示的数据, 查询之后按display_order排序, 在通过索取值取指定的数量
    queryset = models.CarouselModel.objects.filter(is_delete=False, is_show=False).order_by('display_order')[
               :settings.CAROUSEL_SHOW_QUANTITY]
    # 设置使用的模型序列化器
    serializer_class = Serializer.CarouselModelSerializer
    def list(self, request, *args, **kwargs):
        # 调用父类的方法, 修改的源码恢复原样
        res = super().list(request, *args, **kwargs)
        print(res)
        return api_response.ResponseDataFormat(data=res.data)
5. 请求路由
* 1. 路由分发, 在项目名下的总路由文件中配置路由分发.
...
urlpatterns = [
    # 修改为访问的页面为xadmin的页面
    re_path('^xadmin/', xadmin.site.urls),
    re_path('^media/(?P.*)', serve, {'document_root': settings.MEDIA_ROOT}),
    # 路由分发到user app
    re_path('user/', include('user.urls')),
    # 路由分发到home app
    re_path('home/', include('home.urls'))
]
* 2. 在home app目录中新建urls.py 子路由文件
from django.urls import re_path
from home import views
urlpatterns = [
    # 不能有^
    re_path(r'api/Carousel/', views.CarouselAPI.as_view())
]
6. 注册表到后台
* 1. 在home的admin.py 中把轮播图表中注册到xadmin后台管理中
import xadmin

from . import models
# 将轮播图表添加到后台管理中
xadmin.site.register(models.CarouselModel)
7. 后台管理
* 1. 启动程序 登入到后台 http://127.0.0.1:8000/xadmin/

2.学城项目 头部底部组件&首页轮播图_第10张图片

* 2. 为轮播图表添加数据
序号:     1
图片名称: Carouse1
轮播图:   选中图片
跳转地址: free-course
图片简介: 点击图片跳转到免费课程的路由地址

2.学城项目 头部底部组件&首页轮播图_第11张图片

2.学城项目 头部底部组件&首页轮播图_第12张图片

序号:     2
图片名称: Carouse2
轮播图:   选中图片
跳转地址: actual-course
图片简介: 点击图片跳转到实战课程的路由地址

2.学城项目 头部底部组件&首页轮播图_第13张图片

序号:     3
图片名称: Carouse3
轮播图:   选中图片
跳转地址: light-course
图片简介: 点击图片跳转到轻课的路由地址

2.学城项目 头部底部组件&首页轮播图_第14张图片

8. 接口测试
启动程序访问轮播图的接口 http://127.0.0.1:8000/home/api/Carousel/

2.学城项目 头部底部组件&首页轮播图_第15张图片

7.4 前端获取数据
前端 轮播图组件的生命周期钩子函数created中发送axios请求获取数据.
<template>

  <div id="Carousel">
    
    <el-carousel indicator-position="outside" height="400px">
      
      <el-carousel-item v-for="(item, index) in carousel_list" :key="index">
        
        <router-link :to="item.link"> 
          <img :src="item.img" :alt="item.name" >
        router-link>
      el-carousel-item>
    el-carousel>
  div>

template>

<script>
export default {
  name: 'Carousel',
  // 定义模板中使用的变量
  data() {
    return {
      carousel_list: []
    }
  },
  //
  created() {
    // 发送axios请求 http://127.0.0.1:8000 + /home/api/Carousel/ 注意不会自动加/
    this.$axios.get(this.$settings.base_url + '/home/api/Carousel/').then(response => {
      this.carousel_list = response.data.data
      // console.log(this.carousel_list)
    }).catch(error => {
      alert(error)
    })
  }
}
script>

<style scoped>
/* 高400px 最小宽度1200px */
.el-carousel__item {
  height: 400px;
  min-width: 1200px;
}

/*  高400px 图片居中 */
.el-carousel__item img {
  height: 400px;
  margin-left: calc(50% - 1920px / 2);
}
style>

7.5 图片跳转路由

2.学城项目 头部底部组件&首页轮播图_第16张图片

* 1. src目录views目录下新建页面组件
     FreeCourse.vue      免费课程页面组件
     ActualCourse.vue    实战课程页面组件
     LightCourse.vue     轻课页面组件

<template>
  <div>
    <Head>Head>
    <h1>实战课程页面h1>
    <Footer>Footer>
  div>

template>

<script>

// 导入头部组件
import Head from '@/components/Head'
// 导入底部组件
import Footer from '@/components/Footer'

export default {
  name: "actual-course.vue",
  components: {
    Head,  // 头部组件
    Footer,  // 底部组件
  }
}
script>

<style scoped>

style>

<template>
  <div>
    <Head>Head>
    <h1>免费课程页面h1>
    <Footer>Footer>
  div>
template>

<script>

// 导入头部组件
import Head from '@/components/Head'
// 导入底部组件
import Footer from '@/components/Footer'

export default {
  name: "free-course.vue",
  components: {
    Head,  // 头部组件
    Footer,  // 底部组件
  }
}
script>

<style scoped>

style>

<template>
  <div>
    <Head>Head>
    <h1>轻课页面h1>
    <Footer>Footer>
  div>
template>

<script>
// 导入头部组件
import Head from '@/components/Head'
// 导入底部组件
import Footer from '@/components/Footer'

export default {
  name: "light-course.vue",
  components: {
    Head,  // 头部组件
    Footer,  // 底部组件
  }
}
script>

<style scoped>

style>

2.学城项目 头部底部组件&首页轮播图_第17张图片

* 2. src目录下的router目录下的index.js文件中 添加路由
     免费课程页面路由
     实战课程页面路由
     轻课程页面路由
import Vue from 'vue'
import VueRouter from 'vue-router'
import HomeView from '../views/HomeView.vue'

// 导入免费课程页面组件
import FreeCourse from '../views/FreeCourse.vue'
// 导入实战课程页面组件
import ActualCourse from '../views/ActualCourse'
// 导入轻课程页面组件
import LightCourse from '../views/LightCourse'

Vue.use(VueRouter)

const routes = [
    // 主页路由
    {
        path: '/',  // 路由
        name: 'home',  // 名字
        component: HomeView  // 使用的的组件
    },
    // 免费课程页面路由
    {
        path: '/free-course',
        name: 'FreeCourse',
        component: FreeCourse
    },
    // 实战课程页面路由
    {
        path: '/actual-course',
        name: 'ActualCourse',
        component: ActualCourse
    },
    // 轻课程页面路由
    {
        path: '/light-course',
        name: 'LightCourse',
        component: LightCourse
    },
]

const router = new VueRouter({
    routes
})

export default router

你可能感兴趣的:(8.学城项目,vue.js,javascript,bootstrap)