• Vue2_人力资源管理系统项目笔记


    一、项目技术

    1、 技术栈

    基于vue-admin-template 后台管理系统二次开发

    ES6, vue, vuex, vue-router, vue-cli, axios, element-ui

    2、技术解决方案

    • RBAC 权限设计(RBAC: 基于角色的访问控制权限的基本模型)
    • 多语言
    • Excel 导入导出
    • 反向代理
    • 请求模块封装

    3、页面截图

    在这里插入图片描述

    二、环境搭建

    1、 从远程仓库克隆项目到本地

    // 从码云拉取代码 
    git clone https://gitee.com/panjiachen/vue-admin-template.git
    
    • 1
    • 2

    克隆之后,首先查看项目文件夹,了解各个文件的作用
    在这里插入图片描述

    2、启动项目

    每个项目文件夹下,有个package.json里的scripts,记录了只有当前文件夹下才能使用的npm run 自定义命令
    在这里插入图片描述
    我们的是dev

    npm run dev 
    
    • 1

    注意:① 一定要看好终端要在package.json所在文件夹(项目根目录文件)
    ② 启动项目的时候注意是否有第三方依赖包(node_modules)
    如果没有就npm i 先下载(npm i 的意思是:根据当前命令所在的文件夹下的package.json中记录的包名和版本,帮助我们全部下载到node_mudules内支撑我们整个项目的正常运行)

    // 建议不要用 cnpm 安装 会有各种诡异的bug 可以通过如下操作解决 npm 下载速度慢的问题
    npm install --registry=https://registry.npm.taobao.org
    
    • 1
    • 2

    启动成功截图
    在这里插入图片描述

    3、解读各个文件代码作用

    (看一个文件找相关联的文件,不要看的太深,最多看两层)

    文件名作用
    main.js入口文件,挂载路由,挂载vue-store、全局注册Element、App.vue根组件
    api 文件将所有的网络请求方法放在 api 目录下统一管理,按照模块来划分对应的文件
    assets 文件存放项目的静态图片
    components 文件封装的全局组件,可以等到用的时候再去分析
    Icons 文件字体图标
    layout是项目登录以后, 见到的布局的框, 左侧导航, 上部导航和下部的内容部分
    router/index.js路由相关的配置, 以及路由规则对象数组
    storevuex结构
    styles样式文件
    utils整个项目用到的工具, 封装起来集中管理, 逻辑页面需要的时候进行调用
    permission.js控制路由权限判断、登录和非登录的相关设置
    settings.js封装了全局的一些变量, 方便一键修改

    4、公共资源图片和统一样式

    将common文件夹的两个文件放到到styles目录下,然后在**index.scss**中引入该样式

    5、建立远程Git仓库并完成页面初始提交

    详细步骤:https://blog.csdn.net/Vest_er/article/details/127508844

    三、登录模块

    登录界面实现效果如图
    在这里插入图片描述

    1、设置固定的本地访问端口和网站名称

    在正式开发业务之前,先将项目的本地端口网站名称进行一下调整
    详细步骤:https://blog.csdn.net/Vest_er/article/details/127518687

    2、登录页面的基础布局

    ① 设置头部背景

    <div class="title-container">
           <h3 class="title">
             <img src="@/assets/common/login-logo.png" alt="">
           h3>
    div>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ② 设置背景图片

    如需要在样式表中使用@别名的时候,需要在@前面加上一个~符号,否则不识别

    /* reset element-ui css */
    .login-container {
      background-image: url('~@/assets/common/login.jpg'); // 设置背景图片
      background-position: center; // 将图片位置设置为充满整个屏幕
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    ③ 设置手机号和密码的字体颜色

    $light_gray: #68b0fe;  // 将输入框颜色改成蓝色
    
    • 1

    ④ 设置输入表单整体背景色

    .el-form-item {
      border: 1px solid rgba(255, 255, 255, 0.1);
      background: rgba(255, 255, 255, 0.7); // 输入登录表单的背景色
      border-radius: 5px;
      color: #454545;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ⑤ 设置错误信息的颜色

    .el-form-item__error {
      color: #fff
    }
    
    • 1
    • 2
    • 3

    ⑥ 设置登录按钮的样式

    .loginBtn {
      background: #407ffe;
      height: 64px;
      line-height: 32px;
      font-size: 24px;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    ⑦ 修改显示的提示文本和登录文本

    <div class="tips">
         <span style="margin-right:20px;">账号: 13800000002span>
         <span> 密码: 123456span>
    div>
    
    • 1
    • 2
    • 3
    • 4

    3、登录表单的校验

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

    表单校验的先决条件

    3个条件缺一不可(loginRules:trigger校验的触发方式 ,validator自定义函数)
    在这里插入图片描述

    ② 手机号和密码的校验

    字段名对应
    将username改为mobile,实际接口中采用的是mobile的字段,为了更方便的写代码,所以我们将username改成mobile
    校验手机号和校验密码
    新规则:手机号必填,并且进行格式校验,密码必填,长度6-16位之间

    data() {
      const validateMobile = (rule, value, callback) => {
        // 校验成功 callback()
        validMobile(value) ? callback() : callback(new Error('请输入正确的手机号'))
      }
      const validatePassword = (rule, value, callback) => {
        if (value.length < 6) {
          callback(new Error('密码长度为6-16位'))
        } else {
          callback()
        }
      }
      return {
        loginForm: {
          mobile: '13800000002',
          password: '123456'
        },
        loginRules: {
          // trigger校验的触发方式 ,validator自定义函数
          mobile: [{ required: true, trigger: 'blur', message: '手机号不能为空' }, { validator: validateMobile, trigger: 'blur' }],
          password: [{ required: true, trigger: 'blur', message: '密码长度为6-16位' }, { validator: validatePassword, trigger: 'blur', min: 6, max: 16 }]
        },
        loading: false,
        passwordType: 'password',
        redirect: undefined
      }
    },
    
    • 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

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

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

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

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

    ③ Vue-Cli脚手架配置跨域代理

    目的:解决浏览器跨域问题
    详细讲解:vue脚手架配置代理

    devServer: {
    	Proxy: {
    	   '/api': { // 匹配所有以'/api'开头的请求路径
    	     target: 'http://ihrm-java.itheima.net', // 代理目标的基本路径
    	     changeOrigin: true // 伪装路径
    	   }
    	 },
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    ④ 封装单独的登录接口

    export const login = (data) => request({ url: "/sys/login", method: "POST",data })
    
    • 1

    ⑤ 封装Vuex的登录Action并处理token

    vuex详解:Vuex安装使用详解及案例练习(彻底搞懂vuex)
    在这里插入图片描述
    utils/auth.js中,基础模板已经为我们提供了获取token,设置token,删除token的方法,可以直接使用
    token不能每次都通过登录获取,我们可以将token放置到本地的缓存中

    import { getToken, setToken, removeToken } from '@/utils/auth'
    // 状态
    // 初始化的时候从缓存中读取状态 并赋值到初始化的状态上
    // Vuex的持久化 如何实现 ? Vuex和前端缓存相结合
    const state = {
      token: getToken() // 设置token初始状态   token持久化 => 放到缓存中
    }
    
    // 修改状态
    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和 缓存数据的同步
      }
    }
    
    const state = {
      token: getToken()
    }
    
    • 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

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

    环境变量
    在request中设置baseUrl

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

    处理axios的响应拦截器

    // 响应拦截器
    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
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

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

    引入actions辅助函数

    import { mapActions } from 'vuex'  // 引入vuex的辅助函数
    
    • 1

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

    methods: {
        ...mapActions(['user/login'])
    }
    
    • 1
    • 2
    • 3

    调用登录

      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
              }
            }
          })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    四、主页模块

    1、主页的token拦截处理

    ① 权限拦截的流程图

    在这里插入图片描述
    ② 流程图转化代码
    src/permission.js是专门处理路由权限的

    // 权限拦截 导航守卫 路由守卫  router
    import router from "@/router"; // 引入路由实例
    import store from "@/store"; // 引入vuex store实例
    import NProgress from "nprogress"; // 引入一份进度条插件
    import "nprogress/nprogress.css"; // 引入进度条样式
    
    router.beforeEach((to, from, next) => {
      NProgress.start(); // 开启进度条
      const whileName = ["/login", "/404"];
      if (store.getters.token) {
        if (to.path == "/login") {
          next("/");
        } else {
          next();
        }
      } else {
        if (whileName.indexOf(to.path) > -1) {
          next();
        } else {
          next();
        }
      }
      NProgress.done();
    });
    
    router.afterEach(function () {
      NProgress.done(); // 关闭进度条
    });
    
    • 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

    2、主页的左侧导航样式

    ① 主页布局架构
    在这里插入图片描述
    ② 最终效果
    在这里插入图片描述

    3、获取用户资料接口和token注入

    ① 获取用户资料接口

    // 获取信息
    export const getInfo = (data) => {
      request({ url: "/sys/profile", method: "POST", data });
    };
    
    • 1
    • 2
    • 3
    • 4

    ② 统一注入token`src/utils/request.js

    service.interceptors.request.use(config => {
      // 在这个位置需要统一的去注入token
      if (store.getters.token) {
        // 如果token存在 注入token
        config.headers['Authorization'] = `Bearer ${store.getters.token}`
      }
      return config // 必须返回配置
    }, error => {
      return Promise.reject(error)
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    4、封装获取用户资料的action并共享用户状态

    目标: 在用户的vuex模块中封装获取用户资料的action,并设置相关状态

    userInfo为什么我们不设置为null,而是设置为 {}

    因为我们会在**getters**中引用userinfo的变量,如果设置为null,则会引起异常和报错

    5、权限拦截处调用获取资料action

    ① 权限拦截器调用action

    用户资料有个硬性要求,**必须有token**才可以获取,那么我们就可以在确定有token的位置去获取用户资料

    在这里插入图片描述

    ② 获取头像接口合并数据

  • 相关阅读:
    127. SAP UI5 应用的全局配置(Global Configuration) 的设计和使用
    flink 总结
    AI AIgents时代 - (四.) HuggingGPT & MetaGPT
    Python程序性能测试小方法
    [极致用户体验] 我又来帮掘金修专栏bug了,顺便教你个超牛逼的分割线CSS!
    OpenCV分水岭分割算法2
    Apple Car 还没问世,苹果已先将 iPhone 拉入汽车战场?
    代码随想录算法训练营第3天| 203.移除链表元素 、 707.设计链表 、 206.反转链表
    微信小程序开发实战-setData字段特别多时怎么办,试试动态遍历字段并提取赋值
    如何使用Java进行安全测试?
  • 原文地址:https://blog.csdn.net/Vest_er/article/details/127170256