vhr前端-1

前后分离,前端采用vue脚手架创建工程.
原项目地址:github.com/lenve/vhr

1.项目初始化

1.初始化vue工程

1.安装vue脚手架

npm install -g @vue/cli(默认4.x版本)

2.初始化vue工程

切换到工作目录
vue create 项目名称(可以选择2.x或3.x版本)

3.启动项目

npm run serve(默认8080端口,如果有占用会往后移)

4.查看vue及vue/cli版本

npm list vue -g

在这里插入图片描述

5.配置服务器和本地地址+端口

创建vue.config.js

let proxyObj = {};
proxyObj['/'] = {
    ws: false,
    // 远程地址和端口
    target: 'http://localhost:8081',
    changeOrigin: true,
    pathRewrite: {
        '^/': ''
    }
}
// 指定远程服务器的地址
module.exports = {
    devServer: {
        // 本地地址端口
        host: 'localhost',
        port: 8080,
        proxy: proxyObj
    },
}

2.安装所需组件

1.安装vue-router

npm i vue-router@3.2.0(默认安装4.x版本,vue2.x支持3.x的vue-router,所以必须指定版本)
然后在src下新建router.js

import Vue from 'vue'
import Router from 'vue-router'
import Login from './views/Login.vue'

Vue.use(Router)

export default new Router({
    mode: 'history',
    routes: [
        {
            path: '/',
            name: 'Login',
            component: Login,
            hidden: true
        },
    ]
})

然后main.js引入router
在这里插入图片描述
在这里插入图片描述
然后app.Vue中添加标签
在这里插入图片描述
这样就会默认访问Login这个组件了。

2.安装element-ui

 npm install --save element-ui
 

然后在main.js中引入

import 'element-ui/lib/theme-chalk/index.css';
import ElementUI from 'element-ui';
Vue.use(ElementUI);

3.安装axios

npm install --save axios

然后创建api.Js封装axios请求

export const postRequest = (url, params) => {
    return axios({
        method: 'post',
        url: `${base}${url}`,
        data: params
    })
}

引入文件

import { postRequest } from "./utils/api";
import { postKeyValueRequest } from "./utils/api";
import { putRequest } from "./utils/api";
import { deleteRequest } from "./utils/api";
import { getRequest } from "./utils/api";

注册插件

Vue.prototype.postRequest = postRequest;
Vue.prototype.postKeyValueRequest = postKeyValueRequest;
Vue.prototype.putRequest = putRequest;
Vue.prototype.deleteRequest = deleteRequest;
Vue.prototype.getRequest = getRequest;

在main中引入的作用就是可以全局使用,不用在每个文件中都引入了

3.初始页面布局

1.element-ui的container布局

样式整体如下:
在这里插入图片描述
从官网导入的样式整体不会占满全屏
需要增加样式
home.vue(登录后跳转主页)中增加样式

/* 左侧导航栏样式 */
.el-aside {
  background-color: #d3dce6;
  color: #333;
  text-align: center;
  /*container占满全屏 解决屏幕超出一页 把多出的底部的高度减去 */
  height: calc(100vh - 60px);
}
/* 左侧菜单样式 菜单也要占满屏幕 */
.el-menu {
  height: 100%;
  text-align: left;
}

App.Vue中增加样式

#app {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

2.底部样式

          <!-- 底部 -->
          <el-footer>
            <el-link type="info" href="http://www.baidu.com" target="_blank"
              >友情链接</el-link
            >
          </el-footer>
.el-footer {
  /* background-color: #c8d3d670; */
  color: #333;
  text-align: center;
  line-height: 55px;
}

效果:
在这里插入图片描述

3.顶部样式

html

      <!-- 顶部 -->
      <el-header class="homeHeader">
        <!-- 标题 -->
        <div class="title">微人事</div>
        <!-- 右侧账户设置 -->
        <div>
        	<!-- 下拉菜单 -->
          <el-dropdown @command="commandHandler">
            <span class="el-dropdown-link">
              超级管理员<img src="./images/touxiang.png" alt="" />
            </span>
            <el-dropdown-menu slot="dropdown">
              <el-dropdown-item command="userinfo">个人中心</el-dropdown-item>
              <el-dropdown-item command="setting">设置</el-dropdown-item>
              <el-dropdown-item command="logout" divided
                >注销登录</el-dropdown-item
              >
            </el-dropdown-menu>
          </el-dropdown>
        </div>
      </el-header>

css

/* 顶部样式 */
.el-header {
  background-color: #409eff;
  line-height: 60px;
}
/* 顶部左侧样式 */
/* 两个div同行显示 float属性 */
.title {
  text-align: left;
  font-size: 30px;
  font-family: 楷体;
  color: #ebeff3ee;
  float: left;
}
/* 顶部右侧样式 保证两个div在同一行 */
.el-dropdown {
  float: right;
}
/* 顶部右侧样式 */
.el-dropdown-link img {
  /* 指定头像图片尺寸 并且变成圆形 */
  width: 48px;
  height: 48px;
  border-radius: 24px;
  margin-left: 8px;
  /* margin-top: 26px; */
}

.el-dropdown-link {
  /* 高度和父盒子一样 保证元素居中*/
  height: 60px;
  /* 将盒子变成弹性布局  */
  display: flex;
  /* 盒子中的元素 按照主轴居中对齐 默认主轴为横向 */
  align-items: center;
  font-size: medium;
  color: #ebeff3ee;
  font-family: 楷体;
}

效果
在这里插入图片描述

4.左侧导航栏样式

html
element-ui中的NavMenu 导航菜单

        <!-- 左侧导航栏 -->
        <el-aside width="200px">
          <!-- :unique-opened="true" 只有一个子菜单打开 -->
          <el-menu
            default-active="1"
            class="el-menu-vertical-demo"
            @open="handleOpen"
            @close="handleClose"
            active-text-color="#409eff"
            :unique-opened="true"
          >
            <el-submenu index="1">
              <template slot="title">
                <i class="el-icon-user-solid"></i>
                <span>员工资料</span>
              </template>
              <!-- 点击进行路由切换 -->
              <el-menu-item index="1-1" @click="handleRoute('/empinfo')">
                <i class="el-icon-menu"></i>
                <span slot="title">基本资料</span>
              </el-menu-item>
            </el-submenu>

            <el-submenu index="2">
              <template slot="title">
                <i class="el-icon-location"></i>
                <span>人事管理</span>
              </template>
              <el-menu-item index="2-1" @click="handleRoute('/empaward')"
                >员工奖惩</el-menu-item
              >
              <el-menu-item index="2-2" @click="handleRoute('/emptrain')"
                >员工培训</el-menu-item
              >
              <el-menu-item index="2-3" @click="handleRoute('/empmove')"
                >员工调动</el-menu-item
              >

              <el-submenu index="2-4">
                <template slot="title">员工调薪</template>
                <el-menu-item
                  index="2-4-1"
                  @click="handleRoute('/empsalaryraise')"
                  >员工加薪</el-menu-item
                >
                <el-menu-item
                  index="2-4-2"
                  @click="handleRoute('/empsalarycut')"
                  >员工降薪</el-menu-item
                >
              </el-submenu>
            </el-submenu>

            <el-menu-item index="3" disabled>
              <i class="el-icon-document"></i>
              <span slot="title">导航三</span>
            </el-menu-item>
            <el-menu-item index="4">
              <i class="el-icon-setting"></i>
              <span slot="title">导航四</span>
            </el-menu-item>
          </el-menu>
        </el-aside>

样式在3.1中。
效果:
在这里插入图片描述

5.main主体的样式

html

          <!-- 主体内容 -->
          <el-main>
            <!-- 面包屑导航 不是主页的话进行显示 -->
            <el-breadcrumb
              separator-class="el-icon-arrow-right"
              v-if="this.$router.currentRoute.path != '/home'"
            >
              <!-- 显示首页 点击可回到首页 -->
              <el-breadcrumb-item :to="{ path: '/home' }"
                >首页</el-breadcrumb-item
              >
              <!-- 遍历显示首页之后的多个路径 -->
              <el-breadcrumb-item>
                {{ this.$router.currentRoute.name }}
              </el-breadcrumb-item>

              <!-- <el-breadcrumb-item
                v-for="item in this.uriList()"
                :key="item.path"
              >
                {{ item.name }}
              </el-breadcrumb-item> -->
            </el-breadcrumb>

            <router-view> </router-view>

            <div
              class="homeWelcome"
              v-if="this.$router.currentRoute.path == '/home'"
            >
              欢迎来到微人事!
            </div>
          </el-main>

这里面包屑导航部分问题比较大,目前实现的是,单独显示首页和其他的路径,一共两个,
想实现多级路径显示,然后只有首页高亮。
方式:不能采用全部遍历的方式,那样只有最后一项导航不会高亮,所以需要首页单独显示高亮,然后通过computed获取this.$route.matched,删除第一个元素后返回,然后遍历返回的数组,这样可以实现面包屑导航的效果,但是main中引入的其他页面无法显示
写到这忽然相出一个办法:

              <el-breadcrumb-item  v-for="(item,index) in $route.matched.slice(1)" 
                :key="index">
                {{item.name}}</el-breadcrumb-item>

遍历的时候直接排除第一个,试了一下果然可以!
改一下routes配置:
在这里插入图片描述
点击传入的路径
在这里插入图片描述
效果:
在这里插入图片描述

这里有个注意事项:
必须加一个父组件的页面,也就是员工调薪这个页面,然后在父组件中加
<router-view></router-view>
这样才能显示出想要的两个子页面。这个员工调薪界面其实没啥用,有没有更好的办法呢,有待大神指教啊

改过之后的main:

          <!-- 主体内容 -->
          <el-main>
            <!-- 面包屑导航 不是主页的话进行显示 -->
            <el-breadcrumb separator-class="el-icon-arrow-right" v-if="this.$router.currentRoute.path != '/home'">
              <!-- 显示首页 点击可回到首页 -->
              <el-breadcrumb-item :to="{ path: '/home' }"
                >首页</el-breadcrumb-item
              >
              <!-- 显示首页之后的路径 -->
              <!-- <el-breadcrumb-item>
                {{ this.$router.currentRoute.name }}
              </el-breadcrumb-item> -->

              <!-- 遍历除首页的其他路径 -->
              <el-breadcrumb-item  v-for="(item,index) in $route.matched.slice(1)" 
                :key="index">
                {{item.name}}</el-breadcrumb-item>

            </el-breadcrumb>

            <router-view> </router-view>

            <div class="homeWelcome" v-if="this.$router.currentRoute.path == '/home'">
              欢迎来到微人事!
            </div>
          </el-main>

css

.el-main {
  /* background-color: #c8d3d670; */
  color: #333;
  /* text-align: center; */
  /* line-height: 160px; */
  /* 背景图片 */
  /* background-image:url("./images/gradient2.png"); */
  /* background-repeat: repeat-x; */
  /* background-size: 100% 100%; */
  /* background-attachment: fixed; */
  /* 加上这个高度后main满屏的时候 底部仍然固定 */
  height: 300px;
}

最终主界面
(main部分在主界面的时候不显示面包屑导航)
在这里插入图片描述

6.控制组件在main部分显示,不切换页面

Router.js中给home路由添加子路由
在这里插入图片描述
配置菜单项点击事件
在这里插入图片描述

然后在el-main中添加routerview
在这里插入图片描述

4.message消息提示

使用axios的response拦截和element-ui的message

import axios from 'axios'
import { Message } from 'element-ui';
import router from '../router'
axios.interceptors.response.use(success => {
    // console.log(success.data);
    // 判断后端传回的状态码
    if (success.data.code >= 80001) {
        // Message.error(success.data.Message)
        // console.log(123);
        // 显示错误消息
        Message.error({ message: success.data.msg })
        //80001为未登录返回的状态码,跳转到首页
        if (success.data.code == 80001)
            router.replace("/")
        return;
    }
    //显示其他消息 如登陆成功之类的
    if (success.data.msg) {
        Message.success({ message: success.data.msg })
    }
    return success.data;
}, error => {
    // console.log(error);
    //错误消息提示
    if (error.response.status == 504 || error.response.status == 404) {
        Message.error({ message: '服务器被吃了( ╯□╰ )' })
    } else if (error.response.status == 403) {
        Message.error({ message: '权限不足,请联系管理员' })
    } else if (error.response.status == 401) {
        mymessage.error({ message: error.response.data.msg ? error.response.data.msg : '尚未登录,请登录' })
        router.replace('/');
    } else {
        if (error.response.data.msg) {
            Message.error({ message: error.response.data.msg })
        } else {
            Message.error({ message: '未知错误!' })
        }
    }
    return;
})

效果
在这里插入图片描述

5.vuex

参考链接
前端登陆成功后需要返回用户的角色信息(login组件),前端根据角色进行权限分配(home组件),数据在两个组件进行操作,所以需要用到vuex保存全局数据,这里只记录了本项目用到的vuex部分内容。

Vuex 实现了一个单向数据流,在全局拥有一个 state 存放数据,当组件要更改 state 中的数据时,必须通过 mutations 进行,mutations 同时提供了订阅者模式供外部插件调用获取 state 数据的更新。而当所有异步操作(常用于调用后端接口异步获取数据)或批量的同步操作需要走 actions,但 actions 也是无法直接修改 state 的,还是需要通过触发 mutations 中的方法,然后 mutations 来修改 state 的数据。数据变更后相应推送给组件,组件重新渲染到视图上。

1.安装依赖包

npm install vuex@3.6.2 --save (对应vue2.x部分,必须指定版本)

2.导入vuex包

在utils目录下新建store.js文件

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

//创建store对象
const store = new Vuex.Store({
    // state 中存放的就是全局共享的数据
    state: {
        count: []
    },
    // 变更数据的方法,可以调用此方法修改state中数据
    mutations: {
        add(state, step) {
            state.count = step //将step的值赋给state.count
        }
    },
    //异步操作变更数据
    actions: {
        addAsync(context, step) {
            //context:上下文对象,相当于箭头函数中的this,和 store 实例具有相同的属性和方法
            context.commit('add', step)

        }
    }
})
// 全局可用
export default store;

3.将 store 对象加到vue实例中

main.js中
在这里插入图片描述

4.触发方式

1.mutations

 this.$store.commit('add')

2.Action

this.$store.dispatch("addAsync", ["admin", "user"]);

5.测试效果

count是store中定义的全局变量,
在login.vue的方法中赋值,在home.vue中使用
在这里插入图片描述

在这里插入图片描述
效果:
在这里插入图片描述
这里find方法在项目部署到nginx服务器时会报错,find is not a function 原因未知,改成了用indexOf方法,如果元素不存在会返回-1,这里也有个坑,indexof的‘o’一定要大写,不然也是not a function。一定记住啊,查这个问题废了很长时间。。。。
这里还有另一个方案:就是定义store的getter方法。
这里的全局变量是存的用户角色,可以定义getter方法获取到权限数组中是否含有某种角色。

    getters: {
        hasPermission: state => (id) => {
            //该find方法是 判断permissionList权限数组中的值是否有某个功能权限值 
            //有的话就返回权限数组 数组!=undefined 为true 
            //没有的话回返回undefinde undefinde!=undefind 为false
            return state.roles.find((item) => item == id) != undefined
        },

    },

使用方法:
在需要用到这个全局变量的组件中加入方法
在这里插入图片描述
这是判断如果不存在这个’ROLE_admin’这个角色,可以进入判断
在这里插入图片描述
这是判断如果有这个’ROLE_admin’角色,就展示这个标签的内容
在这里插入图片描述

6.页面刷新404问题

由于router采用了history模式,浏览器显示的路径不是真实路径,刷新页面的时候会强制请求当前路径,不存在肯定就是404了。
解决办法:
vue.config.js中
如果路径不存在就访问当前路径
在这里插入图片描述
这样又出现一个新问题,就是页面刷新后全局变量失效。
解决办法:
store.js中:
在这里插入图片描述

6.项目部署

服务器采用nginx

1.npm run build

执行过后在项目 根目录会生成dist文件夹

2.将dist文件夹内容复制到nginx静态目录下

在这里插入图片描述

3.配置nginx.conf

在这里插入图片描述
此时访问nginx服务器的地址,就可以访问前端页面了
在这里插入图片描述

2.业务逻辑

1.登录界面

在这里插入图片描述
账号 密码 验证码 三部分
html

<template>
  <div>
    <el-form
      :rules="rules"
      ref="loginForm"
      v-loading="loading"
      element-loading-text="正在登录..."
      element-loading-spinner="el-icon-loading"
      element-loading-background="rgba(0, 0, 0, 0.8)"
      :model="loginForm"
      class="loginContainer"
    >
      <h3 class="loginTitle">系统登录</h3>
      <el-form-item prop="username">
        <el-input
          size="normal"
          type="text"
          v-model="loginForm.username"
          auto-complete="off"
          placeholder="请输入用户名"
          @blur="checkUsername()"
        ></el-input>
      </el-form-item>
      <el-form-item prop="password">
        <el-input
          size="normal"
          type="password"
          v-model="loginForm.password"
          auto-complete="off"
          placeholder="请输入密码"
          show-password="true"
        >
        </el-input>
      </el-form-item>
      <el-form-item prop="code">
        <el-input
          size="normal"
          type="text"
          v-model="loginForm.code"
          auto-complete="off"
          placeholder="点击图片更换验证码"
          @keydown.enter.native="submitLogin"
          style="width: 250px"
        ></el-input>
        <img
          :src="vcUrl"
          @click="updateVerifyCode"
          alt=""
          style="cursor: pointer"
        />
      </el-form-item>
      <el-button
        size="normal"
        type="primary"
        style="width: 100%"
        @click="submitLogin"
        >登录</el-button
      >
    </el-form>
  </div>
</template>

js

<script>
export default {
  name: "Login",
  data() {
    return {
      loading: false,
      // 验证码 加载时从后台获取
      vcUrl: "/verifyCode?time=" + new Date(),
      //表单内容 指定默认的账号密码
      loginForm: {
        username: "admin",
        password: "12345",
        code: "",
      },
      //校验规则
      rules: {
        username: [
          { required: true, message: "请输入用户名", trigger: "blur" },
        ],
        password: [{ required: true, message: "请输入密码", trigger: "blur" }],
        code: [{ required: true, message: "请输入验证码", trigger: "blur" }],
      },
    };
  },
  methods: {
  // 检查用户名是否存在 失去焦点校验
    checkUsername() {
      this.getRequest("/checkUsername", {
        username: this.loginForm.username,
      });
    },
    //点击更新验证码
    updateVerifyCode() {
      this.loginForm.code = "";
      this.vcUrl = "/verifyCode?time=" + new Date();
    },
    // 提交表单
    submitLogin() {
      this.$refs.loginForm.validate((valid) => {
        if (valid) {
          this.loading = true;
          this.postRequest("/dologin", this.loginForm).then((resp) => {
            this.loading = false;
            if (resp) {
            //全局参数 该账户拥有的角色
              this.$store.dispatch("addAsync", ["admin", "user"]);
              // this.$store.commit('INIT_CURRENTHR', resp.obj);
              // window.sessionStorage.setItem("user", JSON.stringify(resp.obj));
              let path = this.$route.query.redirect;
              //页面跳转 到home
              this.$router.replace(
                path == "/" || path == undefined ? "/home" : path
              );
            } else {
              // 清空验证码输入框
              this.loginForm.code = "";
              // 重新获取验证码
              this.vcUrl = "/verifyCode?time=" + new Date();
            }
          });
        } else {
          return false;
        }
      });
    },
  },
};
</script>

css

<style>
.loginContainer {
  /* 边框圆角半径 */
  border-radius: 15px;
  background-clip: padding-box;
  /* 外边距 */
  margin: 180px auto;
  width: 350px;
  padding: 15px 35px 15px 35px;
  background: #fff;
  border: 1px solid #eaeaea;
  box-shadow: 0 0 25px #cac6c6;
}

.loginTitle {
  margin: 15px auto 20px auto;
  text-align: center;
  color: #505458;
}

.el-form-item__content {
  display: flex;
  align-items: center;
}
</style>

2.个人中心

点击右上角的头像的下拉框中的个人中心按钮,进到Hrinfor界面,显示当前登录的Hr的信息。
效果
在这里插入图片描述
展示当前hr的基本信息,可以修改基本信息和密码。

页面分三部分,信息展示使用el-card, 两个el-dialog为修改信息和密码的弹框。
在这里插入图片描述

1.数据展示部分

html

    <el-card class="box-card">
      <!--昵称 -->
      <div slot="header" class="clearfix">
        <span>{{ roleInfo.name }}</span>
      </div>
      <div>
        <!-- 头像 这里采用本地存储  -->
        <div
          style="display: flex; justify-content: center; margin-bottom: 10px"
        >
          <img
            src="./images/touxiang.png"
            alt=""
            style="width: 100px; height: 100px; border-radius: 50px"
          />
        </div>
        <!-- 用户信息 -->
        <!-- <el-tag> 标签 -->
        <div>
          电话号码:
          <el-tag>{{ roleInfo.telephone }}</el-tag>
        </div>
        <div style="margin-top: 5px; margin-bottom: 5px">
          手机号码:
          <el-tag>{{ roleInfo.phone }}</el-tag>
        </div>
        <div>
          居住地址:
          <el-tag>{{ roleInfo.address }}</el-tag>
        </div>
        <!-- 当前hr拥有的角色 -->
        <div style="margin-top: 5px">
          用户标签:
          <el-tag
            type="success"
            style="margin-right: 5px"
            v-for="(r, index) in roleInfo.rolesZh"
            :key="index"
          >
            {{ r }}
          </el-tag>
        </div>
        <!-- 两个按钮 -->
        <div
          style="display: flex; justify-content: space-around; margin-top: 10px"
        >
          <el-button type="primary" @click="updateHrInfo">修改信息</el-button>
          <el-button type="danger" @click="passwordFormVisible = true"
            >修改密码</el-button
          >
        </div>
      </div>
    </el-card>

样式采用element-ui自带card样式
js:
数据部分

      roleInfo: {},
      hrId: this.$store.state.hrId,

方法部分:
页面加载创建完成,获取当前Hr的信息,根据全局变量hrId,获取成功后将数据赋值给roleInfo变量,然后做数据展示。

    //获取用户信息
    getHrinfo() {
      // alert(this.hrId);
      this.getRequest("/getHrInfo", {
        hrId: this.hrId,
      }).then((resp) => {
        if (resp) {
          this.roleInfo = resp.data;
        }
      });
    },
    created: function () {
    this.getHrinfo();
  },

后台返回的数据格式

{
	"msg": "",
	"code": 0,
	"data": {
		"id": 3,
		"name": "超级管理员",
		"phone": "18568887789",
		"telephone": "029-82881234",
		"address": "深圳南山",
		"enabled": null,
		"username": null,
		"password": null,
		"userface": "./images/touxiang1.png",
		"remark": null,
		"roles": ["ROLE_admin"],
		"rolesZh": ["系统管理员"]
	}
}

2.修改用户信息

在这里插入图片描述
html

    <el-dialog
      title="个人信息修改"
      :visible.sync="updateHrInfoViewVisible"
      width="25%"
    >
      <el-form
        :model="hrForm"
        label-width="100px"
        ref="hrForm"
        :rules="roleInfoRules"
      >
        <el-form-item label="用户昵称" prop="name">
          <el-input :clearable="true" v-model="hrForm.name"> </el-input>
        </el-form-item>
        <el-form-item label="电话号码" prop="phone">
          <el-input :clearable="true" v-model="hrForm.phone"> </el-input>
        </el-form-item>
        <el-form-item label="手机号码" prop="telephone">
          <el-input :clearable="true" v-model="hrForm.telephone"> </el-input>
        </el-form-item>
        <el-form-item label="居住地址" prop="address">
          <el-input :clearable="true" v-model="hrForm.address"> </el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="updateHrInfoViewVisible = false">取 消</el-button>
        <el-button type="primary" @click="modifyUserInfo('hrForm')"
          >确 定</el-button
        >
      </div>
    </el-dialog>

js
数据部分:

//控制弹框是否显示 false为不显示
updateHrInfoViewVisible: false,
//修改用户信息的临时表单数据
hrForm: {
  name: "",
  phone: "",
  telephone: "",
  address: "",
},
//修改用户信息时 昵称的校验规则 对应标签中的 :rules="roleInfoRules"
roleInfoRules: {
  name: [{ validator: validateName, trigger: "blur" }],
},
//昵称校验方法
var validateName = (rule, value, callback) => {
  if (value === "") {
    callback(new Error("请输入昵称"));
  } else {
    callback();
  }
};

方法部分:

    //发送请求修改用户信息 也是根据id id是在roleInfo中,深拷贝时roleInfo也会得到该值
    modifyUserInfo(roleInfo) {
      this.$refs[roleInfo].validate((valid) => {
        if (valid) {
          this.postRequest("/modifyUserInfo", this.hrForm).then((resp) => {
            if (resp) {
              //修改完成,重新获取hr数据
              this.getHrinfo();
              this.updateHrInfoViewVisible = false;
            }
          });
        } else {
          return false;
        }
      });
    },
    //点修改信息的事件 弹出修改框 给hrForm赋值 赋值用深拷贝的方式
    updateHrInfo() {
      this.updateHrInfoViewVisible = true;
      //深拷贝 浅拷贝的话改这个值 roleInfo的值也会被改
      this.hrForm = JSON.parse(JSON.stringify(this.roleInfo));
    },

3.修改密码

在这里插入图片描述

    <el-dialog title="密码修改" :visible.sync="passwordFormVisible" width="25%">
      <el-form
        :model="ruleForm"
        :rules="rules"
        label-width="100px"
        ref="ruleForm"
      >
        <el-form-item label="请输入旧密码" prop="oldpass">
          <el-input
            type="password"
            show-password
            :clearable="true"
            v-model="ruleForm.oldpass"
            autocomplete="off"
          >
          </el-input>
        </el-form-item>
        <el-form-item label="请输入新密码" prop="pass">
          <el-input
            type="password"
            show-password
            :clearable="true"
            v-model="ruleForm.pass"
            autocomplete="off"
          >
          </el-input>
        </el-form-item>
        <el-form-item label="确认新密码" prop="checkPass">
          <el-input
            type="password"
            show-password
            :clearable="true"
            v-model="ruleForm.checkPass"
            autocomplete="off"
          >
          </el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="passwordFormVisible = false">取 消</el-button>
        <el-button type="primary" @click="modefyPassword('ruleForm')"
          >确 定</el-button
        >
      </div>
    </el-dialog>

数据部分:

//控制弹框显示
passwordFormVisible: false,
//修改密码时的表单数据
ruleForm: {
  oldpass: "",
  pass: "",
  checkPass: "",
},
//修改密码的校验规则 对应标签中的:rules="rules"
rules: {
  oldpass: [{ validator: validatePass, trigger: "blur" }],
  pass: [{ validator: validatePass, trigger: "blur" }],
  checkPass: [{ validator: validatePass2, trigger: "blur" }],
},
//第一个密码校验方法
var validatePass = (rule, value, callback) => {
  if (value === "") {
    callback(new Error("请输入密码"));
  } else {
    if (this.ruleForm.checkPass !== "") {
      this.$refs.ruleForm.validateField("checkPass");
    }
    callback();
  }
};
//第二个密码校验方法
var validatePass2 = (rule, value, callback) => {
  if (value === "") {
    callback(new Error("请再次输入密码"));
  } else if (value !== this.ruleForm.pass) {
    callback(new Error("两次输入密码不一致!"));
  } else {
    callback();
  }
};

方法部分

    //发请求修改密码
    modefyPassword(formName) {
      //el form 中需要加ref="ruleForm" 不然会报错: undefined (reading 'validate')
      this.$refs[formName].validate((valid) => {
        if (valid) {
        	//根据hrId修改
          this.ruleForm.hrId = this.hrId;
          this.postRequest("/modefyPassword", this.ruleForm).then((resp) => {
            if (resp) {
              this.getRequest("/logout");
              this.$router.replace("/");
            }
          });
        } else {
          return false;
        }
      });
    },

3.超级管理员设置

效果
在这里插入图片描述
三部分:搜索部分+显示所有的Hr信息部分+添加hr部分,
在这里插入图片描述

1.搜索部分

html
div 弹性布局 元素居中
@keydown.enter.native回车调用方法

        <div style="margin-top: 10px; display: flex; justify-content: center">
            <el-input v-model="keyword" placeholder="通过用户名搜索用户..." prefix-icon="el-icon-search"
                style="width: 400px; margin-right: 10px" @keydown.enter.native="doSearch"></el-input>
            <el-button icon="el-icon-search" type="primary" @click="doSearch">搜索</el-button>
        </div>

js
数据部分
搜索关键字

keyword: '',

方法:
获取所有的hr信息,传入keyword

doSearch(){
   this.getHrs()
},  
getHrs() {
  this.getRequest("/getHrs", {
    keyword:this.keyword,
  }).then((resp) => {
    if (resp) {
      this.hrs = resp.data;
    }
  });
},

2.hr信息部分

html

        <div class="hr-container">
            <!-- 遍历所有的hr信息 每个hr信息都是一个el-card -->
            <el-card class="hr-card" v-for="(hr, index) in hrs" :key="index">
                <!--名字和删除图标  -->
                <div slot="header" class="clearfix">
                    <span>{{ hr.name }}</span>
                    <el-button style="float: right; padding: 3px 0; color: #e30007" type="text" icon="el-icon-delete"
                        @click="deleteHr(hr)"></el-button>
                </div>
                <!-- 头像 -->
                <div style="display: flex; justify-content: center; margin-bottom: 10px">
                    <img src="./images/touxiang.png" alt="" style="width: 60px; height: 60px; border-radius: 50px" />
                </div>
                <!-- 用户信息 -->
                <div class="userinfo-container">
                    <div>用户名:{{ hr.name }}</div>
                    <div>手机号码:{{ hr.phone }}</div>
                    <div>电话号码:{{ hr.telephone }}</div>
                    <div>地址:{{ hr.address }}</div>
                    <div>
                        用户状态:
                        <el-switch v-model="hr.enabled" active-text="启用" @change="enabledChange(hr.id,hr.enabled)"
                            active-color="#13ce66" inactive-color="#ff4949" inactive-text="禁用" :active-value="1"
                            :inactive-value="0">
                        </el-switch>
                    </div>
                    <div>
                        用户角色:
                        <el-tag type="success" style="margin-right: 4px" v-for="(role, indexj) in hr.rolesZh"
                            :key="indexj">{{ role }}
                        </el-tag>
                        <!-- 角色列表 -->
                        <el-popover placement="right" title="角色列表" @show="showPop(hr)" @hide="hidePop(hr)" width="220"
                            trigger="click">
                            <el-select v-model="selectedRoles" multiple placeholder="请选择">
                                <el-option v-for="(item,i) in allroles" :key="i" :label="item.nameZh"
                                    :value="item.nameZh">
                                </el-option>
                            </el-select>
                            <el-button slot="reference" icon="el-icon-more" type="text"></el-button>
                        </el-popover>
                    </div>
                    <div>备注:{{ hr.remark }}</div>
                </div>
            </el-card>
            <!-- 最后一个card 点击添加hr信息 -->
            <el-card class="hr-card" style="min-height:300px;">
                <div style="display: flex; justify-content: center;margin-top:40% ;cursor: pointer">
                    <el-tooltip class="item" effect="dark" content="添加新的Hr" placement="right-end">
                        <i class="el-icon-circle-plus-outline" @click="addHr"></i>
                    </el-tooltip>
                </div>
            </el-card>
        </div>

css

<style>

.userinfo-container div {
  font-size: 12px;
  color: #409eff;
}
.hr-container {
  margin-left: 20px;
  margin-top: 10px;
  display: flex;
  /* 必要的时候可以拆行 */
  flex-wrap: wrap;
  /* 盒子平均分配居中布局 但是最后一行盒子不满的话 也会居中 */
  /* justify-content: space-around; */
}

/* justify-content: space-around; 让最后一行元素靠左对齐 效果是最后一行和上一行不对齐 所以没用  */
/* 采用固定盒子 也就是card加左右边距margin */
/* 
// .hr-container::after {
//   content: "";
//   display: block;
//   flex: 1; /* 与flex: auto;等效都是自适应剩余空间
}
*/

.hr-card {
  width: 350px;
  margin: 20px;
}
</style>

js
数据部分

hrs: {},//所有的hr信息
allroles: [], //所有角色信息
selectedRoles: [],//当前选择的角色

页面创建时 给hrs赋值
在这里插入图片描述
后端返回的数据格式 数组格式

{
	"msg": "",
	"code": 0,
	"data": [{
		"id": 5,
		"name": "李白",
		"phone": "18568123489",
		"telephone": "029-82123434",
		"address": "海口美兰",
		"enabled": 1,
		"username": null,
		"password": null,
		"userface": "./images/touxiang1.png",
		"remark": null,
		"roles": null,
		"rolesZh": []
	}, {
		"id": 14,
		"name": "李四",
		"phone": null,
		"telephone": null,
		"address": null,
		"enabled": 1,
		"username": null,
		"password": null,
		"userface": "./images/touxiang1.png",
		"remark": null,
		"roles": null,
		"rolesZh": []
	}]
}

修改用户角色时 给allroles selectedRoles赋值
在这里插入图片描述
后端返回的数据格式
数组

{
	"msg": "",
	"code": 0,
	"data": [{
		"id": 1,
		"name": null,
		"nameZh": "部门经理"
	}, {
		"id": 2,
		"name": null,
		"nameZh": "人事专员"
	}, {
		"id": 3,
		"name": null,
		"nameZh": "招聘主管"
	}, {
		"id": 4,
		"name": null,
		"nameZh": "培训主管"
	}, {
		"id": 5,
		"name": null,
		"nameZh": "薪酬绩效主管"
	}, {
		"id": 6,
		"name": null,
		"nameZh": "系统管理员"
	}]
}
1.修改用户状态

在这里插入图片描述
在这里插入图片描述
js
传入当前hrId和使能状态

   //修改hr的使能情况
   enabledChange(id, enabled) {
     this.getRequest("/modifyEnabled", { id: id, enabled: enabled }).then(
       (resp) => {
         if (resp) {
           this.getHrs();
         }
       }
     );
   },
2.修改用户角色

在这里插入图片描述
在这里插入图片描述
打开弹框再关闭时会触发角色更新

    hidePop(hr) {
      //新增前要判断 当前选中的角色中,去掉目前hr有的角色
      //遍历选中的角色,如果是目前hr有的角色 就从selectedRoles中删掉
      // for (var i = this.selectedRoles.length - 1; i >= 0; i--) {
      //     for (var j = hr.rolesZh.length - 1; j >= 0; j--) {
      //         if (this.selectedRoles[i] == hr.rolesZh[j]) {
      //             this.selectedRoles.splice(i, 1);
      //             break;
      //         }
      //     }
      // }
      //发送请求 给hr新增角色 直接传当前选择的所有角色,后端处理时需要删掉当前hr有的角色,然后插入这些数据。成功后刷新所有hr的信息。      
      this.postRequest("/modifyRoles", {
        id: hr.id,
        rolesZh: this.selectedRoles,
      }).then((resp) => {
        if (resp) {
          this.getHrs();
        }
      });
      // console.log(this.selectedRoles)
    },
3.删除hr信息

在这里插入图片描述
在这里插入图片描述
js

    deleteHr(hr) {
    	//弹出确认框
      this.$confirm("此操作将删除该Hr的信息, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
      	//发送请求
        this.getRequest("/deleteHr", {
          hrId: hr.id,
        }).then((resp) => {
          if (resp) {
          //成功后刷新所有hr
            this.getHrs();
          }
        });
      });
    },

3.新增hr信息

在这里插入图片描述
html

    <el-dialog
      title="添加Hr账号"
      :visible.sync="addHrInfoViewVisible"
      width="25%"
    >
      <el-form
        :model="hrForm"
        label-width="100px"
        ref="hrForm"
        :rules="roleInfoRules"
      >
        <el-form-item label="用户昵称" prop="name">
          <el-input :clearable="true" v-model="hrForm.name"> </el-input>
        </el-form-item>
        <el-form-item label="电话号码" prop="phone">
          <el-input :clearable="true" v-model="hrForm.phone"> </el-input>
        </el-form-item>
        <el-form-item label="手机号码" prop="telephone">
          <el-input :clearable="true" v-model="hrForm.telephone"> </el-input>
        </el-form-item>
        <el-form-item label="居住地址" prop="address">
          <el-input :clearable="true" v-model="hrForm.address"> </el-input>
        </el-form-item>
        <el-form-item label="账号" prop="username">
          <el-input :clearable="true" v-model="hrForm.username"> </el-input>
        </el-form-item>
        <el-form-item label="密码" prop="password">
          <el-input :clearable="true" v-model="hrForm.password"> </el-input>
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button @click="addHrInfoViewVisible = false">取 消</el-button>
        <el-button type="primary" @click="addHrInfo('hrForm')">确 定</el-button>
      </div>
    </el-dialog>

js
data部分

addHrInfoViewVisible: false, //显示弹框
//表单数据
hrForm: {
  name: "",
  phone: "",
  telephone: "",
  address: "",
  username: "",
  password: "",
},
//新增hr信息时 昵称的校验规则
roleInfoRules: {
  name: [{ validator: validateName, trigger: "blur" }],
},
//昵称校验方法
var validateName = (rule, value, callback) => {
  if (value === "") {
    callback(new Error("请输入昵称"));
  } else {
    callback();
  }
};

方法

//点击按钮 控制弹框出来
addHr() {
  this.addHrInfoViewVisible = true;
},
//发送请求插入数据
addHrInfo(roleInfo) {
  this.$refs[roleInfo].validate((valid) => {
    if (valid) {
      this.postRequest("/addHrInfo", this.hrForm).then((resp) => {
        if (resp) {
          this.getHrs();
          this.addHrInfoViewVisible = false;
        }
      });
    } else {
      return false;
    }
  });
},

4.员工资料

界面效果
在这里插入图片描述
功能:
数据分页展示,根据员工名字模糊查询,根据其他信息匹配查询,删除、修改、新增员工信息,导入导出excel格式的数据。

1.数据分页展示

html

    <!-- 表格部分 -->
    <div style="margin-top: 10px">
      <!-- 绑定数据为emps :row-class-name="tableRowClassName"指定每行的类名 显示不同颜色背景 -->
      <el-table :data="emps" border style="width: 100%" v-loading="loading" element-loading-text="正在加载..."
        element-loading-spinner="el-icon-loading" element-loading-background="rgba(0, 0, 0, 0.8)"
        :row-class-name="tableRowClassName">
        <!-- 名字这一列固定 不随滚动条滚动 -->
        <el-table-column property="name" fixed label="姓名" width="100" height="50px">
        </el-table-column>
        <el-table-column property="workId" label="工号" width="100">
        </el-table-column>
        <el-table-column property="workState" label="在职状态" width="100">
        </el-table-column>
        <el-table-column property="gender" label="性别" width="60">
        </el-table-column>
        <el-table-column property="birthday" label="出生日期" width="120">
        </el-table-column>
        <el-table-column property="idCard" label="身份证号码" width="170">
        </el-table-column>
        <el-table-column property="wedlock" label="婚姻状况" width="80">
        </el-table-column>
        <el-table-column property="nationEntity.name" label="民族" width="70">
        </el-table-column>
        <el-table-column property="nativePlace" label="籍贯" width="80">
        </el-table-column>
        <el-table-column property="politicsstatusEntity.name" label="政治面貌" width="120">
        </el-table-column>
        <el-table-column property="email" label="电子邮件" width="170">
        </el-table-column>
        <el-table-column property="phone" label="电话号码" width="120">
        </el-table-column>
        <el-table-column property="address" label="联系地址" width="190">
        </el-table-column>
        <el-table-column property="departmentEntity.name" label="所属部门" width="100">
        </el-table-column>
        <el-table-column property="positionEntity.name" label="职位" width="100">
        </el-table-column>
        <el-table-column property="joblevelEntity.name" label="职称" width="100">
        </el-table-column>
        <el-table-column property="engageForm" label="聘用形式" width="100">
        </el-table-column>
        <el-table-column property="tiptopDegree" label="最高学历" width="100">
        </el-table-column>
        <el-table-column property="specialty" label="专业" width="150">
        </el-table-column>
        <el-table-column property="school" label="毕业院校" width="140">
        </el-table-column>
        <el-table-column property="beginDate" label="入职日期" width="100">
        </el-table-column>
        <el-table-column property="conversionTime" label="转正日期" width="100">
        </el-table-column>
        <el-table-column property="beginContract" label="合同起始日期" width="110">
        </el-table-column>
        <el-table-column property="endContract" label="合同终止日期" width="110">
        </el-table-column>
        <el-table-column property="contractTerm" label="合同期限/年" width="100">
        </el-table-column>
        <!-- 操作这一列固定在最右边 -->
        <el-table-column fixed="right" label="操作" width="100">
          <template slot-scope="scope">
            <el-button @click="showEditEmpView(scope.row)" style="padding: 4px" size="mini">编辑
            </el-button>
            <el-button type="danger" @click="deleteEmp(scope.row)" size="mini" style="padding: 3px">删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <!-- 分页工具展示-->
    <div class="block" style="float: right; margin: 10px">
      <!--            <span class="demonstration">完整功能</span>-->
      <el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="page.pageNum"
        :page-sizes="[5, 10, 15, 20, 25, 30, 35, 40, 45, 50]" :page-size="page.pageSize"
        layout="total, sizes, prev, pager, next, jumper" :total="page.total">
      </el-pagination>
    </div>    

css

//去掉表格的padding 缩小表格每行的高度
.el-table {
  /deep/ th {
    padding: 10;
  }
  /deep/ td {
    //大于0的时候不管多少都一个效果 ???
    padding: 10;
    
  }
//不同类名显示不同背景颜色
  /deep/ .warning-row {
    background: oldlace;
  }
  /deep/ .success-row {
    background: #e2f7d6;
  }
}

js
data:

loading: false, //查询数据时的显示状态 true就转圈圈
emps: [], //存所有员工
//分页参数 当前页 每页大小 总数
page: {
  pageNum: 1,
  pageSize: 10,
  total: 0,
},

methods:

    //获取所有的员工
    getEmps() {
      this.loading = true;
      //查询时选择了部门,新增或修改时选择了部门 depId保存的是选择的部门id数组 后端只需要最后一级的部门id,在查询前进行处理
      if (this.depId.length > 0) {
        this.search.departmentId = this.depId[this.depId.length - 1];
      }
      //发送查询请求,传入分页参数和条件搜索项
      this.getRequest("/getEmps", {
        pageNum: this.page.pageNum,
        pageSize: this.page.pageSize,
        searchParam: this.search,
      }).then((resp) => {
        //有回复的话,给emps赋值,给分页参数赋值
        this.loading = false;
        if (resp) {
          this.emps = resp.data.list;
          this.page.pageNum = resp.data.pageNum;
          this.page.pageSize = resp.data.pageSize;
          this.page.total = resp.data.total;
        }
      });
    },
    //绑定的每行的类名,根据每行的workState进行判断
    tableRowClassName({row}) {
      if (row.workState === '离职') {
        return 'success-row';
      } 
      else
        return 'warning-row';
    },
    //处理每页显示条数
    handleSizeChange(val) {
      this.page.pageSize = val;
      this.getEmps();
      console.log(`每页 ${val}`);
    },
    //处理页面切换
    handleCurrentChange(val) {
      this.page.pageNum = val;
      this.getEmps();
      console.log(`当前页: ${val}`);
    },

created

  //页面加载 获取所有的用户
  created: function () {
    this.getEmps();
  },

回复数据:
data中list为员工数据,类型为数组,其他为分页参数,表格标签中绑定的数值为这里的数值。

{
	"msg": "",
	"code": 0,
	"data": {
		"total": 637,
		"list": [{
			"id": 1,
			"name": "江南一点雨",
			"gender": "女",
			"birthday": "1990-01-01",
			"idCard": "610122199001011256",
			"wedlock": "已婚",
			"nationId": 2,
			"nativePlace": "陕西",
			"politicId": 13,
			"email": "laowang@qq.com",
			"phone": "18565558897",
			"address": "深圳市南山区",
			"departmentId": 124,
			"jobLevelId": 9,
			"posId": 29,
			"engageForm": "劳务合同",
			"tiptopDegree": "本科",
			"specialty": "信息管理与信息系统",
			"school": "深圳大学",
			"beginDate": "2018-01-01",
			"workState": "在职",
			"workId": "00000001",
			"contractTerm": 2.0,
			"conversionTime": "2018-04-01",
			"notWorkDate": null,
			"beginContract": "2018-01-01",
			"endContract": "2020-01-01",
			"workAge": null,
			"nationEntity": {
				"id": 2,
				"name": "蒙古族"
			},
			"politicsstatusEntity": {
				"id": 13,
				"name": "普通公民"
			},
			"departmentEntity": {
				"id": 124,
				"name": "线下营销2部",
				"parentid": null,
				"deppath": null,
				"enabled": null,
				"isparent": null,
				"children": null
			},
			"joblevelEntity": {
				"id": 9,
				"name": "教授",
				"titlelevel": null,
				"createdate": null,
				"enabled": null
			},
			"positionEntity": {
				"id": 29,
				"name": "技术总监",
				"createdate": null,
				"enabled": null
			}
		}],
		"pageNum": 1,
		"pageSize": 10,
		"size": 10,
		"startRow": 1,
		"endRow": 10,
		"pages": 64,
		"prePage": 0,
		"nextPage": 2,
		"isFirstPage": true,
		"isLastPage": false,
		"hasPreviousPage": false,
		"hasNextPage": true,
		"navigatePages": 8,
		"navigatepageNums": [1, 2, 3, 4, 5, 6, 7, 8],
		"navigateFirstPage": 1,
		"navigateLastPage": 8
	}
}

2.搜索功能

1.根据名字模糊搜索

效果
在这里插入图片描述
html

        <!-- 输入框 可清除的 绑定的数据为v-model="search.employeeName" 回车搜索@keydown.enter.native="getEmps -->
        <!-- :disabled="showAdvanceSearchView" 如果是高级搜索的话此搜索不可用 -->
        <el-input placeholder="请输入员工名进行搜索,可以直接回车搜索..." prefix-icon="el-icon-search" clearable
          style="width: 350px; margin-right: 10px" v-model="search.employeeName" @keydown.enter.native="getEmps"
          :disabled="showAdvanceSearchView"></el-input>
        <el-button icon="el-icon-search" type="primary" @click="getEmps" :disabled="showAdvanceSearchView">
          搜索
        </el-button>

点击搜索会根据输入的条件获取所有员工
在这里插入图片描述

2.高级搜索

在这里插入图片描述

html

        <!-- 点击触发两个事件 1.获取初始数据 2.展示搜索页面  -->
        <el-button type="primary" @click="init_data();handSearchView()">
          <i :class="
            showAdvanceSearchView
              ? 'fa fa-angle-double-up'
              : 'fa fa-angle-double-down'
          " aria-hidden="true"></i>
          高级搜索
        </el-button>
        
    <!-- 高级搜索部分 -->
    <!-- transaction是动画效果 -->
    <!-- solid(实线) -->
    <!-- box-sizing: border-box就是将border和padding数值包含在width和height之内,这样的好处就是修改border和padding数值盒子的大小不变。 -->
    <transition name="slide-fade">
      <div v-show="showAdvanceSearchView" style="
          border: 1px solid #409eff;
          border-radius: 5px;
          box-sizing: border-box;
          padding: 5px;
          margin: 10px 0px;
        ">
        <!--el-row 行格式  el-col行内容 col可以有很多个 但是span的总值为24 不够的话可以设置:offset -->
        <el-row>
          <el-col :span="5">
            政治面貌:
            <el-select v-model="search.politicId" placeholder="政治面貌" size="mini" style="width: 130px" clearable>
              <el-option v-for="item in politicsstatus" :key="item.id" :label="item.name" :value="item.id">
              </el-option>
            </el-select>
          </el-col>
          <el-col :span="4">
            民族:
            <el-select v-model="search.nationId" placeholder="民族" size="mini" style="width: 130px" clearable>
              <el-option v-for="item in nations" :key="item.id" :label="item.name" :value="item.id">
              </el-option>
            </el-select>
          </el-col>
          <el-col :span="4">
            职位:
            <el-select v-model="search.posId" placeholder="职位" size="mini" style="width: 130px" clearable>
              <el-option v-for="item in positions" :key="item.id" :label="item.name" :value="item.id">
              </el-option>
            </el-select>
          </el-col>
          <el-col :span="4">
            职称:
            <el-select v-model="search.jobLevelId" placeholder="职称" size="mini" style="width: 130px" clearable>
              <el-option v-for="item in joblevels" :key="item.id" :label="item.name" :value="item.id">
              </el-option>
            </el-select>
          </el-col>
          <!-- 按钮组 -->
          <el-col :span="7">
            聘用形式:
            <el-radio-group v-model="search.engageForm">
              <el-radio label="劳动合同">劳动合同</el-radio>
              <el-radio label="劳务合同">劳务合同</el-radio>
            </el-radio-group>
          </el-col>
        </el-row>
        <el-row style="margin-top: 10px">
          <el-col :span="5">
            所属部门:
            <el-cascader size="mini" v-model="depId" :options="options" :props="optionProps" :show-all-levels="false"
              :checkStrictly="true" clearable>
            </el-cascader>
            <!-- </div> -->
          </el-col>
          <el-col :span="9">
            入职日期:
            <el-date-picker v-model="search.start" type="date" placeholder="选择日期" format="yyyy 年 MM 月 dd 日"
              value-format="yyyy-MM-dd" size="mini">
            </el-date-picker>
            至:
            <el-date-picker v-model="search.end" type="date" placeholder="选择日期" format="yyyy 年 MM 月 dd 日"
              value-format="yyyy-MM-dd" size="mini">
            </el-date-picker>
          </el-col>

          <!-- 按钮组 -->
          <el-col :span="5">
            在职状态:
            <el-radio-group v-model="search.workState">
              <el-radio label="在职">在职</el-radio>
              <el-radio label="离职">离职</el-radio>
            </el-radio-group>
          </el-col>

          <el-col :span="5">
            <el-button size="mini" @click="clearSearch()">取消</el-button>
            <el-button size="mini" icon="el-icon-search" type="primary" @click="getEmps()">搜索</el-button>
          </el-col>
        </el-row>
      </div>
    </transition>        

js
data:

//查询参数 姓名为基础模糊查询 其他为高级查询
search: {
  // workState:"",     //在职状态
  // employeeName: "", //员工姓名
  // departmentId: "", //部门的id
  // politicId: "", //政治面貌id
  // nationId: "", //民族id
  // posId: "", //职位id
  // jobLevelId: "", //职称id
  // engageForm: "", //聘用形式
  // start: "", //入职日期起始时间
  // end: "", //入职日期结束时间
},
showAdvanceSearchView: false, //高级搜索弹框显示
//点击高级搜索时会获取这些可选值
joblevels: [], //职称
positions: [], //职位
nations: [], //民族     				//数据为id和name
politicsstatus: [], //政治面貌
departments: [], //部门
//el-cascader 选择之后的id数组 这个要特殊处理 只需用到数组的最后一个值,也就是最终选择的那一项。
//在发送查询请求时处理 获取该数组的最后一个值:this.depId[this.depId.length - 1] 然后赋值给 search.departmentId
depId: [],
//部门选择el- cascader选择的数据,获取到数据后将数据赋值给他
options: [],
//对应显示的内容label为部门名字 选择的值id为部门id
//这里是给el- cascader选择数据做映射,
optionProps: {
	 value: "id",
	 label: "name",
	 children: "children",
	 checkStrictly: true, //可以选择任意一级
	 expandTrigger: "hover", //鼠标放上触发
},

在这里插入图片描述

methods:

	//高级搜索按钮绑定的第一个事件,控制搜索框弹出
    handSearchView(){
      this.showAdvanceSearchView = !this.showAdvanceSearchView;
    },
    //高级查询时初始化可选数据  高级搜索按钮绑定的第二个事件
    init_data() {
      //获取所有的职称
      if (this.joblevels == undefined || this.joblevels.length <= 0) {
        this.getRequest("/joblevel/getJoblevels", {}).then((resp) => {
          if (resp) {
            this.joblevels = resp.data;
          }
        });
      }
      //获取所有政治面貌
      if (this.politicsstatus == undefined || this.politicsstatus.length <= 0) {
        this.getRequest("/politicsstatus/getPoliticsstatuss", {}).then(
          (resp) => {
            if (resp) {
              this.politicsstatus = resp.data;
            }
          }
        );
      }
      //获取所有民族
      if (this.nations == undefined || this.nations.length <= 0) {
        this.getRequest("/nations/getNations", {}).then((resp) => {
          if (resp) {
            this.nations = resp.data;
          }
        });
      }
      //获取所有的职位
      if (this.positions == undefined || this.positions.length <= 0) {
        this.getRequest("/position/getPositions", {}).then((resp) => {
          if (resp) {
            this.positions = resp.data;
          }
        });
      }
      //获取所有的部门
      if (this.departments == undefined || this.departments.length <= 0) {
        this.getRequest("/department/getDepartments", {}).then((resp) => {
          if (resp) {
          	//在这里给options赋值 获取到所有的部门(包含children属性)
            this.departments = resp.data;
            this.options = this.departments;
            // this.options.value = this.departments.id;
            // this.options.label = "123";
            // this.options.children = this.departments.children;
            this.rewriteKey(this.options);
          }
        });
      }
    },
    //解决选择部门时el-cascader最后显示无数据的问题
    rewriteKey(val) {
      val.forEach((item) => {
        item.children = item.children.length !== 0 ? item.children : undefined;
        if (item.children) {
          this.rewriteKey(item.children);
        }
      });
    },
    //点击取消时,取消搜索框,清空搜索数据
    clearSearch(){
    this.search={};
    // this.options.length=0;
    this.showAdvanceSearchView=false;
    }

3.编辑和新增用户信息

1.新增

在这里插入图片描述
在这里插入图片描述
html

        <el-button type="primary" icon="el-icon-plus" @click="showAddEmpView">
          添加用户
        </el-button>
        

dialog弹框,代码有点多—
一个大表单,行格式

    <el-dialog :title="title" :visible.sync="dialogVisible" width="80%" @close='clearEmp'>
      <div>
        <el-form :model="emp" :rules="rules" ref="empForm">
          <!-- 第一行 -->
          <el-row>
            <el-col :span="6">
              <el-form-item label="姓名:" prop="name">
                <el-input size="mini" style="width: 150px" prefix-icon="el-icon-edit" v-model="emp.name"
                  placeholder="请输入员工姓名"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="5">
              <el-form-item label="性别:" prop="gender">
                <el-radio-group v-model="emp.gender" style="margin-top: 13px">
                  <el-radio label=""></el-radio>
                  <el-radio label=""></el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="出生日期:" prop="birthday">
                <el-date-picker v-model="emp.birthday" size="mini" type="date" value-format="yyyy-MM-dd"
                  style="width: 150px" placeholder="出生日期">
                </el-date-picker>
              </el-form-item>
            </el-col>
            <el-col :span="7">
              <el-form-item label="政治面貌:" prop="politicId">
                <el-select v-model="emp.politicId" placeholder="政治面貌" size="mini" style="width: 200px">
                  <el-option v-for="item in politicsstatus" :key="item.id" :label="item.name" :value="item.id">
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
          </el-row>
          <!-- 第二行 -->
          <el-row>
            <el-col :span="6">
              <el-form-item label="民族:" prop="nationId">
                <el-select v-model="emp.nationId" placeholder="民族" size="mini" style="width: 150px">
                  <el-option v-for="item in nations" :key="item.id" :label="item.name" :value="item.id">
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="5">
              <el-form-item label="籍贯:" prop="nativePlace">
                <el-input size="mini" style="width: 120px" prefix-icon="el-icon-edit" v-model="emp.nativePlace"
                  placeholder="请输入籍贯"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="电子邮箱:" prop="email">
                <el-input size="mini" style="width: 150px" prefix-icon="el-icon-message" v-model="emp.email"
                  placeholder="请输入电子邮箱"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="7">
              <el-form-item label="联系地址:" prop="address">
                <el-input size="mini" style="width: 200px" prefix-icon="el-icon-edit" v-model="emp.address"
                  placeholder="请输入联系地址"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <!-- 第三行 -->
          <el-row>
            <el-col :span="6">
              <el-form-item label="职位:" prop="posId">
                <el-select v-model="emp.posId" placeholder="职位" size="mini" style="width: 150px">
                  <el-option v-for="item in positions" :key="item.id" :label="item.name" :value="item.id">
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="5">
              <el-form-item label="职称:" prop="jobLevelId">
                <el-select v-model="emp.jobLevelId" placeholder="职称" size="mini" style="width: 150px">
                  <el-option v-for="item in joblevels" :key="item.id" :label="item.name" :value="item.id">
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="所属部门:" prop="depId">
                <el-cascader size="mini" v-model="depId" :options="options" :props="optionProps_a"
                  :show-all-levels="false" style="margin-top: 5px" :placeholder="depId2"></el-cascader>
              </el-form-item>
            </el-col>
            <el-col :span="7">
              <el-form-item label="电话号码:" prop="phone">
                <el-input size="mini" style="width: 200px" prefix-icon="el-icon-phone" v-model="emp.phone"
                  placeholder="电话号码"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <!-- 第四行 -->
          <el-row>
            <el-col :span="6">
              <el-form-item label="工号:" prop="workId">
                <el-input size="mini" style="width: 150px" prefix-icon="el-icon-edit" v-model="emp.workId"
                  placeholder="工号" disabled></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="5">
              <el-form-item label="学历:" prop="tiptopDegree">
                <el-select v-model="emp.tiptopDegree" placeholder="学历" size="mini" style="width: 150px">
                  <el-option v-for="item in tiptopDegrees" :key="item" :label="item" :value="item">
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="毕业院校:" prop="school">
                <el-input size="mini" style="width: 150px" prefix-icon="el-icon-edit" v-model="emp.school"
                  placeholder="毕业院校名称"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="7">
              <el-form-item label="专业名称:" prop="specialty">
                <el-input size="mini" style="width: 200px" prefix-icon="el-icon-edit" v-model="emp.specialty"
                  placeholder="请输入专业名称"></el-input>
              </el-form-item>
            </el-col>
          </el-row>
          <!-- 第五行 -->
          <el-row>
            <el-col :span="6">
              <el-form-item label="入职日期:" prop="beginDate">
                <el-date-picker v-model="emp.beginDate" size="mini" type="date" value-format="yyyy-MM-dd"
                  style="width: 130px" placeholder="入职日期">
                </el-date-picker>
              </el-form-item>
            </el-col>
            <el-col :span="5">
              <el-form-item label="转正日期:" prop="conversionTime">
                <el-date-picker v-model="emp.conversionTime" size="mini" type="date" value-format="yyyy-MM-dd"
                  style="width: 130px" placeholder="转正日期">
                </el-date-picker>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="合同起始日期:" prop="beginContract">
                <el-date-picker v-model="emp.beginContract" size="mini" type="date" value-format="yyyy-MM-dd"
                  style="width: 130px" placeholder="合同起始日期">
                </el-date-picker>
              </el-form-item>
            </el-col>
            <el-col :span="7">
              <el-form-item label="合同终止日期:" prop="endContract">
                <el-date-picker v-model="emp.endContract" size="mini" type="date" value-format="yyyy-MM-dd"
                  style="width: 150px" placeholder="合同终止日期">
                </el-date-picker>
              </el-form-item>
            </el-col>
          </el-row>
          <!-- 第六行 -->
          <el-row>
            <el-col :span="8">
              <el-form-item label="身份证号码:" prop="idCard">
                <el-input size="mini" style="width: 180px" prefix-icon="el-icon-edit" v-model="emp.idCard"
                  placeholder="请输入身份证号码"></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="聘用形式:" prop="engageForm">
                <el-radio-group v-model="emp.engageForm" style="margin-top: 13px">
                  <el-radio label="劳动合同">劳动合同</el-radio>
                  <el-radio label="劳务合同">劳务合同</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="婚姻状况:" prop="wedlock">
                <el-radio-group v-model="emp.wedlock" style="margin-top: 13px">
                  <el-radio label="已婚">已婚</el-radio>
                  <el-radio label="未婚">未婚</el-radio>
                  <el-radio label="离异">离异</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="clearEmp">取 消</el-button>
        <el-button type="primary" @click="doAddEmp">确 定</el-button>
      </span>
    </el-dialog>

需要注意的是选择部门,搜索时可以选择任意级别部门,新增和修改只能选最后一级部门
js
data:

//新增和修改员工信息绑定的对象
emp: {
  // workId: "",
},
dialogVisible: false, //新增或编辑员工信息
title:'',//修改或者新增
depId2: "",//修改部门时部门名回显值
//新增或者修改员工时必须部门选到最后一级
optionProps_a: {
  value: "id",
  label: "name",
  children: "children",
  checkStrictly: false, //只能选最后一级菜单
  expandTrigger: "hover", //鼠标放上触发
},
//新增和修改时学历选择
tiptopDegrees: [
  "本科",
  "大专",
  "硕士",
  "博士",
  "高中",
  "初中",
  "小学",
  "其他",
],
//校验规则
rules: {
  name: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
  gender: [{ required: true, message: '请输入性别', trigger: 'blur' }],
  birthday: [{ required: true, message: '请输入出生日期', trigger: 'blur' }],
  idCard: [{ required: true, message: '请输入身份证号码', trigger: 'blur' }, {
    pattern: /(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{2}$)/,
    message: '身份证号码格式不正确',
    trigger: 'blur'
  }],
  wedlock: [{ required: true, message: '请输入婚姻状况', trigger: 'blur' }],
  nationId: [{ required: true, message: '请输入您组', trigger: 'blur' }],
  nativePlace: [{ required: true, message: '请输入籍贯', trigger: 'blur' }],
  politicId: [{ required: true, message: '请输入政治面貌', trigger: 'blur' }],
  email: [{ required: true, message: '请输入邮箱地址', trigger: 'blur' }, {
    type: 'email',
    message: '邮箱格式不正确',
    trigger: 'blur'
  }],
  phone: [{ required: true, message: '请输入电话号码', trigger: 'blur' }],
  address: [{ required: true, message: '请输入员工地址', trigger: 'blur' }],
  departmentId: [{ required: true, message: '请输入部门名称', trigger: 'blur' }],
  jobLevelId: [{ required: true, message: '请输入职称', trigger: 'blur' }],
  posId: [{ required: true, message: '请输入职位', trigger: 'blur' }],
  engageForm: [{ required: true, message: '请输入聘用形式', trigger: 'blur' }],
  tiptopDegree: [{ required: true, message: '请输入学历', trigger: 'blur' }],
  specialty: [{ required: true, message: '请输入专业', trigger: 'blur' }],
  school: [{ required: true, message: '请输入毕业院校', trigger: 'blur' }],
  beginDate: [{ required: true, message: '请输入入职日期', trigger: 'blur' }],
  workState: [{ required: true, message: '请输入工作状态', trigger: 'blur' }],
  workID: [{ required: true, message: '请输入工号', trigger: 'blur' }],
  contractTerm: [{ required: true, message: '请输入合同期限', trigger: 'blur' }],
  conversionTime: [{ required: true, message: '请输入转正日期', trigger: 'blur' }],
  notworkDate: [{ required: true, message: '请输入离职日期', trigger: 'blur' }],
  beginContract: [{ required: true, message: '请输入合同起始日期', trigger: 'blur' }],
  endContract: [{ required: true, message: '请输入合同结束日期', trigger: 'blur' }],
  workAge: [{ required: true, message: '请输入工龄', trigger: 'blur' }],
}

methods:

//添加员工控制弹窗
showAddEmpView() {
  this.init_data(); //初始化部门等可选择的数据
  this.title = "添加员工";
  this.getMaxWordID(); //获取最大员工号,用户不可指定不可修改
  this.dialogVisible = true;//控制弹框
},
//获取最大workid 新增时用
getMaxWordID() {
  //获取到部门id数组的最后一个元素
  this.getRequest("/getMaxWorkID").then((resp) => {
    if (resp) {
      this.emp.workId = resp.data;
    }
  });
},
//添加或修改员工信息
doAddEmp() {
  //如果有id说明是修改
  if (this.emp.id) {
    this.$refs['empForm'].validate(valid=>{
      if(valid){
        if (this.depId.length > 0) {
          this.emp.departmentId = this.depId[this.depId.length - 1];
        }
        this.putRequest("/updateEmp", this.emp).then((resp) => {
          if (resp) {
            // this.dialogVisible = false;
            //修改完成后重新加载页面,清空一下数据, 也可以用this.$router.go(0)
            location.reload();
          }
        });
      }
    })
  } else {
    this.$refs['empForm'].validate(valid=>{
      if(valid){
        if (this.depId.length > 0) {
          this.emp.departmentId = this.depId[this.depId.length - 1];
        }
        this.postRequest("/addEmp", this.emp).then((resp) => {
          if (resp) {
            // this.dialogVisible = false;
            //添加完成后重新加载页面,情况一下数据, 也可以用this.$router.go(0)
            location.reload();
          }
        });
      }
    })
  }
},
2.修改

和新增不同,修改需要回显数据,取消新增后要清空绑定的emp数据,不然新增时也会显示。
js

    //编辑员工控制弹框
    showEditEmpView(row) {
      this.init_data();
      this.dialogVisible = true;
      this.emp = row;//获取到当前行的员工信息进行回显,其中部门需要单独回显
      this.title = "编辑员工信息";
      //部门选择的初始化值
      this.depId2 = row.departmentEntity.name;
    },
    //修改时退出修改框 清空emp数据 防止别处显示
    clearEmp(){
      this.emp={};
      this.depId2='';
      this.dialogVisible = false;
    },    

4.导入和导出

导出
主要是发送请求,后端进行处理

        <el-button type="success" @click="exportData" icon="el-icon-download">
          导出数据
        </el-button>
    //导出数据
    exportData() {
      window.location = "http://localhost:8081/vhr/exportEmpData";
    },

导入
选择文件后发送请求“/vhr/importEmpData”,保存数据

        <el-upload :show-file-list="false" :disabled="importDataDisabled" :before-upload="beforeUpload"
          :on-success="onSuccess" :on-error="onError" style="display: inline-flex; margin-right: 8px"
          action="/vhr/importEmpData">
          <el-button :disabled="importDataDisabled" type="success" icon="el-icon-upload2">
            {{importDataBtnText}}
          </el-button>
        </el-upload>

js
data

importDataDisabled: false, //导入时按钮不可点击
importDataBtnText: '导入数据', //导入时显示正在导入

methods:

	//导入失败
    onError() {
      this.importDataBtnText = "导入数据";
      this.importDataBtnIcon = "el-icon-upload2";
      this.importDataDisabled = false;
    },
    //导入成功
    onSuccess() {
      this.importDataBtnText = "导入数据";
      this.importDataBtnIcon = "el-icon-upload2";
      this.importDataDisabled = false;
      this.getEmps();
    },
    //正在导入
    beforeUpload() {
      this.importDataBtnText = "正在导入";
      this.importDataBtnIcon = "el-icon-loading";
      this.importDataDisabled = true;
    },

5.删除员工信息

            <el-button
              type="danger"
              @click="deleteEmp(scope.row)"
              size="mini"
              style="padding: 3px"
              >删除
            </el-button>

method

    //删除
    deleteEmp(row) {
      //console.log(row);
      this.$confirm("此操作将永久删除该员工信息, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.deleteRequest("/deleteEmpById", { empId: row.id }).then(() => {
            this.loading = false;
            this.getEmps();
            // if (resp) {
            //   this.$message({
            //     type: "success",
            //     message: "删除成功!",
            //   });
            // }
          });
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
    },

5.员工奖惩

在这里插入图片描述

1.数据分页展示

html
el-table表格展示数据

    <!-- 第二个div 展示数据 -->
    <!-- 绑定数据 表头数据居中 内容数据居中 指定每行的类名,展示不同效果   -->
    <div style="width: 100%; margin-bottom: 10px">
      <el-table
        :data="empEcs"
        :header-cell-style="{ 'text-align': 'center' }"
        :cell-style="{ 'text-align': 'center' }"
        :row-class-name="tableRowClassName"
      >
        <el-table-column property="id" label="编号" width="100" height="50px">
        </el-table-column>
        <el-table-column
          property="empName"
          label="姓名"
          width="100"
          height="50px"
        >
        </el-table-column>
        <el-table-column
          property="ecDate"
          sortable
          label="奖罚日期"
          width="200"
          height="50px"
        >
        </el-table-column>
        <el-table-column
          property="ecReason"
          label="奖罚原因"
          show-overflow-tooltip
          width="100"
          height="50px"
        >
        </el-table-column>
        <el-table-column
          property="ecPoint"
          label="奖罚分数"
          width="100"
          height="50px"
        >
        </el-table-column>
        <!-- 根据类别进行过滤 -->
        <el-table-column
          prop="ecType"
          label="奖罚类别"
          width="100"
          :filters="[
            { text: '惩罚', value: 1 },
            { text: '奖励', value: 0 },
          ]"
          :filter-method="filterTag"
          filter-placement="bottom-end"
        >
          <template slot-scope="scope">
            <el-tag
              :type="scope.row.ecType === 1 ? 'primary' : 'success'"
              disable-transitions
              >{{ scope.row.ecType == 1 ? "惩罚" : "奖励" }}
            </el-tag>
          </template>
        </el-table-column>
        <el-table-column
          property="remark"
          label="备注"
          show-overflow-tooltip
          width="100"
          height="50px"
        >
        </el-table-column>
        <el-table-column label="操作" width="190" height="50px">
          <template slot-scope="scope">
            <el-button
              type="primary"
              @click="showEditEmpEcView(scope.row)"
              size="medium"
              style="padding: 4px"
              >编辑
            </el-button>
            <el-button
              type="danger"
              @click="deleteEmpEc(scope.row)"
              size="medium"
              style="padding: 4px"
              >删除
            </el-button>
          </template>
        </el-table-column>
      </el-table>
    </div>
    <!--第三个div 分页工具展示-->
    <div>
      <!--            <span class="demonstration">完整功能</span>-->
      <el-pagination
        @size-change="handleSizeChange"
        @current-change="handleCurrentChange"
        :current-page="page.pageNum"
        :page-sizes="[5, 10, 15, 20, 25, 30, 35, 40, 45, 50]"
        :page-size="page.pageSize"
        layout="total, sizes, prev, pager, next, jumper"
        :total="page.total"
      >
      </el-pagination>
    </div>    

css

<style lang="less" scoped>
.el-table {
  // /deep/ th {
  //   padding: 10;
  // }

  // /deep/ td {
  //   //大于0的时候不管多少都一个效果 ???
  //   padding: 10;
  // }

  /deep/ .warning-row {
    //大于0的时候不管多少都一个效果 ???
    // padding: 10; background: oldlace;
    background: oldlace;
  }

  /deep/ .success-row {
    //大于0的时候不管多少都一个效果 ???
    // padding: 10;
    background: #e2f7d6;
  }
}
</style>

data

      //数据展示数组 存的是奖惩对象
      empEcs: [
        {
          id: 1,
          empName: "张三",
          ecDate: "2021-6-4 11:22:33",
          ecReason: "迟到迟到迟到",
          ecPoint: "张三",
          ecType: "奖励",
          remark: "张三",
        },
      ],
      //分页参数
      page: {
        pageNum: 1,
        pageSize: 10,
        total: 0,
      },

methods

    //获取所有的奖惩信息
    getEcs() {
      //发送查询请求,传入分页参数和条件搜索项
      this.getRequest("/empec/getEmpEcs", {
        pageNum: this.page.pageNum,
        pageSize: this.page.pageSize,
        searchName: this.searchName,
      }).then((resp) => {
        //有回复的话,给empEcs赋值,给分页参数赋值
        if (resp) {
          this.empEcs = resp.data.list;
          this.page.pageNum = resp.data.pageNum;
          this.page.pageSize = resp.data.pageSize;
          this.page.total = resp.data.total;
        }
      });
    },
    //绑定的每行的类名,根据每行的ecType进行判断
    tableRowClassName({ row }) {
      // 如果是奖励
      if (row.ecType === 0) {
        return "success-row";
      } else return "";
    },   
    //处理每页显示条数
    handleSizeChange(val) {
      this.page.pageSize = val;
      this.getEcs();
      console.log(`每页 ${val}`);
    },
    //处理页面切换
    handleCurrentChange(val) {
      this.page.pageNum = val;
      this.getEcs();
      console.log(`当前页: ${val}`);
    },  
    //查找惩罚和奖励
    filterTag(value, row) {
      return row.ecType === value;
    },       

2.根据名字关键子模糊搜索

    <!-- 第一个div 搜索和添加用户 -->
    <!-- 弹性布局 两个子元素两边排列 -->
    <div
      style="
        display: flex;
        justify-content: space-between;
        margin: 10px 0px;
        width: 100%;
      "
    >
      <!-- 搜索 -->
      <div>
        <!-- 输入框 可清除的 绑定的数据为v-model="employeeName" 回车搜索@keydown.enter.native="getEmps -->
        <!-- :disabled="showAdvanceSearchView" 如果是高级搜索的话此搜索不可用 -->
        <el-input
          placeholder="请输入员工名进行搜索,可以直接回车搜索..."
          prefix-icon="el-icon-search"
          clearable
          style="width: 350px; margin-right: 10px"
          v-model="searchName"
          @keydown.enter.native="getEcs"
        >
        </el-input>
        <el-button icon="el-icon-search" type="primary" @click="getEcs">
          搜索
        </el-button>
      </div>
      <!-- 添加奖惩信息 -->
      <div>
        <el-button type="primary" icon="el-icon-plus" @click="addEmpEc">
          添加奖惩
        </el-button>
      </div>
    </div>

data

      // 查询参数
      searchName: "",

method
也是获取所有奖惩信息 getEcs(),传入输入的名字关键字

3.删除员工奖惩信息

html
在这里插入图片描述
method
删除之前弹框确认

   //删除数据绑定事件
    deleteEmpEc(row) {
      this.$confirm("此操作将永久删除该奖惩信息, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.deleteRequest("/empec/deleteEmpEcById", { id: row.id }).then(
            () => {
              this.getEcs();
              // if (resp) {
              //   this.$message({
              //     type: "success",
              //     message: "删除成功!",
              //   });
              // }
            }
          );
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
    },

4.新增和修改奖惩信息

1.修改

html
在这里插入图片描述
修改框效果
在这里插入图片描述

修改框:

    <!-- 添加和修改弹框 -->
    <el-dialog :title="title" :visible.sync="dialogVisible" @close="clearEmpEc">
      <div>
        <el-form :model="empEc" :rules="rules" ref="empEcForm">
          <el-row>
            <el-col :span="6">
              <el-form-item label="姓名:">
                <!-- 绑定的数据为用户id 展示的数据label为用户名字 选择的值 value也为用户id -->
                <!-- 这样回显数据时 传回用户id可以显示该id的名字 -->
                <el-select
                  size="mini"
                  style="width: 150px"
                  v-model="empEc.eid"
                  filterable
                  placeholder="可按名字搜索"
                >
                  <el-option
                    v-for="item in options"
                    :key="item.id"
                    :label="item.name"
                    :value="item.id"
                  >
                  </el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="7">
              <el-form-item label="奖罚类别:" prop="ecType">
               <!-- 绑定的数据empEc.ecType为整形 需要用:label这种形式 -->
                <el-radio-group v-model="empEc.ecType" style="margin-top: 13px">
                  <el-radio :label="0">奖励</el-radio>
                  <el-radio :label="1">惩罚</el-radio>
                </el-radio-group>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="奖罚日期:" prop="ecDate">
               <!-- 指定日期格式:value-format="yyyy-MM-dd" -->
                <el-date-picker
                  v-model="empEc.ecDate"
                  size="mini"
                  type="date"
                  value-format="yyyy-MM-dd"
                  style="width: 150px"
                  placeholder="奖罚日期"
                >
                </el-date-picker>
              </el-form-item>
            </el-col>
          </el-row>
          <el-row>
            <el-col :span="6">
              <el-form-item label="奖罚原因:" prop="ecReason">
                <el-input
                  size="mini"
                  style="width: 150px"
                  prefix-icon="el-icon-edit"
                  v-model="empEc.ecReason"
                  placeholder="请输入奖罚原因"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="7">
              <el-form-item label="奖罚分数:" prop="ecPoint">
                <el-input
                  size="mini"
                  style="width: 150px"
                  prefix-icon="el-icon-edit"
                  v-model="empEc.ecPoint"
                  placeholder="奖罚分数(0-100)"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>
            <el-col :span="6">
              <el-form-item label="备注:" prop="remark">
                <el-input
                  size="mini"
                  style="width: 150px"
                  prefix-icon="el-icon-edit"
                  v-model="empEc.remark"
                  placeholder="备注信息"
                  clearable
                ></el-input>
              </el-form-item>
            </el-col>
          </el-row>
        </el-form>
      </div>
      <span slot="footer" class="dialog-footer">
        <el-button @click="clearEmpEc">取 消</el-button>
        <el-button type="primary" @click="doAddEmpEc">确 定</el-button>
      </span>
    </el-dialog>

data

      //名字选择 保存所有名字和员工id
      options: [],
      //修改和新增的标题
      title: "",
      //修改和新增弹框可见
      dialogVisible: false,
      //新增和修改奖惩对象
      empEc: {},      

methods

    //控制修改奖惩弹框
    showEditEmpEcView(row) {
      //获取所有员工以供选择
      this.getEmps();
      this.title = "修改奖惩信息";
      this.empEc = row;
      this.dialogVisible = true;     
    },
    //获取所有的员工信息 员工id和姓名
    getEmps() {
      this.getRequest("/empec/getEmps", {}).then((resp) => {
        //有回复的话,给options赋值,给分页参数赋值
        if (resp) {
          this.options = resp.data;
        }
      });
    },    
    //执行添加和修改奖惩 如果empEc有id说明是修改
    doAddEmpEc() {
      if (this.empEc.id) {
        this.postRequest("/empec/updateEmpEc", this.empEc).then((resp) => {
          if (resp) {
            //修改完成后重新加载页面,情况一下数据, 也可以用this.$router.go(0)
            location.reload();
          }
        });
      } else {
        this.postRequest("/empec/addEmpEc", this.empEc).then((resp) => {
          if (resp) {
            //添加完成后重新加载页面,情况一下数据, 也可以用this.$router.go(0)
            location.reload();
          }
        });
      }
    },    
    //清空修改框的内容 点取消或者“x”关闭修改框时,清空empEc,保证新增时不会显示修改时回显的数据。
    clearEmpEc() {
      this.dialogVisible = false;
      this.empEc = {};
    },    
2.新增

method

    //添加弹框控制
    addEmpEc() {
      this.getEmps();
      this.title = "添加奖惩信息";
      this.dialogVisible = true;
    },
    //执行添加和修改奖惩
    doAddEmpEc() {
      if (this.empEc.id) {
        this.postRequest("/empec/updateEmpEc", this.empEc).then((resp) => {
          if (resp) {
            //修改完成后重新加载页面,情况一下数据, 也可以用this.$router.go(0)
            location.reload();
          }
        });
      } else {
        this.postRequest("/empec/addEmpEc", this.empEc).then((resp) => {
          if (resp) {
            //添加完成后重新加载页面,情况一下数据, 也可以用this.$router.go(0)
            location.reload();
          }
        });
      }
    },    

6.员工培训

在这里插入图片描述

在这里插入图片描述
css

<style  lang="less">
// card父div的样式
.hr-container {
  margin-left: 20px;
  margin-top: 10px;
  display: flex;
  /* 必要的时候可以拆行 */
  flex-wrap: wrap;
  /* 盒子平均分配居中布局 但是最后一行盒子不满的话 也会居中 */
  /* justify-content: space-around; */
}
/* 采用固定盒子 也就是card加左右边距margin */
.hr-card {
  width: 350px;
  margin: 20px;
}
/* 显示报名数量的样式 */
.item1 {
  margin-top: 10px;
  margin-right: 40px;
}
</style>

1.展示所有培训

html

    <div class="hr-container">
      <!-- 遍历所有的trains信息 每个train信息都是一个el-card -->
      <el-card class="hr-card" v-for="(train, index) in trains" :key="index">
        <!--名字和删除图标  -->
        <div slot="header" class="clearfix" style="text-align: center">
          <span>{{ train.name }}</span>

          <el-button
            style="float: right; padding: 3px 0; color: #e30007"
            type="text"
            icon="el-icon-delete"
            @click="deleteTrain(train.id)"
          ></el-button>
        </div>
        <!-- 头像 展示报名人数 el-badge-->
        <div
          style="display: flex; justify-content: center; margin-bottom: 10px"
        >
          <el-badge :value="train.total" class="item1">
            <img
              src="../images/java1.png"
              alt=""
              style="width: 60px; height: 60px; border-radius: 60px"
            />
          </el-badge>
        </div>
        <!-- 用户信息 -->
        <div class="userinfo-container">
          <div style="font-size: 18px; color: #e30007">
            培训日期:{{ train.trainDate }}
          </div>

          <div style="font-size: 18px; color: #e30007">
            培训内容:{{ train.trainContent }}
          </div>

          <div style="font-size: 18px; color: #e30007">
            备注:{{ train.remark }}
          </div>
 		<!-- 热度 el-rate评分图  show-score展示分数 score-template="{value}"分数数据绑定 -->
          <div
            style="
              display: flex;
              font-size: 18px;
              margin-top: 2px;
              color: #e30007;
            "
          >
            热度:
            <el-rate
              v-model="train.hot"
              disabled
              show-score
              text-color="#ff9900"
              score-template="{value}"
              style="margin-top: 3px"
            >
            </el-rate>
          </div>
          <div style="font-size: 18px; margin-top: 3px; color: #e30007">
            培训人员:
            <!-- 弹框为表格 固定宽度,超过宽度增加滚动条 需要在app.vue中增加样式
            在引用该组件的.vue中加这个样式也可以 但是不能加上 scoped 因为你这个组件的样式是全局的
            .el-popover{
              height: 300px; 
              overflow: auto;
            } -->
            <el-popover
              placement="right"
              width="320"
              trigger="click"
              class="pop-up"
            >
              <el-table :data="empData">
                <el-table-column
                  width="100"
                  property="empName"
                  label="姓名"
                ></el-table-column>
                <el-table-column
                  width="130"
                  property="depName"
                  label="部门"
                ></el-table-column>
                <!-- 删除图标展示 el-tooltip:文字提示-->
                <el-table-column width="70" label="操作">
                  <template slot-scope="scope">
                    <el-tooltip
                      content="移除该员工"
                      placement="right"
                      effect="light"
                    >
                      <el-button
                        style="padding: 3px 0; color: #e30007"
                        type="text"
                        icon="el-icon-delete"
                        @click="deleteTrainEmp(train.id, scope.row)"
                      ></el-button>
                    </el-tooltip>
                  </template>
                </el-table-column>
              </el-table>
              <!-- 点击获取报名的员工信息 -->
              <el-button
                slot="reference"
                size="mini"
                type="primary"
                @click="getTrainEmps(train.id)"
                >人员列表</el-button>
   <!-- 添加培训人员弹框 多级选择 collapse-tags:选择多个的时候采用+数字的方法 而不是扩大选择框 -->
            </el-popover>
            <!-- <el-button slot="reference" size="mini" type="success" style="margin-left:15px">添加培训人员 -->
            <!-- <div style="font-size:18px;margin-top: 3px;color: #e30007;">
              添加培训人员
            </div> -->
            <el-cascader
              ref="myCascader"
              :show-all-levels="false"
              :options="options"
              :props="optionProps"
              collapse-tags
              clearable
              placeholder="添加培训人员"
              size="small"
              style="width: 120px; margin-left: 10px"
              @visible-change="xiala($event, train.id, index)"
              v-model="train.selectId"
            >
            </el-cascader>
            <!-- </el-button> -->
          </div>
        </div>
      </el-card>
      <!-- 最后一个card 点击添加hr信息 -->
      <!-- style="min-height: 200px" 自己一行时保证card的高度 不会变矮-->
      <el-card class="hr-card" style="min-height: 300px">
        <div
          style="
            display: flex;
            justify-content: center;
            margin-top: 40%;
            cursor: pointer;
          "
        >
          <el-tooltip
            class="item"
            effect="dark"
            content="添加新的培训计划"
            placement="right-end"
          >
            <i class="el-icon-circle-plus-outline" @click="addEmpTrain"></i>
          </el-tooltip>
        </div>
      </el-card>
    </div>

data

      trains: [
        {
          name: "java培训",
          trainDate: "2022-6-6",
          trainContent: "zha",
          remark: "hahah",
          total:0hot:0},
      ],

method:

    //获取所有的培训
    gettrains() {
      //发送查询请求,传入分页参数和条件搜索项
      this.getRequest("/emptrain/getEmpTrains", {
        searchName: this.searchName,
      }).then((resp) => {
        //有回复的话,给empEcs赋值,给分页参数赋值

        if (resp) {
          this.trains = resp.data;
        }
      });
    },

2.人员列表

展示员工姓名和部门
在这里插入图片描述
data
包含员工名称和部门名称
在这里插入图片描述
method

//获取所有参与培训的员工 点击人员列表时触发
    getTrainEmps(id) {
      // alert(id);
      this.getRequest("/emptrain/getTrainEmps", {
        trainId: id,
      }).then((resp) => {
        //有回复的话,给empEcs赋值,给分页参数赋值

        if (resp) {
          this.empData = resp.data;
        }
      });
    },
    //删除培训的员工 传进来培训id和当前行
    deleteTrainEmp(trainId, row) {
      // alert(trainId)
      // alert(row.empId);
      this.deleteRequest("/emptrain/deleteTrainEmp", {
        trainId: trainId,
        empId: row.empId,
      }).then((resp) => {
        if (resp) {
          //重新获取参与的员工
          this.getTrainEmps(trainId);
          //重新获取所有的培训 刷新培训人数
          this.gettrains();
        }
      });
    },    

3.添加培训人员

在这里插入图片描述
data

	//映射规则 值为id展示name
      optionProps: {
        value: "id",
        label: "name",
        multiple: true,
        children: "children",
        // checkStrictly: true, //可以选择任意一级
        expandTrigger: "hover", //鼠标放上触发
      },
      //菜单项
      options: [
        {
          value: 1,
          label: "东南",
          children: [
            {
              value: 2,
              label: "上海",
              children: [
                { value: 3, label: "普陀" },
                { value: 4, label: "黄埔" },
                { value: 5, label: "徐汇" },
              ],
            },
          ],
        },
      ],
      //培训类
      train: {
        selectId: [],
      },      

method

    // 弹出下拉菜单时触发的事件 需要获取可参加培训的人员
    //弹回下拉菜单时触发事件 更新培训人员 和总数量
    xiala(type, id, index) {
      // alert(id);
      //弹出菜单
      if (type) {
      //获取可参加培训的人员
        this.getOptions(id);
      } else {
      //收回菜单
        //获取选择的节点 true:只获取叶子节点 也就是员工
        let node = this.$refs.myCascader[index].getCheckedNodes(true); // 获取选中节点对象
        // console.log(node[0].data.id)
        //获取已选择节点的id
        node.forEach((item) => {
          this.train.selectId.push(item.data.id);
        });
        // console.log(this.train.selectId)
        //传入已选择的员工id和该培训id
        this.doAddTrainEmps(this.train.selectId, id);
      }
    },
    //添加参与培训的员工
    doAddTrainEmps(selectId, trainId) {
      // console.log(selectId);

      // selectId.forEach((item) => {
      //删除数组第一个元素
      //     item.shift();
      // });
      //将数组中的数组变为number
      // for (var i = 0; i < selectId.length;i++){
      //   selectId[i]=selectId[i][0]
      // }
      this.postRequest("/emptrain/doAddTrainEmps", {
        selectId: selectId,
        trainId: trainId,
      }).then((resp) => {
        if (resp) {
          //添加完成后重新获取培训 更新总人数
          this.gettrains();
        }
      });
    },    
    

4.添加培训

添加基础培训信息,参与培训人员需要单独添加
在这里插入图片描述
data

       添加培训相关
      //规则
      rules: {
        name: [{ required: true, message: "请输入活动名称", trigger: "blur" }],
        trainDate: [{ required: true, message: "请选择日期", trigger: "blur" }],
        trainContent: [
          { required: true, message: "请输入培训内容", trigger: "blur" },
        ],
        hot: [
          { required: true, message: "请选择活动资源", trigger: "blur" },
          {
            pattern: /^([0-4]\.?\d{0,1}|5)$/,
            message: "评分0-5,且最多1位小数",
            trigger: "blur",
          }, //0-5 可包含以为小数
        ],
        remark: [{ required: true, message: "请输入备注", trigger: "blur" }],
      },
      dialogVisible: false,

methods

    //添加培训控制弹框
    addEmpTrain() {
      this.dialogVisible = true;
    },
    //重置表单
    resetForm(formName) {
      this.$refs[formName].resetFields();
    },
    //提交表单
    submitForm(train) {
      this.$refs[train].validate((valid) => {
        if (valid) {
          // console.log(this.train)
          // alert('submit!');
          this.postRequest("/emptrain/doAddTrain", this.train).then((resp) => {
            if (resp) {
              //添加完成后重新加载页面,情况一下数据, 也可以用this.$router.go(0)
              this.dialogVisible = false;
              this.gettrains();
            }
          });
        } else {
          console.log("error submit!!");
          return false;
        }
      });
    },

5.删除培训

method

    // 删除培训
    deleteTrain(trainId) {
      this.$confirm("此操作将永久删除该培训信息, 是否继续?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      })
        .then(() => {
          this.deleteRequest("/emptrain/deleteTrain", {
            trainId: trainId,
          }).then((resp) => {
            if (resp) {
              //重新获取所有的培训
              this.gettrains();
            }
          });
        })
        .catch(() => {
          this.$message({
            type: "info",
            message: "已取消删除",
          });
        });
    },
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值