借助XLXS封装Excel导入组件

133 阅读2分钟

组件代码

支持点击上传、拖拽上传

<template>
  <div class="upload-excel">
    <div class="btn-upload">
      <el-button type="primary" :loading="loading" @click="handleUpload">{{
        $t('msg.uploadExcel.upload')
      }}</el-button>
    </div>
    <input
      type="file"
      ref="excelUploadInput"
      class="excel-upload-input"
      accept=".xlsx, .xls"
      @change="handleChange"
    />

    <div
      class="drop"
      @drop.stop.prevent="handleDrop"
      @dragover.stop.prevent="handleDragover"
      @dragenter.stop.prevent="handleDragover"
    >
      <i class="el-icon-upload" />
      <span>{{ $t('msg.uploadExcel.drop') }}</span>
    </div>
  </div>
</template>

<script setup>
import { ElMessage } from 'element-plus'
import { ref, defineProps } from 'vue'
import XLSX from 'xlsx'
import { getHeaderRow, isExcel } from './utils'

const loading = ref(false)
const excelUploadInput = ref(null)
const props = defineProps({
  // 上传之前的回调
  beforeUpload: Function,
  // 上传之后的回调
  onSuccess: Function,
})

const handleUpload = () => {
  // 触发handleChange事件
  excelUploadInput.value.click()
}
const handleChange = (e) => {
  const files = e.target.files
  const rawFile = files[0]
  if (!rawFile) return
  // 解析数据
  upload(rawFile)
}
const upload = (rawFile) => {
  excelUploadInput.value.value = null
  // 如果用户没有指定上传前的回调
  if (!props.beforeUpload) {
    readData(rawFile)
    return
  }
  // 如果用户指定了上传前的回调,那么只有返回true的时候,才会执行对应的后续操作
  const before = props.beforeUpload(rawFile)
  if (before) {
    readData(rawFile)
  }
}

// 读取方法 异步
const readData = (rawFile) => {
  loading.value = true
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    // 读取操作时完成时触发
    reader.onload = (e) => {
      // 1.获取到解析后的数据
      const data = e.target.result
      // 2.利用XLSX对数据进行解析
      const workbook = XLSX.read(data, { type: 'array' })
      // 3.获取第一张表格名称
      const firstSheetName = workbook.SheetNames[0]
      // 4. 读取sheet1的数据
      const workSheet = workbook.Sheets[firstSheetName]
      // 5.解析数据表头
      const header = getHeaderRow(workSheet)
      // 6. 解析数据体
      const results = XLSX.utils.sheet_to_json(workSheet)
      // 7.传入解析之后的数据
      generateData({ header, results })
      // 8.处理loading
      loading.value = false
      // 9.成功的回调
      resolve()
    }
    reader.readAsArrayBuffer(rawFile)
  })
}
// 根据导入内容,生成数据
const generateData = (excelData) => {
  props.onSuccess && props.onSuccess(excelData)
}

// 拖拽上传
const handleDrop = (e) => {
  // 上传中
  if (loading.value) return
  const files = e.dataTransfer.files
  if (files.length !== 1) {
    ElMessage.error('你必须要有一个文件')
    return
  }
  const rawFile = files[0]
  if (!isExcel(rawFile)) {
    ElMessage.warning('文件必须是 .xlsx, .xls, .csv格式结尾的')
    return
  }
  upload(rawFile)
}
const handleDragover = (e) => {
  e.dataTransfer.dropEffect = 'copy'
}
</script>

<style lang="scss" scoped>
.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: 60px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    color: #bbb;
    i {
      font-size: 60px;
      display: block;
    }
  }
}
</style>

工具类代码

import XLSX from 'xlsx'
/**
 * 获取表头(通用方式)
 */
export const getHeaderRow = (sheet) => {
  const headers = []
  const range = XLSX.utils.decode_range(sheet['!ref'])
  let C
  const R = range.s.r
  /* start in the first row */
  for (C = range.s.c; C <= range.e.c; ++C) {
    /* walk every column in the range */
    const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
    /* find the cell in the first row */
    let hdr = 'UNKNOWN ' + C // <-- replace with your desired default
    if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
    headers.push(hdr)
  }
  return headers
}
//判断是否是excel文件
export const isExcel = (file) => {
  return /\.(xlsx|xls|csv)$/.test(file.name)
}

使用组件

<upload-excel :onSuccess="onSuccess"></upload-excel>
const onSuccess = async ({ header, results }) => {
  //headers是表头的字段 results是表格每一行的内容
  console.log(header, results)
}