1.员工管理-导出excel
- 导出员工接口返回的是二进制
- axios配置responseType为blob接收二进制流文件为Blob格式
- 按装file-saver包,实现下载Blob文件
- npm install add file-saver
- 导出员工excel的接口 (src/api/employee.js)
export function exportEmployee(){
return request({
url:'/sys/user/export',
//改变接收数据的类型
responseType:'blob'//使用blob接收二进制文件流
})
}
- 拦截器判断是不是blob类型,如果是 直接返回数据,不再进行解构(src/utils/request.js)
//响应拦截器
service.interceptors.response.use((response)=>{
if(response.data instanceof Blob){
return response.data
}
})
- 点击按钮调用接口,使用file-saver将Blob转化成文件下载
<el-button size="mini" @click="exportEmployee">excel导出</el-button>
import FileSaver from 'file-saver'
import { exportEmployee } from '@/api/employee'
methods:{
async exportEmployee(){
const result = await exportEmployee()//导出所有的员工
console.log(result)//使用一个npm包 直接将blob文件下载到本地 file-saver
FileSaver.saveAs(result,'员工信息表.xlsx')
}
}
2.员工管理-excel组件
- 创建员工导入组件(src/views/employee/components/import-excel.vue)
<el-dialog width="500px" title="员工导入" :visible="showExcelDialog" @close="$emit('update:showExcelDialog',false)">
<el-row type="flex" justify="center">
<div class="upload-excel">
<input type="file" accept=".xlsx,.xls" ref="excel-upload-input" >
<div class="drop">
<i class="el-icon-upload"></i>
<el-button type="text">下载导入模板</el-button>
<span>将文件拖到此处或
<el-button type="text" >点击上传</el-button>
</span>
</div>
</div>
</el-row>
<el-row type="flex" justify=" end">
<!-- update:props属性名,值 直接修改 .sync修饰符的属性值 -->
<el-button size="mini" type="primary" @click="$emit('update:showExcelDialog')"></el-button>
</el-row>createError.js?2d83:16 Uncaught (in promise) Error: Request failed with status code 429
</el-dialog>
- 在员工管理页面-导入该组件并注册使用(src/views/employee/index.vue)
import ImportExcel from './components/import-excel.vue'
export default{
data(){
showExcelDialog:false//控制excel的弹窗显示和隐藏
},
components:{
ImportExcel
}
}
- 使用该组件,并且应用变量(src/views/employee/index.vue)
<import-excel :show-excel-dialog.sync = "showExcelDialog" />
- 点击excel导入按钮
<el-button size='mini' @click="showExcelDialog = true">excel导入</el-button>
3、下载导入模板
- 下载模板的api(src/api/employee.js)
// 下载导入模板
export function getExportTemplate(){
return request({
url:'/sys/user/import/template',
responseType:'blob'//二进制文件流
})
}
- 点击按钮进行下载(src/views/employee/components/import-excel.vue)
<el-button type='text' @click="getTemplate">下载导入模板</el-button>
async getTemplate(){
const data = await getExportTemplate();
FileSaver.saveAs(data,'员工导入模板.xlsx')
}
4.员工管理-员工导入-上传excel
- 上传excel的api(src/api/employee.js)
export function uploadExcel(data){
return request({
url:'/sys/user/import',
method:'post',
data //form-data类型
})
}
- 点击上传-弹出文件选择框(src/views/employee/components/import-excel.vue)
<el-button type="text" @click="handleUpload">点击上传</el-button>
handleUpload(){
this.$refs['excel-upload-input'].click()
}
- 监听文件改变-上传excel-关闭弹窗(src/views/employee/components/import-excel.vue)
JavaScript
<input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="uploadChange" />
async uploadChange(event){
console.log(event.target.files)
const files = event.target.files //input的文件列表
if(files.length > 0){
//大于0 说明有文件要上传
const data = new FormData()
//file:file类型
data.append('file',files[0])//将文件参数加入到formData中
try{
await uploadExcel(data)
//成功
this.$emit('uploadSuccess') //通知父组件 上传成功
this.$emit('update:showExcelDialog',false)//关闭弹层
}catch{
//捕获失败
//this.$refs['excel-upload-input'].value= ''
}finally{
//不论成功或者失败都执行finally
this.$refs['excel-upload-input'].value=''
}
}
}
- 不论成功或者失败,再点击上传都会去选择一个新的excel,使用finally等到最后,将内容清空。
- 父组件需要监听上传成功的事件(src/views/employee/index.vue)
<import-excel :show-excel-dialog.sync="showExcelDialog" @uploadSuccess="getEmployeeList"></import-excel>
5.员工管理-删除员工
- 删除员工的接口-位置(src/api/employee.js)
//删除员工
export function delEmployee(id){
return request({
method:'delete',
url:`/sys/user/${id}`
})
}
- 气泡确认位置(src/views/employee/index.vue)
<template v-slot="{row}">
<el-button size="mini" type="text">查看</el-button>
<el-button size="mini" type="text">角色</el-button>
<el-popconfirm trigger="hover" title="确认删除该行数据吗?" @onConfirm="confirmDel(row.id)">
<el-button slot="reference" style="margin-left:10px" size="mini" type="text">删除</el-button>
</el-popconfirm>
</template>
- 删除方法
async confirmDel(id){
await delEmployee(id){
if(this.list.length === 1 && this.queryParams.page > 1) this.queryParams.page--
this.getEmployeeList()
this.$message.success('删除成功')
}
}
6.员工详情和路由
- 创建一个员工详情组件-位置(src/views/employee/detail.vue)
<template>
<div class="dashboard-container">
<div class="app-container">
<div class="edit-form">
<el-form ref="userForm" label-width="220px">
<!-- 姓名 部门 -->
<el-row>
<el-col :span="12">
<el-form-item label="姓名" prop="username">
<el-input size="mini" class="inputW" />
</el-form-item>
</el-col>
</el-row>
<!-- 工号 入职时间 -->
<el-row>
<el-col :span="12">
<el-form-item label="工号" prop="workNumber">
<el-input size="mini" class="inputW" />
</el-form-item>
</el-col>
</el-row>
<!--手机 聘用形式 -->
<el-row>
<el-col :span="12">
<el-form-item label="手机" prop="mobile">
<el-input
size="mini"
class="inputW"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="部门" prop="departmentId">
<!-- 放置及联部门组件 -->
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="聘用形式" prop="formOfEmployment">
<el-select size="mini" class="inputW" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="入职时间" prop="timeOfEntry">
<el-date-picker
size="mini"
type="date"
value-format="yyyy-MM-dd"
class="inputW"
/>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="转正时间" prop="correctionTime">
<el-date-picker
size="mini"
type="date"
class="inputW"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 员工照片 -->
<el-row>
<el-col :span="12">
<el-form-item label="员工头像">
<!-- 放置上传图片 -->
</el-form-item>
</el-col>
</el-row>
<!-- 保存个人信息 -->
<el-row type="flex">
<el-col :span="12" style="margin-left:220px">
<el-button size="mini" type="primary">保存更新</el-button>
</el-col>
</el-row>
</el-form>
</div>
</div>
</div>
</template>
<script>
export default {
}
</script>
<style scoped lang="scss">
.edit-form {
background: #fff;
padding: 20px;
.inputW {
width: 380px
}
}
</style>
- 配置员工详情路由信息(src/router/modules/employee.js)
{
path:'/employee/detail',//员工详情地址
component:()=>import('@/view/employee/detail.vue'),
hidden:true,//隐藏在左侧菜单
meta:{
title:'员工详情' //显式在导航的文本
}
}
- 点击添加员工跳转到详情页(src/views/employee/index.vue)
<el-button size="mini" type="primary" @click="$router.push('/employee/detail')">添加员工</el-button>
7.员工详情-表单数据校验
- 姓名-必填-1-4个字符
- 手机号-必填-格式校验
- 部门-必填
- 聘用形式-必填
- 入职时间-必填
- 转正时间-必填-不能小于入职时间
- 定义数据和规则(src/views/employee/detail.vue)
<el-form label-width="220px" ref="userForm" :model="userInfo" :rules="rules" >
<!-- 姓名 部门 -->
<el-row>
<el-col :span="12">
<el-form-item prop="username" label="姓名" >
<el-input v-model="userInfo.username" size="mini" class="inputW">
</el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 工号 入职时间 -->
<el-row>
<el-col :span="12">
<el-form-item prop="workNumber" label="工号" >
<!-- 工号是系统生成的 -->
<el-input v-model="userInfo.workNumber" disabled size="mini" class="inputW"></el-input>
</el-form-item>
</el-col>
</el-row>
<!-- 手机 聘用形式 -->
<el-row>
<el-col :span="12">
<el-form-item prop="mobile" label="手机" >
<el-input v-model="userInfo.mobile" size="mini" class="inputW"></el-input>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item prop="departmentId" label="部门" >
<!-- 级联部门组件 -->
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item prop="formOfEmployment" label="聘用形式" >
<el-select v-model="userInfo.formOfEmployment" size="mini" class="inputW">
<el-option label="正式" :value="1" />
<el-option label="非正式" :value="2" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item prop="timeOfEntry" label="入职时间" >
<el-date-picker v-model="userInfo.timeOfEntry" size="mini" type="date" value-format="yyyy-MM-dd" class="inputW"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item prop="correctionTime" label="转正时间" >
<el-date-picker v-model="userInfo.correctionTime" size="mini" type="date" class="inputW"></el-date-picker>
</el-form-item>
</el-col>
</el-row>
<!-- 员工照片 -->
<el-row>
<el-col :span="12">
<el-form-item label="员工头像">
<!-- 放上传图片 -->
</el-form-item>
</el-col>
</el-row>
<!-- 保存个人信息 -->
<el-row type="flex">
<el-col :span="12" style="margin-left:220px;">
<el-button size="mini" @click="saveData" type="primary">保存更新</el-button>
</el-col>
</el-row>
</el-form>
data() {
return {
userInfo:{
username:'',//用户名
mobile:'',//手机号
workNumber:'',//工号
formOfEmployment:null,//聘用形式
departmentId:null,//部门id
timeOfEntry:'',//入职时间
correctionTime:''//转正时间
},
rules:{
username:[
{
required:true,message:'请输入姓名',trigger:'blur'
},
{
min:1,max:4,message:'姓名为1-4位'
}
],
mobile:[
{
required:true,
message:'请输入手机号',
trigger:'blur'
},
{
pattern:/^1[3-9]\d{9}$/,
message:'手机号格式不正确',
trigger:'blur'
}
],
formOfEmployment:[
{
required:true,
message:'请选择聘用形式',
trigger:'blur'
}
],
departmentId:[
{
required:true,
message:'请选择部门',
trigger:'blur'
}
],
timeOfEntry:[
{
required:true,
message:'请选择入职时间',
trigger:'blur'
}
],
correctionTime:[
{
required:true,
message:'请选择转正时间',
trigger:'blur'
},
{
validator:(rule,value,callback)=>{
if(this.userInfo.timeOfEntry) {
if(new Date(this.userInfo.timeOfEntry) > new Date(value)){
callback(new Error('转正时间不能小于入职时间'))
return
}
}
callback()
}
}
]
}
}
},
methods: {
saveData(){
this.$refs.userForm.validate()
}
}
8.员工详情-部门级联组件
- Cascader级联组件的特性
- options为一个树形结构的数据源
- props可以设置数据源中的字段例如 label(展示) value(存取)
- separator为展示的分隔符
- 创建select-tree组件(src/views/employee/components/select-tree.vue)
<template>
<el-cascader size="mini" :options="treeData" :props="props" separator="-">
</el-cascader>
</template>
<script>
import { getDepartment } from '@/api/department';
import { transListToTreeData } from '@/utils';
export default {
data() {
return {
treeData:[],//赋值给级联组件的options
props:{
label:'name',//要展示的字段
value:'id' //要存储的字段
}
}
},
created(){
this.getDepartment()
},
methods: {
async getDepartment(){
this.treeData = transListToTreeData(await getDepartment(),0) //将组织架构的数据 转化树形赋值给treeData
}
}
}
</script>
<style scoped lang="scss">
.el-cascader{
width: 300px;
}
</style>
- 使用组件(src/views/employee/detail.vue)
<el-form-item prop="departmentId" label="部门" >
<!-- 级联部门组件 -->
<selectTree v-model="userInfo.departmentId" />
</el-form-item>
import selectTree from './components/selectTree.vue'
components:{
selectTree
}
9.员工详情-级联组件-双向绑定
- 接收value属性(src/views/employee/components/select-tree.vue)
<el-cascader
:value="value"
size="mini"
:options="treeData"
:props="props"
separator="-"
@change="changeValue"
/>
export default{
props:{
value:{
type:Number,
default:null
}
},
data() {
return {
treeData: [],//赋值给级联组件的options
props: {
label: 'name',//要展示的字段
value: 'id' //要存储的字段
},
}
},
created() {
this.getDepartment()
},
methods: {
async getDepartment() {
this.treeData = transListToTreeData(await getDepartment(), 0) //将组织架构的数据 转化树形赋值给treeData
},
changeValue(list) {
//取到数组的最后一项
if (list.length > 0) {
this.$emit('input', list[list.length - 1]) //将最后一位的id取出 传出去
} else {
this.$emit('input', null)//如果长度为0 说明值为空
}
}
}
}
- 级联改变触发input事件(src/views/employee/components/select-tree.vue)
changeValue(list){
//取到数组的最后一项
if(list.length > 0){
this.$emit('input',list[list.length -1])
}else{
this.$emit('input',null) // 如果长度为0 说明值为空
}
}
10.保存更新
- 保存更新-新增员工接口-位置(src/api/employee.js)
export function addEmployee(data){
return request({
url:'/sys/user',
methods:'post',
data
})
}
- 点击保存按钮进行新增-代码位置(src/views/employee/detail.vue)
saveData(){
this.$refs.userForm.validate(isOK=>{
if(isOK){
//校验通过
await addEmployee(this.userInfo)
this.$message.success('新增成功');
this.$router.push('/employee')
}
})
}
11.员工详情-编辑员工-查看员工
- 获取员工详情接口(src/api/employee.js)
export function getEmployeeDetail(id){
return request({
url:`/sys/user/${id}`
})
}
- 点击查看时跳转到详情携带id(src/views/employee/index.vue)
<el-button size="mini" @click="$router.push(`/employee/detail/${row.id}`)" type="text">查看</el-button>
- 配置详情的路由支持新增模式和编辑模式(src/router/modules/employee.js)
- ?标识可有可无,可以传id也可以不传,页面都能正确显式
{
path:'/employee/detail/:id?',//
component:()=>import('@/views/employee/detail.vue'),
hidden:true,
meta:{
title:'员工详情'
}
}
- 员工详情判断是否有id,有id就查询详情数据(src/views/employee/detail.vue)
created(){
//获取路由参数中的id
this.$route.params.id && this.getEmployeeDetail()
},
methods:{
async getEmployeeDetail(){
this.userInfo = await getEmployeeDetail(this.$route.params.id)
}
}
12.员工详情-编辑员工-保存
- 更新员工的接口(src/api/employee.js)
//更新员工
export function updateEmployee(data){
return request({
url:`/sys/user/${data.id}`,
method:'put',
data
})
}
- 保存时区分保存和新增(src/views/employee/detail.vue)
saveData(){
this.$refs.userForm.validate(async isOK=>{
if(isOK){
//编辑模式
if(this.$route.params.id){
await updateEmployee(this.userInfo)
this.$message.success('更新成功')
}else{
//校验通过
await addEmployee(this.userInfo)
this.$message.success('新增员工成功')
}
this.$router.push('/employee')
}
})
},
- 当编辑模式时,让手机号不可编辑(src/views/employee/detail.vue)
<el-input
v-model="userInfo.mobile"
:disabled="!!$route.params.id"
size="mini"
class="inputW"
/>