我是靠谱客的博主 阳光香菇,这篇文章主要介绍人力资源中台项目(day02),现在分享给大家,希望可以做个参考。

登录表单的校验

目标对登录表单进行规则校验

基础模板已经有了基础校验的代码,所以我们这一章节更多的是修正和完善

el-form表单校验的先决条件

手机号和密码的校验

字段名对应

为什么要对应? 因为基础模板采用的是username的字段,但是实际接口中采用的是mobile的字段,为了更方便的写代码,所以我们将username改成mobile

这里除了字段名,还有我们的规则校验名称,以及prop名称。

英文提示变成中文

基础模板中都是placeHolder占位符是英文,要变成中文

登录按钮文字同样需要换成中文

校验手机号和校验密码

基础模板中,已经做了校验,我们针对代码进行一些优化

新规则:手机号必填,并且进行格式校验,密码必填,长度6-16位之间

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
data() {    // 自定义校验函数    const validateMobile = function(rule, value, callback) {      // 校验value      // if (validMobile(value)) {      //   // 如果通过 直接执行callback      //   callback()      // } else {      //   callback(new Error('手机号格式不正确'))      // }      validMobile(value) ? callback() : callback(new Error('手机号格式不正确'))   } ​    return {      loginForm: {        mobile: '13800000002',        password: '123456'     },      loginRules: {        mobile: [{ required: true, trigger: 'blur', message: '手机号不能为空' }, {          validator: validateMobile, trigger: 'blur'       }],        password: [{ required: true, trigger: 'blur', message: '密码不能为空' }, {          min: 6, max: 16, message: '密码的长度在6-16位之间 ', trigger: 'blur'       }]     },      loading: false,      passwordType: 'password',      redirect: undefined   } },

我们在utils/validate.js方法中增加了一个校验手机号的方法

复制代码
1
2
3
4
5
6
/** * 校验手机号 * **/ export function validMobile(str) {  return /^1[3-9]d{9}$/.test(str) // 校验手机号 }

utils/validate.js是一个专门存放校验工具方法的文件

关于修饰符

关于修饰符

在该页面中,我们发现了事件的几个修饰符 @keyup.enter.native @click.native.prevent

@keyup.enter属于按键修饰符,如果我们想监听在按回车键的时候触发,可以如下编写

复制代码
1
2
<!-- 只有在 `key` 是 `Enter` 时调用 `vm.submit()` --> <input v-on:keyup.enter="submit">

@keyup.enter.native 表示监听组件的原生事件,比如 keyup就是于input的原生事件,这里写native表示keyup是一个原生事件

提交代码

本节任务:实现登录用户的手机号和密码校验

Vue-Cli配置跨域代理

目标: 通过配置vue-cli的代理解决跨域访问的问题

为什么会出现跨域?

当下,最流行的就是前后分离项目,也就是前端项目后端接口并不在一个域名之下,那么前端项目访问后端接口必然存在跨域的行为.

怎么解决这种跨域 ?

请注意,我们所遇到的这种跨域是位于开发环境的,真正部署上线时的跨域是生产环境

解决开发环境的跨域问题

开发环境的跨域

开发环境的跨域,也就是在vue-cli脚手架环境下开发启动服务时,我们访问接口所遇到的跨域问题,vue-cli为我们在本地开启了一个服务,可以通过这个服务帮我们代理请求,解决跨域问题

这就是vue-cli配置webpack的反向代理

采用vue-cli的代理配置

vue-cli的配置文件即vue.config.js,这里有我们需要的 代理选项

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
module.exports = {  devServer: {   // 代理配置    proxy: {        // 这里的api 表示如果我们的请求地址有/api的时候,就出触发代理机制        // localhost:8888/api/abc => 代理给另一个服务器        // 本地的前端 =》 本地的后端 =》 代理我们向另一个服务器发请求 (行得通)        // 本地的前端 =》 另外一个服务器发请求 (跨域 行不通)        '/api': {        target: 'www.baidu.com', // 我们要代理的地址        changeOrigin: true, // 是否跨域 需要设置此值为true 才可以让本地服务代理我们发出请求         // 路径重写        pathRewrite: {            // 重新路由 localhost:8888/api/login => www.baidu.com/api/login            '^/api': '' // 假设我们想把 localhost:8888/api/login 变成www.baidu.com/login 就需要这么做       }     },   } } }

以上就是我们在vue-cli项目中配置的代理设置

接下来,我们在代码中将要代理的后端地址变成 后端接口地址

复制代码
1
2
3
4
5
6
7
8
9
10
// 代理跨域的配置    proxy: {      // 当我们的本地的请求 有/api的时候,就会代理我们的请求地址向另外一个服务器发出请求      '/api': {        target: 'http://ihrm-java.itheima.net/', // 跨域请求的地址        changeOrigin: true // 只有这个值为true的情况下 才表示开启跨域     }   }

本节注意:我们并没有进行pathRewrite,因为后端接口就是ihrm-java.itheima.net/api这种格式,所以不需要重写

vue.config.js的改动如果要生效,需要进行重启服务

同时,还需要注意的是,我们同时需要注释掉 mock的加载,因为mock-server会导致代理服务的异常

复制代码
1
2
// before: require('./mock/mock-server.js'), // 注释mock-server加载

生产环境的跨域

生产环境表示我们已经开发完成项目,将项目部署到了服务器上,这时已经没有了vue-cli脚手架的辅助了,我们只是把打包好的html+js+css交付运维人员,放到Nginx服务器而已,所以此时需要借助Nginx的反向代理来进行

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server{    # 监听9099端口   listen 9099;    # 本地的域名是localhost   server_name localhost;    #凡是localhost:9099/api这个样子的,都转发到真正的服务端地址http://baidu.com   location ^~ /api {       proxy_pass http://baidu.com;   }     }

注意:这里的操作一般由运维人员完成,需要前端进行操作,这里我们进行一下简单了解

更多正向代理和反向代理知识,请阅读这篇文章Nginx反向代理

提交代码

本节注意:我们并没有进行pathRewrite,因为后端接口就是ihrm-java.itheima.net/api这种格式,所以不需要重写

本节任务: 配置vue-cli的反向代理,实现后端接口的跨域访问

封装单独的登录接口

目标 在单独请求模块中,单独封装登录接口

完成登录模块之后,我们需要对登录接口进行封装

首先,查阅接口文档中的登录接口

基础模板已经有了原来的登录代码,我们只需要进行简单的改造即可

复制代码
1
2
3
4
5
6
7
8
9
export function login(data) {  // 返回一个axios对象 => promise // 返回了一个promise对象  return request({    url: '/sys/login', // 因为所有的接口都要跨域 表示所有的接口要带 /api    method: 'post',    data }) }

如图

提交代码

本节任务:封装单独的登录接口

封装Vuex的登录Action并处理token

目标在vuex中封装登录的action,并处理token

在这个小节中,我们将在vuex中加入对于用户的登录的处理

在Vuex中对token进行管理

在传统模式中,我们登录的逻辑很简单,如图

上图中,组件直接和接口打交道,这并没有什么问题,但是对于用户token这一高频使用的钥匙,我们需要让vuex来介入,将用户的token状态共享,更方便的读取,如图

实现store/modules/user.js基本配置

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
// 状态 const state = {} // 修改状态 const mutations = {} // 执行异步 const actions = {} export default {  namespaced: true,  state,  mutations,  actions } ​

设置token的共享状态

复制代码
1
2
3
const state = {  token: null }

我们需要知道,钥匙不能每次都通过登录获取,我们可以将token放置到本地的缓存中

utils/auth.js中,基础模板已经为我们提供了获取token,设置token,删除token的方法,可以直接使用

只需要将存储的key放置成特定值即可

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import Cookies from 'js-cookie' ​ const TokenKey = 'hrsaas-ihrm-token' // 设定一个独一无二的key ​ export function getToken() {  return Cookies.get(TokenKey) } ​ export function setToken(token) {  return Cookies.set(TokenKey, token) } ​ export function removeToken() {  return Cookies.remove(TokenKey) } ​

初始化token状态 - store/modules/user.js

复制代码
1
2
3
4
5
6
7
import { getToken, setToken, removeToken } from '@/utils/auth' // 状态 // 初始化的时候从缓存中读取状态 并赋值到初始化的状态上 // Vuex的持久化 如何实现 ? Vuex和前端缓存相结合 const state = {  token: getToken() // 设置token初始状态   token持久化 => 放到缓存中 }

提供修改token的mutations

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 修改状态 const mutations = {  // 设置token  setToken(state, token) {    state.token = token // 设置token 只是修改state的数据 123 =》 1234    // vuex变化 => 缓存数据    setToken(token) // vuex和 缓存数据的同步 },  // 删除缓存  removeToken(state) {    state.token = null // 删除vuex的token    removeToken() // 先清除 vuex 再清除缓存 vuex和 缓存数据的同步 } }

封装登录的Action

封装登录的action

登录action要做的事情,调用登录接口,成功后设置token到vuex,失败则返回失败

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 执行异步 const actions = {  // 定义login action 也需要参数 调用action时 传递过来的参数  async login(context, data) {    const result = await login(data) // 实际上就是一个promise result就是执行的结果    // axios默认给数据加了一层data    if (result.data.success) {      // 表示登录接口调用成功 也就是意味着你的用户名和密码是正确的      // 现在有用户token      // actions 修改state 必须通过mutations      context.commit('setToken', result.data.data)   } } }

上述代码中,我们使用了async/await语法,如果用then语法也是可以的

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 为什么async/await 不用返回new Promise,因为 async函数本身就是 Promise,promise的值返回的值   login(context, data) {    return new Promise(function(resolve) {      login(data).then(result => {        if (result.data.success) {          context.commit('setToken',  result.data.data) // 提交mutations设置token          resolve()  // 表示执行成功了       }     })   }) }

以上两种写法都是OK的,我们在项目研发过程中,尽可能的采用前一种

除此之外,为了更好的让其他模块和组件更好的获取token数据,我们可以在store/getters.js中将token值作为公共的访问属性放出

复制代码
1
2
3
4
5
6
7
const getters = {  sidebar: state => state.app.sidebar,  device: state => state.app.device,  token: state => state.user.token // 在根级的getters上 开发子模块的属性给别人看 给别人用 } export default getters ​

提交代码

通过本节内容,我们要掌握在Vuex中如何来管理共享状态

本节任务:封装Vuex的登录Action并处理token

request中环境变量和异常的处理

目标设置request环境变量和异常处理

区分axios在不同环境中的请求基础地址

为什么会有环境变量之分? 如图

从上图可以看出,开发环境实际上就是在自己的本地开发或者要求不那么高的环境,但是一旦进入生产,就是真实的数据。 拿银行作比喻,如果你在开发环境拿生产环境的接口做测试,银行系统就会发生很大的风险。

前端主要区分两个环境,开发环境,生产环境

也就是两个环境发出的请求地址是不同的,用什么区分呢?

环境变量

复制代码
1
$ process.env.NODE_ENV # 当为production时为生产环境 为development时为开发环境

环境文件

我们可以在.env.development.env.production定义变量,变量自动就为当前环境的值

基础模板在以上文件定义了变量VUE_APP_BASE_API,该变量可以作为axios请求的baseURL

我们会发现,在模板中,两个值分别为/dev-api/prod-api

但是我们的开发环境代理是/api,所以可以统一下

复制代码
1
2
3
# 开发环境的基础地址和代理对应 不需要引号 VUE_APP_BASE_API = /api  
复制代码
1
2
3
# 这里配置了/api,意味着需要在Nginx服务器上为该服务配置 nginx的反向代理对应/prod-api的地址   不需要引号 VUE_APP_BASE_API = /prod-api  

本节注意:我们这里生产环境和开发环境设置了不同的值,后续我们还会在生产环境部署的时候,去配置该值所对应的反向代理,反向代理指向哪个地址,完全由我们自己决定,不会和开发环境冲突

在request中设置baseUrl

复制代码
1
2
3
4
5
6
7
8
9
10
const service = axios.create({  // 如果执行 npm run dev 值为 /api 正确 /api 这个代理只是给开发环境配置的代理  // 如果执行 npm run build 值为 /prod-api 没关系 运维应该在上线的时候 给你配置上 /prod-api的代理  baseURL: process.env.VUE_APP_BASE_API, // 设置axios请求的基础的基础地址  timeout: 5000 // 定义5秒超时 }) // 创建一个axios的实例

处理axios的响应拦截器

OK,除此之外,axios返回的数据中默认增加了一层data的包裹,我们需要在这里处理下

并且,人资项目的接口,如果执行失败,只是设置了successfalse,并没有reject,我们需要一并处理下

处理逻辑如图

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 响应拦截器 service.interceptors.response.use(response => {  // axios默认加了一层data  const { success, message, data } = response.data  //   要根据success的成功与否决定下面的操作  if (success) {    return data } else {    // 业务已经错误了 还能进then ? 不能 ! 应该进catch    Message.error(message) // 提示错误消息    return Promise.reject(new Error(message)) } }, error => {  Message.error(error.message) // 提示错误信息  return Promise.reject(error) // 返回执行错误 让当前的执行链跳出成功 直接进入 catch })

既然在request中已经默认去除了一层data的外衣,所以我们也将上节login的action进行一下改动

处理登录的返回结构问题

复制代码
1
2
3
4
5
6
7
8
9
10
 async login(context, data) {    // 经过响应拦截器的处理之后 这里的result实际上就是 token    const result = await login(data) // 实际上就是一个promise result就是执行的结果    // axios默认给数据加了一层data    // 表示登录接口调用成功 也就是意味着你的用户名和密码是正确的    // 现在有用户token    // actions 修改state 必须通过mutations    context.commit('setToken', result) }

提交代码

本节任务: 完成request环境变量和异常的处理

登录页面调用登录action,处理异常

目标 调用vuex中的登录action,并跳转到主页

按照如图的业务逻辑,把剩下的内容在登录页面引入

引入actions辅助函数

复制代码
1
import { mapActions } from 'vuex'  // 引入vuex的辅助函数

引入action方法

此处,我们采用直接引入模块action的方式,后面我们采用分模块的引用方式

复制代码
1
2
3
methods: {    ...mapActions(['user/login']) }

调用登录

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
    this.$refs.loginForm.validate(async isOK => {        if (isOK) {          try {            this.loading = true            // 只有校验通过了 我们才去调用action            await this['user/login'](this.loginForm)            // 应该登录成功之后            // async标记的函数实际上一个promise对象            // await下面的代码 都是成功执行的代码            this.$router.push('/')         } catch (error) {            console.log(error)         } finally {            // 不论执行try 还是catch 都去关闭转圈            this.loading = false         }       }     })

提交代码

本节注意:我们调用的是Vuex中子模块的action,该模块我们进行了namespaced: true,所以引用aciton时需要带上user/, 并且在使用该方法时,直接使用 this['user/login'], 使用this.user/login 语法是错误的

本节任务:登录页面调用登录action,处理异常

主页的token拦截处理

目标:根据token处理主页的访问权限问题

权限拦截的流程图

我们已经完成了登录的过程,并且存储了token,但是此时主页并没有因为token的有无而被控制访问权限

接下来我们需要实现以下如下的流程图

在基础框架阶段,我们已经知道src/permission.js是专门处理路由权限的,所以我们在这里处理

流程图转化代码

流程图转化的代码

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 权限拦截 导航守卫 路由守卫 router import router from '@/router' // 引入路由实例 import store from '@/store' // 引入vuex store实例 import NProgress from 'nprogress' // 引入一份进度条插件 import 'nprogress/nprogress.css' // 引入进度条样式 ​ const whiteList = ['/login', '/404'] // 定义白名单 所有不受权限控制的页面 // 路由的前置守卫 router.beforeEach(function(to, from, next) {  NProgress.start() // 开启进度条  // 首先判断有无token  if (store.getters.token) {    //   如果有token 继续判断是不是去登录页    if (to.path === '/login') {      // 表示去的是登录页      next('/') // 跳到主页   } else {      next() // 直接放行   } } else {    // 如果没有token    if (whiteList.indexOf(to.path) > -1) {      // 如果找到了 表示在在名单里面      next()   } else {      next('/login') // 跳到登录页   } }  NProgress.done() // 手动强制关闭一次 为了解决 手动切换地址时 进度条的不关闭的问题 }) // 后置守卫 router.afterEach(function() {  NProgress.done() // 关闭进度条 }) ​

在导航守卫的位置,我们添加了NProgress的插件,可以完成进入时的进度条效果

提交代码

本节任务:完成主页中根据有无token,进行页面访问的处理

主页的左侧导航样式

目标设置左侧的导航样式

接下来我们需要将左侧导航设置成如图样式

主页的布局组件位置src/layout

主页布局架构

左侧导航组件的样式文件styles/siderbar.scss

设置背景渐变色

复制代码
1
2
3
.sidebar-container {      background: -webkit-linear-gradient(bottom, #3d6df8, #5b8cff); }

设置左侧导航背景图片

复制代码
1
2
3
.scrollbar-wrapper {    background: url('~@/assets/common/leftnavBg.png') no-repeat 0 100%; }

注意:在scss中,如果我们想要使用@别名,需要在前面加上一个~才可以

设置菜单选中颜色

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  .el-menu {     border: none;     height: 100%;     width: 100% !important;     a{       li{         .svg-icon{           color: #fff;           font-size: 18px;           vertical-align: middle;           .icon{             color:#fff;           }         }         span{           color: #fff;         }         &:hover{           .svg-icon{             color: #43a7fe           }           span{             color: #43a7fe;           }         }       }     }   }

注意:因为我们后期没有二级菜单,所以这里暂时不用对二级菜单的样式进行控制

显示左侧logo图片 src/setttings.js

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module.exports = { ​  title: '人力资源管理平台', ​  /**   * @type {boolean} true | false   * @description Whether fix the header   */  fixedHeader: false, ​  /**   * @type {boolean} true | false   * @description Whether show the logo in sidebar   */  sidebarLogo: true // 显示logo } ​

设置头部图片结构 src/layout/components/Sidebar/Logo.vue

复制代码
1
2
3
4
5
6
7
8
<div class="sidebar-logo-container" :class="{'collapse':collapse}">    <transition name="sidebarLogoFade">      <router-link key="collapse" class="sidebar-logo-link" to="/">        <img src="@/assets/common/logo.png" class="sidebar-logo ">      </router-link>    </transition>  </div>

设置大图和小图的样式

复制代码
1
2
3
4
5
6
7
8
9
&.collapse {    .sidebar-logo {      margin-right: 0px;      width: 32px;      height: 32px;   } } // 小图样式
复制代码
1
2
3
4
5
6
.sidebar-logo {      width: 140px;      vertical-align: middle;      margin-right: 12px; } // 大图样式

去除logo的背景色

提交代码

本节任务: 完成主页的左侧导航样式

本节注意:我们该项目中没有二级显示菜单,所以二级菜单的样式并没有做过多处理,同学们不必在意

设置头部内容的布局和样式

目标设置头部内容的布局和样式

我们需要把页面设置成如图样式

头部组件位置 layout/components/Navbar.vue

添加公司名称注释面包屑

复制代码
1
2
3
4
5
6
7
 <div class="app-breadcrumb">     江苏传智播客教育科技股份有限公司      <span class="breadBtn">体验版</span>  </div> <!-- <breadcrumb class="breadcrumb-container" /> --> ​

公司样式

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
.app-breadcrumb {  display: inline-block;  font-size: 18px;  line-height: 50px;  margin-left: 10px;  color: #ffffff;  cursor: text;  .breadBtn {    background: #84a9fe;    font-size: 14px;    padding: 0 10px;    display: inline-block;    height: 30px;    line-height: 30px;    border-radius: 10px;    margin-left: 15px; } }

头部背景渐变色

复制代码
1
2
3
.navbar {    background-image: -webkit-linear-gradient(left, #3d6df8, #5b8cff); }

汉堡组件图标颜色 src/components/Hamburger/index.vue

注意这里的图标我们使用了svg,设置颜色需要使用svg标签的fill属性

设置svg图标为白色

复制代码
1
2
3
4
5
6
7
8
9
<svg      :class="{'is-active':isActive}"      class="hamburger"      viewBox="0 0 1024 1024"      xmlns="http://www.w3.org/2000/svg"      width="64"      height="64"      fill="#fff"    >

右侧下拉菜单设置

将下拉菜单调节成首页/项目地址/退出登录

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
   <div class="right-menu">      <el-dropdown class="avatar-container" trigger="click">        <div class="avatar-wrapper">          <img src="@/assets/common/bigUserHeader.png" class="user-avatar">          <span class="name">管理员</span>          <i class="el-icon-caret-bottom" style="color:#fff" />        </div>        <el-dropdown-menu slot="dropdown" class="user-dropdown">          <router-link to="/">            <el-dropdown-item>             首页            </el-dropdown-item>          </router-link>          <a target="_blank" href="https://gitee.com/shuiruohanyu/hrsaas53">            <el-dropdown-item>项目地址</el-dropdown-item>          </a>          <el-dropdown-item divided @click.native="logout">            <span style="display:block;">退出登录</span>          </el-dropdown-item>        </el-dropdown-menu>      </el-dropdown>    </div>

头像和下拉菜单样式

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 .user-avatar {          cursor: pointer;          width: 30px;          height: 30px;          border-radius: 15px;          vertical-align: middle; ​   }   .name {          color: #fff;          vertical-align: middle;          margin-left:5px;   }   .user-dropdown {           color: #fff;   }

用户名和头像我们先用了假数据进行,下小章节,会进行这份数据的获取

​​​​​​​

最后

以上就是阳光香菇最近收集整理的关于人力资源中台项目(day02)的全部内容,更多相关人力资源中台项目(day02)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(74)

评论列表共有 0 条评论

立即
投稿
返回
顶部