前端如何实现Excel文件导入

1,714 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第12天,点击查看活动详情

当然我们主角还是vue-element-admin啦,一个半成品的后台管理系统,那么接下来看看这个文件给我们提供了什么条件吧

实现的功能

image.png

批量新增员工就需要excel的导入导出啦

员工导入组件封装

目标:封装一个导入excel数据 的组件

首先封装一个类似的组件,首先需要注意的是,类似功能,vue-element-admin已经提供了,我们只需要改造即可 代码地址

类似功能性的组件,我们只需要会使用和封装即可

excel导入功能需要使用npm包**xlsx,所以需要安装xlsx**插件 (new FileReader() 选中了某个文件, 可以通过FileReader去读)

$ yarn add xlsx

将vue-element-admin提供的导入功能新建一个组件,位置: src/components/UploadExcel

注册全局的导入excel组件

// 该文件, 可以进行组件的批量全局注册, 可以以插件的方式使用 => Vue.use(xxx)
// 将来这个插件(对象), 被Vue.use使用时, 会执行内部的install方法, 进行插件的初始化
import PageTools from './PageTools'
import UploadExcel from './UploadExcel'
export default {
  // 插件的初始化, 插件给你提供的全局的功能, 都可以在这里配置
  install(Vue) {
    // 进行组件的全局注册
    Vue.component('PageTools', PageTools) // 注册工具栏组件
    Vue.component('UploadExcel', UploadExcel) // 注册导入excel组件
  }
}

温馨小提示:如果你引入组件上传文件报错的话,打开vue-element-admin源代码中瞅瞅,看看XLSX的安装是不是指定的版本,如果不是可以加上* as 导入:如下图

image.png

建立公共导入的页面路由

新建一个公共的导入页面, 即import路由组件 src/views/import/index.vue

<template>
  <upload-excel />
</template>

<script>
export default {
  name: 'Import'
}
</script>

挂载路由src/router/index.js

{
    path: '/import',
    component: Layout,
    hidden: true, // 隐藏在左侧菜单中
    children: [{
      path: '', // 二级路由path什么都不写 表示二级默认路由
      component: () => import('@/views/import')
    }]
},

效果图:

image.png

分析excel导入代码,封装接口

从点击按钮开始, 分析代码, 这里点击按钮, 触发了input:file 的click事件, 进行了上传

<div class="btn-upload">
  <el-button :loading="loading" size="mini" type="primary" @click="handleUpload">
    点击上传
  </el-button>
</div>

配置成功函数

<template>
  <upload-excel :on-success="handleSuccess" />
</template>
<script>
export default {
  name: 'Import',
  methods: {
    handleSuccess({ header, results }) {
      console.log(header, results)
    }
  }
}
</script>

封装批量导入员工的api接口

/** *
 * 封装一个批量导入员工的接口
 * data: [{}, {}, {}, {}, ... ]
 * ***/
export function reqImportEmployee(data) {
  return request({
    url: 'xxx',
    method: 'post',
    data
  })
}

实现excel导入

获取导入的excel数据, 导入excel接口

import { reqImportEmployee } from '@/api/employees'
export default {
  name: 'Import',
  methods: {
    async handleSuccess({ header, results }) {
      // header中, results中的数据是中文的, 但是提交给后台的要是英文的
      const userRelations = {
        '入职日期': 'timeOfEntry',
        '手机号': 'mobile',
        '姓名': 'username',
        '转正日期': 'correctionTime',
        '工号': 'workNumber'
      }
      const arr = []
      results.forEach(item => {
        const userInfo = {}
        for (const key in item) {
          userInfo[userRelations[key]] = item[key]
        }
        arr.push(userInfo)
      })
      // 调用导入接口
      await reqImportEmployee(arr) // 调用导入接口
      this.$router.back()
    }
  }
}

当excel中有日期格式的时候,实际转化的值为一个数字,我们需要一个方法进行转化

Excel存储的日期是从1900年1月1日开始按天数来计算的,也就是说1900年1月1日在Excel中是1。

formatExcelDate(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 < 10 ? '0' + month : month) + format + (date < 10 ? '0' + date : date)
  }
  return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date)
}

需要注意,导入的手机号不能和之前的存在的手机号重复

处理日期:

<script>
import { reqImportEmployee } from '@/api/employees'
export default {
  name: 'Import',
  methods: {
    async handleSuccess({ header, results }) {
      // header中, results中的数据是中文的, 但是提交给后台的要是英文的
      const userRelations = {
        '入职日期': 'timeOfEntry',
        '手机号': 'mobile',
        '姓名': 'username',
        '转正日期': 'correctionTime',
        '工号': 'workNumber'
      }
      const arr = []
      results.forEach(item => {
        const userInfo = {}
        for (const key in item) {
          if (['timeOfEntry', 'correctionTime'].includes(userRelations[key])) {
            userInfo[userRelations[key]] = this.formatExcelDate(item[key], '-')
          } else {
            userInfo[userRelations[key]] = item[key]
          }
        }
        arr.push(userInfo)
      })
      // 调用导入接口
      await reqImportEmployee(arr) // 调用导入接口
      this.$router.back()
    },
​
    // 转换excel的日期格式
    formatExcelDate(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 < 10 ? '0' + month : month) + format + (date < 10 ? '0' + date : date)
      }
      return year + (month < 10 ? '0' + month : month) + (date < 10 ? '0' + date : date)
    }
  }
}
</script>

为了让这个页面可以服务更多的导入功能,我们可以在页面中用参数来判断,是否是导入员工

员工页面跳转

<el-button size="small" type="warning" @click="$router.push('/import?type=user')">excel导入</el-button>
computed: {
  type() {
    return this.$route.query.type
  }
},

添加判断

async handleSuccess({ header, results }) {
  if (this.type === 'user') {
    ...
  }
},

大概具体过程就是这样,如有不足,请多多指教