vue.js+elementUI学习01之后台管理登录验证实现axios和springMVC交互

版权声明: https://blog.csdn.net/dream_broken/article/details/77451639

      前段时间学习了vue.js的一些相关知识。现在动手敲代码,想实现一个简单的后台管理,包括登录验证、菜单导航、列表、增删改查,菜单/按钮的权限控制等一些常见功能。当然网上也有很多例子了,只是想自己敲一遍代码。一直从事后台开发,公司都是有专门的前端设计及开发的,所以本人的js/css基础非常差,只能随意弄弄了。

     网上找了下,这篇博文给的例子非常不错http://www.cnblogs.com/fhen/p/6721930.html,也主要参考了下界面的实现,他的数据交互模拟是用了mock.js,本人作为后台开发专业程序员,所以学习过程中,会使用spring boot快速创建一些api支持。


项目搭建  


    使用vue-cli创建项目,准备工作及创建方法就不多说了,前面有相关博文了http://blog.csdn.net/dream_broken/article/details/73293391


   创建个项目admin-demo-01,这是分步骤学习的,01是登录,后面还有02,03....慢慢的一点点功能实现。

   看package.json


已经默认有vue,vue-router了,由于要使用axios作为和后台的交互(就像jquery的ajax),element-ui作为控件,所以需要安装axios/element-ui,执行命令

cnpm install axios --save

cnpm install element-ui --save


然后初始化下把整个项目都初始化npm install,然后运行起来cnpm run dev,浏览器访问



登录界面

   新创建的项目,运行后访问看到就是上面的那图,我们需要去掉它,同时显示我们的登录页面,打开App.vue,把img那去掉。


main.js修改为

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'


Vue.config.productionTip = false
Vue.use(ElementUI)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
    el: '#app',
    router,
    template: '<App/>',
    components: { App }
})

接着,在components下创建一个Login.vue.

<template>
  <div>
    <h1>{{ msg }}</h1>
  </div>
</template>

<script>
export default {
  name: '登录',
  data () {
    return {
      msg: 'welcome login'
    }
  }
}
</script>

router下的index.js初始是这样的

import Vue from 'vue'
import Router from 'vue-router'
import Hello from '@/components/Hello'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'Hello',
      component: Hello
    }
  ]
})
它默认指向components/Hello,那我们修改下

import Vue from 'vue'
import Router from 'vue-router'

// 懒加载方式,当路由被访问的时候才加载对应组件
const Login = resolve => require(['@/components/Login'], resolve)

Vue.use(Router)

export default new Router({
    routes: [{
        path: '/',
        name: '登录',
        component: Login
    }]
})
页面就刷新如下图了


  那接下来就改造登录页:账号输入框、密码输入框、登录按钮,Login.vue修改如下

<template>
  <el-form ref="AccountFrom" :model="account" :rules="rules" label-position="left" label-width="0px"
           class="demo-ruleForm login-container">
    <h3 class="title">系统登录</h3>
    <el-form-item prop="username">
      <el-input type="text" v-model="account.username" auto-complete="off" placeholder="账号"></el-input>
    </el-form-item>
    <el-form-item prop="pwd">
      <el-input type="password" v-model="account.pwd" auto-complete="off" placeholder="密码"></el-input>
    </el-form-item>
    <el-checkbox v-model="checked" checked class="remember">记住密码</el-checkbox>
    <el-form-item style="width:100%;">
      <el-button type="primary" style="width:100%;" >登录</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
export default {
  name: '登录',
  data () {
    return  {
        logining: false,
        account: {
          username: '',
          pwd: ''
        },
        rules: {
          username: [
            {required: true, message: '请输入账号', trigger: 'blur'},
            //{ validator: validaePass }
          ],
          pwd: [
            {required: true, message: '请输入密码', trigger: 'blur'},
            //{ validator: validaePass2 }
          ]
        },
        checked: true
      };
  }
}
</script>


<style>
  body{
    background: #DFE9FB;
  }
  .login-container{
    width:350px;
    margin-left:35%;
  }
</style>
页面刷新



 这时点击登录还没有交互能力的。

axios和mock.js实现交互

  上面说了axios类似jquery的ajax。在src下建立个api文件夹,然后建立个api.js


import axios from 'axios'

axios.defaults.baseURL = 'http://127.0.0.1:80';


export const requestLogin = params => { return axios.post('/user/login', params).then(res => res.data) }


然后再建立个index.js

import * as api from './api'

export default api

这是向后台发起请求/user/login,以post的方式。但是我们还没做后台服务。那这时可以使用mock.js进行拦截,然后模拟后台服务返回的数据。

mock模式

   在src目录下创建文件夹mock,创建文件index.js

import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'

import { LoginUsers, users } from './data/user'

export default {

    init() {
        let mock = new MockAdapter(axios)
            // mock success request
        mock.onGet('/success').reply(200, {
            msg: 'success'
        })

        // mock error request
        mock.onGet('/error').reply(500, {
            msg: 'failure'
        })

        // 登录
        mock.onPost('/user/login').reply(arg => {
            let { username, password } = JSON.parse(arg.data)
            return new Promise((resolve, reject) => {
                let token = null


                let hasUser = LoginUsers.some(u => {
                    if (u.username === username && u.password === password) {
                        token = 'adminXXXXXX'
                        return true
                    }
                })

                if (hasUser) {
                    resolve([200, { code: 200, msg: '请求成功', token: token }])
                } else {
                    resolve([200, { code: 500, msg: '账号或密码错误' }])
                }

            })
        })



    }

}


mock下建立data文件夹,data文件夹下创建user.js

/**
 * 用来模拟用户的一些信息
 */
import Mock from 'mockjs'
const LoginUsers = [{
    id: 1,
    username: 'admin',
    password: '123456',
    email: '[email protected]',
    name: '程序员'
}]

export { LoginUsers, users }

Login.vue添加登录方法


<template>
  <el-form ref="AccountFrom" :model="account" :rules="rules" label-position="left" label-width="0px"
           class="demo-ruleForm login-container">
    <h3 class="title">系统登录</h3>
    <el-form-item prop="username">
      <el-input type="text" v-model="account.username" auto-complete="off" placeholder="账号"></el-input>
    </el-form-item>
    <el-form-item prop="pwd">
      <el-input type="password" v-model="account.pwd" auto-complete="off" placeholder="密码"></el-input>
    </el-form-item>
    <el-checkbox v-model="checked" checked class="remember">记住密码</el-checkbox>
    <el-form-item style="width:100%;">
      <el-button type="primary" style="width:100%;" @click.native.prevent="handleLogin" :loading="logining" >登录</el-button>
    </el-form-item>
  </el-form>
</template>

<script>
  import {requestLogin} from '../api/api';
  //import NProgress from 'nprogress'
  export default {
    data() {
      return {
        logining: false,
        account: {
          username: 'admin',
          pwd: '123456'
        },
        rules: {
          username: [
            {required: true, message: '请输入账号', trigger: 'blur'},
            //{ validator: validaePass }
          ],
          pwd: [
            {required: true, message: '请输入密码', trigger: 'blur'},
            //{ validator: validaePass2 }
          ]
        },
        checked: true
      };
    },
    methods: {
      handleLogin() {
        this.$refs.AccountFrom.validate((valid) => {
          if (valid) {

            this.logining = true;
            //NProgress.start();
            var loginParams = { username: this.account.username, password: this.account.pwd };
          //    let loginParams = new URLSearchParams();
          //    loginParams.append("username",this.account.username);
           //   loginParams.append("password",this.account.pwd);
            requestLogin(loginParams).then(data => {
             debugger;
              this.logining = false;
              
              let { msg, code, token } = data;
              if(code == '200'){
                //登录成功,把用户信息保存在sessionStorage中
                sessionStorage.setItem('access-token', token);
                //跳转到后台主界面
                this.$router.push({ path: '/home' });
              }else{
                this.$message({
                  message: msg,
                  type: 'error'
                });
              }
              
            });

          } else {
            console.log('error submit!!');
            return false;
          }
        });
      }
    }
  }

</script>


<style>
  body{
    background: #DFE9FB;
  }
  .login-container{
    width:350px;
    margin-left:35%;
  }
</style>


看到登录成功后,是要跳到后台主界面Home的,所以创建个Home.vue

<template>
  <div >
    <h1>{{ msg }}</h1>
   
  </div>
</template>

<script>
export default {
  name: '后台主界面',
  data () {
    return {
      msg: '后台主界面'
    }
  }
}
</script>


router下的index.js那也要修改下了

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'
//import Login from '@/components/Login'
// 懒加载方式,当路由被访问的时候才加载对应组件
const Login = resolve => require(['@/components/Login'], resolve)

Vue.use(Router)

let router = new Router({
    routes: [{
            path: '/',
            name: '登录',
            component: Login
        }, {
            path: '/login',
            name: '登录',
            component: Login
        },
        {
            path: '/home',
            name: '后台主界面',
            component: Home
        }

    ]
})

// 访问之前,都检查下是否登录了
router.beforeEach((to, from, next) => {
    // console.log('to:' + to.path)
    if (to.path.startsWith('/login')) {
        window.sessionStorage.removeItem('access-token')
        next()
    } else {
        let token = window.sessionStorage.getItem('access-token')
        if (!token) {
            next({ path: '/login' })
        } else {
            next()
        }
    }
})

export default router

在main.js中引入mock

// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

import ElementUI from 'element-ui'
import 'element-ui/lib/theme-default/index.css'

import Mock from './mock'
Mock.init()

Vue.config.productionTip = false
Vue.use(ElementUI)

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
    el: '#app',
    router,
    template: '<App/>',
    components: { App }
})

初始化mockjs,axios-mock-adapter

cnpm install mockjs --save

cnpm axios-mock-adapter

最后运行cnpm run dev的时候报错

babel-runtime/core-js/json/stringify in ./src/mock/index.js, ./~/[email protected]@babel-loader/lib!./~/[email protected]@vue-loader/lib/selector.js?typ
e=script&index=0!./src/components/Login.vue


   是使用了JSON引起的,安装提示执行cnpm install babel-runtime --save

最后在package.json中看到的样子


src下的文件结构



运行起来cnpm run dev

出现登录界面,如果如果的用户不正确(不再mock/data/user.js的LoginUsers中,则会提示账号或密码错误


如果正确,就会进入后台主界面,如果没登录,直接访问主界面的url  http://localhost:8080/home,则会自动刷新到登陆页面

spring boot搭建后台

  上面的代码,当登录的时候,发起登录请求/user/login的时候,被axios-mock-adapter拦截了,并没有正真和后台交互,这种模式适合纯粹的前端开发阶段,但最后还是要和后台联调的。

main.js中去掉

//import Mock from './mock'
//Mock.init()
再次登陆的时候,就无法登录了,看浏览器控制台就看到404了


现在使用eclipse写个后台的用户管理user-server,使用spring boot快速搭建。



UserController.java

package com.fei.springboot.controller;

import javax.servlet.http.HttpServletRequest;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.fei.springboot.controller.util.TokenUtil;

@RestController
@RequestMapping("/user")
public class UserController {

	private static Logger log = LoggerFactory.getLogger(UserController.class);
	
	@CrossOrigin(origins="*")//允许跨域请求
	@RequestMapping(value="/login",method=RequestMethod.POST)
	public JSONObject login(String username,String password,HttpServletRequest request){
		log.info("登录请求...username="+username+"  pwd=" + password);
		JSONObject r = new JSONObject();
		if("admin".equals(username) && "123456".equals(password))	{
			r.put("code", "200");
			r.put("msg", "登录成功");
			r.put("token", TokenUtil.getToken(username));
		}else{
			r.put("code", "500");
			r.put("msg", "登录失败");
		}
		return r;
	}
	
}

其他文件代码,看github上完整代码吧 https://github.com/269941633/vue.js2.X-elementUI

好了,一切准备就绪了,点击“登录”,但是提示登录失败了,看后台控制台日志,接收到的用户名和密码是null。。。这是怎么回事?使用postman工具测试下接口,访问是OK的。。。赶紧百度找原因,在这篇博文中,解说得非常详细http://www.jianshu.com/p/042632dec9fb,原因找到了,解决方法有3中。按照那博文的说法,原因就是axios判断参数是object对象时,header中的Content-type是application/json;charset=UTF-8,而springMVC是默认application/x-www-form-urlencoded;charset=UTF-8,平时我们用jquery的ajax的时候,默认就是application/x-www-form-urlencoded;charset=UTF-8了,和后台匹配,所以我们都不需要额外做处理。看下axios.js的源码


如果参数是Object,而且headers中没有设置contentType,那就默认'application/json;charset=utf-8',把Object转为json字符串,那也就是说,如果使用axios.post的时候指定了headers的content为application/x-www-form-urlencoded;charset=UTF-8就可以了呢?试了下
api.js修改前

export const requestLogin = params => { return axios.post('/user/login', params).then(res => res.data) }
发起请求


后台接收不到数据,修改api.js

let config = { headers: { 'content-type': 'application/x-www-form-urlencoded;charset=UTF-8' } }
export const requestLogin = params => { return axios.post('/user/login', params, config).then(res => res.data) }


request headers 中的content-type的确是变了,但是后台仍然没接收到数据。用postman测试


成功,查看发送的信息


发现是参数,axios是json字符串,而postman那是&拼接起来的。

那只能要么后台接收参数的时候使用@RequestBody,要么前台使用URLSearchParams,

试试URLSearchParams,Login.vue,修改下

// var loginParams = { username: this.account.username, password: this.account.pwd };
              let loginParams = new URLSearchParams();
              loginParams.append("username",this.account.username);
              loginParams.append("password",this.account.pwd);
googel浏览器OK,但是360浏览器却报Uncaught ReferenceError: URLSearchParams is not defined,网上的一些说法是 URLSearchParams,并不是所有浏览器都支持的。

   修改后台UserController.java

package com.fei.springboot.controller;

import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import com.alibaba.fastjson.JSONObject;
import com.fei.springboot.controller.util.TokenUtil;

@RestController
@RequestMapping("/user")
public class UserController {

	private static Logger log = LoggerFactory.getLogger(UserController.class);
	
	@CrossOrigin(origins="*")//允许跨域请求
	@RequestMapping(value="/login",method=RequestMethod.POST)
	public JSONObject login(@RequestBody Map<String,String> map){
		String username = map.get("username");
		String password = map.get("password");
		
		log.info("登录请求...username="+username+"  pwd=" + password);
		
		JSONObject r = new JSONObject();
		if("admin".equals(username) && "123456".equals(password))	{
			r.put("code", "200");
			r.put("msg", "登录成功");
			r.put("token", TokenUtil.getToken(username));
		}else{
			r.put("code", "500");
			r.put("msg", "登录失败");
		}
		return r;
	}
	
}

这样,后台成功接收到axios传的参数了。


完整源码






猜你喜欢

转载自blog.csdn.net/dream_broken/article/details/77451639