springboot+vue 手把手搭建微人事项目(二):完成登录的前后端对接、Home的title、左侧菜单制作

一、前后端进行对接

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'

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

/*上面已经导入了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: "尚未登录"})
    }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:8081', //目标转发的地址
    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配置路由

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

9、登录跳转

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

 submitLogin(){
                this.$refs.loginForm.validate((valid) =>{  // Element自带的校验
                    if(valid){
                        //这是在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('/home')
                            }
                        })
                    }else {
                        this.$message.error("请输入所有字段");
                        return false;
                    }
                })
            }

10、测试

在这里插入图片描述

二、Home页的Title制作

1、使用Element UI中的Container布局容器

https://element.eleme.cn/#/zh-CN/component/container
在这里插入图片描述
复制响应布局代码过来并给头部添加背景色
在这里插入图片描述
效果:
在这里插入图片描述
发现有默认的边距,这时我们需要在public/index.html的body加上样式style="margin:0px; padding:0px;" 即可消除边距

在这里插入图片描述
消除后刷新页面:
可以看到已经没有边距了
在这里插入图片描述

2、完善Home组件

<template>
    <div>
        <el-container>
            <el-header class="homeHeader">
                <div class="title">微人事</div>
                <el-dropdown class="userInfo" @command="commandHandler">  <!--@command="commandHandler" 点击菜单项触发的事件回调-->
                    <span class="el-dropdown-link">
                        {{user.name}}
                        <i><img :src="user.userface" 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">Aside</el-aside>
                <el-main>Main</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;
    }

</style>
  • 每个下拉菜单都是一个点击按钮,所以要给下拉菜单添加点击事件,可以使command=" "点击菜单项触发的事件回调
  • 然后在标签里面添加@command=“commandHandler” 点击事件
  • 再在script标签里面加上method方法

3、效果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注销成功后回到登录页面:
在这里插入图片描述
登录成功后:
在这里插入图片描述

三、左侧菜单

1、侧栏

https://element.eleme.cn/#/zh-CN/component/menu
在这里插入图片描述
修改后:
在这里插入图片描述

 <el-aside width="200px">
    <el-menu>
          <el-submenu index="1">
              <template slot="title">
                  <i class="el-icon-location"></i>
                  <span>导航一</span>
              </template>
              <el-menu-item-group>
                  <el-menu-item index="1-1">选项1</el-menu-item>
                  <el-menu-item index="1-2">选项2</el-menu-item>
              </el-menu-item-group>
          </el-submenu>
      </el-menu>
  </el-aside>

在这里插入图片描述

2、新建两个测试用的组件

在这里插入图片描述

3、动态渲染菜单

在这里插入图片描述

  • Home.vue
<template>
    <div>
        <el-container>
            <el-header class="homeHeader">
                <div class="title">微人事</div>
                <el-dropdown class="userInfo" @command="commandHandler">  <!--@command="commandHandler" 点击菜单项触发的事件回调-->
                    <span class="el-dropdown-link">
                        {{user.name}}
                        <i><img :src="user.userface" 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>
                         <!--这个遍历拿到的是index.js里面的routers地址数组 -->
                         <!-- !item.hidden 是将home和login的路由名隐藏,不需要在左侧菜单渲染出来 -->
                        <el-submenu index="1" 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>
                    <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;
    }

</style>

4、在router/index.js配置

在这里插入图片描述

  • router/index.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import Login from '../views/Login.vue'
import Home from '../views/Home.vue'
import Test1 from '../views/Test1'
import Test2 from '../views/Test2'

Vue.use(VueRouter)

  const routes = [
  {
    path: '/',
    name: 'Login',
    component: Login,
    hidden: true
  },
  {
    path: '/home',
    name: 'Home',
    component: Home,
    hidden: true //做个标记,然后在Home.vue里面进行判断
  },
  {
    path: '/home',
    name: '导航一',
    component: Home,
    children:[
      {
        path: '/test1',
        name: '选项1',
        component: Test1
      },
      {
        path: '/test2',
        name: '选项2',
        component: Test2
      }
    ]
  }

]

const router = new VueRouter({
  routes
})

export default router

5、效果

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值