前后分离,前端采用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:0,
hot: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: "已取消删除",
});
});
},