乐趣区

关于vue.js:vuexlsx实现前端导入导出功能

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
}
  • html
<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('没有可导出的已实现工作')
      }
    },
退出移动版