首页 > 其他分享 >记录:excel导入导出js-xlsx,处理合并

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

时间:2022-08-17 15:38:11浏览次数:58  
标签: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
From: https://www.cnblogs.com/yun10011/p/16593954.html

相关文章

  • [JSOI2007] 字串加密
    题链:luoguJS同学?Description让JS同学对环形字符串进行重组加密。加密规则是:列出\(n\)个字符串并字典序升序,一次取末尾字符作为加密后的长度为\(n\)的密码串。......
  • 原生get请求读取本地json文件,electron vue
    1readLocalFile(fileUrl){2letxhr=null3if(window.XMLHttpRequest){4xhr=newXMLHttpRequest()5}else{6/......
  • JSP内置对象、MVC开发模式
    JSP内置对象内置对象:在jsp页面中不需要创建,直接使用对象变量名  真实类型  作用*pagecontext......
  • JSP指令导入标签库、JSP指令导入注释
    JSP指令导入标签库includ:页面包含的,导入页面的资源文件ttavlib:导入资源  JSP指令导入注释1,html注释:<!---->:只能注释html代码片段2,......
  • js - 文件名
    js-文件名修改文件名notice只修改文件名称保留文件格式后缀获取文件名称和格式后缀优化版/***@methodgetFileNameandExt*@description获取文件名称和格......
  • 两个jsp之间传递参数
    以下为请求端,用form表单默认传递  接收端用request.getParameter("name")来接收 ......
  • node.js安装过程中遇到的坑
    node.js的安装noide.js的完全卸载node.js的安装一些报错的解决1.node.js的完全卸载第一步:打开系统自带的应用管理器卸载ndoe.js。第二步:删除和node.js相关......
  • js中的双问号和“?.“的含义和使用
    总结: this.approveRecords[2].files[0].id会报错因为files:[],所以写成this.approveRecords[2]?.files[0]?.id让它链不下去就好了??表示:只有左侧的值为null或undefin......
  • java之fastjson序列化和反序列化
    全解史上最快的JSON解析库-alibabaFastjson:https://zhuanlan.zhihu.com/p/72495484Fastjson:https://blog.csdn.net/rustwei/article/details/121162202......
  • ExtJS - UI组件 - MessageBox
    更新记录转载请注明出处:https://www.cnblogs.com/cqpanda/p/16587505.html2022年8月17日发布。2022年8月13日从笔记迁移到博客。ExtJS教程汇总:https://www.cnblog......