人力资源管理后台 === 主页模块

目录

1.获取用户资料在Vuex中共享

2.显示用户头像和用户名

3.处理头像为空的场景

4.处理token失效的问题

5.调整下拉菜单,实现退出登录

6.修改密码功能实现

6.1-修改密码-弹出层

6.2-修改密码-表单结构

6.3-修改密码-表单校验

6.4-修改密码-确定和取消

7. 清理组件和路由

8.创建项目所需要的组件和路由

9.扩展-解析左侧菜单原理

10. 左侧菜单显示项目logo


1.获取用户资料在Vuex中共享

登录成功跳转到主页之后,可以获取用户资料,获取的资料在Vuex中共享,这样用户就可以很方便的获取该信息

  • 获取流程

image.png

  • 在什么位置获取?

image.png

之前在导航守卫的位置,可以确定已经有了token,这个位置获取资料更佳,因为导航守卫在页面发生跳转时出发,这时不论你从何时何地进来,如果发现你没获取资料,都可以非常清楚直观的获取资料。判断条件也使得不会发生重复的加载。

  • 封装获取用户资料的API-代码位置(src/api/user.js)
export function getUserInfo() {
  return request({
    url: '/sys/profile'
  })
}
  • Vuex中声明用户信息状态,修改用户信息的mutations, 和获取用户信息的action-代码位置(src/store/modules/user.js)
const state = {
  token: getToken(),
  userInfo: {} // 这里有一个空对象,为了放置后面取数据报错
}

const mutations = {
  setUserInfo(state, userInfo) {
    state.userInfo = userInfo
  }
}
const actions = {
  async getUserInfo (context) {
    const result = await getUserInfo()
    context.commit("setUserInfo", result)
  }
}
  • 通过getters声明userId(快捷访问)-代码位置(src/store/getters.js)
const getters = {
  userId: state => state.user.userInfo.userId,
}
export default getters

  • 在权限拦截处调用action-代码位置(src/pemission.js)
import router from '@/router'
import nprogress from 'nprogress'
import 'nprogress/nprogress.css'
import store from '@/store'

/**
 *前置守卫
 *
*/

const whiteList = ['/login', '/404']
router.beforeEach(async (to, from, next) => {
  nprogress.start()
  if (store.getters.token) {
    // 存在token
    if (to.path === '/login') {
      // 跳转到主页
      next('/') // 中转到主页
      // next(地址)并没有执行后置守卫
      nprogress.done()
    } else {
      // 判断是否获取过资料
      if (!store.getters.userId) {
        await store.dispatch("user/getUserInfo")
      }
      next() // 放行
    }
  } else {
    // 没有token
    if (whiteList.includes(to.path)) {
      next()
    } else {
      next('/login') // 中转到登录页
      nprogress.done()
    }
  }
})

/** *
 * 后置守卫
 * **/
router.afterEach(() => {
  nprogress.done()
})

有同学会问,为什么用户信息不做持久化管理呢? 不需要缓存吗? 用户信息不同于token,token是当前用户的唯一标识,在几个小时内都是有效的,但是用户信息可能会进行修改或者发生变化,所以用户信息在一般的项目或者业务中不进行缓存。

2.显示用户头像和用户名

image.png

首先解析一下顶部组件的内部位置

image.png

因为上个小节,将资料已经放到了Vuex中,可以通过getters开放属性,直接在组件中引用即可

  • Vuex中使用getters暴露属性-代码位置(src/store/getters.js)
const getters = {
  token: state => state.user.token,
  userId: state => state.user.userInfo.userId,
  avatar: state => state.user.userInfo.staffPhoto, // 头像
  name: state => state.user.userInfo.username, // 用户名称
}
export default getters

  • 在Navbar组件引入getters-代码位置(src/layout/components/NavBar.vue)
export default {
  computed: {
    // 引入头像和用户名称
    ...mapGetters([
      'sidebar',
      'avatar',
      'name'
    ])
  },
}
  • NavBar组件显示用户名和头像-代码位置(src/layout/components/NavBar.vue)
<div class="avatar-wrapper">
          <!-- 头像 -->
          <img :src="avatar" class="user-avatar">
          <!-- 用户名称 -->
          <span class="name">{{ name }}</span>
          <!-- 图标 -->
          <i class="el-icon-setting" />
</div>
  • 设置头像和用户名的样式-代码位置(src/layout/components/NavBar.vue)
      .avatar-wrapper {
        margin-top: 5px;
        position: relative;
        display: flex;
        align-items: center;
        .name {
          //  用户名称距离右侧距离
          margin-right: 10px;
          font-size: 16px;
        }
        .el-icon-setting {
          font-size: 20px;
        }
        .user-avatar {
          cursor: pointer;
          width: 30px;
          height: 30px;
          border-radius: 50%;
        }
      }
  

3.处理头像为空的场景

不是所有用户都有头像的,当用户没有头像时要显示对应的内容

image.png

  • 条件判断-代码位置(src/layout/components/NavBar.vue)
<div class="avatar-wrapper">
          <!-- 头像 -->
          <img v-if="avatar" :src="avatar" class="user-avatar">
          <span v-else class="username">{{ name?.charAt(0) }}</span>
          <!-- 用户名称 -->
          <span class="name">{{ name }}</span>
          <!-- 图标 -->
          <i class="el-icon-setting" />
        </div>
  • 样式
 .username {
          width: 30px;
          height: 30px;
          text-align: center;
          line-height: 30px;
          border-radius: 50%;
          background: #04c9be;
          color: #fff;
          margin-right: 4px;
        }

这里我们使用了可选链操作符 ?, 它的意思是当?前面的变量为空时,它不会继续往下执行,防止报错,如
null?.name,但是我们当前的版本不支持?的编译,所以需要升级一下对应的版本

$ npm i vue@2.7.0  vue-template-compiler@2.7.0   # 升级vue版本️

升级完成后,重启服务就可以了。

4.处理token失效的问题

token是有时效性的,当token超时,我们需要做一下处理

image.png

  • 请求拦截器处理-代码位置(src/utils/request.js)
// 响应拦截器
service.interceptors.response.use(..., async(error) => {
  if (error.response.status === 401) {
    Message({ type: 'warning', message: 'token超时了' })
    // 说明token超时了
    await store.dispatch('user/logout') // 调用action 退出登录
    //  主动跳到登录页
    router.push('/login') // 跳转到登录页
    return Promise.reject(error)
  }
  // error.message
  Message.error(error.message)
  return Promise.reject(error)
})
  • 实现Vuex的登出action-代码位置(src/store/modules/user.js)
const actions = {
  // 退出登录的action
  logout(context) {
    context.commit('removeToken') // 删除token
    context.commit('setUserInfo', {}) // 设置用户信息为空对象
  }
}

5.调整下拉菜单,实现退出登录

image.png

退出登录流程

image.png

  • Navbar中点击退出登录-代码位置(src/layout/components/NavBar.vue)
  <el-dropdown-item @click.native="logout">
          <span style="display:block;">登出</span>
  </el-dropdown-item>
  • 退出方法-代码位置(src/layout/components/NavBar.vue)
export default  {
  methods: {
     async logout() {
      // 调用退出登录的action
      await this.$store.dispatch('user/logout')
      this.$router.push('/login')
    },
  }
}

注意:native修饰符表示给组件的根元素注册事件️

6.修改密码功能实现

  • 整体流程

image.png

image.png

6.1-修改密码-弹出层

image.png

  • 注册修改密码点击事件-代码位置(src/layout/components/NavBar.vue)
<!-- prevent阻止默认事件 -->
 <a target="_blank" @click.prevent="updatePassword">
    <el-dropdown-item>修改密码</el-dropdown-item>
 </a>
  • 声明变量和方法控制弹层显示-代码位置(src/layout/components/NavBar.vue)
export default {
  data() {
    return  {
      showDialog: false
    }
  },
  methods: {
    updatePassword() {
      this.showDialog = true
    }
  }
}
  • 放置弹层组件-代码位置(src/layout/components/NavBar.vue)
<el-dialog width="500px" title="修改密码" :visible.sync="showDialog" >
</el-dialog>

6.2-修改密码-表单结构

image.png

  • 表单结构-代码位置(src/layout/components/NavBar.vue)
  <el-form  label-width="120px" >
        <el-form-item label="旧密码" >
          <el-input  show-password size="small" />
        </el-form-item>
        <el-form-item label="新密码" >
          <el-input  show-password size="small" />
        </el-form-item>
        <el-form-item label="重复密码" >
          <el-input  show-password size="small" />
        </el-form-item>
        <el-form-item>
          <el-button size="mini" type="primary" >确认修改</el-button>
          <el-button size="mini" >取消</el-button>
        </el-form-item>
      </el-form>

6.3-修改密码-表单校验

image.png

  • 声明数据和规则-代码位置(src/layout/components/NavBar.vue)
export default {
  data () {
    return  {
     passForm: {
        oldPassword: '', // 旧密码
        newPassword: '', // 新密码
        confirmPassword: '' // 确认密码字段
      },
     rules: {
        oldPassword: [{ required: true, message: '旧密码不能为空', trigger: 'blur' }], // 旧密码
        newPassword: [{ required: true, message: '新密码不能为空', trigger: 'blur' }, {
          trigger: 'blur',
          min: 6,
          max: 16,
          message: '新密码的长度为6-16位之间'
        }], // 新密码
        confirmPassword: [{ required: true, message: '重复密码不能为空', trigger: 'blur' }, {
          trigger: 'blur',
          validator: (rule, value, callback) => {
            // value
            if (this.passForm.newPassword === value) {
              callback()
            } else {
              callback(new Error('重复密码和新密码输入不一致'))
            }
          }
        }] // 确认密码字段
      }
    }
  }
}
  • 绑定属性-代码位置(src/layout/components/NavBar.vue)
      <el-form ref="passForm" label-width="120px" :model="passForm" :rules="rules">
        <el-form-item label="旧密码" prop="oldPassword">
          <el-input v-model="passForm.oldPassword" show-password size="small" />
        </el-form-item>
        <el-form-item label="新密码" prop="newPassword">
          <el-input v-model="passForm.newPassword" show-password size="small" />
        </el-form-item>
        <el-form-item label="重复密码" prop="confirmPassword">
          <el-input v-model="passForm.confirmPassword" show-password size="small" />
        </el-form-item>
        <el-form-item>
          <el-button size="mini" type="primary" >确认修改</el-button>
          <el-button size="mini" >取消</el-button>
        </el-form-item>
      </el-form>

6.4-修改密码-确定和取消

  • 确定和取消流程

image.png

  • 封装修改密码方法-代码位置(src/api/user.js)
/**
 * 更新密码
 * **/
export function updatePassword(data) {
  return request({
    url: '/sys/user/updatePass',
    method: 'put',
    data
  })
}

  • 确定方法-代码位置(src/layout/components/NavBar.vue)
// 确定
    btnOK() {
      this.$refs.passForm.validate(async isOK => {
        if (isOK) {
          // 调用接口
          await updatePassword(this.passForm)
          this.$message.success('修改密码成功')
          this.btnCancel()
        }
      })
    },
  • 取消方法-代码位置(src/layout/components/NavBar.vue)
btnCancel() {
      this.$refs.passForm.resetFields() // 重置表单
      // 关闭弹层
      this.showDialog = false
}
  • 监听弹层关闭事件
    <el-dialog @close="btnCancel" width="500px" title="修改密码" :visible.sync="showDialog" >

有同学有疑惑,不是用了.sync修饰符吗?可以直接关闭弹层,为何还要监听close事件,这是因为sync修饰符只能关闭弹层,我们还需要将表单进行重置,因为你之前打开弹层可能修改了一半或者残留一些校验信息,再打开的时候这些信息应该都被重置,所以这里用监听事件的方式再重置一下表单,保证功能的体验和完整性。

7. 清理组件和路由

主页功能开发完了,我们要清理一下模板中残余文件

image.png

image.png

  • 路由只保留登录-主页-404
  • 请求模块只保留user.js模块

8.创建项目所需要的组件和路由

人力资源项目需要这些模块

  ├── approval            # 审批管理
  ├── attendance          # 考勤管理
  ├── department          # 组织架构
  ├── employee            # 员工管理
  ├── permission          # 权限管理
  ├── role                # 角色管理
  ├── salary              # 工资管理
  ├── social              # 社保管理

  • 创建模块

image.png

大家可以直接从当天课程的素材中拷贝已经准备好的路由/组件

组件结构样例
 

image.png


路由模块样例
 

image.png


路由的统一导入
 

image.png


在src/router/index.js中集成到当前路由中

image.png

9.扩展-解析左侧菜单原理

image.png

左侧菜单的数据来源于路由模块的信息, 会根据路由信息的hidden属性来判断是否显示该路由信息到菜单,
菜单属性中的图表和标题来源于路由meta中的icon和title属性

  • 分析过程

sidebar组件引入路由信息
 

image.png


循环渲染路由信息
 

image.png


sidebarItem组件根据条件渲染-传递icon和title属性给item组件
 

image.png


item组件接收icon和title属性,使用函数型组件完成渲染

image.png

logo有两种展示形态,当菜单展开时,显示大图,当菜单折叠时,显示小图

image.png

image.png

  • 通过settings.js的设置,将logo显示出来-代码位置(src/settings.js)
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)
<template>
  <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>
</template>
  • 调整logo样式-代码位置(src/layout/components/Sidebar/Logo.vue)
<style lang="scss" scoped>
.sidebarLogoFade-enter-active {
  transition: opacity 1.5s;
}

.sidebarLogoFade-enter,
.sidebarLogoFade-leave-to {
  opacity: 0;
}

.sidebar-logo-container {
  position: relative;
  width: 100%;
  height: 50px;
  line-height: 50px;
  text-align: center;
  overflow: hidden;

  & .sidebar-logo-link {
    height: 100%;
    width: 100%;

    & .sidebar-logo {
      width: 140px;
      vertical-align: middle;
      margin-right: 12px;
    }

    & .sidebar-title {
      display: inline-block;
      margin: 0;
      color: #fff;
      font-weight: 600;
      line-height: 50px;
      font-size: 14px;
      font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
      vertical-align: middle;
    }
  }

  &.collapse {
    .sidebar-logo {
      margin-right: 0px;
      width: 32px;
      height: 32px;
    }
  }
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/191731.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

设计模式精讲:掌握单例模式的实现与优化

掌握单例模式的实现与优化 一、引言&#xff1a;如何学习设计模式&#xff1f;二、前置知识&#xff1a;对象的创建的销毁2.1、拷贝构造2.2、拷贝赋值构造2.3、移动构造2.4、移动赋值构造 三、单例模式的定义四、单例模式的实现与优化4.1、版本一4.2、版本二4.3、版本三4.4、版…

均匀球形分布的随机三维单位向量

生成具有均匀球形分布的随机三维单位向量[参考] import numpy as np import matplotlib.pyplot as plt def random_three_vector():"""Generates a random 3D unit vector (direction) with a uniform spherical distributionAlgo from http://stackoverflow.c…

论文阅读:C2VIR-SLAM: Centralized Collaborative Visual-Inertial-Range SLAM

前言 论文全程为C2VIR-SLAM: Centralized Collaborative Visual-Inertial-Range Simultaneous Localization and Mapping&#xff0c;是发表在MDPI drones&#xff08;二区&#xff0c;IF4.8&#xff09;上的一篇论文。这篇文章使用单目相机、惯性测量单元( IMU )和UWB设备作为…

Node——npm包管理器的使用

Node.js使用npm对包进行管理&#xff0c;其全称为Node Package Manager&#xff0c;开发人员可以使用它安装、更新或者卸载Node.js的模块 1、npm包管理器基础 1.1、npm概述 npm是Node.js的标准软件包管理器&#xff0c;其在2020年3月17日被GitHub收购&#xff0c;而且保证永…

1.9 字符数组

1.9 字符数组 一、字符数组概述二、练习 一、字符数组概述 所谓字符数组&#xff0c;就是char类型的数组&#xff0c;比如 char a[]&#xff0c;是C语言中最常用的数组类型&#xff0c;先看一个程序 #include <stdio.h> #define MAXLINE 1000 //最大行长度限制 int get…

软件介绍02- flameshot截图软件(linux系统可用)

1 软件介绍 在Windows和mac平台一直都使用着snipaste截图&#xff0c;非常好用&#xff0c;又能够钉图。遗憾是并没有开发linux版本&#xff0c;真不知道为什么。 好在终于找到一款截图软件&#xff0c;flameshot截图软件&#xff0c;可以平替snipaste。 下载网址&#xff1a;…

什么是好的FPGA编码风格?(3)--尽量不要使用锁存器Latch

前言 在FPGA设计中&#xff0c;几乎没人会主动使用锁存器Latch&#xff0c;但有时候不知不觉中你的设计莫名其妙地就生成了一堆Latch&#xff0c;而这些Latch可能会给你带来巨大的麻烦。 什么是锁存器Latch&#xff1f; Latch&#xff0c;锁存器&#xff0c;一种可以存储电路…

【Linux】进程间通信

进程间通信 1. 进程间通信介绍1.1 进程间通信目的1.2 进程间通信发展1.3 进程间通信分类1.4 进程间通信的本质理解 2. 管道3. 匿名管道3.1 pipe()函数3.2 站在文件描述符角度-深度理解管道3.3 站在内核角度-管道本质3.4 匿名管道使用步骤3.4 管道读写规则3.5 管道的读与写的五种…

复数的乘幂与方根

1、乘积与商 设 几何意义&#xff1a; &#xff1a;逆时针旋转一个角度&#xff0c;并伸长倍 &#xff1a;顺时针旋转一个角度&#xff0c;并伸长倍 *特别&#xff1a;不存在 :对实行了一次旋转变换&#xff0c;且长度不变&#xff0c;旋转角为 例题&#xff1a; 2、幂与…

windows下docker环境搭建与运行实战

背景 学习docker使用&#xff0c;需要环境&#xff0c;今天主要的目标是在windows环境下安装docker环境。 为什么要这么搞&#xff0c;主要是企业内部服务器&#xff0c;都是跟公网隔离的&#xff0c;没有访问公网权限&#xff0c;所以镜像什么的&#xff0c;从公网拉取完全没…

MySQL的undo log 与MVCC

文章目录 概要一、undo日志1.undo日志的作用2.undo日志的格式3. 事务id&#xff08;trx_id&#xff09; 二、MVCC1.版本链2.ReadView3.REPEATABLE READ —— 在第一次读取数据时生成一个ReadView4.快照读与当前读 小结 概要 Undo Log&#xff1a;数据库事务开始之前&#xff0…

qt-C++笔记之不使用ui文件纯C++构建时控件在布局管理器作用下的默认位置和大小实践

qt-C笔记之不使用ui文件纯C构建时控件在布局管理器作用下的默认位置和大小实践 code review! 文章目录 qt-C笔记之不使用ui文件纯C构建时控件在布局管理器作用下的默认位置和大小实践1.ChatGPT解释2.ChatGPT——resize()和move()详解3.默认大小和位置——示例运行一4.默认大小…

31 - MySQL调优之SQL语句:如何写出高性能SQL语句?

从今天开始&#xff0c;我将带你一起学习 MySQL 的性能调优。MySQL 数据库是互联网公司使用最为频繁的数据库之一&#xff0c;不仅仅因为它开源免费&#xff0c;MySQL 卓越的性能、稳定的服务以及活跃的社区都成就了它的核心竞争力。 我们知道&#xff0c;应用服务与数据库的交…

3D建模对制造企业的价值

除非你在过去几年一直躲在岩石下,否则你可能听说过“3D 建模”和“3D 渲染”这些术语。 但为什么这项技术如此重要,尤其是对于产品制造公司而言? 简而言之,它减少了项目时间和成本。 这为制造商提供了更多的设计试验空间。 未能利用 3D 建模技术的公司很快就会落后于竞争对…

MYSQL基础之【正则表达式,事务处理】

文章目录 前言MySQL 正则表达式MySQL 事务事务控制语句事务处理方法PHP中使用事务实例 后言 前言 hello world欢迎来到前端的新世界 &#x1f61c;当前文章系列专栏&#xff1a;Mysql &#x1f431;‍&#x1f453;博主在前端领域还有很多知识和技术需要掌握&#xff0c;正在不…

P18 C++ 继承

目录 前言 01 不使用继承会让你多打很多无用的代码 02 继承 最后的话 前言 本期我们学习 C 面向对象编程中的继承。 面向对象程序设计中最重要的一个概念是继承。继承允许我们依据另一个类来定义一个类&#xff0c;这使得创建和维护一个应用程序变得更容易。这样做&#…

BC77 简单计算器(牛客)

#include <stdio.h> int main() {double a, b, d;//用来接收浮点数char c;//用来接受符号scanf("%lf %c %lf", &a, &c, &b);if (c || c - || c * || c /)//判断输入的运算符号不包括在&#xff08;、-、*、/&#xff09;范围内{switch (c)//根…

从0开始学习JavaScript--构建强大的JavaScript图片库

在现代Web开发中&#xff0c;图像是不可或缺的一部分&#xff0c;而构建一个强大的JavaScript图片库能够有效地管理、展示和操作图像&#xff0c;为用户提供更丰富的视觉体验。本文将深入探讨构建JavaScript图片库的实用技巧&#xff0c;并通过丰富的示例代码演示如何实现各种功…

因子分析例题(多元统计分析期末复习)

例一 设某客观现象可用 X {X} X( X 1 {X_1} X1​&#xff0c; X 2 {X_2} X2​&#xff0c; X 3 {X_3} X3​)’ 来描述&#xff0c;在因子分析时&#xff0c;从约相关阵出发计算特征值为 λ 1 {λ_1} λ1​1.754&#xff0c; λ 2 {λ_2} λ2​1&#xff0c; λ 3 {λ_3} λ3​…

transformers pipeline出现ConnectionResetError的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…