zoukankan      html  css  js  c++  java
  • 路飞学城前后端分离项目2

     路飞学城前后端分离项目2

    推荐课程的开发

    需求:

      我们在点击推荐课程的时候,会有该推荐课程的详细页面

    思路:

       为每一个推荐课程标签添加事件,重新向后台请求数据加载页面,但是url不会发生改变

      使用router-link,因为都在在detail路由中,页面不会重新加载,所以只要url改变,页面不改变

    需要说明的是,vue跟jquery不同,因为vue是单页面,所有他做的只是页面组件的重建与销毁

    如若你只是改变路由的话,那么页面不会有变化.

    如果你只是改变组件那么路由不会发生变化.

    我们开发的时候需要两个一起改变

    <template>
      <div>
        <h1>课程详细页面</h1>
        <div>
          <p>{{detail.course}}</p>
          <p>{{detail.img}}</p>
          <p>{{detail.level}}</p>
          <p>{{detail.slogon}}</p>
          <p>{{detail.title}}</p>
          <p>{{detail.why}}</p>
          <div>
            <ul v-for="item in detail.chapter">
              <li>{{item.name}}</li>
            </ul>
          </div>
    
          <div>
            <h3>推荐课程</h3>
            <ul v-for="item in detail.recommends">
              <!--<li><router-link :to="{name:'detail',params:{id:item.id}}">{{item.title}}</router-link></li>-->
              <li @click="changeDetail(item.id)">{{item.title}}</li>
            </ul>
          </div>
    
        </div>
      </div>
    </template>
    
    <script>
      export default {
        name: "index",
        data() {
          return {
            detail:{
              course:null,
              img:null,
              level:null,
              slogon:null,
              title:null,
              why:null,
              chapter:[],
              recommends:[],
            }
          }
        },
        mounted(){
          var id = this.$route.params.id
          this.initDetail(id)
        },
        methods:{
          initDetail(nid){
            var that = this
            this.$axios.request({
              url:'http://127.0.0.1:8000/api/v1/course/'+ nid +'/',
              method:'GET'
            }).then(function (arg) {
              if(arg.data.code === 1000){
                that.detail = arg.data.data
              }else{
                alert(arg.data.error)
              }
            })
          },
          changeDetail(id){
            this.initDetail(id)
            this.$router.push({name:'detail',params:{id:id}})
          }
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    

     vue中跳转路径方法补充:

    this.$router.push({name:'detail',params:{id:id}})
    

     用户登录

    需求:

    •   实现用户的登录并放置vue-cookies中
    •   并且随机生成token,访问需要登录的页面都带着token值

    思路:

    •  用户登录的时候先在前端获取用户名密码、如果有token值,生成token,如果没有则创建
    •    将token值和nikname返回给前端
    •    前端在vue-cookies保存token值和nikname作持久化,
    •    登录成功则显示nikname和注销

    前端登录页面

    Login.vue

    <template>
      <div>
        <h1>用户登录</h1>
        <div>
          <p>
            <input type="text" placeholder="请输入用户名" v-model="username">
          </p>
          <p>
            <input type="password" placeholder="请输入密码"  v-model="password">
          </p>
          <input type="button" value="登录" @click="doLogin">
        </div>
      </div>
    </template>
    
    <script>
      export default {
        data() {
          return {
            username:'',
            password:''
          }
        },
        methods:{
          doLogin(){
            var that = this
            this.$axios.request({
              url:'http://127.0.0.1:8000/api/v1/auth/',
              method:'POST',
              data:{
                user:this.username,
                pwd:this.password
              },
              headers:{
                'Content-Type':'application/json',
              }
            }).then(function (arg) {
              if (arg.data.code === 1000){
                that.$store.commit('saveToken',{token:arg.data.token,username:that.username})
              }else{
                alert(arg.data.error)
              }
            }).catch(function (arg) {
              console.log('发生错误')
            })
          }
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    

     store目录下的store.vue

    import Vue from 'vue'
    import Vuex from 'vuex'
    import Cookie from 'vue-cookies'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
      // 组件中通过 this.$store.state.username 调用
      state: {
        username: Cookie.get("username"),  #store每次都从cookie中取值
        token:Cookie.get("token"),
      },
      mutations: {
        // 组件中通过 this.$store.commit(saveToken,参数)  调用
        saveToken: function (state, userToken) {
          state.username = userToken.username;
          state.token = userToken.token;
          Cookie.set("username", userToken.username, "20min")
          Cookie.set("token", userToken.token, "20min")
    
        },
        clearToken: function (state) {
          state.username = null
          state.token = null
          Cookie.remove('username')
          Cookie.remove('token')
    
        }
      }
    })
    

     在store中,使用vue设置全局变量,vue-cookies保存全局变量,使其刷新后仍然存在于vue-cookies中

    vuex全局变量的用法

    1、创建store.js

    2、main.js中   

    import store from './store/store'
    放到实例化里边
    

     组建中可以通过this.$store.state.username调用

    vue-cookies的下载和使用

    下载

    npm install vue-cookis --save
    

    使用:

    如果登录成功,调用store的mutations方法:

    关于向后台发送数据的简单请求和复杂请求

    条件:
    1、请求方式:HEAD、GET、POST
    2、请求头信息:
    Accept
    Accept-Language
    Content-Language
    Last-Event-ID
    Content-Type 对应的值是以下三个中的任意一个
    application/x-www-form-urlencoded
    multipart/form-data
    text/plain

    注意:同时满足以上两个条件时,则是简单请求,否则为复杂请求

    复杂请求处理

    如果是复杂请求,那么就会先发一个options请求到服务里进行'预检'

    询问服务器是否允许我用post请求进行访问.如果是options请求过来后,

    给他加上一个特殊的请求头,那么就可以通过了

    class CORSMiddleware(MiddlewareMixin):
    
        def process_response(self,request,response):
            # 添加响应头
    
            # 允许你的域名来获取我的数据
            # response['Access-Control-Allow-Origin'] = "*"
    
            # 允许你携带Content-Type请求头
            # response['Access-Control-Allow-Headers'] = "Content-Type"
    
            # 允许你发送DELETE,PUT
            # response['Access-Control-Allow-Methods'] = "DELETE,PUT"
            response['Access-Control-Allow-Origin'] = "*"
    
            if request.method == "OPTIONS":
                response['Access-Control-Allow-Headers'] = "Content-Type"
                response['Access-Control-Allow-Methods'] = "PUT,DELETE"
    
            return response
    

     使用中间件为每次请求添加响应头

    后端登录操作

    models.py中

    class UserInfo(models.Model):
        '''用户表'''
        username=models.CharField(verbose_name='用户名',max_length=32)
        password=models.CharField(verbose_name='密码',max_length=64)
        nikname=models.CharField(max_length=32,verbose_name='昵称')
        def __str__(self):
            return self.nikname
    
    class Token(models.Model):
        user=models.OneToOneField('UserInfo',on_delete=models.CASCADE,verbose_name='用户')
        token=models.CharField(max_length=128)
    
        def __str__(self):
            return self.user
    

     url.py

    url(r'^auth/$', account.AuthView.as_view()),
    

     view.py中

    from rest_framework.views import APIView
    from rest_framework.response import Response
    from django.shortcuts import HttpResponse
    from api import models
    import uuid


    class AuthView(APIView): def post(self,request,*args,**kwargs): """ 用户登录认证 :param request: :param args: :param kwargs: :return: """ ret = {'code':1000} user = request.data.get('user') pwd = request.data.get('pwd') user = models.UserInfo.objects.filter(user=user,pwd=pwd).first() if not user: ret['code'] = 1001 ret['error'] = '用户名或密码错误' else: uid = str(uuid.uuid4()) models.UserToken.objects.update_or_create(user=user,defaults={'token':uid}) ret['token'] = uid return Response(ret)

     vue-拦截器

    用拦截器实现的功能:

      在需要登录才能访问的页面,如果未登录访问就拦截下来,跳转到登录页面

    登录成功后再跳到访问的页面

    1、在路径中添加   meta

     {
          path: '/news',
          name: 'news',
          component: News,
          meta:{
            requireAuth:true
          }
        },
    

     2、main.js中

    router.beforeEach(function (to, from, next) {
      if(to.meta.requireAuth){
        // 要去的url只有登陆成功后才能访问
        if (store.state.token) {
          next() #next表示去访问,本来是去哪访问就去哪访问
        } else {
          next({name: 'login',query: {backUrl: to.fullPath}})
        }
      }else{
        next()
      }
    })
    

     3、login中做判断,跳转url

     }).then(function (arg) {
              if (arg.data.code === 1000){
                that.$store.commit('saveToken',{token:arg.data.token,username:that.username})
    
                var url = that.$route.query.backUrl
                if(url){
                  that.$router.push({path:url})
                }else{
                  that.$router.push({path:'/index'})
                }
    

     后端认证

    用户获取到后端接口,直接不登录访问后端的接口,

    如果后端没有认证的话,那么接口的内容就会被暴露

    Micro.vue

    <template>
      <div>
        <h1>LuffyX学位:{{title}}</h1>
    
      </div>
    </template>
    
    <script>
      export default {
        name: "index",
        data() {
          return {
            title:null
          }
        },
        mounted(){
          this.initMicro()
        },
        methods:{
          initMicro(){
            var that = this
            this.$axios.request({
              url:'http://127.0.0.1:8000/api/v1/micro/',
              method:'GET',
              params:{
                token:this.$store.state.token
              }
            }).then(function (arg) {
              if(arg.data.code === 1000){
                that.title = arg.data.title
              }
            })
    
          }
        }
      }
    </script>
    
    <style scoped>
    
    </style>
    

     url.py

     url(r'^micro/$', course.MicroView.as_view()),
    

     auth认证

    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from api import models
    
    class LuffyAuth(BaseAuthentication):
    
        def authenticate(self, request):
            token = request.query_params.get('token')
            obj = models.UserToken.objects.filter(token=token).first()
            if not obj:
                raise AuthenticationFailed({'code':1001,'error':'认证失败'})
            return (obj.user.user,obj)
    

     view.py中使用

    class MicroView(APIView):
        authentication_classes = [LuffyAuth,]
    
        def get(self,request,*args,**kwargs):
            ret = {'code':1000,'title':'微职位'}
            return Response(ret)
    

     API的统一存放

    当你写了很多API后,你就会发现你自己不知道写了哪些路由,

    那么如果有人问你要接口的时候你可能会找很久.那么我们将api的路由

    统一放到vue的store里

      放在api的store里有什么好处?

    • 查找的时候变的方便,只要在store里查找就可以
    • 发送axios请求的时候可以在store里获取
    const store = new Vuex.Store({
      state: {
        apiList:{
         Course:'http://127.0.0.1:8000/api/v2/course/',
          CourseDetail:'http://127.0.0.1:8000/api/v2/course/',
          Login:'http://127.0.0.1:8000/api/v2/login/',
          Micro:'http://127.0.0.1:8000/api/v2/micro/',
    
        }
    
      },
    

     

    I can feel you forgetting me。。 有一种默契叫做我不理你,你就不理我

  • 相关阅读:
    面向对象 委托
    面向对象 继承 接口
    C# 面向对象 , 类与对象
    C# 知识点回顾
    SQL 数据库知识点回顾
    C# 10 总复习
    spring boot jpa 表关联查询分组 group by 去重
    钉钉新增考勤组 设置考勤规则
    elasticsearch 学习之父子关联查询 parent/child
    Elasticsearch 查询学习
  • 原文地址:https://www.cnblogs.com/weidaijie/p/10439352.html
Copyright © 2011-2022 走看看