Loading...
墨滴

XDragon

2021/03/14  阅读:13  主题:默认主题

SpringBoot+Vue+Mybatis-plus 博客(二):完成登录的前后端对接、完善左侧菜单栏

SpringBoot+Vue+Mybatis-plus 博客(一):完成博客后台前端登录页面、后端登录接口 SpringBoot+Vue+Mybatis-plus 博客(二):完成登录的前后端对接、完善左侧菜单栏 SpringBoot+Vue+Mybatis-plus 博客(三):完成搜索及博客列表展示功能前后端 SpringBoot+Vue+Mybatis-plus 博客(四):完成发布文章、编辑文章、删除文章及查询文章功能 SpringBoot+Vue+Mybatis-plus 博客(五):完成分类管理和标签管理前后端对接 SpringBoot+Vue+Mybatis-plus 博客(六):完成评论管理前后端交互 SpringBoot+Vue+Mybatis-plus 博客(七):完成友链管理前后端对接

@

一、登录的前后端对接

1、前端安装axios

npm install axios

2、新建utils/api.js

在这里插入图片描述
在这里插入图片描述
  • utils / api.js
import axios from 'axios'
/* 单独引入 此时调用方法为 Message(options)。我们也为每个 type 定义了各自的方法,如 Message.success(options)。
   并且可以调用 Message.closeAll() 手动关闭所有实例。*/

import { Message } from 'element-ui'
//导入路由
import router from '../router'

//处理响应信息的响应拦截器

/*上面已经导入了axios的对象了,这个对象里面有个响应的拦截器axios.interceptors.response.use(),use里面的data是服务端响应给你的数据,
该拦截器有两个回调函数,一个是success,一个是error,和jQuery里面的ajax一样请求数据的时候也有两个回调函数,一个success,一个error,
可以简单的理解为http的响应码是200的,它会进入到success方法中来,400以上的会进入到error来
*/

axios.interceptors.response.use(success => {
    //success.status: http的响应码   
    //success.data.status == 500: 返回json的status
    if(success.status && success.status == 200 && success.data.status == 500 ){
        //把后台的出错消息写出来
        Message.error({message: success.data.msg})
        return;
    }
    if(success.data.msg){
        //把操作成功的信息也弹出来
        Message.success({message: success.data.msg})
    }
    return success.data
},error =>{ //失败的处理
    if(error.response.status == 504 || error.response.status == 404){
        Message.error({message: "服务器被吃了哦!"})
    }else if(error.response.status == 403){
        Message.error({message: "权限不足,请联系管理员"})
    }else if(error.response.status == 401){
        Message.error({message: "尚未登录"})
        router.replace('/')
    }else{
        //服务器返回的错误信息
        if(error.response.data.msg){
            Message.error({message: error.response.data.msg})
        }else{
            Message.error({message: "未知错误"})
        }
    }
    return;
})

//封装请求
let base = ''//避免前缀要一个一个去改,太麻烦
//
export const postKeyValueRequest=(url,params)=>{

    return axios({
        method: 'post',  
        url: `${base}${url}`, //特别注意这里是``引用变量的方式,而不是''
        data: params, //直接这样以json的形式传给服务器,这是不支持的,所有还需要定义下面的transformRequest
        transformRequest: [function (data){
            let ret = '';
            for(let i in data){
               //往ret上面追加变量 
                ret+=encodeURIComponent(i) + '=' + encodeURIComponent(data[i]) + '&'
            }
            console.log(ret);
            return ret;
        }],
        //定义header
        headers:{
            'Content-Type':'application/x-www-form-urlencoded '
        }

    })

}

3、在Login.vue中使用

先导入

import {postKeyValueRequest} from "@/utils/api"

在这里插入图片描述 点击按钮时发起请求: 在这里插入图片描述

  submitLogin(){
                this.$refs.loginForm.validate((valid) =>{  // Element自带的校验
                    if(valid){
                        //这是在api.js封装的请求
                        postKeyValueRequest('/doLogin',this.loginForm).then(resp=>{
                            if(resp){
                                alert(JSON.stringify(resp))
                            }
                        })
                    }else {
                        this.$message.error("请输入所有字段");
                        return false;
                    }
                })
            }

4、配置请求转发的代理对象(解决跨域)

新建vue.config.js 在这里插入图片描述

  • vue.config.js
//配置请求转发的代理

//定义代理的对象
let proxyObj = {};

//拦截http请求
proxyObj['/'] = {
    ws: false,  //关掉websocket
    target: 'http://localhost:8082'//目标转发的地址
    changeOrigin: true,
    pathRewrite:{  //请求地址重写
        '^/'''  //拦截到的地址不去修改它
    }
}

//把上面的导出来
module.exports = {
    devServer:{  //配置开发环境
        host: 'localhost'//端口号
        port: 8080,
        proxy: proxyObj  //代理对象
    }
}

5、测试

  • 输入错误时 在这里插入图片描述

  • 账号密码都正确时 在这里插入图片描述

6、在api.js封装其它登录请求

以后想要引入这些封装好的登录方法直接在methods方法里面使用this.方法名就行

export const postRequest=(url, params)=>{
    return axios({
        method: 'post',
        url: `${base}${url}`,
        data: params  //这是用json来传递的,所以不用加transformRequest转换了
    })
}

export const putRequest=(url, params)=>{
    return axios({
        method: 'put',
        url: `${base}${url}`,
        data: params //这是用json来传递的,所以不用加transformRequest转换了
    })
}

export const getRequest=(url, params)=>{
    return axios({
        method: 'get',
        url: `${base}${url}`,
        data: params //这是用json来传递的,所以不用加transformRequest转换了
    })
}

export const deleteRequest=(url, params)=>{
    return axios({
        method: 'delete',
        url: `${base}${url}`,
        data: params //这是用json来传递的,所以不用加transformRequest转换了
    })
}

7、制作vue插件

在main.js中引入刚封装好的请求 在这里插入图片描述

import {postKeyValueRequest} from "./utils/api";
import {postRequest} from "./utils/api";
import {putRequest} from "./utils/api";
import {getRequest} from "./utils/api";
import {deleteRequest} from "./utils/api";

Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.postRequest = postRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.getRequest = getRequest;
Vue.prototype.deleteRequest = deleteRequest;

8、新建Home.vue组件并在router/index.js配置路由

在这里插入图片描述 配置路由 在这里插入图片描述

import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../components/Login.vue'
import Home from '../views/Home.vue'

Vue.use(VueRouter)

const routes = [
  {
    path: '/',
    redirect: '/admin',
    name: 'Login',
    component: Login,
    hidden: true
  },
  {
    path: '/admin',
    name: 'Login',
    component: Login,
    hidden: true
  },
  {
    path: '/admin/home',
    name: 'Home',
    component: Home
  },
  
]

const router = new VueRouter({
  routes
})

export default router

9、登录跳转

在这里插入图片描述 在这里插入图片描述

            submitLogin(){
                this.$refs.loginForm.validate((validate) =>{  // Element自带的校验
                    if(validate){
                        //这是在api.js封装的请求
                        this.postKeyValueRequest('/doLogin',this.loginForm).then(resp=>{
                            if(resp){
                                //resp:从服务端拿到的数据  用户的数据要保存到哪里? 保存在sessionStorage  关闭浏览器就没了
                                window.sessionStorage.setItem("user", JSON.stringify(resp.obj));
                                //页面跳转  replace:替换  用push的话,可以使用后退按钮回到登录页,用replace不可以回到登录页
                                this.$router.replace('/admin/home')
                            }
                        })
                    }else {
                        this.$message.error("请输入所有字段");
                        return false;
                    }
                })
            }

10、添加路由守卫

未登录时访问其他路由,会重定向到登录页,登录成功后,会直接跳转到未登录时访问的页面。 在这里插入图片描述 在这里插入图片描述 完整的main.js代码:

import Vue from 'vue'
import App from './App.vue'
import router from './router'

import ElementUI from 'element-ui';  //引入Element ui
import { Message } from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css';

import {postKeyValueRequest} from "./utils/api";
import {postRequest} from "./utils/api";
import {putRequest} from "./utils/api";
import {getRequest} from "./utils/api";
import {deleteRequest} from "./utils/api";

Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.postRequest = postRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.getRequest = getRequest;
Vue.prototype.deleteRequest = deleteRequest;


Vue.config.productionTip = false

Vue.use(ElementUI); //使用 ElementUI

router.beforeEach((to,from,next)=>{
  //to: 将要去哪里  from: 从哪里来的  next: 下一步,放行
  //如果要去的页面是Login页,直接放行
  if(to.path=='/' || to.path=='/admin'){
    next();
  }else{
    //如果有user,说明已经登录了
    if(window.sessionStorage.getItem("user")){
        next();
    }else{
      //没有登录就去登录页并记录你刚才要去的路由
      //to.path是你刚要去的路由,但因未登录被拦截了,在登录之后就会直接去到你刚访问的路由
      Message.error({message: "尚未登录,请先登录"})
      next('/?redirect=' + to.path);
    }
   
  }
})

new Vue({
  router,
  render: h => h(App)
}).$mount('#app')

11、测试

在这里插入图片描述 在这里插入图片描述

登录成功后: 在这里插入图片描述

12、添加回车登录

 @keydown.enter.native="submitLogin"
在这里插入图片描述
在这里插入图片描述

到此,登录的前后端就已经对接完成了。

二、完善左侧菜单栏

1、先看效果

首页 在这里插入图片描述 博客管理: 在这里插入图片描述

在这里插入图片描述 在这里插入图片描述

2、修改home.vue

添加首页导航 在这里插入图片描述 添加面包屑 在这里插入图片描述 修改面包屑样式 在这里插入图片描述 完整的home.vue代码

<template>
    <div>
        <el-container>
            <el-header class="homeHeader">
                <a href="/#/admin/home"><div class="title">小L星光站后台</div></a>
                <el-dropdown class="userInfo" @command="commandHandler">  <!--@command="commandHandler" 点击菜单项触发的事件回调-->
                    <span class="el-dropdown-link">
                        {{user.name}}
                        <i><img :src="user.avatar" alt=""></i>
                    </span>
                    <el-dropdown-menu slot="dropdown">
                        <el-dropdown-item command="userInfo">个人中心</el-dropdown-item>
                        <el-dropdown-item command="setting">设置</el-dropdown-item>  
                         <!--disabled:禁用的    divided:有分隔线-->
                        <el-dropdown-item command="logout" divided>注销登录</el-dropdown-item>
                    </el-dropdown-menu>
                </el-dropdown>
            </el-header>
            <el-container>
                <el-aside width="200px">
                   
                    <el-menu  router>
                         <el-menu-item index="/admin/home">
                            <i class="el-icon-menu"></i>
                            <span slot="title">首页</span>
                          </el-menu-item>
                         <!--这个遍历拿到的是index.js里面的routers地址数组 -->
                         <!-- !item.hidden 是将home和login的路由名隐藏,不需要在左侧菜单渲染出来 -->
                        <el-submenu :index="item.path" v-for="(item,index) in this.$router.options.routes" v-if="!item.hidden" :key="index">   
                            <template slot="title">
                                <i class="el-icon-location"></i>
                                <span>{{item.name}}</span>
                            </template>
                            <el-menu-item :index="child.path" v-for="(child,indexj) in item.children" :key="indexj">
                                {{child.name}}
                            </el-menu-item>                        
                        </el-submenu>
                    </el-menu>
                </el-aside>

                <el-main>
                    <!-- 面包屑 -->
                    <el-breadcrumb separator-class="el-icon-arrow-right" v-if="this.$router.currentRoute.path!='/admin/home'">
                        <el-breadcrumb-item :to="{ path: '/admin/home' }">首页</el-breadcrumb-item>
                        <el-breadcrumb-item>{{this.$router.currentRoute.name}}</el-breadcrumb-item>
                    </el-breadcrumb>
                    <div class="homeWelcome" v-if="this.$router.currentRoute.path=='/admin/home'">
                        欢迎来到小L星光站!
                    </div>
                    
                    <router-view/>
                </el-main>

            </el-container>
        </el-container>
    </div>
</template>
 
<script>
    export default {
        name: "Home",
        data(){
            return{
                user:JSON.parse(window.sessionStorage.getItem("user"))  //这样得到的数据是字符串,要用JSON.parse方法吧字符串转换成json数据
 
            }
        },
        methods:{
            commandHandler(cmd){  //该方法有一个参数,cmd
                if(cmd=='logout'){
                    this.$confirm('此操作将注销登录, 是否继续?''提示', {
                            confirmButtonText: '确定',
                            cancelButtonText: '取消',
                            type: 'warning'
                        }).then(() => {
                            this.getRequest("/logout");
                            window.sessionStorage.removeItem("user")
                            this.$router.replace("/")
                        
                        }).catch(() => {
                            this.$message({
                                type: 'info',
                                message: '已取消注销'
                            });          
                        });
 
                }
            }
        }
    }
</script>
 
<style>
    .homeHeader{
        background-color:#409eff;
        display: flex;
        align-items: center; /*竖轴上居中*/
        justify-content:space-between; /*空白的地方在中间*/
        padding: 0 15px;
        box-sizing: border-box;
    }
    /* 字体样式 */
    .title{
        font-size: 30px;
        font-family: 华文行楷;
        color: #ffffff;
    }
    /*设置鼠标移上去显示为手指*/
    .userInfo{
        cursor: pointer; 
    }
    /* 头像样式 */
    .el-dropdown-link img{
        width: 48px;
        height: 48px;
        border-radius: 24px;
        margin-left: 8px;
    }
    /* 用户名样式 */
    .el-dropdown-link{
        display: flex;
        align-items: center;
    }
    /* 面包屑样式 */
    .homeWelcome{
        text-align: center;
        font-size: 30px;
        font-family: 华文行楷;
        color: #409eff;
        padding-top: 50px;
    }
    a{
        text-decoration:none;
    }


</style>

3、在view目录下新建blogs包

并新建AllBlogs.vue、CategoryBlog.vue 等6个组件 在这里插入图片描述 AllBlogs.vue示例:(其他组件类似这个) 在这里插入图片描述

4、修改router/index.js

在这里插入图片描述
在这里插入图片描述

完整的router/index.js:

import Vue from 'vue'
import VueRouter from 'vue-router'

import Login from '../components/Login.vue'
import Home from '../views/Home.vue'

import WriteBlog from '../views/blogs/WriteBlog'
import AllBlogs from '../views/blogs/AllBlogs'
import CategoryBlog from '../views/blogs/CategoryBlog'
import TagBlog from '../views/blogs/TagBlog'
import CommentBlog from '../views/blogs/CommentBlog'
import LinksBlog from '../views/blogs/LinksBlog'


Vue.use(VueRouter)

const routes = [
  { path: '/',redirect: '/admin',name: 'Login', component: Login, hidden: true  },

  { path: '/admin', name: 'Login', component: Login,hidden: true},
    
  {
    path: '/admin/home',
    name: '博客管理',
    component: Home,
    children:[
      { path: '/admin/writeblog', name: '写博客',  component: WriteBlog  },
      { path: '/admin/allblogs', name: '文章管理',  component: AllBlogs  },
      { path: '/admin/categoryblog', name: '分类专栏',  component: CategoryBlog  },
      { path: '/admin/tagblog', name: '标签管理',  component: TagBlog  },
      { path: '/admin/commentblog', name: '评论管理',  component: CommentBlog  },
      { path: '/admin/linksblog', name: '友链管理',  component: LinksBlog  },
      
    ]
  },
  
]

const router = new VueRouter({
  routes
})

export default router


关注【小L星光】回复 “博客” 即可获整个项目源码 ~ 在这里插入图片描述

XDragon

2021/03/14  阅读:13  主题:默认主题

作者介绍

XDragon