ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

记录:excel导入导出js-xlsx,处理合并

2022-08-17 15:31:36  阅读:140  来源: 互联网

标签:xlsx const excel js cell item let workbook table


效果

image

前情提要

后端传excel坐标数据,前端自己处理模板,找资料后,选择直接载入xlsx方式。

准备工作

npm i xlsx

import * as XLSX from 'xlsx'

导入

提取数据

    let reader = new FileReader()
    //读入file
    reader.readAsBinaryString(file)
    reader.onload = (e) => {
      let data = e.target.result
      //读取file, 提取数据
      let workbook = XLSX.read(data, { type: 'binary', cellStyles: true })
	  //workbook.Styles不太对应
      let sheetNames = workbook.SheetNames
      let sheets = workbook.Sheets
      parsingTable(sheets[sheetNames[0]])
    }
/**
1. base64: 以base64方式读取;
2. binary: BinaryString格式(byte n is data.charCodeAt(n))
3. string: UTF8编码的字符串;
4. buffer: nodejs Buffer;
5. array: Uint8Array,8位无符号数组;
6. file: 文件的路径(仅nodejs下支持)*/

处理数据

左边的数据变成右边数据
image

    得到列表范围
    ['!ref'])
    得到合并数据
    ['!merges']

const parsingTable = (table) => {
    let header = [] //表格列
    let dataSource = [] //表格数据
    let maxRowIndex = 0 //最大行数
    let keys = Object.keys(table)
    const range = XLSX.utils.decode_range(table['!ref'])
    maxRowIndex = range['e']['r'] - range['s']['r']
    for (let [i, h] of keys.entries()) {
      //提取key中的英文字母
      let col = h.replace(/[^A-Z]/g, '')
      //单元格是以A-1的形式展示的,所以排除包含!的key
      h.indexOf('!') === -1 && header.indexOf(col) === -1 && header.push(col)
      //如果!ref不存在时,  设置某一列最后一个单元格的索引为最大行数
      if (
        (!table['!ref'] || !table['!ref'].includes(':')) &&
        header.some((c) => table[`${c}${i}`])
      ) {
        maxRowIndex = i > maxRowIndex ? i : maxRowIndex
      }
    }
    header = header.sort((a, b) => a.localeCompare(b)) //按字母顺序排序 [A, B, ..., E, F]
    //excel的行表示为 1, 2, 3, ......, 所以index起始为1
    //从1开始,maxRowIndex需要+1
    for (let index = 1; index <= maxRowIndex + 1; index++) {
      let row = [] //行
      //每行的单元格集合, 例: [A1, ..., F1]
      row = header.map((item) => {
        let key = `${item}${index}`
        let cell = table[key]
        return {
          key,
          name: cell ? cell.v : ''
        }
      })
      dataSource.push(row)
    }
    console.log(dataSource)
    //setArrList(dataSource)
    //合并单元格
    if (table['!merges']) {
      for (let item of table['!merges']) {
        //s开始  e结束  c列  r行  (行、列的索引都是从0开始的)
        for (let r = item.s.r; r <= item.e.r; r++) {
          for (let c = item.s.c; c <= item.e.c; c++) {
            // console.log('=======', r, c)
            //查找单元格时需要r+1
            //例:单元格A1的位置是{c: 0, r:0}
            let rowIndex = r + 1
            let cell = dataSource[r].find((a) => a.key === `${header[c]}${rowIndex}`)
            if (cell) {//除了第一行都置为0
              if(c === item.s.c&&r === item.s.r){
                cell.rowspan = item.e.r - item.s.r + 1 //纵向合并
                cell.colspan = item.e.c - item.s.c + 1 //横向合并
              }else{
                cell.rowspan = 0
                cell.colspan = 0
              }
            }
          }
        }
      }
    }
    setArrList(dataSource)
  }

渲染

react写法,vue大差不差

 <Spin spinning={loading}>
      <div id="can">
        {/* <input type="file" ref={inputFile} onChange={fileChange} /> 
        <button onClick={createBook}>导出</button>*/}
        <table id="tableView">
          <tbody>
            {arrList.map((item, index) => {
              return (
                <tr key={index}>
                  {item.map((i, k) => {
                    return i.rowspan !== 0 && i.colspan !== 0 && (
                      <td key={k} colSpan={i.colspan} rowSpan={i.rowspan}>
                        {i.name ??
                          data[`${i.key.replace(/[^A-Z]/g, '')},${i.key.replace(/[^0-9]/g, '')}`]}
                      </td>
                    )
                  })}
                </tr>
              )
            })}
          </tbody>
        </table>
      </div>
    </Spin>

导出

创建blob

const createBook = () => {
    //使用table_to_sheet或table_to_book其中一种方法
    //table_to_sheet的用法
    //console.log(inputFile);
    // let files = inputFile.current.files
    console.log(document.getElementById('tableView'))
    let worksheet = XLSX.utils.table_to_sheet(document.getElementById('tableView'))
    let workbook = {
      SheetNames: [],
      Sheets: {}
    }
    workbook.SheetNames.push('sheet1')
    worksheet['!cols'] = [{ wch: 20 }] //设置第一列的列宽
    workbook.Sheets['sheet1'] = worksheet
    //table_to_book的用法
    // let workbook = XLSX.utils.table_to_book(document.getElementById('tableView'));
    let data = XLSX.write(workbook, {
      bookType: 'xlsx', // 要生成的文件类型
      type: 'array'
    })
    let blobData = new Blob([data], { type: 'application/octet-stream' })
    exportFn(blobData)
  }

下载

const exportFn = (blob) => {
    const fileName = '料场报表.xlsx'
    let downloadElement = document.createElement('a')
    let href = window.URL.createObjectURL(blob) //创建下载的链接
    downloadElement.href = href
    downloadElement.download = fileName //下载后文件名
    document.body.appendChild(downloadElement)
    downloadElement.click() //点击下载
    document.body.removeChild(downloadElement) //下载完成移除元素
    window.URL.revokeObjectURL(href) //释放blob
    message.success('已成功导出!')
  }

完整代码

点击查看代码
import React, { useState, useEffect, useRef } from 'react'
import * as XLSX from 'xlsx'
import axios from 'axios'
import { Spin, message } from 'antd'
export default function Canvas(props) {
  const { data, loading } = props
  const [arrList, setArrList] = useState([])
  const inputFile = useRef(null)
  useEffect(() => {
    getNetworkFile()
  }, [])
  const getNetworkFile = () => {
    axios({
      url: '/rep.xlsx',
      method: 'get',
      responseType: 'blob'
    }).then((blobData) => {
      console.log(blobData)
      //将blob转为file类型
      let file = new File([blobData.data], '报表', { type: blobData.type })
      fileReader(file)
    })
  }
  //导出
  const createBook = () => {
    //使用table_to_sheet或table_to_book其中一种方法
    //table_to_sheet的用法
    //console.log(inputFile);
    // let files = inputFile.current.files
    console.log(document.getElementById('tableView'))
    let worksheet = XLSX.utils.table_to_sheet(document.getElementById('tableView'))
    let workbook = {
      SheetNames: [],
      Sheets: {}
    }
    workbook.SheetNames.push('sheet1')
    worksheet['!cols'] = [{ wch: 20 }] //设置第一列的列宽
    workbook.Sheets['sheet1'] = worksheet
    //table_to_book的用法
    // let workbook = XLSX.utils.table_to_book(document.getElementById('tableView'));
    let data = XLSX.write(workbook, {
      bookType: 'xlsx', // 要生成的文件类型
      type: 'array'
    })
    let blobData = new Blob([data], { type: 'application/octet-stream' })
    exportFn(blobData)
  }
  const exportFn = (blob) => {
    const fileName = '料场报表.xlsx'
    let downloadElement = document.createElement('a')
    let href = window.URL.createObjectURL(blob) //创建下载的链接
    downloadElement.href = href
    downloadElement.download = fileName //下载后文件名
    document.body.appendChild(downloadElement)
    downloadElement.click() //点击下载
    document.body.removeChild(downloadElement) //下载完成移除元素
    window.URL.revokeObjectURL(href) //释放blob
    message.success('已成功导出!')
  }
  const fileReader = (file) => {
    let reader = new FileReader()
    //读入file
    reader.readAsBinaryString(file)
    reader.onload = (e) => {
      let data = e.target.result
      //读取file, 提取数据
      let workbook = XLSX.read(data, { type: 'binary', cellStyles: true })
      let sheetNames = workbook.SheetNames
      let sheets = workbook.Sheets
      console.log(e);
      parsingTable(sheets[sheetNames[0]])
    }
  }
  const fileChange = () => {
    let files = inputFile.current.files
    console.log(files)
    fileReader(files[0])
  }
  //对数据进行处理,实现表格合并展示的功能
  const parsingTable = (table) => {
    let header = [] //表格列
    let dataSource = [] //表格数据
    let maxRowIndex = 0 //最大行数
    let keys = Object.keys(table)
    const range = XLSX.utils.decode_range(table['!ref'])
    maxRowIndex = range['e']['r'] - range['s']['r']
    for (let [i, h] of keys.entries()) {
      //提取key中的英文字母
      let col = h.replace(/[^A-Z]/g, '')
      //单元格是以A-1的形式展示的,所以排除包含!的key
      h.indexOf('!') === -1 && header.indexOf(col) === -1 && header.push(col)
      //如果!ref不存在时,  设置某一列最后一个单元格的索引为最大行数
      if (
        (!table['!ref'] || !table['!ref'].includes(':')) &&
        header.some((c) => table[`${c}${i}`])
      ) {
        maxRowIndex = i > maxRowIndex ? i : maxRowIndex
      }
    }
    header = header.sort((a, b) => a.localeCompare(b)) //按字母顺序排序 [A, B, ..., E, F]
    //excel的行表示为 1, 2, 3, ......, 所以index起始为1
    //从1开始,maxRowIndex需要+1
    for (let index = 1; index <= maxRowIndex + 1; index++) {
      let row = [] //行
      //每行的单元格集合, 例: [A1, ..., F1]
      row = header.map((item) => {
        let key = `${item}${index}`
        let cell = table[key]
        return {
          key,
          name: cell ? cell.v : ''
        }
      })
      dataSource.push(row)
    }
    console.log(dataSource)
    //setArrList(dataSource)
    //合并单元格
    if (table['!merges']) {
      for (let item of table['!merges']) {
        //s开始  e结束  c列  r行  (行、列的索引都是从0开始的)
        for (let r = item.s.r; r <= item.e.r; r++) {
          for (let c = item.s.c; c <= item.e.c; c++) {
            // console.log('=======', r, c)
            //查找单元格时需要r+1
            //例:单元格A1的位置是{c: 0, r:0}
            let rowIndex = r + 1
            let cell = dataSource[r].find((a) => a.key === `${header[c]}${rowIndex}`)
            if (cell) {//除了第一行都置为0
              if(c === item.s.c&&r === item.s.r){
                cell.rowspan = item.e.r - item.s.r + 1 //纵向合并
                cell.colspan = item.e.c - item.s.c + 1 //横向合并
              }else{
                cell.rowspan = 0
                cell.colspan = 0
              }
            }
          }
        }
      }
    }
    setArrList(dataSource)
  }
  return (
    <Spin spinning={loading}>
      <div id="can">
        {/* <input type="file" ref={inputFile} onChange={fileChange} /> 
        <button onClick={createBook}>导出</button>*/}
        <table id="tableView">
          <tbody>
            {arrList.map((item, index) => {
              return (
                <tr key={index}>
                  {item.map((i, k) => {
                    return i.rowspan !== 0 && i.colspan !== 0 && (
                      <td key={k} colSpan={i.colspan} rowSpan={i.rowspan}>
                        {i.name ??
                          data[`${i.key.replace(/[^A-Z]/g, '')},${i.key.replace(/[^0-9]/g, '')}`]}
                      </td>
                    )
                  })}
                </tr>
              )
            })}
          </tbody>
        </table>
      </div>
    </Spin>
  )
}

标签:xlsx,const,excel,js,cell,item,let,workbook,table
来源: https://www.cnblogs.com/yun10011/p/16593954.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有