使用vueAdmin开发后台管理系统(登录篇)

本文介绍了如何使用vue-element-admin开发在线考试系统教师端的流程,模板的下载过程略过,项目部署后的目录结构如图所示


image.png

我自己的前端学的不怎么样,可以说是个半吊子,只能粗略的用一下,文章主要是针对那些对后端比较了解,但是前端了解的比较少,只需要会用就行的人,帮你们少走点弯路,都是自己踩了很多坑走过来的。

前面几段是根据我自己的业务需求配置的,如果想要看登录的实现具体过程可以从登录页面初步修改章节看起

修改访问端口

项目文件如图所示,在vue.config文件中存放了配置文件,由于本项目所有前端页面都使用nginx为入口,所以需要配置项目的端口号,在该文件中找到port的配置信息,修改为3001

const port = process.env.port || process.env.npm_config_port || 3001 // dev port

跨域问题的解决

因为本地使用了域名模拟,所以需要解决跨域问题,在配置文件中修改如下信息


image.png

本项目后端使用了微服务的技术,要在网关中也允许跨域


image.png

通过teacher.exam.com域名访问后一直在请求info?t=1585471430974这个地址,具体原因可以自行百度,这里只提供了解决方案找到node_modules里的sockjs文件,将1605行注释


image.png

登录页面初步修改

具体的路由还有目录结构请自行查看官方网站,这里只截取需要更改的地方

登录页面对username进行了验证,这里我们需要改成自己的逻辑,本项目的用户名并没有限制,所以只进行了是否为空的验证

const validateUsername = (rule, value, callback) => {
       if (value=='') {
        callback(new Error('请输入用户名'))
      } else {
        callback()
      }
    }
    const validatePassword = (rule, value, callback) => {
      if (value.length < 6) {
        callback(new Error('密码不能小于6位'))
      } else {
        callback()
      }
    }

登录的提交方法代码如下,这里建议尽量不要修改他默认的代码,感觉作者在登录方面写的文档对于新手不太友好,也没有讲的非常清楚

这里登录的流程其实是对token进行了验证,在登录后,服务端会返回一个token,然后会自动调用getInfo方法获取用户的信息,主要是this.$store.dispatch('user/login', this.loginForm)这行代码,映射了多个文件,需要了解逻辑的同学可以参考这篇文章https://www.jianshu.com/p/29c5b8cd593e

handleLogin() {
      this.$refs.loginForm.validate(valid => {
        if (valid) {
          this.loading = true
          this.$store.dispatch('user/login', this.loginForm).then(() => {
            this.$router.push({ path: this.redirect || '/' })
            this.loading = false
          }).catch(() => {
            this.loading = false
          })
        } else {
          console.log('error submit!!')
          return false
        }
      })
    }

登录页面如下图所示


image.png

登录页面html模板代码

<template>
  <div class="login-container">
    <el-form ref="loginForm" :model="loginForm" :rules="loginRules" class="login-form" auto-complete="on" label-position="left">

      <div class="title-container">
        <h3 class="title">你的系统名称</h3>
      </div>

      <el-form-item prop="username">
        <span class="svg-container">
          <svg-icon icon-class="user" />
        </span>
        <el-input
          ref="username"
          v-model="loginForm.username"
          placeholder="请输入用户名"
          name="username"
          type="text"
          tabindex="1"
          auto-complete="on"
        />
      </el-form-item>

      <el-form-item prop="password">
        <span class="svg-container">
          <svg-icon icon-class="password" />
        </span>
        <el-input
          :key="passwordType"
          ref="password"
          v-model="loginForm.password"
          :type="passwordType"
          placeholder="请输入密码"
          name="password"
          tabindex="2"
          auto-complete="on"
          @keyup.enter.native="handleLogin"
        />
        <span class="show-pwd" @click="showPwd">
          <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
        </span>
      </el-form-item>

      <el-button :loading="loading" type="primary" style="width:100%;margin-bottom:30px;" @click.native.prevent="handleLogin">登录</el-button>



    </el-form>
  </div>
</template>

默认登录流程分析

这里我们只看默认情况下,是如何进行登录的,这里因为代码我已经是改好的,所以简单粗暴,只看他请求完返回了什么数据

1.第一个请求是获取token,请求路径也不再展示,可以看到在response的data中,是一个键值对形式的值,key为token,value是可以自定义的


image.png

2.第二个请求是根据token拿到用户的信息,这里面的数据比较多,还是Map形式的data数据,存放了roles,introduction,avtar,name,分别是用户的权限,用户的介绍,用户的头像和名称


image.png

知道了要返回什么数据,我们就可以动手写后台代码了,但是请求路径还是需要更改的,这里其实最后是调用的api下的user文件,前面放的那篇大佬的文章也有写为什么是这个文件,可以自己去看,我这里主要重前后端交互实操


image.png

修改user.js

export function login(data) {
  return request({
    url: 'login',
    method: 'post',
    data,
    baseURL:'http://api.exam.com/api/auth/'
  })
}

这里说明一下,method指定的是请求的方式,data为post请求的携带信息,我这里添加了baseURL是后端业务需求,可以自己定义,这样完整的请求路径是http://api.exam.com/api/auth/login

export function getInfo(token) {
  return request({
    url: 'info',
    method: 'get',
    params: { token },
    baseURL:'http://api.exam.com/api/auth/'
  })
}

这里params是get请求携带的参数,会放在url后面,这里基本上都是默认的代码,建议不要修改他的代码,后端根据这个来写就好

后台代码编写

这里直接上controller的代码

    @PostMapping("login")
    @ApiOperation(value="登录", notes="登录")
    public Request login(@RequestBody Map<String,String> data,
                         HttpServletRequest request,
                         HttpServletResponse response) {
        String username = data.get("username");
        String password = data.get("password");

        //登录
        String token = this.authService.login(username, password);
        if (StringUtils.isBlank(token)) {
            System.out.println("没有token");
            return new Request(false,400,"登录失败");
        }
        //CookieUtils.setCookie(request,response,cookieName,token,60*60,false);
        Map<String,Object> tokenMap = new HashMap<>();
        tokenMap.put("token",token);
        return new Request(true,20000,"登录成功",tokenMap);
    }

CookieUtils.setCookie(request,response,cookieName,token,60*60,false);这个可以不需要,因为我是别的业务也是用这个来登录的,直接在后端给Cookie赋值的,这个前端项目是通过服务端生成token之后返回给他,然后由他来做Cookie的生成

authService.login其实里面的业务逻辑也很简单,就是做一个简单的校验,然后生成一个token返回,这个需要看自己的业务需求进行修改

public String login(String username, String password){

            try {
                //校验用户名和密码
                User user = userClient.queryUser(username, password);
                if (user == null){
                    System.out.println("用户账号密码错误");
                }
                //生成token
                String token = JwtUtils.generateToken(new UserInfo(user.getId(), username,user.getRole()), prop.getPrivateKey(), prop.getExpire());

                return token;
            }catch (Exception e){
                e.printStackTrace();
            }
        return null;
    }

注:特别注意!!!!,这里返回code值必须是20000,我一开始返回200,一直纳闷明明已经获取数据成功了,还不会跳转,就是因为前端不知道哪里进行了控制,20000是请求成功的返回码,这里是自己太菜了,也不会改,如果有大佬知道在哪里修改,可以给我留言谢谢。其实看默认的执行流程就应该想到,如果跟着默认的流程修改,返回的数据肯定要是相似的,特别是状态码跟数据。

这里其实就基本成功了,看一下请求返回的数据,我自己增加了一个message和flag用来做请求的友好提示,这个Request类是自己封装的,可以根据自己需要,但是结构一定要跟默认流程保持一致


image.png

但是这样还不能进行跳转,还缺了获取用户信息的后端代码,这里其实是对token进行了解析,然后返回一个用户实体信息

    @GetMapping("info")
    public Request getInfo(@RequestParam("token") String token){
        try {
            //解析token
            UserInfo info = JwtUtils.getInfoFromToken(token,prop.getPublicKey());
            return new Request(true,20000,"已登录",info);
        }catch (Exception e){
            return new Request(false,202,"token已过期");
        }
    }

UserInfo类

public class UserInfo {
    private Long id;
    private String username;
    private int role;
}

这样就可以顺利登陆了,看一下实际返回给前端的信息


image.png

解决首页没有name值

如果跟着我的代码写,到刚刚的地方登陆成功后,会发现首页的name没有值,这里牵涉到vuex的一些知识,由于我也不懂,所以只提供解决方案

image.png

打开store文件夹下的user文件,找到getInfo方法

getInfo({ commit, state }) {
    return new Promise((resolve, reject) => {
      getInfo(state.token).then(response => {
        const { data } = response

        if (!data) {
          reject('Verification failed, please Login again.')
        }

        const { name, avatar } = data

        commit('SET_NAME', name)
        commit('SET_AVATAR', avatar)
        resolve(data)
      }).catch(error => {
        reject(error)
      })
    })
  },

稍微有点经验的人应该都能发现,这里其实我们的data里的数据跟默认的还是有区别的,我是根据自己业务需求定义的
const { name, avatar } = data
这句代码其实跟我的数据是不匹配的,我的data中存放的是username,id,role,所以这样提取肯定是会有问题的
修改为,其实这里我也不需要role,但是我会需要用户的id,修改后代码如下
const { username, id } = data

然后不难发现下面的两句代码我们也需要修改,这里我不讲原理,只讲实现

commit('SET_NAME', username)
commit('SET_ID', id)

修改完成后还是会有问题,还需要在文件最上方的代码进行修改,就是这一段

const mutations = {
  RESET_STATE: (state) => {
    Object.assign(state, getDefaultState())
  },
  SET_TOKEN: (state, token) => {
    state.token = token
  },
  SET_NAME: (state, name) => {
    state.name = name
  },
  SET_AVATAR: (state, avatar) => {
    state.avatar = avatar
  }
}

看到第三行,我们应该就大概知道这其实就是在赋值,通过commit去传递值,大概就是这样,这里我添加了自己需要的id

  SET_ID: (state, id) => {
    state.id = id
  },

设置完之后,我们找到主页的index文件,小小的修改一下


image.png

这样自己可以先试一下,其实name出来了,id还是没有出来,我们就可以继续找原因,然后就发现了...mapGetters这个不知道是什么东西啊,但是好像在哪里见过

image.png

这里我们发现了getters文件,打开看看,豁然开朗

const getters = {
  sidebar: state => state.app.sidebar,
  device: state => state.app.device,
  token: state => state.user.token,
  avatar: state => state.user.avatar,
  name: state => state.user.name,
}
export default getters

将id加上 id: state => state.user.id大功告成

image.png
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • feisky云计算、虚拟化与Linux技术笔记posts - 1014, comments - 298, trac...
    不排版阅读 3,941评论 0 5
  • 概要 64学时 3.5学分 章节安排 电子商务网站概况 HTML5+CSS3 JavaScript Node 电子...
    阿啊阿吖丁阅读 9,395评论 0 3
  • 面试题一:https://github.com/jimuyouyou/node-interview-questio...
    R_X阅读 1,664评论 0 5
  • 人生中,观众向来比朋友多。 观众只会让人从视觉上舒服,朋友却会让你内心感动。 朋友不是天天见面,吃喝玩乐、相互吹捧...
    虎妞_b272阅读 274评论 0 1
  • 文|付佳武 秋天的雨夜感觉格外的冰冷,于是我加了一床被子,卧在被窝里听着中国之声,将自己置于悲情中写心底的文字,好...
    牧童笔录阅读 523评论 0 0