封装通用工具栏组件

  1. <template>
  2. <el-card>
  3. <div class="page-tools">
  4. <!-- 左侧 -->
  5. <div class="left">
  6. <div class="tips">
  7. <i class="el-icon-info" />
  8. <span>本月: 社保在缴 公积金在缴</span>
  9. </div>
  10. </div>
  11. <div class="right">
  12. <!-- 右侧 -->
  13. <el-button type="primary" size="small">历史归档</el-button>
  14. <el-button type="primary" size="small">导出</el-button>
  15. </div>
  16. </div>
  17. </el-card>
  18. </template>
  19. <script>
  20. export default {}
  21. </script>
  22. <style lang="scss" scoped>
  23. .page-tools {
  24. display: flex;
  25. justify-content: space-between;
  26. align-items: center;
  27. ::v-deep.tips {
  28. line-height: 34px;
  29. padding: 0px 15px;
  30. border-radius: 5px;
  31. border: 1px solid rgba(145, 213, 255, 1);
  32. background: rgba(230, 247, 255, 1);
  33. i {
  34. margin-right: 10px;
  35. color: #409eff;
  36. }
  37. }
  38. }
  39. </style>

里面的内容可以通过插槽来配置这样就可以根据不同页面配置不同内容 具名插槽将来在使用组件的时候,只需要按照对应的插槽名称就可以在特定的位置插入内容

  1. <template>
  2. <el-card>
  3. <div class="page-tools">
  4. <!-- 左侧 -->
  5. <div class="left">
  6. <slot name="left" />
  7. </div>
  8. <div class="right">
  9. <!-- 右侧 -->
  10. <slot name="right" />
  11. </div>
  12. </div>
  13. </el-card>
  14. </template>
  1. <template>
  2. <div class="employees-container">
  3. <div class="app-container">
  4. <page-tools>
  5. <!-- 插入到left插槽位置 -->
  6. <template #left>
  7. <i class="el-icon-info" />
  8. <span>本月: 社保在缴 公积金在缴</span>
  9. </template>
  10. <!-- 插入到right插槽位置 -->
  11. <template #right>
  12. <el-button type="primary" size="small">导入excel</el-button>
  13. <el-button type="primary" size="small">导出excel</el-button>
  14. <el-button type="primary" size="small">新增员工</el-button>
  15. </template>
  16. </page-tools>
  17. </div>
  18. </div>
  19. </template>
  20. <script>
  21. import PageTools from '@/components/PageTools'
  22. export default {
  23. components:{
  24. PageTools
  25. }
  26. }
  27. </script>

全局注册组件

  1. import PageTools from '@/components/PageTools'
  2. Vue.component('PageTools', PageTools)

封装成插件

开发 install是一个固定方法 有一个参数是在Vue

  • 函数写法 函数本身会被当成install方法,只需要在函数内部写
  • 对象写法 需要写入install方法

插件注册

  • 需要在new Vue之前
  1. // 1.开发
  2. import PageTools from '@/components/PageTools'
  3. const pageTools = {
  4. // install方法固定写法 vue文档中规定的插件需要暴漏一个install方法
  5. install(Vue) {
  6. Vue.component('page-tools', PageTools)
  7. }
  8. }
  9. export default pageTools
  10. // 2.vue.use(pageTools) 相当于执行了 这个方法内部的 install方法并传入了一个实参 Vue构造函数

员工列表渲染

  1. <template>
  2. <div class="employees-container">
  3. <div class="app-container">
  4. <page-tools>
  5. <template #left>
  6. <div class="tips">
  7. <i class="el-icon-info" />
  8. <span>总记录数: 200</span>
  9. <div>
  10. </template>
  11. <template #right>
  12. <el-button type="warning" size="small">excel导入</el-button>
  13. <el-button type="danger" size="small">excel导出</el-button>
  14. <el-button type="primary" size="small">新增员工</el-button>
  15. </template>
  16. </page-tools>
  17. <el-card style="margin-top: 10px;">
  18. <el-table border>
  19. <el-table-column label="序号"/>
  20. <el-table-column label="姓名"/>
  21. <el-table-column label="工号"/>
  22. <el-table-column label="聘用形式"/>
  23. <el-table-column label="部门"/>
  24. <el-table-column label="入职时间" width="180"/>
  25. <el-table-column label="操作" fixed="right" width="200">
  26. <template>
  27. <el-button type="text" size="small">查看</el-button>
  28. <el-button type="text" size="small">分配角色</el-button>
  29. <el-button type="text" size="small">删除</el-button>
  30. </template>
  31. </el-table-column>
  32. </el-table>
  33. </el-card>
  34. </div>
  35. </div>
  36. </template>
  1. /**
  2. *
  3. * @param {*} params { page 当前页数 size每页条数 }
  4. * @returns
  5. */
  6. export function getEmployeeListApi(params) {
  7. return request({
  8. methods: 'get',
  9. url: '/sys/user',
  10. params
  11. })
  12. }

获取到的 入职方式和时间 不符合要求 格式化处理 采用插槽获取到当前行的数据 {{}} 显示数据 这样就可以处理到数据 定义一个方法 传入当前的数据 函数的返回值 会显示在{{}}这里

枚举

枚举写法 一次性列出 所有的对应情况 然后通过对象取值 称之为枚举 比多分枝语句对应关系更加清晰 方便拓展 通常少量的 不会发生变化的使用前端枚举 大量的变化必须接口来做

  1. disFormOfEmployment(value) {
  2. const type = {
  3. 1: '正式员工',
  4. 2: '非正式员工',
  5. 3: '老板亲戚',
  6. 4: '临时工'
  7. }
  8. return type[value]
  9. },
  1. <template>
  2. <div class="employees-container">
  3. <div class="app-container">
  4. <page-tools>
  5. <template #left>
  6. <div class="tips">
  7. <i class="el-icon-info" />
  8. <span>总记录数: 200</span>
  9. </div>
  10. </template>
  11. <template #right>
  12. <el-button type="warning" size="small">excel导入</el-button>
  13. <el-button type="danger" size="small">excel导出</el-button>
  14. <el-button type="primary" size="small">新增员工</el-button>
  15. </template>
  16. </page-tools>
  17. <el-card style="margin-top: 10px;">
  18. <el-table :data="employeeList" border>
  19. <el-table-column label="序号" type="index" />
  20. <el-table-column label="姓名" prop="username" />
  21. <el-table-column label="工号" prop="workNumber" />
  22. <el-table-column label="聘用形式">
  23. <template #default="{row}">
  24. {{ disFormOfEmployment(row.formOfEmployment) }}
  25. </template>
  26. </el-table-column>
  27. <el-table-column label="部门" prop="departmentName" />
  28. <el-table-column label="入职时间" width="180">
  29. <template #default="{row}">
  30. {{ disTimeOfEntry(row.timeOfEntry) }}
  31. </template>
  32. </el-table-column>
  33. <el-table-column>
  34. <el-table-column label="操作" fixed="right" width="200">
  35. <template>
  36. <el-button type="text" size="small">查看</el-button>
  37. <el-button type="text" size="small">分配角色</el-button>
  38. <el-button type="text" size="small">删除</el-button>
  39. </template>
  40. </el-table-column>
  41. </el-table-column>
  42. </el-table>
  43. </el-card>
  44. </div>
  45. </div>
  46. </template>
  47. <script>
  48. import dayjs from 'dayjs'
  49. import { getEmployeeListApi } from '@/api/employeeSimple'
  50. export default {
  51. data() {
  52. return {
  53. parmas: {
  54. page: 1, // 当前页
  55. size: 1 // 每页条数
  56. },
  57. employeeList: [],
  58. total: 0
  59. }
  60. },
  61. mounted () {
  62. this.getEmployeeList()
  63. },
  64. methods: {
  65. async getEmployeeList() {
  66. const res = await getEmployeeListApi(this.parmas)
  67. this.employeeList = res.data.rows
  68. this.total = res.total
  69. // console.log(data)
  70. },
  71. disFormOfEmployment(value) {
  72. const type = {
  73. 1: '正式员工',
  74. 2: '非正式员工',
  75. 3: '老板亲戚',
  76. 4: '临时工'
  77. }
  78. return type[value]
  79. },
  80. disTimeOfEntry(time) {
  81. return dayjs(time).format('YYYY-MM-DD')
  82. },
  83. pageChange(page) {
  84. this.parmas.page = page
  85. this.getEmployeeList()
  86. }
  87. }
  88. }
  89. </script>
  90. <style lang="scss" scoped></style>

分页器

@current-change="pageChange" 添加这个事件 点击会调用该函数且传入当前页码

  1. <div style="height: 60px; margin-top: 10px">
  2. <el-pagination
  3. :total="total"
  4. :current-page="parmas.page"
  5. :page-size="parmas.size"
  6. layout="prev, pager, next"
  7. @current-change="pageChange"
  8. />
  9. </div>
  1. export default {
  2. data() {
  3. return {
  4. parmas: {
  5. page: 1, // 当前页
  6. size: 1 // 每页条数
  7. },
  8. employeeList: [],
  9. total: 0
  10. }
  11. },
  12. mounted () {
  13. this.getEmployeeList()
  14. },
  15. methods: {
  16. async getEmployeeList() {
  17. const res = await getEmployeeListApi(this.parmas)
  18. this.employeeList = res.data.rows
  19. this.total = res.data.total
  20. },
  21. pageChange(page) {
  22. this.parmas.page = page
  23. this.getEmployeeList()
  24. }
  25. }
  26. }
  27. </script>

删除功能

默认插槽 #defalut=”{row}” 获取当前行数据 给删除按钮添加click 传入id

  1. // 删除角色
  2. /**
  3. *
  4. * @param {*} id 员工id
  5. * @returns
  6. */
  7. export const delEmployee = id => request.delete(`/sys/user/${id}`)
  1. // 删除角色
  2. async delEmployee(id) {
  3. try {
  4. await delEmployee(id)
  5. // 重新获取列表渲染
  6. this.getEmployeeList()
  7. this.$message.success('删除角色成功')
  8. } catch (error) {
  9. this.$message.error(error)
  10. }
  11. },

新增功能

封装弹层组件 父子通信来控制弹层显隐 弹层组件内表单收集信息 (这里的联级组件选择的时候 会返回给一个数组 数组的最后一条才是需要的数据) departmentName: this.formData.departmentName[this.formData.departmentName.length - 1] 如果一个对象中发生了key重复 后面的会覆盖前面的 提交过去的就是处理好的那一条字符串 不再是组件提供的数组

  1. <template>
  2. <el-dialog title="新增员工" :visible="showDialog" @open="openDialog" @close="closeDialog">
  3. <!-- 表单 -->
  4. <el-form ref="formRef" label-width="120px" :model="formData" :rules="rules">
  5. <el-form-item label="姓名" prop="username">
  6. <el-input v-model="formData.username" style="width:50%" placeholder="请输入姓名" />
  7. </el-form-item>
  8. <el-form-item label="手机" prop="mobile">
  9. <el-input v-model="formData.mobile" style="width:50%" placeholder="请输入手机号" />
  10. </el-form-item>
  11. <el-form-item label="入职时间" prop="timeOfEntry">
  12. <el-date-picker v-model="formData.timeOfEntry" style="width:50%" placeholder="请选择入职时间" />
  13. </el-form-item>
  14. <el-form-item label="聘用形式" prop="formOfEmployment">
  15. <el-select v-model="formData.formOfEmployment" style="width:50%" placeholder="请选择">
  16. <el-option
  17. v-for="item in options"
  18. :key="item.type"
  19. :label="item.name"
  20. :value="item.id"
  21. />
  22. </el-select></el-form-item>
  23. <el-form-item label="工号" prop="workNumber">
  24. <el-input v-model="formData.workNumber" style="width:50%" placeholder="请输入工号" />
  25. </el-form-item>
  26. <el-form-item label="部门" prop="departmentName">
  27. <el-cascader
  28. v-model="formData.departmentName"
  29. placeholder="请选择部门"
  30. :options="departmentList"
  31. :props="{ checkStrictly: true, value:'name' ,label:'name' }"
  32. clearable
  33. />
  34. </el-form-item>
  35. <el-form-item label="转正时间">
  36. <el-date-picker v-model="formData.correctionTime" style="width:50%" placeholder="请选择转正时间" />
  37. </el-form-item>
  38. </el-form>
  39. <!-- footer插槽 -->
  40. <template #footer>
  41. <el-button>取消</el-button>
  42. <el-button type="primary" @click="submit">确定</el-button>
  43. </template>
  44. </el-dialog>
  45. </template>
  46. <script>
  47. import transTree from '@/utils/transTree'
  48. import { getDepartmentsApi } from '@/api/departments'
  49. import { addEmployee } from '@/api/employeeSimple'
  50. export default {
  51. props: {
  52. showDialog: {
  53. type: Boolean
  54. }
  55. },
  56. data() {
  57. return {
  58. // 部门数据
  59. departmentList: [],
  60. // 录用形式
  61. options: [
  62. { id: 1, name: '正式员工' },
  63. { id: 2, name: '非正式员工' }
  64. ],
  65. formData: {
  66. username: '', // 用户名
  67. mobile: '', // 手机号
  68. formOfEmployment: '', // 聘用形式
  69. workNumber: '', // 工号
  70. departmentName: '', // 部门
  71. timeOfEntry: '', // 入职时间
  72. correctionTime: '' // 转正时间
  73. },
  74. rules: {
  75. username: [
  76. { required: true, message: '用户姓名不能为空', trigger: ['blur', 'change'] },
  77. { min: 1, max: 4, message: '用户姓名为1-4位', trigger: ['blur', 'change'] }
  78. ],
  79. mobile: [
  80. { required: true, message: '手机号不能为空', trigger: ['blur', 'change'] },
  81. { pattern: /^1[3-9]\d{9}$/, message: '手机号格式不正确', trigger: ['blur', 'change'] }
  82. ],
  83. formOfEmployment: [
  84. { required: true, message: '聘用形式不能为空', trigger: ['blur', 'change'] }
  85. ],
  86. workNumber: [
  87. { required: true, message: '工号不能为空', trigger: ['blur', 'change'] }
  88. ],
  89. departmentName: [
  90. { required: true, message: '部门不能为空', trigger: ['blur', 'change'] }
  91. ],
  92. timeOfEntry: [
  93. { required: true, message: '请选择入职时间', trigger: ['blur', 'change'] }
  94. ]
  95. }
  96. }
  97. },
  98. methods: {
  99. closeDialog() {
  100. this.$emit('close_dialog')
  101. // 重置表单数据
  102. this.formData = {
  103. username: '',
  104. mobile: '',
  105. formOfEmployment: '',
  106. workNumber: '',
  107. departmentName: '',
  108. timeOfEntry: '',
  109. correctionTime: ''
  110. }
  111. // 重置校验结果
  112. this.$refs.addForm.resetFields()
  113. },
  114. async getdepartment() {
  115. const res = await getDepartmentsApi()
  116. this.departmentList = transTree(res.data.depts)
  117. console.log(this.departmentList)
  118. },
  119. openDialog() {
  120. this.getdepartment()
  121. },
  122. submit() {
  123. this.$refs.formRef.validate(async(isOk) => {
  124. if (isOk) {
  125. // departmentName 会出现多个 这里只需要上传一个
  126. // 如果一个对象中发生了key重复 后面的会覆盖前面的 提交过去的就是处理好的那一条字符串 不再是组件提供的数组
  127. await addEmployee({ ...this.formData,
  128. departmentName: this.formData.departmentName[this.formData.departmentName.length - 1]
  129. })
  130. this.$message.success('新增成功')
  131. this.closeDialog()
  132. }
  133. })
  134. }
  135. }
  136. }
  137. </script>

导入导出功能

导入

新建一个页面在点击导入的时候他会跳转到这个页面

  1. {
  2. path: '/import',
  3. component: Layout,
  4. redirect: '/dashboard',
  5. children: [{
  6. path: '',
  7. name: 'import',
  8. component: () => import('@/views/import')
  9. }]
  10. },

在项目模板中寻找到相同的功能 根据路由找到

项目地址 https://gitee.com/panjiachen/vue-element-admin
预览地址 https://panjiachen.gitee.io/vue-element-admin/#/excel/upload-excel

页面

这里依赖一个uploadexcel组件 需要下载 xlsx 依赖组 ,件提供了俩个props, 一个为解析Excel成功之后调用的函数 一个为上传之前的校验函数

  1. <template>
  2. <div class="app-container">
  3. <upload-excel-component :on-success="handleSuccess" :before-upload="beforeUpload" />
  4. <el-table :data="tableData" border highlight-current-row style="width: 100%;margin-top:20px;">
  5. <el-table-column v-for="item of tableHeader" :key="item" :prop="item" :label="item" />
  6. </el-table>
  7. </div>
  8. </template>
  9. <script>
  10. import UploadExcelComponent from '@/components/UploadExcel/index.vue'
  11. import { importEmployee } from '@/api/employeeSimple'
  12. import { getImportJsData } from '@/utils/excel'
  13. export default {
  14. name: 'UploadExcel',
  15. components: { UploadExcelComponent },
  16. data() {
  17. return {
  18. tableData: [],
  19. tableHeader: []
  20. }
  21. },
  22. methods: {
  23. beforeUpload(file) {
  24. // 文件大小 /1024 两次 就换算到单位为m
  25. const isLt1M = file.size / 1024 / 1024 < 1
  26. if (isLt1M) {
  27. return true
  28. }
  29. this.$message({
  30. message: 'Please do not upload files larger than 1m in size.',
  31. type: 'warning'
  32. })
  33. return false
  34. },
  35. async handleSuccess({ results, header }) {
  36. // 1.拿到数据 通过格式化方法 处理成后端要求的数据格式
  37. // 2.调用真实接口传递数据
  38. const headerRelation = {
  39. '姓名': 'username',
  40. '手机号': 'mobile',
  41. '入职日期': 'timeOfEntry',
  42. '工号': 'workNumber',
  43. '聘用形式': 'formOfEmployment',
  44. '部门': 'departmentName'
  45. }
  46. const formatData = getImportJsData(results, headerRelation)
  47. await importEmployee(formatData)
  48. this.$message.success('提交成功')
  49. this.$router.back()
  50. }
  51. }
  52. }
  53. </script>

uploadexcel组件

这里的使用的xlsx版本比较老 ,需要导出所有并改别名为之前的 import * as XLSX from 'xlsx' 否则导入会报错如下 image.png

  1. <template>
  2. <div>
  3. <input ref="excel-upload-input" class="excel-upload-input" type="file" accept=".xlsx, .xls" @change="handleClick">
  4. <div class="drop" @drop="handleDrop" @dragover="handleDragover" @dragenter="handleDragover">
  5. Drop excel file here or
  6. <el-button :loading="loading" style="margin-left:16px;" size="mini" type="primary" @click="handleUpload">
  7. Browse
  8. </el-button>
  9. </div>
  10. </div>
  11. </template>
  12. <script>
  13. import * as XLSX from 'xlsx'
  14. export default {
  15. props: {
  16. // 解析前的校验函数 看文件是否大于1m 格式是否满足
  17. beforeUpload: Function, // eslint-disable-line
  18. // 解析成功之后的回调函数 会自动调用 并且会把解析之后的数据当成实参传入
  19. onSuccess: Function// eslint-disable-line
  20. },
  21. data() {
  22. return {
  23. loading: false,
  24. excelData: {
  25. header: null,
  26. results: null
  27. }
  28. }
  29. },
  30. methods: {
  31. generateData({ header, results }) {
  32. this.excelData.header = header
  33. this.excelData.results = results
  34. this.onSuccess && this.onSuccess(this.excelData)
  35. },
  36. handleDrop(e) {
  37. e.stopPropagation()
  38. e.preventDefault()
  39. if (this.loading) return
  40. const files = e.dataTransfer.files
  41. if (files.length !== 1) {
  42. this.$message.error('Only support uploading one file!')
  43. return
  44. }
  45. const rawFile = files[0] // only use files[0]
  46. if (!this.isExcel(rawFile)) {
  47. this.$message.error('Only supports upload .xlsx, .xls, .csv suffix files')
  48. return false
  49. }
  50. this.upload(rawFile)
  51. e.stopPropagation()
  52. e.preventDefault()
  53. },
  54. handleDragover(e) {
  55. e.stopPropagation()
  56. e.preventDefault()
  57. e.dataTransfer.dropEffect = 'copy'
  58. },
  59. handleUpload() {
  60. this.$refs['excel-upload-input'].click()
  61. },
  62. handleClick(e) {
  63. const files = e.target.files
  64. const rawFile = files[0] // only use files[0]
  65. if (!rawFile) return
  66. this.upload(rawFile)
  67. },
  68. upload(rawFile) {
  69. this.$refs['excel-upload-input'].value = null // fix can't select the same excel
  70. if (!this.beforeUpload) {
  71. this.readerData(rawFile)
  72. return
  73. }
  74. const before = this.beforeUpload(rawFile)
  75. if (before) {
  76. this.readerData(rawFile)
  77. }
  78. },
  79. readerData(rawFile) {
  80. this.loading = true
  81. return new Promise((resolve, reject) => {
  82. const reader = new FileReader()
  83. reader.onload = e => {
  84. const data = e.target.result
  85. const workbook = XLSX.read(data, { type: 'array' })
  86. const firstSheetName = workbook.SheetNames[0]
  87. const worksheet = workbook.Sheets[firstSheetName]
  88. const header = this.getHeaderRow(worksheet)
  89. const results = XLSX.utils.sheet_to_json(worksheet)
  90. this.generateData({ header, results })
  91. this.loading = false
  92. resolve()
  93. }
  94. reader.readAsArrayBuffer(rawFile)
  95. })
  96. },
  97. getHeaderRow(sheet) {
  98. const headers = []
  99. const range = XLSX.utils.decode_range(sheet['!ref'])
  100. let C
  101. const R = range.s.r
  102. /* start in the first row */
  103. for (C = range.s.c; C <= range.e.c; ++C) { /* walk every column in the range */
  104. const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
  105. /* find the cell in the first row */
  106. let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
  107. if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
  108. headers.push(hdr)
  109. }
  110. return headers
  111. },
  112. isExcel(file) {
  113. return /\.(xlsx|xls|csv)$/.test(file.name)
  114. }
  115. }
  116. }
  117. </script>
  118. <style scoped>
  119. .excel-upload-input{
  120. display: none;
  121. z-index: -9999;
  122. }
  123. .drop{
  124. border: 2px dashed #bbb;
  125. width: 600px;
  126. height: 160px;
  127. line-height: 160px;
  128. margin: 0 auto;
  129. font-size: 24px;
  130. border-radius: 5px;
  131. text-align: center;
  132. color: #bbb;
  133. position: relative;
  134. }
  135. </style>

使用excel表格批量导入

封装一个工具uitils/excel导入里面的getImportJsData方法

  1. /**
  2. * @description: 获取导出时的表头数据和表格数据
  3. * @param {*} { sourceData:后端返回的源数据,header:表格头中因为对应关系}
  4. * @return {*}
  5. */
  6. // 处理函数中对key判断如果发生当前它是聘用形式 通过枚举做一下处理 返回中文
  7. // 枚举处理函数 根据1/2返回正式或者非正式
  8. function transEmployment(value) {
  9. const TYPES = {
  10. 1: '正式',
  11. 2: '非正式'
  12. }
  13. return TYPES[value]
  14. }
  15. export function getExportData(sourceData, headerRelation) {
  16. const data = sourceData.map(item => {
  17. const arr = []
  18. Object.values(headerRelation).forEach(key => {
  19. // 关键位置:把所有的value不做任何处理直接扔到了数组中 导致excel是源数据
  20. // 如果是当前要处理的是聘用形式 就先转化一步再添加到数组中
  21. if (key === 'formOfEmployment') {
  22. const formatValue = transEmployment(item[key])
  23. arr.push(formatValue)
  24. } else {
  25. arr.push(item[key])
  26. }
  27. })
  28. return arr
  29. })
  30. return {
  31. data
  32. }
  33. }
  34. /**
  35. * @description: 获取导入时的处理之后的接口数据
  36. * @param {*} results
  37. * @return {*}
  38. */
  39. export function getImportJsData(results, headerRelation) {
  40. const newArr = []
  41. // 将所有的中文key转换成英文key 然后添加到新数组中
  42. results.forEach(item => {
  43. const map = {}
  44. Object.keys(item).forEach(key => {
  45. map[headerRelation[key]] = item[key]
  46. })
  47. newArr.push(map)
  48. })
  49. // 时间处理
  50. newArr.forEach(item => {
  51. Object.keys(item).forEach(key => {
  52. if (key === 'timeOfEntry') {
  53. item[key] = new Date(formatDate(item[key], '/'))
  54. }
  55. })
  56. })
  57. return newArr
  58. }
  59. export function formatDate(numb, format) {
  60. const time = new Date((numb - 1) * 24 * 3600000 + 1)
  61. time.setYear(time.getFullYear() - 70)
  62. const year = time.getFullYear() + ''
  63. const month = time.getMonth() + 1 + ''
  64. const date = time.getDate() - 1 + ''
  65. if (format && format.length === 1) {
  66. return year + format + (month < 10 ? '0' + month : month) + format + (date < 10 ? '0' + date : date)
  67. }
  68. return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date)
  69. }

调用接口提交处理好的数据

  1. /**
  2. * @description: 导入excel
  3. * @param {*} data
  4. [{
  5. mobile: 15751786320
  6. timeOfEntry: "2019-03-09T16:00:00.000Z"
  7. username: "测试人员1"
  8. workNumber: 88088,
  9. formOfEmployment: 0,
  10. departmentName: "技术部"
  11. }
  12. ]
  13. * @return {*}
  14. */
  15. export function importEmployee(data) {
  16. return request({
  17. url: '/sys/user/batch',
  18. method: 'post',
  19. data
  20. })
  21. }
  1. import { importEmployee } from '@/api/employees'
  2. import { getImportJsData } from '@/utils/excelData'
  3. export default {
  4. name: 'ImportExcel',
  5. methods: {
  6. async handleSuccess({ results, header }) {
  7. // 1.拿到数据 通过格式化方法 处理成后端要求的数据格式
  8. // 2.调用真实接口传递数据
  9. const headerRelation = {
  10. '姓名': 'username',
  11. '手机号': 'mobile',
  12. '入职日期': 'timeOfEntry',
  13. '工号': 'workNumber',
  14. '聘用形式': 'formOfEmployment',
  15. '部门': 'departmentName'
  16. }
  17. const formatData = getImportJsData(results, headerRelation)
  18. await importEmployee(formatData)
  19. }
  20. }
  21. }

导出

查看导出功能 ,根据路由查找

找到了点击导出的事件绑定的函数handleDownload

import(@/vendor/Export2Execel.js) 动态导入 这里写在函数内部 调用了函数才会动态导入这个文件
常见于比较大的包且不是使用频繁的功能 什么时候用 什么时候导入

安装依赖的包 npm i file-saver

使用相同的方法 先创建一个样的js文件

  1. /* eslint-disable */
  2. import { saveAs } from 'file-saver'
  3. import * as XLSX from 'xlsx'
  4. function generateArray(table) {
  5. var out = [];
  6. var rows = table.querySelectorAll('tr');
  7. var ranges = [];
  8. for (var R = 0; R < rows.length; ++R) {
  9. var outRow = [];
  10. var row = rows[R];
  11. var columns = row.querySelectorAll('td');
  12. for (var C = 0; C < columns.length; ++C) {
  13. var cell = columns[C];
  14. var colspan = cell.getAttribute('colspan');
  15. var rowspan = cell.getAttribute('rowspan');
  16. var cellValue = cell.innerText;
  17. if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
  18. //Skip ranges
  19. ranges.forEach(function (range) {
  20. if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
  21. for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
  22. }
  23. });
  24. //Handle Row Span
  25. if (rowspan || colspan) {
  26. rowspan = rowspan || 1;
  27. colspan = colspan || 1;
  28. ranges.push({
  29. s: {
  30. r: R,
  31. c: outRow.length
  32. },
  33. e: {
  34. r: R + rowspan - 1,
  35. c: outRow.length + colspan - 1
  36. }
  37. });
  38. };
  39. //Handle Value
  40. outRow.push(cellValue !== "" ? cellValue : null);
  41. //Handle Colspan
  42. if (colspan)
  43. for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
  44. }
  45. out.push(outRow);
  46. }
  47. return [out, ranges];
  48. };
  49. function datenum(v, date1904) {
  50. if (date1904) v += 1462;
  51. var epoch = Date.parse(v);
  52. return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
  53. }
  54. function sheet_from_array_of_arrays(data, opts) {
  55. var ws = {};
  56. var range = {
  57. s: {
  58. c: 10000000,
  59. r: 10000000
  60. },
  61. e: {
  62. c: 0,
  63. r: 0
  64. }
  65. };
  66. for (var R = 0; R != data.length; ++R) {
  67. for (var C = 0; C != data[R].length; ++C) {
  68. if (range.s.r > R) range.s.r = R;
  69. if (range.s.c > C) range.s.c = C;
  70. if (range.e.r < R) range.e.r = R;
  71. if (range.e.c < C) range.e.c = C;
  72. var cell = {
  73. v: data[R][C]
  74. };
  75. if (cell.v == null) continue;
  76. var cell_ref = XLSX.utils.encode_cell({
  77. c: C,
  78. r: R
  79. });
  80. if (typeof cell.v === 'number') cell.t = 'n';
  81. else if (typeof cell.v === 'boolean') cell.t = 'b';
  82. else if (cell.v instanceof Date) {
  83. cell.t = 'n';
  84. cell.z = XLSX.SSF._table[14];
  85. cell.v = datenum(cell.v);
  86. } else cell.t = 's';
  87. ws[cell_ref] = cell;
  88. }
  89. }
  90. if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
  91. return ws;
  92. }
  93. function Workbook() {
  94. if (!(this instanceof Workbook)) return new Workbook();
  95. this.SheetNames = [];
  96. this.Sheets = {};
  97. }
  98. function s2ab(s) {
  99. var buf = new ArrayBuffer(s.length);
  100. var view = new Uint8Array(buf);
  101. for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
  102. return buf;
  103. }
  104. export function export_table_to_excel(id) {
  105. var theTable = document.getElementById(id);
  106. var oo = generateArray(theTable);
  107. var ranges = oo[1];
  108. /* original data */
  109. var data = oo[0];
  110. var ws_name = "SheetJS";
  111. var wb = new Workbook(),
  112. ws = sheet_from_array_of_arrays(data);
  113. /* add ranges to worksheet */
  114. // ws['!cols'] = ['apple', 'banan'];
  115. ws['!merges'] = ranges;
  116. /* add worksheet to workbook */
  117. wb.SheetNames.push(ws_name);
  118. wb.Sheets[ws_name] = ws;
  119. var wbout = XLSX.write(wb, {
  120. bookType: 'xlsx',
  121. bookSST: false,
  122. type: 'binary'
  123. });
  124. saveAs(new Blob([s2ab(wbout)], {
  125. type: "application/octet-stream"
  126. }), "test.xlsx")
  127. }
  128. export function export_json_to_excel({
  129. multiHeader = [],
  130. header,
  131. data,
  132. filename,
  133. merges = [],
  134. autoWidth = true,
  135. bookType = 'xlsx'
  136. } = {}) {
  137. /* original data */
  138. filename = filename || 'excel-list'
  139. data = [...data]
  140. data.unshift(header);
  141. for (let i = multiHeader.length - 1; i > -1; i--) {
  142. data.unshift(multiHeader[i])
  143. }
  144. var ws_name = "SheetJS";
  145. var wb = new Workbook(),
  146. ws = sheet_from_array_of_arrays(data);
  147. if (merges.length > 0) {
  148. if (!ws['!merges']) ws['!merges'] = [];
  149. merges.forEach(item => {
  150. ws['!merges'].push(XLSX.utils.decode_range(item))
  151. })
  152. }
  153. if (autoWidth) {
  154. /*设置worksheet每列的最大宽度*/
  155. const colWidth = data.map(row => row.map(val => {
  156. /*先判断是否为null/undefined*/
  157. if (val == null) {
  158. return {
  159. 'wch': 10
  160. };
  161. }
  162. /*再判断是否为中文*/
  163. else if (val.toString().charCodeAt(0) > 255) {
  164. return {
  165. 'wch': val.toString().length * 2
  166. };
  167. } else {
  168. return {
  169. 'wch': val.toString().length
  170. };
  171. }
  172. }))
  173. /*以第一行为初始值*/
  174. let result = colWidth[0];
  175. for (let i = 1; i < colWidth.length; i++) {
  176. for (let j = 0; j < colWidth[i].length; j++) {
  177. if (result[j]['wch'] < colWidth[i][j]['wch']) {
  178. result[j]['wch'] = colWidth[i][j]['wch'];
  179. }
  180. }
  181. }
  182. ws['!cols'] = result;
  183. }
  184. /* add worksheet to workbook */
  185. wb.SheetNames.push(ws_name);
  186. wb.Sheets[ws_name] = ws;
  187. var wbout = XLSX.write(wb, {
  188. bookType: bookType,
  189. bookSST: false,
  190. type: 'binary'
  191. });
  192. saveAs(new Blob([s2ab(wbout)], {
  193. type: "application/octet-stream"
  194. }), `${filename}.${bookType}`);
  195. }

给导出绑定点击事件,写入导出方法

export_json_to_excel方法传入的参数 header 导出数据的表头
data 导出的具体数据 要求是二维数组[ [ ] ]
filename 导出的文件名
autoWidth 单元格宽度自适应 true bookType 导出的文件类型 xlsx

  1. import('@/vendor/Export2Excel').then(excel => {
  2. // excel表示导入的模块对象
  3. excel.export_json_to_excel({
  4. header: ['姓名', '工资'], // 表头 必填
  5. data: [
  6. ['刘备', 100],
  7. ['关羽', 500]
  8. ], // 具体数据 必填
  9. filename: 'excel-list', // 文件名称
  10. autoWidth: true, // 宽度是否自适应
  11. bookType: 'xlsx' // 生成的文件类型
  12. })
  13. })

格式化数据 为需要的数据

使用util/excelgetExportData方法 传入导入的数据和对应关系

  1. export function getExportData(sourceData, headerRelation) {
  2. const data = sourceData.map(item => {
  3. const arr = []
  4. Object.values(headerRelation).forEach(key => {
  5. // 关键位置:把所有的value不做任何处理直接扔到了数组中 导致excel是源数据
  6. // 如果是当前要处理的是聘用形式 就先转化一步再添加到数组中
  7. if (key === 'formOfEmployment') {
  8. const formatValue = transEmployment(item[key])
  9. arr.push(formatValue)
  10. } else {
  11. arr.push(item[key])
  12. }
  13. })
  14. return arr
  15. })
  16. return {
  17. data
  18. }
  19. }
  1. // 导出excel表格
  2. exportExcel() {
  3. import('@/views/vendor/Export2Execel.js').then(async excel => {
  4. const res = await getEmployeeListApi(this.parmas)
  5. console.log(res)
  6. const headerRelation = {
  7. '姓名': 'username',
  8. '手机号': 'mobile',
  9. '入职日期': 'timeOfEntry',
  10. '工号': 'workNumber',
  11. '聘用形式': 'formOfEmployment',
  12. '部门': 'departmentName'
  13. }
  14. const { data } = getExportData(res.data.rows, headerRelation)
  15. // excel表示导入的模块对象
  16. excel.export_json_to_excel({
  17. header: Object.keys(headerRelation), // 表头 必填
  18. data: data, // 具体数据 必填
  19. filename: 'excel-list', // 文件名称
  20. autoWidth: true, // 宽度是否自适应
  21. bookType: 'xlsx' // 生成的文件类型
  22. })
  23. })
  24. }