第四篇:VUE 利用 xlsx 和 file-saver 实现 Excel 表格的导入与导出

第四篇:VUE 利用 xlsx 和 file-saver 实现 Excel 表格的导入与导出

要点:本章在 xlsx 和 file-saver 基础上,简单的封装了方法,实现了 表格 Excel 的导出 与 导入。文件类型可支持 xlsx、csv 和 txt 。

页面的大致效果如下:

   

1. npm 安装 xlsx 和  file-saver

npm install xlsx --save
npm install file-saver --save

2. 封装的方法文件(可直接使用)

    2.1 工具类文件名: excel.js

import XLSX from 'xlsx'
import { saveAs } from 'file-saver'

// 自动宽度计算
function AutoWidth(ws, arr) {
  // 设置worksheet每列的最大宽度
  const colWidth = arr.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 }
      }
    })
  )
  // 以第一行为初始值
  const 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
}

// 数组转换成JSON
function formatJSON(key, data) {
  return data.map(v =>
    key.map(i => {
      return v[i]
    })
  )
}

// 字符串转ArrayBuffer
function s2ab(s) {
  const buf = new ArrayBuffer(s.length)
  const view = new Uint8Array(buf)
  for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff
  return buf
}

// 导出EXCEL表格
export function exportDataToExcel({
  header, // 表头名数组
  key, // 列对应字段数组
  data, // 需要导出数据的数组
  fileName, // 导出文件名
  autoWidth = true, // 是否自动宽度
  bookType = 'xlsx' // 导出文件格式
} = {}) {
  // 创建Workbook对象
  const wb = XLSX.utils.book_new()
  const arr = formatJSON(key, data)
  fileName = fileName || 'excel-list'
  arr.unshift(header)
  // 将数组数据转换为worksheet
  const ws = XLSX.utils.aoa_to_sheet(arr)
  if (autoWidth) {
    AutoWidth(ws, arr)
  }
  // 向Workbook对象中追加worksheet和fileName
  XLSX.utils.book_append_sheet(wb, ws, fileName)
  // 生成EXCEL的配置项
  const wbout = XLSX.write(wb, {
    bookType: bookType,
    bookSST: false,
    type: 'binary'
  })
  // 浏览器下载
  saveAs(
    new Blob([s2ab(wbout)], {
      type: 'application/octet-stream'
    }),
    `${fileName}.${bookType}`
  )
}

// 从Excel文件中获取表格头
function getHeaderRow(sheet) {
  const headers = []
  // 将 A1:G8 这种字符串转换为行列对象
  const range = XLSX.utils.decode_range(sheet['!ref'])
  let C
  const R = range.s.r
  // 从第一列开始,遍历范围中的每一列
  for (C = range.s.c; C <= range.e.c; ++C) {
    // 将行列对象转换为 A1 这种字符串
    const cell = sheet[XLSX.utils.encode_cell({ c: C, r: R })]
    // 用默认值替换
    let hdr = 'UNKNOWN ' + C
    if (cell && cell.t) hdr = XLSX.utils.format_cell(cell)
    headers.push(hdr)
  }
  return headers
}

// 读取Excel文件
export function readDataFromExcel(data, type) {
  // 读取Excel文件并保存到Workbook对象
  const workbook = XLSX.read(data, { type: type })
  const firstSheetName = workbook.SheetNames[0]
  // 获取Workbook对象的worksheet
  const worksheet = workbook.Sheets[firstSheetName]
  const header = getHeaderRow(worksheet)
  // 将worksheet转化成数组
  const results = XLSX.utils.sheet_to_json(worksheet)
  return { header, results }
}

export default {
  exportDataToExcel,
  readDataFromExcel
}

   2.2 上传 excel 表格公共组件(可拖拽上传):UploadExcel.vue

<template>
  <div class="upload-excel-wrapper">
    <el-upload
      ref="uploadExcel"
      drag
      action=""
      accept=".xlsx, .xls, .csv"
      :multiple="false"
      :show-file-list="false"
      :auto-upload="false"
      :limit="1"
      :on-exceed="handleLimit"
      :on-change="handleChange"
    >
      <i class="el-icon-upload" />
      <div class="el-upload__text">
        将Excel文件拖到此处,或<em>点击上传</em>
      </div>
    </el-upload>
  </div>
</template>

<script>
import excel from './excel'

export default {
  name: 'UploadExcel',
  methods: {
    handleLimit() {
      this.$message.warning('Excel文件只支持单个上传!')
      return false
    },
    handleChange(file) {
      const rawFile = file.raw
      if (!rawFile) return false
      if (!this.isExcel(rawFile)) {
        this.$message.warning('Excel文件只支持.xlsx, .xls, .csv格式!')
        return false
      }
      if (this.isLimit1M(rawFile)) {
        this.readFile(rawFile)
      }
    },
    isLimit1M(file) {
      const isLimit1M = file.size / 1024 / 1024 < 1
      if (isLimit1M) {
        return true
      } else {
        this.$message.warning('上传的Excel文件大小不能超过1M!')
        return false
      }
    },
    isExcel(file) {
      return /\.(xlsx|xls|csv)$/.test(file.name)
    },
    readFile(file) {
      const reader = new FileReader()
      reader.onload = e => {
        const data = e.target.result
        const { header, results } = excel.readDataFromExcel(data, 'array')
        this.$emit('on-success', { header, results })
      }
      reader.readAsArrayBuffer(file)
      reader.onerror = e => {
        this.$message.error('Excel文件读取出错!' || e)
      }
      this.$refs.uploadExcel.clearFiles()
    }
  }
}
</script>

<style lang="scss">
.upload-excel-wrapper {
  display: flex;
  align-content: center;
  justify-content: center;
  margin-bottom: 30px;
  .el-upload-dragger {
    width: 580px;
  }
}
</style>

2. 使用上面的工具类和上传组件:PageExcel.vue

<template>
  <div class="excel-block">
    <div class="export-excel-wrapper">
      <el-card shadow="always">
        <el-form
          ref="searchForm"
          :inline="true"
          :model="exportParam"
          label-width="100px"
          class="search-form"
        >
          <el-form-item label="文件名:">
            <el-input v-model="exportParam.fileName" placeholder="文件名" />
          </el-form-item>
          <el-form-item label="自动宽度:">
            <el-radio-group v-model="exportParam.autoWidth">
              <el-radio :label="true">自动</el-radio>
              <el-radio :label="false">固定</el-radio>
            </el-radio-group>
          </el-form-item>
          <el-form-item label="文件类型:">
            <el-select v-model="exportParam.type" placeholder="文件类型">
              <el-option value="xlsx" label="xlsx" />
              <el-option value="csv" label="csv" />
              <el-option value="txt" label="txt" />
            </el-select>
          </el-form-item>
          <el-form-item>
            <el-button
              type="primary"
              style="margin-left: 30px"
              @click="handleExport"
              >导出Excel</el-button
            >
          </el-form-item>
        </el-form>
        <el-table
          ref="multipleTable"
          v-loading="listLoading"
          :data="tableData"
          tooltip-effect="dark"
          style="width: 100%"
          size="medium"
          @selection-change="handleSelectionChange"
        >
          <el-table-column type="selection" width="50" align="center" />
          <el-table-column prop="id" label="编号" align="center" />
          <el-table-column prop="name" label="姓名" align="center" />
          <el-table-column prop="sex" label="性别" align="center" />
          <el-table-column prop="phone" label="手机" align="center" />
          <el-table-column prop="education" label="学历" align="center" />
          <el-table-column
            prop="hobby"
            label="爱好"
            align="center"
            width="300"
          />
        </el-table>
      </el-card>
    </div>
    <div class="import-excel-wrapper">
      <el-card shadow="always">
        <UploadExcel @on-success="handleSuccess" />
        <el-table
          :data="tableData2"
          tooltip-effect="dark"
          style="width: 100%"
          size="medium"
        >
          <el-table-column
            v-for="item in tableHeader"
            :key="item"
            :prop="item"
            :label="item"
            align="center"
          />
        </el-table>
      </el-card>
    </div>
  </div>
</template>

<script>
import UploadExcel from './UploadExcel'
import excel from './excel'

export default {
  name: 'PageExcel',
  components: { UploadExcel },
  data() {
    return {
      listLoading: false,
      exportParam: {
        fileName: '',
        autoWidth: true,
        type: 'xlsx'
      },
      tableData: [
        {
          id: 1001,
          name: '张三',
          sex: '男',
          phone: 15266001235,
          education: '高中',
          married: 1,
          forbid: true,
          hobby: '羽毛球、篮球、听歌、阅读'
        },
        {
          id: 1002,
          name: '李四',
          sex: '男',
          phone: 15266006621,
          education: '大专',
          married: 3,
          forbid: false,
          hobby: '乒乓球、排球、游泳'
        },
        {
          id: 1003,
          name: '王五',
          sex: '男',
          phone: 15264848125,
          education: '大学',
          married: 0,
          forbid: true,
          hobby: '旱冰、滑雪、跳高、打游戏'
        },
        {
          id: 1004,
          name: '辛八',
          sex: '男',
          phone: 15248491001,
          education: '初中',
          married: 2,
          forbid: false,
          hobby: '网球、篮球、跳伞'
        },
        {
          id: 1005,
          name: '刘二',
          sex: '女',
          phone: 15248411021,
          education: '大学',
          married: 0,
          forbid: true,
          hobby: '滑翔、游泳、篮球、看电影'
        },
        {
          id: 1006,
          name: '赵七',
          sex: '男',
          phone: 15298621500,
          education: '大专',
          married: 2,
          forbid: true,
          hobby: '游泳、篮球、潜水'
        },
        {
          id: 1007,
          name: '杨一',
          sex: '女',
          phone: 15267499461,
          education: '高中',
          married: 1,
          forbid: false,
          hobby: '冲浪、上网、看书、打游戏'
        }
      ],
      multipleSelection: [],
      tableHeader: [],
      tableData2: []
    }
  },
  created() {},
  mounted() {},
  methods: {
    handleSelectionChange(val) {
      this.multipleSelection = val
    },
    handleExport() {
      if (this.multipleSelection.length) {
        const params = {
          // 对应的表头
          header: ['编号', '姓名', '性别', '手机', '学历', '爱好'],
          // 对应的字段
          key: ['id', 'name', 'sex', 'phone', 'education', 'hobby'],
          // 选择的数据,数组类型
          data: this.multipleSelection,
          // 自动调整宽度
          autoWidth: this.exportParam.autoWidth,
          // 导出的文件名
          fileName: this.exportParam.fileName,
          // 导出的文件类型,xlsx,csv,txt
          bookType: this.exportParam.type
        }
        excel.exportDataToExcel(params)
        // 导出后清空用户的勾选选择
        this.$refs.multipleTable.clearSelection()
      } else {
        this.$message.warning('请勾选要导出的数据项!')
      }
    },
    handleSuccess({ header, results }) {
      this.tableHeader = header
      this.tableData2 = results
    }
  }
}
</script>

<style lang="scss" scoped>
.excel-block {
  .export-excel-wrapper {
    .el-card {
      min-height: 500px;
    }
    .search-form {
      padding-top: 18px;
      margin-bottom: 15px;
      background-color: #f7f8fb;
    }
    .el-table thead {
      font-weight: 600;
      th {
        background-color: #f2f3f7;
      }
    }
  }
  .import-excel-wrapper {
    margin-top: 10px;
    .el-card {
      min-height: 400px;
    }
    .search-form {
      padding-top: 18px;
      margin-bottom: 15px;
      background-color: #f7f8fb;
    }
    .el-table thead {
      font-weight: 600;
      th {
        background-color: #f2f3f7;
      }
    }
  }
}
</style>

3. 浏览的盆友,希望能帮助到您,感谢您的支持🙏

  • 7
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值