基于vue的excel表格导入,拖拽导入,excel文件本地预览 ,前端解析excel文件数据再传给后端

excel表格导入 (demo请查看 gihub https://github.com/cc147151/v3-admin)用户/用户管理 excel导入

一、前端解析excel文件再把数据传给后端(解析表格的时间需要单独处理—如果表格时间格式是带-的普通文本格式可以不需要特殊处理)

解析excel数据使用到了xlsx包

npm i xlsx@0.17.0

新建UploadExcel文件夹作为上传excel组件

UploadExcel
	 --index.vue
	  --utils.js  // 处理表头的方法等
<template>
  <div>
    <div>
      <el-button type="primary" @click="uploadExcel">点击上传</el-button>
    </div>
    <input
      ref="inputRef"
      style="display: none"
      type="file"
      accept=".xlsx, .xls"
      @change="inputChange"
    />
    <div
      class="drop"
      @drop.stop.prevent="handleDrop"
      @dragover.stop.prevent="handleDragover"
      @dragleave.stop.prevent="dropText = '拖拽一个文件'"
    >
      <span>{{ dropText }}</span>
    </div>
  </div>
</template>

<script setup>
import { ref, defineProps } from 'vue'
import XLSX from 'xlsx'
import { getHeaderRow } from './utils'
const props = defineProps({
  beforeUpload: Function,
  uploadSuccess: Function
})
const inputRef = ref(null)
const dropText = ref('拖拽一个文件')
const loading = ref(false)
const uploadExcel = () => {
  inputRef.value.click()
}
const inputChange = (e) => {
  const files = e.target.files[0]
  if (!files) return
  uploadFun(files)
}
const uploadFun = (files) => {
  // 解决同一个文件第二次上传不触发问题
  inputRef.value.value = ''
  if (!props.beforeUpload) {
    readerData(files)
  }
  if (props.beforeUpload(files)) {
    readerData(files)
  } else {
    loading.value = false
    dropText.value = '拖拽一个文件'
  }
}
const readerData = (files) => {
  loading.value = true
  return new Promise((resolve, reject) => {
    // https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
    const reader = new FileReader()
    // 该事件在读取操作完成时触发
    // https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader/onload
    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. 异步完成
      resolve()
      // 启动读取指定的 Blob 或 File 内容
    }
    reader.readAsArrayBuffer(files)
  })
}
const generateData = (excelData) => {
  loading.value = false
  dropText.value = '拖拽一个文件'
  props.uploadSuccess && props.uploadSuccess(excelData)
}

// 拖拽
/**
 * 拖拽文本释放时触发
 */
const handleDrop = (e) => {
  // 上传中跳过
  if (loading.value) return
  dropText.value = '上传中'
  const files = e.dataTransfer.files
  if (files.length !== 1) {
    dropText.value = '拖拽一个文件'
    loading.value = false
    return
  }
  const rawFile = files[0]
  // 触发上传事件
  uploadFun(rawFile)
}

/**
 * 拖拽悬停时触发
 */
const handleDragover = (e) => {
  // https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransfer/dropEffect
  // 在新位置生成源项的副本
  e.dataTransfer.dropEffect = 'copy'
  dropText.value = '可以放手了'
}
</script>

<style scoped lang="scss">
.drop {
  width: 300px;
  height: 300px;
  border: 1px dashed #ccc;
}
</style>

// utils.js
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
}

export const isExcel = file => {
  return /\.(xlsx|xls|csv)$/.test(file.name)
}

使用:

// 使用文件中 创建util.js (转化为符合请求格式的请求参数,对excel表格时间做处理)
/**
 * 导入数据对应表
 */
export const USER_RELATIONS = {
  姓名: 'username',
  联系方式: 'mobile',
  角色: 'role',
  开通时间: 'openTime'
}

/**
 * 解析 excel 导入的时间格式
 */
export const formatDate = numb => {
	if (numb.includes('-')) return numb
  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 + ''
  return (
    year +
    '-' +
    (month < 10 ? '0' + month : month) +
    '-' +
    (date < 10 ? '0' + date : date)
  )
}

<template>
  <div class="">
    <header>导入</header>
    <upload-excel :beforeUpload="beforeUpload" :uploadSuccess="uploadSuccess" />
  </div>
</template>

<script setup>
import {} from 'vue'
import { useRouter } from 'vue-router'
import UploadExcel from '@/components/UploadExcel'
import { USER_RELATIONS, formatDate } from './utils'
import { userBatchImport } from '@/api/user/manage'
import { ElMessage } from 'element-plus'
const router = useRouter()
const getFormatData = (data) => {
  console.log(data)
  const arr = []
  data.forEach((item) => {
    const obj = {}
    Object.keys(item).forEach((key) => {
      obj[USER_RELATIONS[key]] = item[key]
      USER_RELATIONS[key] === 'openTime' &&
        (obj[USER_RELATIONS[key]] = formatDate(item[key]))
    })
    arr.push(obj)
  })
  return arr
}
const beforeUpload = (file) => {
  console.log(file)
  return true
}
const uploadSuccess = async (data) => {
  const paramsData = getFormatData(data.results)
  try {
    await userBatchImport(paramsData)
  } catch (error) {
    console.log(error)
  }
  ElMessage.success('导入成功')
  router.back()
}
</script>

<style lang="scss" scoped></style>

最终后端要求格式为如下:
基于vue的excel表格导入,拖拽导入,excel文件本地预览 ,前端解析excel文件数据再传给后端_第1张图片

预览(注意解析excel表格的时间使用以上js formatDate 处理)

在uploadSuccess的参数里,我们可以拿到xlsx解析header和results,就可以直接渲染到element-plus的表格里,分别作为tableColumn和tableData(即便都是汉语也可以的)来预览了!!!
感谢慕课网!!!

你可能感兴趣的:(vue,前端,vue.js,后端)