[TOC]

两种文件导入形式

  • 点击上传
  • 拖拽上传 ```typescript

<a name="wqWmT"></a>
#  文件选择之后的数据解析处理
使用 xlsx 来解析表单数据
```typescript
<script setup>
import XLSX from 'xlsx'
import { defineProps, ref } from 'vue'
import { getHeaderRow } from './utils'

const props = defineProps({
  // 上传前回调
  beforeUpload: Function,
  // 成功回调
  onSuccess: Function
})

/**
 * 点击上传触发
 */
const loading = ref(false)
const excelUploadInput = ref(null)
const handleUpload = () => {
  excelUploadInput.value.click()
}
const handleChange = e => {
  const files = e.target.files
  const rawFile = files[0] // only use files[0]
  if (!rawFile) return
  upload(rawFile)
}

/**
 * 触发上传事件
 */
const upload = rawFile => {
  excelUploadInput.value.value = null
  // 如果没有指定上传前回调的话
  if (!props.beforeUpload) {
    readerData(rawFile)
    return
  }
  // 如果指定了上传前回调,那么只有返回 true 才会执行后续操作
  const before = props.beforeUpload(rawFile)
  if (before) {
    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 => {
      // 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)
  })
}

/**
 * 根据导入内容,生成数据
 */
const generateData = excelData => {
  props.onSuccess && props.onSuccess(excelData)
}
</script>

getHeaderRow 是 xlsx 解析表头数据的通用方法:

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
}

文件拖入之后的数据解析处理

文件拖入 的实现主要通过 HTML_Drag_and_Drop(HTML 拖放 API) 事件,这里主要使用到其中三个事件:

  1. drop (en-US):当元素或选中的文本在可释放目标上被释放时触发
  2. dragover (en-US):当元素或选中的文本被拖到一个可释放目标上时触发
  3. dragenter (en-US):当拖拽元素或选中的文本到一个可释放目标时触发 ```typescript

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

    excel 导出

    /* eslint-disable */
    import { saveAs } from 'file-saver'
    import XLSX from 'xlsx'
    
    function datenum(v, date1904) {
      if (date1904) v += 1462
      var epoch = Date.parse(v)
      return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000)
    }
    
    function sheet_from_array_of_arrays(data, opts) {
      var ws = {}
      var range = {
        s: {
          c: 10000000,
          r: 10000000
        },
        e: {
          c: 0,
          r: 0
        }
      }
      for (var R = 0; R != data.length; ++R) {
        for (var C = 0; C != data[R].length; ++C) {
          if (range.s.r > R) range.s.r = R
          if (range.s.c > C) range.s.c = C
          if (range.e.r < R) range.e.r = R
          if (range.e.c < C) range.e.c = C
          var cell = {
            v: data[R][C]
          }
          if (cell.v == null) continue
          var cell_ref = XLSX.utils.encode_cell({
            c: C,
            r: R
          })
    
          if (typeof cell.v === 'number') cell.t = 'n'
          else if (typeof cell.v === 'boolean') cell.t = 'b'
          else if (cell.v instanceof Date) {
            cell.t = 'n'
            cell.z = XLSX.SSF._table[14]
            cell.v = datenum(cell.v)
          } else cell.t = 's'
    
          ws[cell_ref] = cell
        }
      }
      if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range)
      return ws
    }
    
    function Workbook() {
      if (!(this instanceof Workbook)) return new Workbook()
      this.SheetNames = []
      this.Sheets = {}
    }
    
    function s2ab(s) {
      var buf = new ArrayBuffer(s.length)
      var view = new Uint8Array(buf)
      for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xff
      return buf
    }
    
    export const export_json_to_excel = ({
      multiHeader = [],
      header,
      data,
      filename,
      merges = [],
      autoWidth = true,
      bookType = 'xlsx'
    } = {}) => {
      // 1. 设置文件名称
      filename = filename || 'excel-list'
      // 2. 把数据解析为数组,并把表头添加到数组的头部
      data = [...data]
      data.unshift(header)
      // 3. 解析多表头,把多表头的数据添加到数组头部(二维数组)
      for (let i = multiHeader.length - 1; i > -1; i--) {
        data.unshift(multiHeader[i])
      }
      // 4. 设置 Excel 表工作簿(第一张表格)名称
      var ws_name = 'SheetJS'
      // 5. 生成工作簿对象
      var wb = new Workbook()
      // 6. 将 data 数组(json格式)转化为 Excel 数据格式
      var ws = sheet_from_array_of_arrays(data)
      // 7. 合并单元格相关(['A1:A2', 'B1:D1', 'E1:E2'])
      if (merges.length > 0) {
        if (!ws['!merges']) ws['!merges'] = []
        merges.forEach((item) => {
          ws['!merges'].push(XLSX.utils.decode_range(item))
        })
      }
      // 8. 单元格宽度相关
      if (autoWidth) {
        /*设置 worksheet 每列的最大宽度*/
        const colWidth = data.map((row) =>
          row.map((val) => {
            /*先判断是否为null/undefined*/
            if (val == null) {
              return {
                wch: 10
              }
            } else if (val.toString().charCodeAt(0) > 255) {
              /*再判断是否为中文*/
              return {
                wch: val.toString().length * 2
              }
            } else {
              return {
                wch: val.toString().length
              }
            }
          })
        )
        /*以第一行为初始值*/
        let result = colWidth[0]
        for (let i = 1; i < colWidth.length; i++) {
          for (let j = 0; j < colWidth[i].length; j++) {
            if (result[j]['wch'] < colWidth[i][j]['wch']) {
              result[j]['wch'] = colWidth[i][j]['wch']
            }
          }
        }
        ws['!cols'] = result
      }
    
      // 9. 添加工作表(解析后的 excel 数据)到工作簿
      wb.SheetNames.push(ws_name)
      wb.Sheets[ws_name] = ws
      // 10. 写入数据
      var wbout = XLSX.write(wb, {
        bookType: bookType,
        bookSST: false,
        type: 'binary'
      })
      // 11. 下载数据
      saveAs(
        new Blob([s2ab(wbout)], {
          type: 'application/octet-stream'
        }),
        `${filename}.${bookType}`
      )
    }
    

    实现 excel 导出功能:

    1. 动态导入 Export2Excel.js

      // 导入工具包
      const excel = await import('@/utils/Export2Excel')
      
    2. 因为从服务端获取到的为 json 数组对象 结构,但是导出时的数据需要为 二维数组,所以我们需要有一个方法来把 **json** 结构转化为 二维数组

    3. 创建转化方法

      1. 创建 views/user-manage/components/Export2ExcelConstants.js 中英文对照表 ```javascript /**
      • 导入数据对应表 */ export const USER_RELATIONS = { 姓名: ‘username’, 联系方式: ‘mobile’, 角色: ‘role’, 开通时间: ‘openTime’ } ```
      1. 创建数据解析方法

        // 该方法负责将数组转化成二维数组
        const formatJson = (headers, rows) => {
        // 首先遍历数组
        // [{ username: '张三'},{},{}]  => [[’张三'],[],[]]
        return rows.map(item => {
        return Object.keys(headers).map(key => {
        // 角色特殊处理
        if (headers[key] === 'role') {
        const roles = item[headers[key]]
        
        return JSON.stringify(roles.map(role => role.title))
        }
        return item[headers[key]]
        })
        })
        }
        
    4. 调用该方法,获取导出的二维数组数据 ```javascript import { USER_RELATIONS } from ‘./Export2ExcelConstants’

    const data = formatJson(USER_RELATIONS, allUser)

    
    5.  调用 `export_json_to_excel` 方法,完成 `excel` 导出 
    ```javascript
      excel.export_json_to_excel({
        // excel 表头
        header: Object.keys(USER_RELATIONS),
        // excel 数据(二维数组结构)
        data,
        // 文件名称
        filename: excelName.value || exportDefaultName,
        // 是否自动列宽
        autoWidth: true,
        // 文件类型
        bookType: 'xlsx'
      })