封装一个通用的工具栏
**目标**:封装一个通用的工具栏供大家使用
通用工具栏的组件结构
在后续的业务开发中,经常会用到一个类似下图的工具栏,作为公共组件,进行一下封装

组件 src/components/PageTools/index.vue
<template>   <el-card class="page-tools">     <el-row type="flex" justify="space-between" align="middle">       <el-col>         <div  v-if="showBefore" class="before">           <i class="el-icon-info" />           <!-- 定义前面得插槽 -->           <slot name="before" />         </div>       </el-col>       <el-col>         <el-row type="flex" justify="end">            <!-- 定义后面的插槽 -->            <slot name="after" />         </el-row>       </el-col>     </el-row>   </el-card> </template>
  <script> export default {   props: {     showBefore: {       type: Boolean,       default: false     }
    } } </script>
  <style lang='scss'>  .page-tools {     margin: 10px 0;     .before {       line-height: 34px;     i {       margin-right: 5px;       color: #409eff;     }     display: inline-block;     padding: 0px 10px;     border-radius: 3px;     border: 1px solid rgba(145, 213, 255, 1);     background: rgba(230, 247, 255, 1);   }  } </style>
 
   | 
 
组件统一注册
为了方便所有的页面都可以不用引用该组件,可以进行全局注册
提供注册入口 src/componets/index.js
 import PageTools from './PageTools' export default {   install(Vue) {          Vue.component('PageTools', PageTools)   } }
 
 
  | 
 
在入口处进行注册 src/main.js
import Component from '@/components' Vue.use(Component) 
   | 
 
提交代码
**本节任务**: 封装一个通用的工具栏
员工列表页面的基本布局和结构
**目标**:实现员工列表页面的基本布局和结构
结构代码 src/employees/index.vue
<template>   <div class="dashboard-container">     <div class="app-container">       <page-tools :show-before="true">         <span slot="before">共166条记录</span>         <template slot="after">           <el-button size="small" type="warning">导入</el-button>           <el-button size="small" type="danger">导出</el-button>           <el-button size="small" type="primary">新增员工</el-button>         </template>       </page-tools>       <!-- 放置表格和分页 -->       <el-card>         <el-table border>           <el-table-column label="序号" sortable="" />           <el-table-column label="姓名" sortable="" />           <el-table-column label="工号" sortable="" />           <el-table-column label="聘用形式" sortable="" />           <el-table-column label="部门" sortable="" />           <el-table-column label="入职时间" sortable="" />           <el-table-column label="账户状态" sortable="" />           <el-table-column label="操作" sortable="" fixed="right" width="280">             <template>               <el-button type="text" size="small">查看</el-button>               <el-button type="text" size="small">转正</el-button>               <el-button type="text" size="small">调岗</el-button>               <el-button type="text" size="small">离职</el-button>               <el-button type="text" size="small">角色</el-button>               <el-button type="text" size="small">删除</el-button>             </template>           </el-table-column>         </el-table>         <!-- 分页组件 -->         <el-row type="flex" justify="center" align="middle" style="height: 60px">           <el-pagination layout="prev, pager, next" />         </el-row>       </el-card>     </div>   </div> </template>
   | 
 

提交代码
**本节任务**:员工列表页面的基本布局和结构
员工列表数据请求和分页加载
**目标**实现员工数据的加载和分页请求
首先,封装员工的加载请求 src/api/employees.js
 
  export function getEmployeeList(params) {   return request({     url: '/sys/user',     params   }) }
 
 
  | 
 
然后,实现加载数据和分页的逻辑
import { getEmployeeList } from '@/api/employees' export default {   data() {     return {       loading: false,       list: [],        page: {         page: 1,          size: 10,         total: 0        }
      }   },   created() {     this.getEmployeeList()   },   methods: {     changePage(newPage) {       this.page.page = newPage       this.getEmployeeList()     },     async getEmployeeList() {       this.loading = true       const { total, rows } = await getEmployeeList(this.page)       this.page.total = total       this.list = rows       this.loading = false     }   } }
  | 
 
绑定表格
<el-card v-loading="loading">   <el-table border :data="list">     <el-table-column label="序号" sortable="" type="index" />     <el-table-column label="姓名" sortable="" prop="username" />     <el-table-column label="工号" sortable="" prop="workNumber" />     <el-table-column label="聘用形式" sortable="" prop="formOfEmployment" />     <el-table-column label="部门" sortable="" prop="departmentName" />     <el-table-column label="入职时间" sortable="" prop="timeOfEntry" />     <el-table-column label="账户状态" sortable="" prop="enableState" />     <el-table-column label="操作" sortable="" fixed="right" width="280">       <template>         <el-button type="text" size="small">查看</el-button>         <el-button type="text" size="small">转正</el-button>         <el-button type="text" size="small">调岗</el-button>         <el-button type="text" size="small">离职</el-button>         <el-button type="text" size="small">角色</el-button>         <el-button type="text" size="small">删除</el-button>       </template>     </el-table-column>   </el-table>   <!-- 分页组件 -->   <el-row type="flex" justify="center" align="middle" style="height: 60px">     <el-pagination       layout="prev, pager, next"       :page-size="page.size"       :current-page="page.page"       :total="page.total"       @current-change="changePage"     />   </el-row> </el-card>
   | 
 
提交代码
**本节任务**员工列表数据请求和分页加载
员工列表中的数据进行格式化
**目标**:将列表中的内容进行格式化
利用列格式化属性处理聘用形式

上小节中,列表中的聘用形式/入职时间和账户状态需要进行显示内容的处理
那么聘用形式中1代表什么含义,这实际上是我们需要的枚举数据,该数据的存放文件位于我们提供的**资源/枚举中,可以将枚举下的文件夹放于src/api**文件夹下
针对聘用形式,可以使用el-table-column的formatter属性进行设置
 import    EmployeeEnum from '@/api/constant/employees' <!-- 格式化聘用形式 -->    <el-table-column label="聘用形式" sortable :formatter="formatEmployment" />       formatEmployment(row, column, cellValue, index) {            const obj = EmployeeEnum.hireType.find(item => item.id === cellValue)      return obj ? obj.value : '未知'    }
   | 
 
过滤器解决时间格式的处理
针对入职时间,我们可以采用作用域插槽进行处理
<el-table-column label="入职时间" sortable prop="timeOfEntry">            <template slot-scope="obj">              {{                obj.row.timeOfEntry | 过滤器              }}            </template>  </el-table-column>
   | 
 
问题来了,过滤器从哪里呢?
在**资源/过滤器中,我们提供了若干工具方法,我们可以将其转化成过滤器,首先将其拷贝到src**
在**main.js**中将工具方法转化成过滤器
import * as filters from '@/filters' 
  Object.keys(filters).forEach(key => {      Vue.filter(key, filters[key]) })
   | 
 
好了,现在可以愉快的用过滤器的方式使用工具类的方法了
<el-table-column label="入职时间" sortable="" align="center">      <!-- 作用域插槽 -->      <template slot-scope="{ row }">{{ row.timeOfEntry | formatDate }}</template> </el-table-column>
   | 
 
最后一项,账户状态,可以用开关组件switch进行显示
<el-table-column label="账户状态" align="center" sortable="" prop="enableState">            <template slot-scope="{ row }">              <!-- 根据当前状态来确定 是否打开开关 -->              <el-switch :value="row.enableState === 1" />            </template>    </el-table-column>
   | 
 
提交代码
本节任务 员工列表中的数据进行格式化
删除员工功能
**目标**实现删除员工的功能
首先封装 删除员工的请求
 
 
  export function delEmployee(id) {   return request({     url: `/sys/user/${id}`,     method: 'delete'   }) }
 
  | 
 
删除功能
 <template slot-scope="{ row }">               <el-button type="text" size="small">查看</el-button>               <el-button type="text" size="small">转正</el-button>               <el-button type="text" size="small">调岗</el-button>               <el-button type="text" size="small">离职</el-button>               <el-button type="text" size="small">角色</el-button>               <el-button type="text" size="small" @click="deleteEmployee(row.id)">删除</el-button>             </template>      // 删除员工     async deleteEmployee(id) {       try {         await this.$confirm('您确定删除该员工吗')         await delEmployee(id)         this.getEmployeeList()         this.$message.success('删 除员工成功')       } catch (error) {         console.log(error)       }     }
  | 
 
提交代码
**本节任务**: 删除员工功能
新增员工功能-弹层-校验-部门
**目标**:实现新增员工的功能
新建员工弹层组件
当我们点击新增员工时,我们需要一个类似的弹层

类似**组织架构**的组件,同样新建一个弹层组件 src/views/employees/components/add-employee.vue
<template>   <el-dialog title="新增员工" :visible="showDialog">     <!-- 表单 -->     <el-form label-width="120px">       <el-form-item label="姓名">         <el-input style="width:50%" placeholder="请输入姓名" />       </el-form-item>       <el-form-item label="手机">         <el-input style="width:50%" placeholder="请输入手机号" />       </el-form-item>       <el-form-item label="入职时间">         <el-date-picker style="width:50%" placeholder="请选择入职时间" />       </el-form-item>       <el-form-item label="聘用形式">         <el-select style="width:50%" placeholder="请选择" />       </el-form-item>       <el-form-item label="工号">         <el-input style="width:50%" placeholder="请输入工号" />       </el-form-item>       <el-form-item label="部门">         <el-input style="width:50%" placeholder="请选择部门" />       </el-form-item>       <el-form-item label="转正时间">         <el-date-picker style="width:50%" placeholder="请选择转正时间" />       </el-form-item>     </el-form>     <!-- footer插槽 -->     <template v-slot:footer>       <el-row type="flex" justify="center">         <el-col :span="6">           <el-button size="small">取消</el-button>           <el-button type="primary" size="small">确定</el-button>         </el-col>       </el-row>     </template>   </el-dialog> </template>
  <script> export default {   props: {     showDialog: {       type: Boolean,       default: false     }   } } </script>
  <style>
  </style>
 
   | 
 
引用弹出层,点击弹出
父组件中引用,弹出层
import AddDemployee from './components/add-employee' <!-- 放置新增组件 -->  <add-employee :show-dialog.sync="showDialog" />
   | 
 
<!-- 放置新增组件 -->  <add-employee :show-dialog.sync="showDialog" />
   <el-button icon="plus" type="primary" size="small" @click="showDialog = true">新增员工</el-button>
 
   | 
 
新增员工的表单校验
封装新增员工api src/api/employees.js
 
  export function addEmployee(data) {   return request({     method: 'post',     url: '/sys/user',     data   }) }
 
 
  | 
 
针对员工属性,添加校验规则
import EmployeeEnum from '@/api/constant/employees'
    data() {     return {       EmployeeEnum,               treeData: [],        showTree: false,        loading: false,        formData: {         username: '',         mobile: '',         formOfEmployment: '',         workNumber: '',         departmentName: '',         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' }],         workNumber: [{ required: true, message: '工号不能为空', trigger: 'blur' }],         departmentName: [{ required: true, message: '部门不能为空', trigger: 'change' }],         timeOfEntry: [{ required: true, message: '入职时间', trigger: 'blur' }]       }     }   }
   | 
 
绑定数据和规则校验
<el-form :model="formData" :rules="rules" label-width="120px">   <el-form-item label="姓名" prop="username">     <el-input v-model="formData.username" style="width:50%" placeholder="请输入姓名" />   </el-form-item>   <el-form-item label="手机" prop="mobile">     <el-input v-model="formData.mobile" style="width:50%" placeholder="请输入手机号" />   </el-form-item>   <el-form-item label="入职时间" prop="timeOfEntry">     <el-date-picker v-model="formData.timeOfEntry" style="width:50%" placeholder="请选择日期" />   </el-form-item>   <el-form-item label="聘用形式" prop="formOfEmployment">     <el-select v-model="formData.formOfEmployment" style="width:50%" placeholder="请选择" />   </el-form-item>   <el-form-item label="工号" prop="workNumber">     <el-input v-model="formData.workNumber" style="width:50%" placeholder="请输入工号" />   </el-form-item>   <el-form-item label="部门" prop="departmentName">     <el-input v-model="formData.departmentName" style="width:50%" placeholder="请选择部门" />   </el-form-item>   <el-form-item label="转正时间" prop="correctionTime">     <el-date-picker v-model="formData.correctionTime" style="width:50%" placeholder="请选择日期" />   </el-form-item> </el-form>
   | 
 
加载部门数据转化树形
聘用形式和选择部门的处理
 员工的部门是从树形部门中选择一个部门
获取部门数据,转化树形
import { getDepartments } from '@/api/departments' import { transListToTreeData } from '@/utils'   data () {       return {                      treeData: [],         showTree: false,         loading: false,        }   },   methods: {       async getDepartments() {       this.showTree = true       this.loading = true       const { depts } = await getDepartments()              this.treeData = transListToTreeData(depts, '')       this.loading = false     },   }
  | 
 
点击部门赋值表单数据
选择部门,赋值表单数据
<el-form-item label="部门" prop="departmentName">         <el-input v-model="formData.departmentName" style="width:50%" placeholder="请选择部门" @focus="getDepartments" />         <!-- 放置一个tree组件 -->         <el-tree           v-if="showTree"           v-loading="loading"           :data="treeData"           default-expand-all=""           :props="{ label: 'name' }"           @node-click="selectNode"         />       </el-form-item>
   | 
 
点击部门时触发
selectNode(node) {   this.formData.departmentName = node.name   this.showTree = false }
  | 
 
聘用形式
<el-form-item label="聘用形式" prop="formOfEmployment">     <el-select v-model="formData.formOfEmployment" style="width:50%" placeholder="请选择">       <!-- 遍历只能遍历组件的数据 -->       <el-option v-for="item in EmployeeEnum.hireType" :key="item.id" :label="item.value" :value="item.id" />     </el-select>   </el-form-item>
   | 
 
新增员工功能-确定-取消
调用新增接口
 async btnOK() {   try {     await this.$refs.addEmployee.validate()          await addEmployee(this.formData)                     this.$parent.getEmployeeList()     this.$parent.showDialog = false   } catch (error) {     console.log(error)   } }, btnCancel() {      this.formData = {     username: '',     mobile: '',     formOfEmployment: '',     workNumber: '',     departmentName: '',     timeOfEntry: '',     correctionTime: ''   }   this.$refs.addEmployee.resetFields()    this.$emit('update:showDialog', false) }
 
  | 
 
新增员工的功能和组织架构的功能极其类似,这里不做过多阐述
提交代码
本节任务 新增员工功能和弹层
员工导入组件封装
**目标**:封装一个导入excel数据的文件

首先封装一个类似的组件,首先需要注意的是,类似功能,vue-element-admin已经提供了,我们只需要改造即可 代码地址
类似功能性的组件,我们只需要会使用和封装即可 
excel导入功能需要使用npm包**xlsx,所以需要安装xlsx**插件
将vue-element-admin提供的导入功能新建一个组件,位置: src/components/UploadExcel
注册全局的导入excel组件
import PageTools from './PageTools' import UploadExcel from './UploadExcel' export default {   install(Vue) {     Vue.component('PageTools', PageTools)      Vue.component('UploadExcel', UploadExcel)    } }
   | 
 
修改样式和布局
<template>  <div class="upload-excel">     <div class="btn-upload">       <el-button :loading="loading" size="mini" type="primary" @click="handleUpload">         点击上传       </el-button>     </div>
      <input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">     <div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">       <i class="el-icon-upload" />       <span>将文件拖到此处</span>     </div>   </div> </template> <style scoped lang="scss"> .upload-excel {   display: flex;   justify-content: center;    margin-top: 100px;    .excel-upload-input{        display: none;         z-index: -9999;      }    .btn-upload , .drop{       border: 1px dashed #bbb;       width: 350px;       height: 160px;       text-align: center;       line-height: 160px;    }    .drop{        line-height: 80px;        color: #bbb;       i {         font-size: 60px;         display: block;       }    } } </style>
   | 
 
提交代码
**本节任务**:员工导入组件封装
员工的导入
**目标**:实现员工的导入
建立公共导入的页面路由
新建一个公共的导入页面,挂载路由 src/router/index.js
{     path: '/import',     component: Layout,     hidden: true,      children: [{       path: '',        component: () => import('@/views/import')     }]   },
  | 
 
创建import路由组件 src/views/import/index.vue
<template>   <!-- 公共导入组件 -->    <upload-excel :on-success="success" /> </template>
 
   | 
 
分析excel导入代码,封装接口
封装导入员工的api接口  
 
 
 
  export function importEmployee(data) {   return request({     url: '/sys/user/batch',     method: 'post',     data   }) }
 
  | 
 
实现excel导入
获取导入的excel数据, 导入excel接口
async  success({ header, results }) {        const userRelations = {       '入职日期': 'timeOfEntry',       '手机号': 'mobile',       '姓名': 'username',       '转正日期': 'correctionTime',       '工号': 'workNumber'     }     const arr = []    results.forEach(item => {       const userInfo = {}       Object.keys(item).forEach(key => {         userInfo[userRelations[key]] = item[key]       })      arr.push(userInfo)      })     await importEmployee(arr)      this.$router.back() }
  | 
 
为了让这个页面可以服务更多的导入功能,我们可以在页面中用参数来判断,是否是导入员工
data() {    return {      type: this.$route.query.type    }  },
  | 
 
当excel中有日期格式的时候,实际转化的值为一个数字,我们需要一个方法进行转化
formatDate(numb, format) {   const time = new Date((numb - 1) * 24 * 3600000 + 1)   time.setYear(time.getFullYear() - 70)   const year = time.getFullYear() + ''   const month = time.getMonth() + 1 + ''   const date = time.getDate() - 1 + ''   if (format && format.length === 1) {     return year + format + month + format + date   }   return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date) }
  | 
 
需要注意,**导入的手机号不能和之前的存在的手机号重复**
逻辑判断 
async  success({ header, results }) {      if (this.type === 'user') {        const userRelations = {          '入职日期': 'timeOfEntry',          '手机号': 'mobile',          '姓名': 'username',          '转正日期': 'correctionTime',          '工号': 'workNumber'        }        const arr = []                results.forEach(item => {                  const userInfo = {}          Object.keys(item).forEach(key => {                      if (userRelations[key] === 'timeOfEntry' || userRelations[key] === 'correctionTime') {              userInfo[userRelations[key]] = new Date(this.formatDate(item[key], '/'))               return            }            userInfo[userRelations[key]] = item[key]          })                    arr.push(userInfo)        })        await importEmployee(arr)        this.$message.success('导入成功')      }      this.$router.back()     },    formatDate(numb, format) {      const time = new Date((numb - 1) * 24 * 3600000 + 1)      time.setYear(time.getFullYear() - 70)      const year = time.getFullYear() + ''      const month = time.getMonth() + 1 + ''      const date = time.getDate() - 1 + ''      if (format && format.length === 1) {        return year + format + month + format + date      }      return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date)    }
  | 
 
员工页面跳转
<el-button type="warning" size="small" @click="$router.push('/import?type=user')">导入</el-button>
 
  | 
 
**目标**: 实现员工的导入
员工导出excel功能
目标: 实现将员工数据导出功能
日常业务中,我们经常遇到excel导出功能, 怎么使用呢
Excel 的导入导出都是依赖于js-xlsx来实现的。
在 js-xlsx的基础上又封装了Export2Excel.js来方便导出数据。
安装excel所需依赖和按需加载
由于 Export2Excel不仅依赖js-xlsx还依赖file-saver和script-loader。
所以你先需要安装如下命令:
npm install xlsx file-saver -S npm install script-loader -S -D
   | 
 
由于js-xlsx体积还是很大的,导出功能也不是一个非常常用的功能,所以使用的时候建议使用懒加载。使用方法如下:
import('@/vendor/Export2Excel').then(excel => {   excel.export_json_to_excel({     header: tHeader,      data,      filename: 'excel-list',      autoWidth: true,      bookType: 'xlsx'    }) })
  | 
 
excel导出参数的介绍
vue-element-admin提供了导出的功能模块,在课程资源/excel导出目录下,放置到src目录下
参数
| 参数 | 
说明 | 
类型 | 
可选值 | 
默认值 | 
| header | 
导出数据的表头 | 
Array | 
/ | 
[] | 
| data | 
导出的具体数据 | 
Array | 
/ | 
[[]] | 
| filename | 
导出文件名 | 
String | 
/ | 
excel-list | 
| autoWidth | 
单元格是否要自适应宽度 | 
Boolean | 
true / false | 
true | 
| bookType | 
导出文件类型 | 
String | 
xlsx, csv, txt, more | 
xlsx | 
excel导出基本的结构
我们最重要的一件事,就是把表头和数据进行相应的对应
因为数据中的key是英文,想要导出的表头是中文的话,需要将中文和英文做对应
const headers = {      '手机号': 'mobile',      '姓名': 'username',      '入职日期': 'timeOfEntry',      '聘用形式': 'formOfEmployment',      '转正日期': 'correctionTime',      '工号': 'workNumber',      '部门': 'departmentName'    }
  | 
 
然后,完成导出代码
 exportData() {         const headers = {     '姓名': 'username',     '手机号': 'mobile',     '入职日期': 'timeOfEntry',     '聘用形式': 'formOfEmployment',     '转正日期': 'correctionTime',     '工号': 'workNumber',     '部门': 'departmentName'   }      import('@/vendor/Export2Excel').then(async excel => {     const { rows } = await getEmployeeList({ page: 1, size: this.page.total })     const data = this.formatJson(headers, rows)
      excel.export_json_to_excel({       header: Object.keys(headers),       data,       filename: '员工信息表',       autoWidth: true,       bookType: 'xlsx'
      })     
                                       }) },
  formatJson(headers, rows) {         return rows.map(item => {     return Object.keys(headers).map(key => {       if (headers[key] === 'timeOfEntry' || headers[key] === 'correctionTime') {         return formatDate(item[headers[key]])        } else if (headers[key] === 'formOfEmployment') {         var en = EmployeeEnum.hireType.find(obj => obj.id === item[headers[key]])         return en ? en.value : '未知'       }       return item[headers[key]]     })    })                         }
 
  | 
 
导出时间格式的处理
formatJson(headers, rows) {   return rows.map(item => {               return Object.keys(headers).map(key => {              if (headers[key] === 'timeOfEntry' || headers[key] === 'correctionTime') {                  return formatDate(item[headers[key]])       } else if (headers[key] === 'formOfEmployment') {         const obj = EmployeeEnum.hireType.find(obj => obj.id === item[headers[key]])         return obj ? obj.value : '未知'       }       return item[headers[key]]     })        })       }
  | 
 
扩展  复杂表头的导出
当需要导出复杂表头的时候,vue-element-admin同样支持该类操作
vue-element-admin 提供的导出方法中有 multiHeader和merges 的参数
| 参数 | 
说明 | 
类型 | 
可选值 | 
默认值 | 
| multiHeader | 
复杂表头的部分 | 
Array | 
/ | 
[[]] | 
| merges | 
需要合并的部分 | 
Array | 
/ | 
[] | 
multiHeader里面是一个二维数组,里面的一个元素是一行表头,假设你想得到一个如图的结构

mutiHeader应该这样定义
const multiHeader = [['姓名', '主要信息', '', '', '', '', '部门']]
   | 
 
multiHeader中的一行表头中的字段的个数需要和真正的列数相等,假设想要跨列,多余的空间需要定义成空串
它主要对应的是标准的表头
const header = ['姓名', '手机号', '入职日期', '聘用形式', '转正日期', '工号', '部门']
   | 
 
如果,我们要实现其合并的效果, 需要设定merges选项
const merges = ['A1:A2', 'B1:F1', 'G1:G2']
   | 
 
merges的顺序是没关系的,只要配置这两个属性,就可以导出复杂表头的excel了
exportData() {     const headers = {       '姓名': 'username',       '手机号': 'mobile',       '入职日期': 'timeOfEntry',       '聘用形式': 'formOfEmployment',       '转正日期': 'correctionTime',       '工号': 'workNumber',       '部门': 'departmentName'     }          import('@/vendor/Export2Excel').then(async excel => {                                          const { rows } = await getEmployeeList({ page: 1, size: this.page.total })       const data = this.formatJson(headers, rows)        const multiHeader = [['姓名', '主要信息', '', '', '', '', '部门']]       const merges = ['A1:A2', 'B1:F1', 'G1:G2']       excel.export_json_to_excel({         header: Object.keys(headers),         data,         filename: '员工资料表',         multiHeader,          merges        })
                                                              })   },         formatJson(headers, rows) {     return rows.map(item => {                     return Object.keys(headers).map(key => {                  if (headers[key] === 'timeOfEntry' || headers[key] === 'correctionTime') {                      return formatDate(item[headers[key]])         } else if (headers[key] === 'formOfEmployment') {           const obj = EmployeeEnum.hireType.find(obj => obj.id === item[headers[key]])           return obj ? obj.value : '未知'         }         return item[headers[key]]       })            })             }
  | 
 
提交代码
**本节任务**实现将员工数据导出功能
员工详情页创建和布局
**目标**:创建员工详情的主要布局页面和基本布局
详情页的基本布局和路由

建立详情页路由
{     path: 'detail/:id',      component: () => import('@/views/employees/detail'),     hidden: true,      meta: {       title: '员工详情'      }   }
  | 
 
建立基本架构
<div class="dashboard-container">     <div class="app-container">       <el-card>         <el-tabs>           <el-tab-pane label="登录账户设置">
              <!-- 放置表单 -->             <el-form label-width="120px" style="margin-left: 120px; margin-top:30px">               <el-form-item label="姓名:">                 <el-input style="width:300px" />               </el-form-item>               <el-form-item label="密码:">                 <el-input style="width:300px" type="password" />               </el-form-item>               <el-form-item>                 <el-button type="primary">更新</el-button>               </el-form-item>             </el-form>           </el-tab-pane>           <el-tab-pane label="个人详情" />           <el-tab-pane label="岗位信息" />         </el-tabs>       </el-card>     </div>   </div>
   | 
 
列表跳转到详情
<el-button type="text" size="small" @click="$router.push(`/employees/detail/${obj.row.id}`)">查看</el-button>
 
  | 
 
读取和保存用户信息的接口
加载个人基本信息 > 该接口已经在之前提供过了 src/api/user.js
 
 
  export function getUserDetailById(id) {   return request({     url: `/sys/user/${id}`   }) }
 
 
  | 
 
保存个人基本信息  src/api/employees.js
 
 
  export function saveUserDetailById(data) {   return request({     url: `/sys/user/${data.id}`,     method: 'put',     data   }) }
 
 
  | 
 
实现用户名和密码的修改
注意:这里有个缺陷,接口中读取的是后端的密文,我们并不能解密,所以当我们没有任何修改就保存时,会校验失败,因为密文超过了规定的12位长度,为了真的修改密码,我们设定了一个临时的字段 password2,用它来存储我们的修改值,最后保存的时候,把password2传给password
用户名和密码的修改  src/views/employees/detail.vue
import { getUserDetailById } from '@/api/user' import { saveUserDetailById } from '@/api/employees' export default {   data() {     return {       userId: this.$route.params.id,        userInfo: {                  username: '',         password2: ''       },       rules: {         username: [{ required: true, message: '姓名不能为空', trigger: 'blur' }],         password2: [{ required: true, message: '密码不能为空', trigger: 'blur' },           { min: 6, max: 9, message: '密码长度6-9位', trigger: 'blur' }]       }     }   },   created() {     this.getUserDetailById()   },   methods: {     async getUserDetailById() {       this.userInfo = await getUserDetailById(this.userId)     },     async saveUser() {       try {                  await this.$refs.userForm.validate()         await saveUserDetailById({ ...this.userInfo, password: this.userInfo.password2 })          this.$message.success('保存成功')       } catch (error) {         console.log(error)       }     }   } }
  | 
 
绑定表单数据
<!-- 放置表单 -->            <el-form ref="userForm" :model="userInfo" :rules="rules" label-width="120px" style="margin-left: 120px; margin-top:30px">              <el-form-item label="姓名:" prop="username">                <el-input v-model="userInfo.username" style="width:300px" />              </el-form-item>              <el-form-item label="新密码:" prop="password2">                <el-input v-model="userInfo.password2" style="width:300px" type="password" />              </el-form-item>              <el-form-item>                <el-button type="primary" @click="saveUser">更新</el-button>              </el-form-item>            </el-form>
   | 
 
提交代码
个人组件和岗位组件封装
封装个人详情组件
我们将员工个人信息分为三部分,账户,个人, 岗位,这个小节我们对个人组件和岗位组件进行封装

封装个人组件   src/views/employees/components/user-info.vue
<template>   <div class="user-info">     <!-- 个人信息 -->     <el-form label-width="220px">       <!-- 工号 入职时间 -->       <el-row class="inline-info">         <el-col :span="12">           <el-form-item label="工号">             <el-input v-model="userInfo.workNumber" class="inputW" />           </el-form-item>         </el-col>         <el-col :span="12">           <el-form-item label="入职时间">             <el-date-picker               v-model="userInfo.timeOfEntry"               type="date"               class="inputW"               value-format="YYYY-MM-DD"             />           </el-form-item>         </el-col>       </el-row>       <!-- 姓名 部门 -->       <el-row class="inline-info">         <el-col :span="12">           <el-form-item label="姓名">             <el-input v-model="userInfo.username" class="inputW" />           </el-form-item>         </el-col>         <el-col :span="12">           <el-form-item label="部门">             <el-input v-model="userInfo.departmentName" class="inputW" />           </el-form-item>         </el-col>       </el-row>       <!--手机 聘用形式  -->       <el-row class="inline-info">         <el-col :span="12">           <el-form-item label="手机">             <el-input v-model="userInfo.mobile" />           </el-form-item>         </el-col>         <el-col :span="12">           <el-form-item label="聘用形式">             <el-select v-model="userInfo.formOfEmployment" class="inputW">               <el-option                 v-for="item in EmployeeEnum.hireType"                 :key="item.id"                 :label="item.value"                 :value="item.id"               />             </el-select>           </el-form-item>         </el-col>       </el-row>       <!-- 员工照片 -->       <el-row class="inline-info">         <el-col :span="12">           <el-form-item label="员工头像">             <!-- 放置上传图片 -->                       </el-form-item>         </el-col>       </el-row>       <!-- 保存个人信息 -->       <el-row class="inline-info" type="flex" justify="center">         <el-col :span="12">           <el-button type="primary" @click="saveUser">保存更新</el-button>           <el-button @click="$router.back()">返回</el-button>
          </el-col>       </el-row>     </el-form>     <!-- 基础信息 -->     <el-form label-width="220px">       <div class="block">         <div class="title">基础信息</div>         <el-form-item label="最高学历">           <el-select v-model="formData.theHighestDegreeOfEducation" class="inputW2">             <el-option               v-for="item in EmployeeEnum.highestDegree"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <!-- 个人头像 -->         <!-- 员工照片 -->
          <el-form-item label="员工照片">           <!-- 放置上传图片 -->         </el-form-item>         <el-form-item label="国家/地区">           <el-select v-model="formData.nationalArea" class="inputW2">             <el-option               v-for="item in EmployeeEnum.isOverseas"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="护照号">           <el-input v-model="formData.passportNo" placeholder="正规护照格式" class="inputW" />         </el-form-item>         <el-form-item label="身份证号">           <el-input v-model="formData.idNumber" placeholder="正规身份证格式" class="inputW" />         </el-form-item>         <el-form-item label="籍贯">           <el-input v-model="formData.nativePlace" placeholder="籍贯地址" class="inputW5" />         </el-form-item>         <el-form-item label="民族">           <el-input v-model="formData.nation" placeholder="请输入民族" class="inputW2" />         </el-form-item>         <el-form-item label="婚姻状况">           <el-select v-model="formData.maritalStatus" class="inputW2">             <el-option               v-for="item in EmployeeEnum.maritaStatus"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="生日">           <el-input v-model="formData.birthday" placeholder="示例 0323" class="inputW" />         </el-form-item>         <el-form-item label="年龄">           <el-input v-model="formData.age" type="number" class="inputW2" />         </el-form-item>         <el-form-item label="星座">           <el-select v-model="formData.constellation" class="inputW2">             <el-option               v-for="item in EmployeeEnum.constellation"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="血型">           <el-select v-model="formData.bloodType" class="inputW2">             <el-option               v-for="item in EmployeeEnum.bloodType"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="户籍所在地">           <el-input v-model="formData.domicile" class="inputW5" />         </el-form-item>         <el-form-item label="政治面貌">           <el-input v-model="formData.politicalOutlook" class="inputW2" />         </el-form-item>         <el-form-item label="入党时间">           <el-date-picker             v-model="formData.timeToJoinTheParty"             type="date"             placeholder="选择日期"             class="inputW"             value-format="yyyy-MM-dd"           />         </el-form-item>         <el-form-item label="存档机构">           <el-input v-model="formData.archivingOrganization" placeholder="请输入" />         </el-form-item>         <el-form-item label="子女状态">           <el-input v-model="formData.stateOfChildren" placeholder="请输入" />         </el-form-item>         <el-form-item label="子女有无商业险">           <el-radio-group v-model="formData.doChildrenHaveCommercialInsurance">             <el-radio label="1">有</el-radio>             <el-radio label="2">无</el-radio>           </el-radio-group>         </el-form-item>         <el-form-item label="有无违法违纪状态">           <el-input v-model="formData.isThereAnyViolationOfLawOrDiscipline" placeholder="请输入" />         </el-form-item>         <el-form-item label="有无重大病史">           <el-input v-model="formData.areThereAnyMajorMedicalHistories" placeholder="请输入" />         </el-form-item>       </div>       <!-- 通讯信息 -->       <div class="block">         <div class="title">通讯信息</div>         <el-form-item label="QQ">           <el-input v-model="formData.qq" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="微信">           <el-input v-model="formData.wechat" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="现居住地">           <el-input v-model="formData.placeOfResidence" placeholder="请输入" />         </el-form-item>         <el-form-item label="通讯地址">           <el-input v-model="formData.postalAddress" placeholder="请输入" />         </el-form-item>         <el-form-item label="联系手机">           <el-input v-model="formData.contactTheMobilePhone" placeholder="11位字符" maxlength="11" class="inputW" @change.native="handlePhone(2)" />         </el-form-item>         <el-form-item label="个人邮箱">           <el-input v-model="formData.personalMailbox" placeholder="请输入" type="mail" class="inputW" />         </el-form-item>         <el-form-item label="紧急联系人">           <el-input v-model="formData.emergencyContact" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="紧急联系电话">           <el-input v-model="formData.emergencyContactNumber" placeholder="11位字符" class="inputW" />         </el-form-item>       </div>       <!-- 账号信息 -->       <div class="block">         <div class="title">账号信息</div>         <el-form-item label="社保电脑号">           <el-input v-model="formData.socialSecurityComputerNumber" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="公积金账号">           <el-input v-model="formData.providentFundAccount" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="银行卡号">           <el-input v-model="formData.bankCardNumber" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="开户行">           <el-input v-model="formData.openingBank" placeholder="请输入" class="inputW" />         </el-form-item>       </div>       <!-- 教育信息 -->       <div class="block">         <div class="title">教育信息</div>         <el-form-item label="学历类型">           <el-select v-model="formData.educationalType" placeholder="请选择">             <el-option               v-for="item in EmployeeEnum.educationType"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="毕业学校">           <el-input v-model="formData.graduateSchool" placeholder="请输入" class="inputW2" />         </el-form-item>         <el-form-item label="入学时间">           <el-date-picker v-model="formData.enrolmentTime" type="data" placeholder="请输入时间" class="inputW" value-format="yyyy-MM-dd" />         </el-form-item>         <el-form-item label="毕业时间">           <el-date-picker v-model="formData.graduationTime" type="data" placeholder="请输入时间" class="inputW" value-format="yyyy-MM-dd" />         </el-form-item>         <el-form-item label="专业">           <el-input v-model="formData.major" placeholder="请输入" class="inputW" />         </el-form-item>       </div>       <!-- 从业信息 -->       <div class="block">         <div class="title">从业信息</div>         <el-form-item label="上家公司">           <el-input v-model="formData.homeCompany" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="职称">           <el-input v-model="formData.title" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="有无竞业限制">           <el-input v-model="formData.isThereAnyCompetitionRestriction" placeholder="请输入" style="width:80%" />         </el-form-item>         <el-form-item label="备注">           <el-input v-model="formData.remarks" type="textarea" placeholder="请输入备注" style="width:80%" />         </el-form-item>         <!-- 保存员工信息 -->         <el-row class="inline-info" type="flex" justify="center">           <el-col :span="12">             <el-button type="primary" @click="savePersonal">保存更新</el-button>             <el-button @click="$router.back()">返回</el-button>           </el-col>         </el-row>       </div>     </el-form>
    </div>
  </template>
   | 
 
本章节个人数据过于**繁杂,庞大**,同学们在开发期间,拷贝代码即可,我们只写关键部位的代码
定义user-info的数据
import EmployeeEnum from '@/api/constant/employees'
  export default {   data() {     return {       userId: this.$route.params.id,       EmployeeEnum,        userInfo: {},       formData: {         userId: '',         username: '',          sex: '',          mobile: '',          companyId: '',          departmentName: '',                   dateOfBirth: '',          timeOfEntry: '',          theHighestDegreeOfEducation: '',          nationalArea: '',          passportNo: '',          idNumber: '',          idCardPhotoPositive: '',          idCardPhotoBack: '',          nativePlace: '',          nation: '',          englishName: '',          maritalStatus: '',          staffPhoto: '',          birthday: '',          zodiac: '',          age: '',          constellation: '',          bloodType: '',          domicile: '',          politicalOutlook: '',          timeToJoinTheParty: '',          archivingOrganization: '',          stateOfChildren: '',          doChildrenHaveCommercialInsurance: '1',          isThereAnyViolationOfLawOrDiscipline: '',          areThereAnyMajorMedicalHistories: '',          qq: '',          wechat: '',          residenceCardCity: '',          dateOfResidencePermit: '',          residencePermitDeadline: '',          placeOfResidence: '',          postalAddress: '',          contactTheMobilePhone: '',          personalMailbox: '',          emergencyContact: '',          emergencyContactNumber: '',          socialSecurityComputerNumber: '',          providentFundAccount: '',          bankCardNumber: '',          openingBank: '',          educationalType: '',          graduateSchool: '',          enrolmentTime: '',          graduationTime: '',          major: '',          graduationCertificate: '',          certificateOfAcademicDegree: '',          homeCompany: '',          title: '',          resume: '',          isThereAnyCompetitionRestriction: '',          proofOfDepartureOfFormerCompany: '',          remarks: ''        }     }   } }
   | 
 
在detail.vue组件中,注册并使用
<el-tab-pane label="个人详情">            <!-- 放置个人详情 -->            <component :is="userComponent" />            <!-- <user-info /> -->  </el-tab-pane>
   | 
 
在以上代码中,我们使用了动态组件component,它通过 **is属性来绑定需要显示在该位置的组件,is属性可以直接为注册组件**的组件名称即可
封装岗位组件
同理,封装岗位组件
封装岗位组件  src/views/employee/components/job-info.vue
<template> <div class="job-info">     <!-- 基础信息 -->     <el-form label-width="220px">       <div class="block">         <div class="title">基础信息</div>         <el-form-item label="岗位">           <el-input v-model="formData.post" placeholder="请输入" class="inputW" />         </el-form-item>         <!-- <el-form-item label="转正日期">           <el-date-picker             v-model="formData.dateOfCorrection"             type="date"             placeholder="选择日期"             value-format="yyyy-MM-dd"           />         </el-form-item> -->         <el-form-item label="转正状态">           <el-select v-model="formData.stateOfCorrection" placeholder="请选择" disabled>             <el-option               v-for="item in EmployeeEnum.stateOfCorrection"               :key="item.value"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="职级">           <el-input v-model="formData.rank" class="inputW" />         </el-form-item>         <el-form-item label="转正评价">           <el-input v-model="formData.correctionEvaluation" type="textarea" placeholder="1-300位字符" />         </el-form-item>         <el-form-item label="汇报对象">           <el-select v-model="formData.reportId" filterable placeholder="请选择" class="inputW">             <el-option v-for="item in depts" :key="item.id" :label="item.username" :value="item.id" />           </el-select>         </el-form-item>         <el-form-item label="HRBP">           <el-select v-model="formData.hrbp" filterable placeholder="请选择" class="inputW">             <el-option v-for="item in depts" :key="item.id" :label="item.username" :value="item.id" class="inputW" />           </el-select>         </el-form-item>         <el-form-item class="formInfo" label="调整司龄(天):">           <el-input v-model="formData.adjustmentAgedays" type="number" placeholder="请输入" class="inputW" />         </el-form-item>         <el-form-item label="首次参加工作时间">           <el-date-picker             v-model="formData.workingTimeForTheFirstTime"             type="date"             placeholder="选择日期"             value-format="yyyy-MM-dd"           />         </el-form-item>         <el-form-item label="调整工龄">           <el-input v-model="formData.adjustmentOfLengthOfService" placeholder="0.00年" class="inputW" disabled />         </el-form-item>       </div>       <!-- 合同信息 -->       <div class="block">         <div class="title">合同信息</div>         <el-form-item class="formInfo" label="首次合同开始时间:">           <el-date-picker             v-model="formData.initialContractStartTime"             type="date"             placeholder="选择日期"             value-format="yyyy-MM-dd"           />         </el-form-item>         <el-form-item label="首次合同结束时间">           <el-date-picker             v-model="formData.firstContractTerminationTime"             type="date"             placeholder="选择日期"             value-format="yyyy-MM-dd"           />         </el-form-item>         <el-form-item label="现合同开始时间">           <el-date-picker             v-model="formData.currentContractStartTime"             type="date"             placeholder="选择日期"             value-format="yyyy-MM-dd"           />         </el-form-item>         <el-form-item label="现合同结束时间">           <el-date-picker             v-model="formData.closingTimeOfCurrentContract	"             type="date"             placeholder="选择日期"             value-format="yyyy-MM-dd"           />         </el-form-item>         <el-form-item label="合同期限">           <el-select v-model="formData.contractPeriod" class="filter-item">             <el-option               v-for="item in EmployeeEnum.contractPeriod"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="续签次数">           <el-select v-model="formData.renewalNumber" class="filter-item">             <el-option               v-for="item in EmployeeEnum.renewalCount"               :key="item.id"               :label="item.value"               :value="item.id"             />           </el-select>         </el-form-item>       </div>       <!-- 招聘信息 -->       <div class="block">         <div class="title">招聘信息</div>         <el-form-item label="其他招聘渠道">           <el-select v-model="formData.otherRecruitmentChannels" placeholder="请选择">             <el-option               v-for="item in EmployeeEnum.resumeSource"               :key="item.id"               :label="item.value"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="招聘渠道">           <el-select v-model="formData.recruitmentChannels" placeholder="请选择">             <el-option               v-for="item in EmployeeEnum.resumeSource"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="社招/校招">           <el-select v-model="formData.socialRecruitment" placeholder="请选择">             <el-option               v-for="item in EmployeeEnum.hireSourceType"               :key="item.value"               :label="item.label"               :value="item.value"             />           </el-select>         </el-form-item>         <el-form-item label="推荐企业/人">           <el-input v-model="formData.recommenderBusinessPeople" placeholder="请输入" class="infoPosition inputW" />         </el-form-item>       </div>       <!-- 从业信息 -->       <el-form-item>         <el-button type="primary" @click="saveJob">保存更新</el-button>         <el-button @click="$router.back()">返回</el-button>       </el-form-item>     </el-form>   </div>
  </template>
   | 
 
定义岗位数据
import EmployeeEnum from '@/api/constant/employees'
  export default {   data() {     return {       userId: this.$route.params.id,       depts: [],       EmployeeEnum,       formData: {         adjustmentAgedays: '',          adjustmentOfLengthOfService: '',          closingTimeOfCurrentContract: '',          companyId: '',          contractDocuments: '',          contractPeriod: '',          correctionEvaluation: '',          currentContractStartTime: '',          firstContractTerminationTime: '',          hrbp: '',          initialContractStartTime: '',          otherRecruitmentChannels: '',          post: '',          rank: null,          recommenderBusinessPeople: '',          recruitmentChannels: '',          renewalNumber: '',          reportId: '',          reportName: null,          socialRecruitment: '',          stateOfCorrection: '',          taxableCity: '',          userId: '',          workMailbox: '',          workingCity: '',          workingTimeForTheFirstTime: ''        }     }   } }
   | 
 
在detail.vue组件中,注册并使用
<el-tab-pane label="岗位详情">            <!-- 放置岗位详情 -->            <component :is="JobInfo" />  </el-tab-pane>
   | 
 
**本节任务**:完成个人组件和岗位组件封装
员工个人信息和岗位信息-读取-保存
**目标**:实现个人信息的岗位信息的读取和校验,保存
读取个人保存个人信息
这个环节里面大部分都是繁杂的属性和重复的过程,所以该环节直接将过程代码拷贝到项目中即可
封装 读取个人信息 保存个人信息  读取岗位信息 保存岗位信息  
 
  export function getPersonalDetail(id) {   return request({     url: `/employees/${id}/personalInfo`   }) }
 
 
 
  export function updatePersonal(data) {   return request({     url: `/employees/${data.userId}/personalInfo`,     method: 'put',     data   }) }
 
 
 
 
 
  export function getJobDetail(id) {   return request({     url: `/employees/${id}/jobs`   }) }
 
 
 
 
  export function updateJob(data) {   return request({     url: `/employees/${data.userId}/jobs`,     method: 'put',     data   }) }
 
  | 
 
读取,保存个人信息  user-info  需要注意:这里的保存实际上分成了两个接口,这是接口的设计,我们只能遵守
import { getPersonalDetail, updatePersonal, saveUserDetailById } from '@/api/employees' import { getUserDetailById } from '@/api/user'  created() {     this.getPersonalDetail()     this.getUserDetailById()   },   methods: {     async getPersonalDetail() {       this.formData = await getPersonalDetail(this.userId)      },     async savePersonal() {       await updatePersonal({ ...this.formData, id: this.userId })       this.$message.success('保存成功')     },     async saveUser() {            await saveUserDetailById(this.userInfo)       this.$message.success('保存成功')     },     async getUserDetailById() {       this.userInfo = await getUserDetailById(this.userId)     }   }
  | 
 
读取保存岗位信息
读取,保存岗位信息  job-info
import { getEmployeeSimple, updateJob, getJobDetail } from '@/api/employees'
  created() {   this.getJobDetail()   this.getEmployeeSimple() }, methods: {   async getJobDetail() {     this.formData = await getJobDetail(this.userId)   },      async getEmployeeSimple() {     this.depts = await getEmployeeSimple()   },      async saveJob() {     await updateJob(this.formData)     this.$message.success('保存岗位信息成功')   } }
  | 
 
提交代码
本节任务 实现个人信息的岗位信息的读取和校验,保存
配置腾讯云Cos
**目标**: 配置一个腾讯云cos
由于上课的开发的特殊性,我们不希望把所有的图片都上传到我们自己的官方服务器上,这里我们可以采用一个腾讯云的图片方案

上边图的意思就是说,我们找一个可以免费上传图片的服务器,帮我们**代管图片,我们在自己的数据库里只保存一个地址就行, 这其实也是很多项目的处理方案,会有一个公共的文件服务器**
第一步,我们必须先拥有一个腾迅云的开发者账号(小心腾讯云的广告电话)
请按照腾讯云的注册方式,注册自己的账号
第二步,实名认证
选择个人账户

填写个人身份信息

下一步,扫描二维码授权

手机端授权

点击领取免费产品

选择对象存储COS

我们免费拥有**6个月的50G流量**的对象存储空间使用权限,足够我们上传用户头像的使用了
点击0元试用,开通服务

到这一步,账号的部分就操作完毕,接下来,我们需要来创建一个存储图片的存储桶
登录 对象存储控制台 ,创建存储桶。设置存储桶的权限为 公有读,私有写


设置cors规则

AllowHeader 需配成*,如下图所示。

因为我们本身没有域名,所以这里设置成*****,仅限于测试,正式环境的话,这里需要配置真实的域名地址
到这里,我们的腾讯云存储桶就设置好了。
封装上传图片组件-上传组件需求分析
目标 梳理整个的上传过程
初始化cos对象参数
| 名称 | 
描述 | 
| SecretId | 
开发者拥有的项目身份识别 ID,用以身份认证,可在 API 密钥管理 页面获取 | 
| SecretKey | 
开发者拥有的项目身份密钥,可在 API 密钥管理 页面获取 | 
注意,上述的参数我们在本次开发过程中,直接将参数放置在前端代码中存储,但是腾讯云本身是不建议这么做的,因为**敏感信息**放在前端很容易被捕获,由于我们本次是测试研发,所以这个过程可以忽略
正确的做法应该是,通过网站调用接口换取敏感信息
相关文档
实例化 上传sdk
var cos = new COS({     SecretId: 'COS_SECRETID',      SecretKey: 'COS_SECRETKEY',  });
  | 
 
到目前为止,我们上传图片准备的内容就已经OK,接下来,我们在**src/componets** 新建一个**ImageUpload** 组件
该组件需要满足什么要求呢?
- 可以显示传入的图片地址
 
- 可以删除传入的图片地址
 
- 可以上传图片到云服务器
 
- 上传到腾讯云之后,可以返回图片地址,显示  
 
- 上传成功之后,可以回调成功函数
 
这个上传组件简单吗?
no ! ! !
看似需求很明确,但是它真正的实现很复杂,我们通过一个图来看一下

从上图中,我们可以看到,实际上是有两种场景的,本地场景和已经上传的场景
下个章节,针对这个场景我们进行开发
封装上传组件-代码实现
**目标**实现上传组件的代码部分
JavaScript SDK 需浏览器支持基本的 HTML5 特性(支持 IE10 以上浏览器),以便支持 ajax 上传文件和计算文件 MD5 值。
新建文件上传组件
安装JavaScript SDK
$ npm i cos-js-sdk-v5 --save
   | 
 
新建上传图片组件 src/components/ImageUpload/index.vue
上传组件,我们可以沿用element的el-upload组件,并且采用照片墙的模式 list-type="picture-card"
放置el-upload组件
<template>   <el-upload list-type="picture-card">      <i class="el-icon-plus" />   </el-upload> </template>
   | 
 
全局注册组件
import PageTools from './PageTools' import UploadExcel from './UploadExcel' import ImageUpload from './ImageUpload' export default {   install(Vue) {     Vue.component('PageTools', PageTools)      Vue.component('UploadExcel', UploadExcel)      Vue.component('ImageUpload', ImageUpload)    } }
 
   | 
 
点击图片进行预览
限定上传的图片数量和action
<template>   <el-upload list-type="picture-card" :limit="1" action="#">   </el-upload> </template>
   | 
 
action为什么给#, 因为前面我们讲过了,我们要上传到腾讯云,需要自定义的上传方式,action给个#防止报错
预览
data() {     return {       fileList: [],        showDialog: false,        imgUrl: ''     }   },
  | 
 
   preview(file) {          this.imgUrl = file.url     this.showDialog = true },  
  | 
 
预览弹层
<el-dialog title="图片" :visible.sync="showDialog">     <img :src="imgUrl" style="width:100%" alt="">  </el-dialog>
   | 
 
根据上传数量控制上传按钮
控制上传显示
computed: {        fileComputed() {      return this.fileList.length === 1    }  },
  | 
 
 <el-upload       :on-preview="preview"       :on-remove="handleRemove"       :on-change="changeFile"       :file-list="fileList"       list-type="picture-card"       action="#"       :limit="1"       :class="{disabled: fileComputed }"     >
  <style> .disabled .el-upload--picture-card {   display: none } </style>
 
   | 
 
删除图片和添加图片
删除文件
handleRemove(file) {             this.fileList = this.fileList.filter(item => item.uid !== file.uid)   },
  | 
 
添加文件
 
 
 
  changeFile(file, fileList) {   this.fileList = fileList.map(item => item) }
 
  | 
 
上传之前检查
控制上传图片的类型和上传大小, 如果不满足条件 返回false上传就会停止
beforeUpload(file) {         const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png']   if (!types.includes(file.type)) {     this.$message.error('上传图片只能是 JPG、GIF、BMP、PNG 格式!')     return false   }      const maxSize = 5 * 1024 * 1024   if (maxSize < file.size) {     this.$message.error('图片大小最大不能超过5M')     return false   }   return true }
  | 
 
上传动作调用上传腾讯云
上传动作为el-upload的http-request属性
:http-request="upload"
         upload(params) {      console.log(params.file)    }
 
   | 
 
我们需要在该方法中,调用腾讯云的上传方法
腾讯云文档地址
身份ID和密钥可以通过腾讯云平台获取
登录 访问管理控制台 ,获取您的项目 SecretId 和 SecretKey。

实现代码
 upload(params) {
    if (params.file) {          cos.putObject({       Bucket: 'shuiruohanyu-106-1302806742',        Region: 'ap-beijing',        Key: params.file.name,        Body: params.file,        StorageClass: 'STANDARD'             }, function(err, data) {              console.log(err || data)     })   } }
 
  | 
 
上传成功之后处理返回数据
如何处理返回成功的返回数据
确定要上传记录id
beforeUpload(file) {          const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png']     if (!types.some(item => item === file.type)) {              this.$message.error('上传图片只能是 JPG、GIF、BMP、PNG 格式!')       return false      }          const maxSize = 5 * 1024 * 1024     if (file.size > maxSize) {              this.$message.error('上传的图片大小不能大于5M')       return false     }          this.currentFileUid = file.uid     return true    },
  | 
 
处理返回数据
 upload(params) {
    if (params.file) {          cos.putObject({       Bucket: 'shuiruohanyu-106-1302806742',        Region: 'ap-beijing',        Key: params.file.name,        Body: params.file,        StorageClass: 'STANDARD'             }, (err, data) => {              console.log(err || data)              if (!err && data.statusCode === 200) {                                             this.fileList = this.fileList.map(item => {                      if (item.uid === this.currentFileUid) {                          return { url: 'http://' + data.Location, upload: true }                                     }           return item         })                }     })   } }
 
  | 
 
我们在fileList中设置了属性为upload为true的属性,表示该图片已经上传成功了,如果fileList还有upload不为true的数据,那就表示该图片还没有上传完毕
上传的进度条显示
为了再上传图片过程中显示进度条,我们可以使用element-ui的进度条显示当前的上传进度
放置进度条
<el-progress v-if="showPercent" style="width: 180px" :percentage="percent" />
   | 
 
通过腾讯云sdk监听上传进度
cos.putObject({                    Bucket: 'laogao-1302806742',           Region: 'ap-guangzhou',           Key: params.file.name,           StorageClass: 'STANDARD',           Body: params.file,                     onProgress: (params) => {            this.percent = params.percent * 100          }        }
  | 
 
完整代码
<template>   <div>     <!-- 放置一个上传组件 -->     <!-- action这里不写上传地址 因为我们是调用腾讯云cos 不是一个地址可以搞定的 要用自定义的上传 -->     <el-upload       :on-preview="preview"       :on-remove="handleRemove"       :on-change="changeFile"       :before-upload="beforeUpload"       :file-list="fileList"       :http-request="upload"       list-type="picture-card"       action="#"       :limit="1"       :class="{disabled: fileComputed }"     >       <i class="el-icon-plus" />     </el-upload>     <!-- 进度条 -->     <el-progress v-if="showPercent" style="width: 180px" :percentage="percent" />     <!-- 放置一个弹层 -->     <!-- sync修饰符自动将弹层关闭了 -->     <el-dialog title="图片" :visible.sync="showDialog">       <img :src="imgUrl" style="width:100%" alt="">     </el-dialog>   </div> </template>
  <script> import COS from 'cos-js-sdk-v5' // 引入腾讯云的包 // 需要实例化 const cos = new COS({   SecretId: 'AKID0mqfEWqlUzIbeSkGRL6c7ML6c0B93To9',   SecretKey: 'JFwNZdeRF2iOp03FFsGNDm44vWFitmNF' }) // 实例化的包 已经具有了上传的能力 可以上传到该账号里面的存储桶了 export default {   data() {     return {       fileList: [],       showDialog: false, // 控制图片的显示或者隐藏       imgUrl: '', // 存储点击的图片地址       currentFileUid: '', // 用一个变量 记住当前上传的图片id       percent: 0,       showPercent: false // 默认不显示进度条     }   },   computed: {     // 设定一个计算属性 判断是否已经上传完了一张     fileComputed() {       return this.fileList.length === 1     }   },   methods: {     preview(file) {       // 这里应该弹出一个层 层里是点击的图片地址       this.imgUrl = file.url       this.showDialog = true     },     handleRemove(file) {       // file是点击删除的文件     //   将原来的文件给排除掉了 剩下的就是最新的数组了       this.fileList = this.fileList.filter(item => item.uid !== file.uid)     },     // 修改文件时触发     // 此时可以用fileList 因为该方法会进来很多遍 不能每次都去push     // fileList因为fileList参数是当前传进来的最新参数 我们只需要将其转化成数组即可 需要转化成一个新的数组     // [] => [...fileList] [] => fileList.map()     // 上传成功之后 还会进来 需要实现上传代码的逻辑 这里才会成功     changeFile(file, fileList) {       this.fileList = fileList.map(item => item)     },     beforeUpload(file) {       // 要开始做文件上传的检查了       // 文件类型 文件大小       const types = ['image/jpeg', 'image/gif', 'image/bmp', 'image/png']       if (!types.includes(file.type)) {         this.$message.error('上传图片只能是 JPG、GIF、BMP、PNG 格式!')         return false       }       //  检查大小       const maxSize = 5 * 1024 * 1024       if (maxSize < file.size) {         this.$message.error('图片大小最大不能超过5M')         return false       }       // file.uid       this.currentFileUid = file.uid // 记住当前的uid       this.showPercent = true       return true     },     // 自定义上传动作 有个参数 有个file对象,是我们需要上传到腾讯云服务器的内容     upload(params) {       if (params.file) {         //  上传文件到腾讯云         cos.putObject({           // 配置           Bucket: 'laogao-1302806742', // 存储桶名称           Region: 'ap-guangzhou', // 存储桶地域           Key: params.file.name, // 文件名作为key           StorageClass: 'STANDARD', // 此类写死           Body: params.file, // 将本地的文件赋值给腾讯云配置           // 进度条           onProgress: (params) => {             this.percent = params.percent * 100           }         }, (err, data) => {           // 需要判断错误与成功           if (!err && data.statusCode === 200) {             // 如果没有失败表示成功了             // 此时认为上传成功了             // this.currentFileUid             // 仍然有个小问题, 比如此时我们正在上传,但是调用了保存,保存在上传过程中进行,             // 此时上传还没有完成  此时可以这样做 : 给所有上传成功的图片 加一个属性 upload: true             this.fileList = this.fileList.map(item => {               if (item.uid === this.currentFileUid) {                 //   upload为true表示 该图片已经成功上传到服务器,地址已经是腾讯云的地址了  就不可以执行保存了                 return { url: 'http://' + data.Location, upload: true } // 将本地的地址换成腾讯云地址               }               return item             })             setTimeout(() => {               this.showPercent = false // 隐藏进度条               this.percent = 0 // 进度归0             }, 2000)
              // 将腾讯云地址写入到fileList上 ,保存的时候 就可以从fileList中直接获取图片地址
              // 此时注意,我们应该记住 当前上传的是哪个图片  上传成功之后,将图片的地址赋值回去           }         })       }     }   } } </script>
  <style> .disabled .el-upload--picture-card {   display: none } </style>
 
   | 
 
上传动作中,用到了上个小节中,我们注册的腾讯云cos的**存储桶名称和地域名称**
通过上面的代码,我们会发现,我们把上传之后的图片信息都给了**fileList数据,那么在应用时,就可以直接获取该实例的fileList数据即可**
提交代码
本节任务 完成上传组件的封装
在员工详情中应用上传组件
**目标**:应用封装好的上传组件
将员工的头像和证件照赋值给上传组件
在**user-info.vue**中放置上传组件
员工头像
<!-- 员工照片 -->     <el-row class="inline-info">       <el-col :span="12">         <el-form-item label="员工头像">           <!-- 放置上传图片 -->           <image-upload ref="staffPhoto" />         </el-form-item>       </el-col>     </el-row>
   | 
 
读取时赋值头像
    async getUserDetailById() {      this.userInfo = await getUserDetailById(this.userId)      if (this.userInfo.staffPhoto) {                this.$refs.staffPhoto.fileList = [{ url: this.userInfo.staffPhoto, upload: true }]      }    },
 
  | 
 
员工证件照
<el-form-item label="员工照片">         <!-- 放置上传图片 -->         <!-- ref不要重名 -->         <image-upload ref="myStaffPhoto" />       </el-form-item>
   | 
 
读取时赋值照片
    async  getPersonalDetail() {      this.formData = await getPersonalDetail(this.userId)      if (this.formData.staffPhoto) {        this.$refs.myStaffPhoto.fileList = [{ url: this.formData.staffPhoto, upload: true }]      }    },
 
  | 
 
保存时处理头像和证件照的保存
当点击保存更新时,获取图片的内容
async  saveUser() {          const fileList = this.$refs.staffPhoto.fileList      if (fileList.some(item => !item.upload)) {              this.$message.warning('您当前还有图片没有上传完成!')       return     }          await saveUserDetailById({ ...this.userInfo, staffPhoto: fileList && fileList.length ? fileList[0].url : '' })     this.$message.success('保存基本信息成功')   },
  | 
 
上面代码中,upload如果为true,表示该图片已经完成上传,以此来判断图片是否已经上传完成
保存时读取头像
async savePersonal() {    const fileList = this.$refs.myStaffPhoto.fileList    if (fileList.some(item => !item.upload)) {            this.$message.warning('您当前还有图片没有上传完成!')      return    }    await updatePersonal({ ...this.formData, staffPhoto: fileList && fileList.length ? fileList[0].url : '' })    this.$message.success('保存基础信息成功')  }
  | 
 
提交代码
本节任务 : 在员工详情中应用上传组件
员工列表显示图片
**目标**:在员工列表中心显示图片
员工的头像可以在列表项中添加一列来进行显示
<el-table-column label="头像" align="center">             <template slot-scope="{row}">               <img                 slot="reference"                 v-imageerror="require('@/assets/common/bigUserHeader.png')"                 :src="row.staffPhoto "                 style="border-radius: 50%; width: 100px; height: 100px; padding: 10px"                 alt=""               >             </template> </el-table-column>
   | 
 
我们尝试用之前的指令来处理图片的异常问题,但是发现只有两三张图片能显示

这是因为有的员工的头像的地址为空,给img赋值空的src不能触发错误事件,针对这一特点,我们需要对指令进行升级
插入节点的钩子里面判断空, 然后在组件更新之后的钩子中同样判断空
export const imageerror = {   inserted(dom, options) {               dom.src = dom.src || options.value
      dom.onerror = function() {              dom.src = options.value     }   },   componentUpdated(dom, options) {     dom.src = dom.src || options.value   } }
 
  | 
 
这样我们可以看到每个用户的头像了,如果没有头像则显示默认图片

**任务**:员工列表显示图片
图片地址生成二维码
目标 将图片地址生成二维码显示
我们想完成这样一个功能,当我们拥有头像地址时,将头像地址生成一个二维码,用手机扫码来访问
首先,需要安装生成二维码的插件
qrcode的用法是
QrCode.toCanvas(dom, info)
   | 
 
dom为一个canvas的dom对象, info为转化二维码的信息
我们尝试将canvas标签放到dialog的弹层中
<el-dialog title="二维码" :visible.sync="showCodeDialog" @opened="showQrCode" @close="imgUrl=''">   <el-row type="flex" justify="center">     <canvas ref="myCanvas" />   </el-row> </el-dialog>
   | 
 
在点击员工的图片时,显示弹层,并将图片地址转化成二维码
showQrCode(url) {      if (url) {     this.showCodeDialog = true           this.$nextTick(() => {              QrCode.toCanvas(this.$refs.myCanvas, url)             })   } else {     this.$message.warning('该用户还未上传头像')   } }
 
  | 
 

打印员工信息
目标 完成个人信息和工作信息的打印功能
新建打印页面及路由
创建页面组件
<template>   <div class="dashboard-container" id="myPrint">     <div class="app-container">       <el-card>         <el-breadcrumb separator="/" class="titInfo ">           <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>           <el-breadcrumb-item>             <router-link :to="{'path':'/employees'}">员工管理</router-link>           </el-breadcrumb-item>           <el-breadcrumb-item>打印</el-breadcrumb-item>         </el-breadcrumb>         <div v-if="type === 'personal'">           <h2 class="centInfo">员工信息表</h2>           <table cellspacing="0" width="100%" class="tableList">             <tr class="title">               <td colspan="8" class="centInfo">基本信息</td>             </tr>             <tr>               <th style="width:10%">姓名</th>               <td colspan="6" style="width:80%">{{ formData.username }}</td>               <td rowspan="5" style="width:10%"><img style="width:155px;height:218px" :src="formData.staffPhoto"></td>
              </tr>             <tr>               <th>性别</th>               <td colspan="6">{{ formData.sex }}</td>             </tr>             <tr>               <th>手机</th>               <td colspan="6">{{ formData.mobile }}</td>             </tr>             <tr>               <th>出生日期</th>               <td colspan="6">{{ formData.dateOfBirth | formatDate }}</td>             </tr>             <tr>               <th>最高学历</th>               <td colspan="6">{{ formData.theHighestDegreeOfEducation }}</td>             </tr>             <tr>               <th style="width:10%">是否可编辑</th>               <td style="width:35%">{{ formData.isItEditable }}</td>               <th style="width:10%">是否隐藏号码</th>               <td colspan="5" style="width:45%">{{ formData.doYouHideNumbers }}</td>             </tr>             <tr>               <th>国家地区</th>               <td>{{ formData.nationalArea }}</td>               <th>护照号</th>               <td colspan="5">{{ formData.passportNo }}</td>             </tr>             <tr>               <th>身份证号</th>               <td>{{ formData.idNumber }}</td>               <th>身份证照片</th>               <td colspan="5">{{ formData.iDCardPhoto }}</td>             </tr>             <tr>               <th>籍贯</th>               <td>{{ formData.nativePlace }}</td>               <th>民族</th>               <td colspan="5">{{ formData.nation }}</td>             </tr>             <tr>               <th>英文名</th>               <td>{{ formData.englishName }}</td>               <th>婚姻状况</th>               <td colspan="5">{{ formData.maritalStatus }}</td>             </tr>             <tr>               <th>员工照片</th>               <td>{{ formData.staffPhoto }}</td>               <th>生日</th>               <td colspan="5">{{ formData.birthday }}</td>             </tr>             <tr>               <th>属相</th>               <td>{{ formData.zodiac }}</td>               <th>年龄</th>               <td colspan="5">{{ formData.age }}</td>             </tr>             <tr>               <th>星座</th>               <td>{{ formData.constellation }}</td>               <th>血型</th>               <td colspan="5">{{ formData.bloodType }}</td>             </tr>             <tr>               <th>户籍所在地</th>               <td>{{ formData.domicile }}</td>               <th>政治面貌</th>               <td colspan="5">{{ formData.politicalOutlook }}</td>             </tr>             <tr>               <th>入党时间</th>               <td>{{ formData.timeToJoinTheParty }}</td>               <th>存档机构</th>               <td colspan="5">{{ formData.archivingOrganization }}</td>             </tr>             <tr>               <th>子女状态</th>               <td>{{ formData.stateOfChildren }}</td>               <th>子女有无商业保险</th>               <td colspan="5">{{ formData.doChildrenHaveCommercialInsurance }}</td>             </tr>             <tr>               <th>有无违法违纪行为</th>               <td>{{ formData.isThereAnyViolationOfLawOrDiscipline }}</td>               <th>有无重大病史</th>               <td colspan="5">{{ formData.areThereAnyMajorMedicalHistories }}</td>             </tr>             <tr class="title">               <td colspan="8" class="centInfo">通讯信息</td>             </tr>             <tr>               <th>QQ</th>               <td>{{ formData.qQ }}</td>               <th>微信</th>               <td colspan="5">{{ formData.weChat }}</td>             </tr>             <tr>               <th>居住证城市</th>               <td>{{ formData.residenceCardCity }}</td>               <th>居住证办理日期</th>               <td colspan="5">{{ formData.dateOfResidencePermit }}</td>             </tr>             <tr>               <th>居住证截止日期</th>               <td>{{ formData.residencePermitDeadline }}</td>               <th>现居住地</th>               <td colspan="5">{{ formData.placeOfResidence }}</td>             </tr>             <tr>               <th>通讯地址</th>               <td>{{ formData.postalAddress }}</td>               <th>联系手机</th>               <td colspan="5">{{ formData.contactTheMobilePhone }}</td>             </tr>             <tr>               <th>个人邮箱</th>               <td>{{ formData.personalMailbox }}</td>               <th>紧急联系人</th>               <td colspan="5">{{ formData.emergencyContact }}</td>             </tr>             <tr>               <th>紧急联系电话</th>               <td colspan="7">{{ formData.emergencyContactNumber }}</td>             </tr>             <tr class="title">               <td colspan="8" class="centInfo">账号信息</td>             </tr>             <tr>               <th>社保电脑号</th>               <td>{{ formData.socialSecurityComputerNumber }}</td>               <th>公积金账号</th>               <td colspan="5">{{ formData.providentFundAccount }}</td>             </tr>             <tr>               <th>银行卡号</th>               <td>{{ formData.bankCardNumber }}</td>               <th>开户行</th>               <td colspan="5">{{ formData.openingBank }}</td>             </tr>             <tr class="title">               <td colspan="8" class="centInfo">教育信息</td>             </tr>             <tr>               <th>学历类型</th>               <td>{{ formData.educationalType }}</td>               <th>毕业学校</th>               <td colspan="5">{{ formData.graduateSchool }}</td>             </tr>             <tr>               <th>入学时间</th>               <td>{{ formData.enrolmentTime }}</td>               <th>毕业时间</th>               <td colspan="5">{{ formData.graduationTime }}</td>             </tr>             <tr>               <th>专业</th>               <td>{{ formData.major }}</td>               <th>毕业证书</th>               <td colspan="5">{{ formData.graduationCertificate }}</td>             </tr>             <tr>               <th>学位证书</th>               <td colspan="7">{{ formData.certificateOfAcademicDegree }}</td>             </tr>             <tr class="title">               <td colspan="8" class="centInfo">从业信息</td>             </tr>             <tr>               <th>上家公司</th>               <td>{{ formData.homeCompany }}</td>               <th>职称</th>               <td colspan="5">{{ formData.title }}</td>             </tr>             <tr>               <th>简历</th>               <td>{{ formData.resume }}</td>               <th>有无竞业限制</th>               <td colspan="5">{{ formData.isThereAnyCompetitionRestriction }}</td>             </tr>             <tr>               <th>前公司离职证明</th>               <td>{{ formData.proofOfDepartureOfFormerCompany }}</td>               <th>备注</th>               <td colspan="5">{{ formData.remarks }}</td>             </tr>           </table>           <div class="foot">签字:___________日期:___________</div>         </div>         <div v-else>           <h2 class="centInfo">岗位信息表</h2>           <table cellspacing="0" width="100%" class="tableList">             <tr class="title">               <td colspan="4" class="centInfo">基本信息</td>             </tr>             <tr>               <th style="width:10%">姓名</th>               <td style="width:40%">{{ formData.username }}</td>               <th style="width:10%">入职日期</th>               <td style="width:40%">{{ formData.dateOfEntry }}</td>             </tr>             <tr>               <th>部门</th>               <td>{{ formData.departmentName }}</td>               <th>岗位</th>               <td>{{ formData.post }}</td>             </tr>             <tr>               <th>工作邮箱</th>               <td>{{ formData.workMailbox }}</td>               <th>工号</th>               <td>{{ formData.workNumber }}</td>             </tr>             <tr>               <th>转正日期</th>               <td>{{ formData.dateOfCorrection }}</td>               <th>转正状态</th>               <td>{{ formData.stateOfCorrection }}</td>             </tr>             <tr>               <th>职级</th>               <td>{{ formData.rank }}</td>               <th>汇报对象</th>               <td>{{ formData.reportName }}</td>             </tr>             <tr>               <th>HRBP</th>               <td>{{ formData.hRBP }}</td>               <th>聘用形式</th>               <td>{{ formData.formOfEmployment }}</td>             </tr>
              <tr>               <th>管理形式</th>               <td>{{ formData.formOfManagement }}</td>               <th>调整司龄</th>               <td>{{ formData.adjustmentAgedays }}</td>             </tr>             <tr>               <th>司龄</th>               <td>{{ formData.ageOfDivision }}</td>               <th>首次参加工作时间</th>               <td>{{ formData.workingTimeForTheFirstTime }}</td>             </tr>
              <tr>               <th>调整工龄天</th>               <td>{{ formData.adjustmentOfLengthOfService }}</td>               <th>工龄</th>               <td>{{ formData.workingYears }}</td>             </tr>             <tr>               <th>纳税城市</th>               <td>{{ formData.taxableCity }}</td>               <th>转正评价</th>               <td>{{ formData.correctionEvaluation }}</td>             </tr>             <tr class="title">               <td colspan="4" class="centInfo">合同信息</td>             </tr>             <tr>               <th>首次合同开始时间</th>               <td>{{ formData.initialContractStartTime }}</td>               <th>首次合同结束时间</th>               <td>{{ formData.firstContractTerminationTime }}</td>             </tr>             <tr>               <th>现合同开始时间</th>               <td>{{ formData.currentContractStartTime }}</td>               <th>现合同结束时间</th>               <td>{{ formData.closingTimeOfCurrentContract }}</td>             </tr>
              <tr>               <th>合同期限</th>               <td>{{ formData.contractPeriod }}</td>               <th>合同文件</th>               <td>{{ formData.contractDocuments }}</td>             </tr>             <tr>               <th>续签次数</th>               <td colspan="3">{{ formData.renewalNumber }}</td>             </tr>             <tr class="title">               <td colspan="4" class="centInfo">招聘信息</td>             </tr>             <tr>               <th>其他招聘渠道</th>               <td>{{ formData.otherRecruitmentChannels }}</td>               <th>招聘渠道</th>               <td>{{ formData.recruitmentChannels }}</td>             </tr>             <tr>               <th>社招校招</th>               <td>{{ formData.socialRecruitment }}</td>               <th>推荐企业人</th>               <td>{{ formData.recommenderBusinessPeople }}</td>             </tr>           </table>           <div class="foot">签字:___________日期:___________</div>         </div>       </el-card>     </div>   </div> </template>
  <script> import { getPersonalDetail, getJobDetail } from '@/api/employees' import { getUserDetailById } from '@/api/user' export default {   data() {     return {       formData: {},       userId: this.$route.params.id,       type: this.$route.query.type // 打印类型     }   },   // 创建完毕状态   created() {     this.type === 'personal' ? this.getPersonalDetail() : this.getJobDetail()   },   // 组件更新   methods: {     async getPersonalDetail() {       this.formData = await getPersonalDetail(this.userId) // 获取个人基本信息     },     async getJobDetail() {       const userInfo = await getUserDetailById(this.userId)       const jobInfo = await getJobDetail(this.userId) // 获取个人基本信息       this.formData = { ...userInfo, ...jobInfo }     }   } } </script>
  <style lang="scss"> .foot {   padding: 30px 0;   text-align: right; } </style>
 
 
   | 
 
该页面内容实际上就是读取个人和详情的接口数据,根据传入的type类型决定显示个人还是岗位
type为**personal时显示个人,为job**时显示岗位
新建打印页面路由
{     path: 'print/:id',      component: () => import('@/views/employees/print'),      hidden: true,     meta: {       title: '打印',        icon: 'people'     }   }
  | 
 
完成详情到打印的跳转
个人
<el-row type="flex" justify="end">            <el-tooltip content="打印个人基本信息">                <router-link :to="`/employees/print/${userId}?type=personal`">                  <i class="el-icon-printer" />                </router-link>             </el-tooltip>  </el-row>
   | 
 
岗位
<el-row type="flex" justify="end">             <el-tooltip content="打印岗位信息">               <router-link :to="`/employees/print/${userId}?type=job`">                 <i class="el-icon-printer"  />               </router-link>             </el-tooltip>   </el-row>
   | 
 
利用vue-print-nb进行打印
首先,打印功能我们借助一个比较流行的插件
它的用法是 
首先注册该插件
import Print from 'vue-print-nb' Vue.use(Print);
   | 
 
使用v-print指令的方式进行打印
<el-row type="flex" justify="end">         <el-button v-print="printObj" size="small" type="primary">打印</el-button>  </el-row>  printObj: {       id: 'myPrint'  }
   | 
 
最终,我们看到的效果

提交代码
**本节任务**打印员工信息