vue+xlsx实现前端导入导出功能

Posted 风流才子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue+xlsx实现前端导入导出功能相关的知识,希望对你有一定的参考价值。

1、安装依赖

cnpm i xlsx --D

2、某个模块中引入或者全局引入

import XLSX from \'xlsx\'

3、导入excel

  • commonjs(公共函数)
/**
 * 1、 String.fromCharCode(65 + i) + 1 :             A1,B1,C1....
 * 2、 String.fromCharCode(65 + j) + (i + 2)         A2,B2,C2...
 *                                                   A3,B3,C3...
 * 测试:
 *  const headers = [{ key: \'date\', title: \'日期\' }, { key: \'name\', title: \'名称\' }]
 *  const  data = [{ date: \'2019-05-31\', name: \'megen.huang\' }, { date: \'2019-06-20\', name: \'小明\' }]
 *  console.log(exportJsonToExcel(headers, data))
 * 使用xlsx插件将json数据导出到Excel中----针对表格数据
 * @param {Array} headers 表头:[{key: \'date\', title: \'日期\'}, {key: \'name\', title: \'名称\'}]
 * @param {Array} data 表体数据:[{date: \'2019-05-31\', name: \'megen.huang\'}, {date: \'2019-06-20\', name: \'小明\'}]
 * @param {String} fileName 导出的文件名称 :\'export.xlsx\'
 */
export function exportJsonToExcel (headers = [], data = [], fileName = \'export.xlsx\') {
// 先处理数据
  data = handleCSV(data)
  const _headers = headers
    .map((item, i) => Object.assign({}, { key: item.key, title: item.title, position: String.fromCharCode(65 + i) + 1 }))
    .reduce((prev, next) => Object.assign({}, prev, { [next.position]: { key: next.key, v: next.title }}), {})

  const _data = data
  // 二维数组
    .map((item, i) => headers.map((key, j) => Object.assign({}, { content: item[key.key], position: String.fromCharCode(65 + j) + (i + 2) })))
    // 二维转一维
    .reduce((prev, next) =>  prev.concat(next))
    // 转成worksheet需要的数据结构
    .reduce((prev, next) => Object.assign({}, prev, { [next.position]: { v: next.content }}), {})
  // 合并 headers 和 data
  const output = Object.assign({}, _headers, _data)
  console.log(\'output\', output)
  // 获取所有单元格的位置
  const outputPos = Object.keys(output)
  // 计算出范围 ,["A1",..., "H2"]
  const ref = `${outputPos[0]}:${outputPos[outputPos.length - 1]}`
  console.log(\'ref\', ref)
  // 构建 workbook 对象
  const wb = {
    SheetNames: [\'mySheet\'],
    Sheets: {
      mySheet: Object.assign(
        {},
        output,
        {
          \'!ref\': ref,
          \'!cols\': headers.map(item => ({ wpx: 120 }))// width in screen pixels
        }
      )
    }
  }
  // 导出 Excel
  XLSX.writeFile(wb, fileName)
}
// 防止CSV注入处理
export function handleCSV (arr) {
  const reg = new RegExp(\'(^=|^-)\')
  if (Array.isArray(arr) && arr.length > 0) {
    for (const item of arr) {
      Object.keys(item).forEach(key => {
        if (item[key] && reg.test(item[key])) {
          item[key] = \'\\\'\' + item[key]
        }
      })
    }
  }
  return arr
}
/**
 * 日期格式转换
 * `第一个参数为传入的以毫秒为单位的时间戳,第二个参数为格式,具体说明见代码;
 * 不传参则返回当前日期,则为“\'yyyy年MM月dd日\'”格式显示.`
 * @param {object} _date 日期
 * @param {string} _format 转换后的日期格式
 */
export function FormatDate (_date, _format) {
  if (_format && !_date) {
    return \'\'
  }
  var date = _date || new Date()
  var format = _format || \'yyyy/MM/dd\'
  date = new Date(_date)
  var map = {
    M: date.getMonth() + 1, // 月份
    d: date.getDate(), // 日
    h: date.getHours(), // 小时
    m: date.getMinutes(), // 分
    s: date.getSeconds(), // 秒
    q: Math.floor((date.getMonth() + 3) / 3), // 季度
    S: date.getMilliseconds() // 毫秒
  }
  format = format.replace(/([yMdhmsqS])+/g, function (all, t) {
    var v = map[t]
    if (v !== undefined) {
      if (all.length > 1) {
        v = \'0\' + v
        v = v.substr(v.length - 2)
      }
      return v
    } else if (t === \'y\') {
      return (date.getFullYear() + \'\').substr(4 - all.length)
    }
    return all
  })
  return format
}
<template>
<div>
  <!-- 这里使用了ElementUI的el-upload组件 -->
  <el-upload
        ref="upload"
        class="upload-demo"
        :action="\'\'"
        :multiple="false"
        :show-file-list="false"
        :limit="1"
        :before-upload="beforeAvatarUpload"
        :file-list="fileList"
      >从Excel导入
      </el-upload>
</div>
</template>
  • js
import { exportJsonToExcel, deepClone, FormatDate } from \'@/utils\'
data(){
  return {
    outJson: [], // 最后要发送给后台的json数据
    // 导入表头必填字段
      excelHeaderRequired: [
        \'序号(必填)\',
        \'子任务名称(必填)\',
        \'子任务内容(必填)\',
        \'执行人(必填)\',
        \'预计开始时间(必填)\',
        \'预计完成时间(必填)\',
        \'紧急程度(必填)\'
      ],
      exportHeader: Object.freeze([{
        key: \'index\', title: \'序号\' }, {
        key: \'workItem\', title: \'事项名称\' }, {
        key: \'taskName\', title: \'子任务名称\' }, {
        key: \'remark\', title: \'子任务内容\' }, {
        key: \'executor\', title: \'执行人\' }, {
        key: \'planStarttime\', title: \'预计开始时间\' }, {
        key: \'planEndtime\', title: \'预计完成时间\' }, {
        key: \'actualStarttime\', title: \'实际开始时间\' }, {
        key: \'actualEndtime\', title: \'实际完成时间\' }, {
        key: \'tagList\', title: \'标签\' }, {
        key: \'orderNum\', title: \'紧急程度\' }, {
        key: \'taskProgress\', title: \'工作进展\' }
      ]),
      order: Object.freeze({
        \'紧急\': 0,
        \'一般\': 1,
        \'重要紧急\': 2
      }),
      orderObj: Object.freeze({
        0: \'紧急\',
        1: \'一般\',
        2: \'重要紧急\'
      }),
  }

}
methods:{
  <!-- 上传之前进行文件校验 -->
  beforeAvatarUpload (file) {
      if (!file) {
        // 没有文件
        return false
      } else if (!/\\.(xls|xlsx)$/.test(file.name.toLowerCase())) {
        // 格式根据自己需求定义
        this.$message.error(\'上传格式不正确,请上传xls或者xlsx格式\')
        return false
      }
      this.handleExcel(file)// 处理数据
      this.$refs.upload.clearFiles()
    },
    handleExcel (file) {
      // 表格导入
      const files = file // excel文件
      const fileReader = new FileReader()
      fileReader.onload = ev => {
        try {
          const data = ev.target.result
          const workbook = XLSX.read(data, {
            type: \'binary\',
            cellDates: true
          })
          const wsname = workbook.SheetNames[0]// 取第一张表
          const tempArr = XLSX.utils.sheet_to_json(workbook.Sheets[wsname]) // 生成json表格内容,如果某个字段列没有填,则解析出来就会没有该字段
          console.log(\'解析出来的数据\', tempArr)
          this.outJson = []
          let isValidPass = true
          for (const item of tempArr) {
            console.log(\'几次\', item)
            const obj = {}
            const bool = this.validFieldRequired(item)
            if (!bool) { // 如果不是true,说明有些必填字段没有填
              isValidPass = false
              break
            }
            const isValidSuccess = this.handleoutJson(item, obj)
            if (!isValidSuccess) {
              isValidPass = false
              break
            } else {
              this.outJson.push(this.successObj)// this.outJson放的就是需要返回给后台的
            }
          }
          if (isValidPass) {
            // 如果this.outJson有数据说明已经全部校验成功可以传给后台了
            batchSaveTask(this.outJson).then(res => {
              this.$message.success(\'导入成功\')
              this.init()
            }).catch(e => {})
          }
        } catch (e) {
          console.log(e)
          return false
        }
      }
      fileReader.readAsBinaryString(files)
    },
    // 校验必填字段的值是否存在
    validFieldRequired (item) {
      let boolean = true
      for (const el of this.excelHeaderRequired) {
        if (!Object.keys(item).includes(el)) {
          this.$message.error(`${el}`)
          boolean = false
          break
        }
      }
      return boolean
    },
    // 将解析的json转为后台需要的字段
    handleoutJson (item, obj) {
      // if (item[\'事项名称(必填)\']) {
      // }
      this.successObj = {}
      obj[\'matterId\'] = this.$route.query.id
      obj[\'taskName\'] = item[\'子任务名称(必填)\'].trim()
      obj[\'remark\'] = item[\'子任务内容(必填)\'].trim()
      // 人员的需要传三个参数
      obj[\'executorAccount\'] = item[\'执行人(必填)\'].trim()
      obj[\'executorId\'] = \'\'
      obj[\'executor\'] = \'\'
      if (!DataType(item[\'预计开始时间(必填)\'], \'date\')) {
        this.$message.error(\'预计开始时间格式不符合\')
        return false
      }
      if (!DataType(item[\'预计完成时间(必填)\'], \'date\')) {
        this.$message.error(\'预计结束时间格式不符合\')
        return false
      }
      if (item[\'预计开始时间(必填)\'] > item[\'预计完成时间(必填)\']) {
        this.$message.error(\'预计开始时间不能晚于预计结束时间\')
        return false
      } else {
        // 需要对时间进行处理
        obj[\'planStarttime\'] = FormatDate(item[\'预计开始时间(必填)\'], \'yyyy-MM-dd hh:mm:ss\')
        // 需要对时间进行处理
        obj[\'planEndtime\'] = FormatDate(item[\'预计完成时间(必填)\'], \'yyyy-MM-dd hh:mm:ss\')
      }

      console.log(item[\'预计开始时间(必填)\'], item[\'预计完成时间(必填)\'])
      if (item[\'紧急程度(必填)\'] && Object.keys(this.order).includes(item[\'紧急程度(必填)\'].trim())) {
        // 需要把汉字转成key传过去
        obj[\'orderNum\'] = this.order[item[\'紧急程度(必填)\'].trim()]
      } else {
        this.$message.error(\'任务等级不存在\')
        return false
      }
      this.successObj = { ...obj }
      return true
    },
}

4、导出excel

  • js
 // 导出
    exportMatter (type, tag) {
      if (type === \'1\' && this.completeList.length > 0) {
        // 这里深copy防止污染原数据
        const arr = deepClone(this.completeList)
        // 需要处理数据
        const sourceData = arr.map((item, index) => {
          item[\'index\'] = index + 1
          item[\'tagList\'] = this.tagsText(item.tagList)
          item[\'orderNum\'] = this.orderObj[item.orderNum]
          item[\'planStarttime\'] = item.planStarttime ? FormatDate(item.planStarttime, \'yyyy/MM/dd hh:mm:ss\') : \'\'
          item[\'planEndtime\'] = item.planEndtime ? FormatDate(item.planEndtime, \'yyyy/MM/dd hh:mm:ss\') : \'\'
          item[\'actualStarttime\'] = item.actualStarttime ? FormatDate(item.actualStarttime, \'yyyy/MM/dd hh:mm:ss\') : \'\'
          item[\'actualEndtime\'] = item.planStarttime ? FormatDate(item.actualEndtime, \'yyyy/MM/dd hh:mm:ss\') : \'\'
          Object.keys(item).forEach(key => {
            item[key] = item[key] === null ? \'\' : item[key]
          })
          return item
        })
        exportJsonToExcel(this.exportHeader, sourceData, \'exportSubTask.xlsx\')
      } else {
        this.$message.error(\'没有可导出的已完成任务\')
      }
    },

以上是关于vue+xlsx实现前端导入导出功能的主要内容,如果未能解决你的问题,请参考以下文章

Vue项目实现导入导出Excel表格功能

基于vue 实现 excel导出导入功能

vue2前端导出带背景色表格 xlsx xlsx-style

Vue中 element的table表格导入 与 导出为excel表格的实现

Vue前端实现excel的导入导出打印功能

vue导入的xlsx没有数据