当前位置: 首页 > news >正文

实现Excel导入和导出

实现Excel导入和导出

今天记录一个Excel导入导出用户信息的实现方式。

  • 导入:点击选择、拖拽 两种方式,用XLSX来解析文件内容
  • 导出:点击导出excel

导入

点击选择、拖拽上传 两种方式

src/components/UploadExcel/index.vue定义了上传组件的内容

  • 点击上传部分:按钮 & 一个隐藏的元素
    • <input>绑定ref="excelUploadInput"
    • 点击按钮:@click="handleUpload"
    • 点击之后,触发handleUpload(),根据excelUploadInput这个引用,触发excelUploadInput.value.click(),模拟点击<input>,弹出文件选择框
  • 拖拽上传部分:一个拖拽区域div

在这里插入图片描述

<template>
  <div class="upload-excel">
    <div class="btn-upload">
      <el-button :loading="loading" type="primary" @click="handleUpload">
        {{ $t('msg.uploadExcel.upload') }}
      </el-button>
    </div>

    <input
      ref="excelUploadInput"
      class="excel-upload-input"
      type="file"
      accept=".xlsx, .xls"
      @change="handleChange"
    />
    <!-- https://developer.mozilla.org/zh-CN/docs/Web/API/HTML_Drag_and_Drop_API -->
    <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>
点击选择

<input>组件设置了display:none,在页面中隐藏,点击上传时需要使用input元素

  • <input>元素绑定了ref='excelUploadInput',所以通过excelUploadInput变量可以操作<input>元素

    点击上传按钮,点击时触发handleUpload()方法,模拟点击input元素,从而弹出文件选择框

const handleUpload = () => {
  excelUploadInput.value.click()
}
  • <input>元素绑定@change="handleChange",所以在文件选择框选择文件后,<input>元素会自动执行handleChange()方法,通过e.target.files获取excel文件
const handleChange = e => {
  const files = e.target.files
  const rawFile = files[0] // only use files[0]
  if (!rawFile) return
  upload(rawFile)
}
  • 执行upload(rawFile),执行一些回调函数(如果有的话),并执行readerData(rawFile),解析数据,并调用generateData({ header, results })传入解析后的表头数据和数据体,该方法中的onSuccess()方法是由父组件调用时传入

    readerData(rawFile)函数:

/**
 * 读取数据(异步)
 */
const readerData = rawFile => {
  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 => {
      console.log(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()
    }
    // 启动读取指定的 Blob 或 File 内容
    reader.readAsArrayBuffer(rawFile)
  })
}
`generateData()`函数:
/**
 * 根据导入内容,生成数据
 */
const generateData = excelData => {
  props.onSuccess && props.onSuccess(excelData)
}
`onSuccess()`函数需要父组件调用`UploadExcel`组件时传入:
const props = defineProps({
  // 上传前回调
  beforeUpload: Function,
  // 成功回调
  onSuccess: Function
})
  • src/views/import/index.vue中调用了UploadExcel组件,并定义了onSuccess()函数

    给后台发请求,将新增数据存入数据库,弹窗提示操作成功,并跳转到/user/manage 页面

/**
 * 数据解析成功之后的回调
 */
const onSuccess = async ({ header, results }) => {
  // 筛选数据
  const updateData = generateData(results)
  // 给后台发请求,更新数据
  await userBatchImport(updateData)
  // 操作成功时弹出的消息。
  ElMessage.success({
    message: results.length + i18n.t('msg.excel.importSuccess'),
    type: 'success'
  })
  // 在导入成功后,用户会被重定向到 /user/manage 页面
  router.push('/user/manage')
}
拖拽上传

拖拽区域如下:

<div
  class="drop"
  @drop.stop.prevent="handleDrop"
  @dragover.stop.prevent="handleDragover"
  @dragenter.stop.prevent="handleDragover"
>

也就是说,“拖进来”以及“撒手之前”都执行handleDragover()方法,“撒手时”执行handleDrop() 方法

  • handleDragover()方法:

    当拖拽元素悬停在目标区域时,浏览器的默认行为是将源元素复制到目标区域,而不是移动它

/**
 * 拖拽悬停时触发
 */
const handleDragover = e => {
  // https://developer.mozilla.org/zh-CN/docs/Web/API/DataTransfer/dropEffect
  // 在新位置生成源项的副本
  e.dataTransfer.dropEffect = 'copy'
}
  • handleDrop() 方法:

    判断文件的个数、类型,没有问题就上传

/**
 * 拖拽文本释放时触发
 */
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.error('文件必须是 .xlsx, .xls, .csv 格式')
    return false
  }
  // 触发上传事件
  upload(rawFile)
}
`upload(rawFile)`上面说过了(点击下面跳转)

执行upload(rawFile),执行一些回调函数(如果有的话),并执行readerData(rawFile),解析数据,并调用generateData({ header, results })传入解析后的表头数据和数据体,该方法中的onSuccess()方法是由父组件调用时传入

导出

  1. 点击 excel 导出按钮,弹出弹窗 dialog

    在这里插入图片描述

  2. 输入导出的 excel 文件名称

  3. 点击导出按钮:

    1. 获取 所有用户列表数据
    2. json 结构数据转化为 excel 数据,并下载

src/views/user-manage/components/Export2Excel.vue组件:

  • 一个弹窗
    • 一个input元素:设置导出的文件名
    • 两个按钮:确定、取消
<template>
  <el-dialog
    :title="$t('msg.excel.title')"
    :model-value="modelValue"
    @close="closed"
    width="30%"
  >
    <el-input
      v-model="excelName"
      :placeholder="$t('msg.excel.placeholder')"
    ></el-input>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="closed">{{ $t('msg.excel.close') }}</el-button>
        <el-button type="primary" @click="onConfirm" :loading="loading">{{
          $t('msg.excel.confirm')
        }}</el-button>
      </span>
    </template>
  </el-dialog>
</template>

点击确定按钮,执行onConfirm()

  • 对后端发起请求获取所有用户数据
  • 将json数据转化为excel需要的二维数组形式
  • 生成excel文件并下载
/**
 * 导出按钮点击事件
 * @returns {Promise<void>}
 */
const onConfirm = async () => {
  loading.value = true
  // 通过 API 获取所有用户的数据。
  const allUser = (await getUserManageAllList()).list
  // 动态导入 Export2Excel 工具,用于将数据导出为 Excel 文件。
  const excel = await import('@/utils/Export2Excel')
  // 调用 formatJson 函数,将用户数据格式化为 Excel 导出所需的二维数组。
  const data = formatJson(USER_RELATIONS, allUser)
  // 调用导入的 export_json_to_excel 函数,执行 Excel 导出下载。
  excel.export_json_to_excel({
    // excel 表头
    header: Object.keys(USER_RELATIONS),
    // excel 数据(二维数组结构)
    data,
    // 文件名称(用户在input元素中输入)
    filename: excelName.value || exportDefaultName,
    // 是否自动列宽
    autoWidth: true,
    // 文件类型
    bookType: 'xlsx'
  })
  closed()
}

相关文章:

  • 服务器自动备份到本地,服务器自动备份到本地的方法有哪些?
  • [250401] OpenAI 向免费用户开放 GPT-4o 图像生成功能 | Neovim 0.11 新特性解读
  • MySQL和Oracle批量插入SQL差异详解
  • 青少年编程与数学 02-014 高中数学知识点 06课题、数学建模与探究活动
  • 第十一届蓝桥杯大赛软件赛省赛C/C++ 大学 B 组
  • Java面试黄金宝典28
  • 单例模式(懒汉模式/饿汉模式)
  • 服务器数据恢复—误格式化NTFS文件系统分区别慌,NTFS数据复活秘籍
  • linux ubuntu下查询包是否包含某个工具
  • 移远通信推出超小Wi-Fi+BLE模组FGM842D系列,赋能智能家居与工业物联网高效互联
  • 【Easylive】MyBatis 批量插入或更新语句解析insertOrUpdateBatch
  • 12.小节
  • 关于React Redux
  • 【3. 软件工程】3.0 软件工程概述
  • Android View事件分发机制深度解析
  • QuecPython 的 VScode 环境搭建和使用教程
  • IS-IS认证
  • MySQL日志管理
  • NVR接入录像回放平台EasyCVR视频融合平台城市/乡镇污水处理厂解决方案
  • 计算机等级考试数据库三级(笔记2)
  • 广西钦州:坚决拥护自治区党委对钟恒钦进行审查调查的决定
  • 习近平会见古共中央第一书记、古巴国家主席迪亚斯-卡内尔
  • 聆听百年唐调正声:唐文治王蘧常吟诵传习的背后
  • 第一集|《刑警的日子》很生活,《执法者们》有班味
  • 以总理内塔尼亚胡称决心彻底击败哈马斯
  • 太原一高中生指出博物馆多件藏品标识不当,馆方已邀请他和专家共同探讨