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

前情提要
后端传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下支持)*/
处理数据
左边的数据变成右边数据

得到列表范围
['!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>
)
}
方法二:直接导出html
useEffect(() => {
getNetworkFile();
}, []);
const getNetworkFile = () => {
axios({
url: "/rep.xlsx",
method: "GET",
responseType: "blob",
}).then((blobData) => {
//将blob转为file类型
let file = new File([blobData.data], "报表", { type: blobData.data.type });
fileReader(file);
});
};
const fileReader = (file) => {
let reader = new FileReader();
//读入file
reader.readAsBinaryString(file);
reader.onload = (e) => {
let side = e.target.result;
//读取file, 提取数据
let workbook = XLSX.read(side, { type: "binary", cellStyles: true });
// let workbook = XLSX.readFile('./rep.xlsx')
let html = "";
workbook.SheetNames.forEach(function (name, index) {
let ws = workbook.Sheets[name];
let str = xlsl.utils.sheet_to_html(ws, { header: 1, defval: "" });
console.log("html");
// 只截取table的内容
let startNo = str.indexOf(`<table>`);
let endNo = str.indexOf(`</table>`);
str = str.substring(startNo, endNo + `</table>`.length);
str = str.replace(/(\b(?:t|v)=".*?")/g, "");
str = str.replace(
"<table>",
`<table border="1" style="border-collapse:collapse; width: 100%; border:1px solid #666666; margin-bottom:5px;font-size:14px;margin: 15px 0;">`
);
html += str;
});
document.getElementById("can").innerHTML = html;
setData();
};
};
const setData = () => {
if (data) {
// Object.keys(data).map((i) => {
// let s = i.split(",").join("");
// document.getElementById(`sjs-${s}`).innerHTML = data[s];
// });
}
};
return <div id="can"></div>;
}
记录:excel导入导出js-xlsx,处理合并的更多相关文章
- JeeSite中Excel导入导出
在各种管理系统中,数据的导入导出是经常用到的功能,通常导入导出以Excel.CSV格式居多.如果是学习的过程中,最好是自己实现数据导入与导出的功能,然而在项目中,还是调用现成的功能比较好.近期一直使用 ...
- java简易excel导入导出工具(封装POI)
Octopus 如何导入excel 如何导出excel github项目地址 Octopus Octopus 是一个简单的java excel导入导出工具. 如何导入excel 下面是一个excel文 ...
- Excel导入导出的业务进化场景及组件化的设计方案(上)
1:前言 看过我文章的网友们都知道,通常前言都是我用来打酱油扯点闲情的. 自从写了上面一篇文章之后,领导就找我谈话了,怕我有什么想不开. 所以上一篇的(下)篇,目前先不出来了,哪天我异地二次回忆的时候 ...
- Excel导入导出帮助类
/// <summary> /// Excel导入导出帮助类 /// 记得引入 NPOI /// 下载地址 http://npoi.codeplex.com/rele ...
- Octopus——excel导入导出工具
Octopus Octopus是一个简易的Excel导入导出工具.目前主要就两个功能: 导入:将excel中一行数据转换为指定的java对象,并通过指定的正则表达式检查合法性. 导出:按照给定的xml ...
- TP5.0 excel 导入导出
引第三方的phpexcel类库放到 ThinkPHP\Library\Vendor\demo下,自己建的文件夹demo 再将Excel.class放到ThinkPHP\Library\Org\clas ...
- 土制Excel导入导出及相关问题探讨
转载请注明出处https://www.cnblogs.com/funnyzpc/p/10392085.html 新的一年,又一个开始,不见收获,却见年龄,好一个猪年,待我先来一首里尔克的诗: < ...
- Java 使用 Jxl 实现 Excel 导入导出
开发过程中经常需要用到数据的导入导出功能,之前用的是POI,这次使用JXL,JXL相对于POI来说要轻量简洁许多,在数据量不大的情况下还是非常实用的.这里做一下使用JXL的学习记录.首先需要导入相应的 ...
- poi excel导入导出
pom <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artif ...
- java jxl excel 导入导出的 总结(建立超链接,以及目录sheet的索引)
最近项目要一个批量导出功能,而且要生成一个单独的sheet页,最后后面所有sheet的索引,并且可以点击进入连接.网上搜索了一下,找到一个方法,同时把相关的excel导入导出操作记录一下!以便以后使用 ...
随机推荐
- 【RcoketMQ】RcoketMQ 5.0新特性(一)- Proxy
为了向云原生演进,提高资源利用和弹性能力,RcoketMQ在5.0进行了架构的调整与升级,先来看新特性之一,增加了Proxy层. 增加Proxy代理层 计算存储分离 计算存储分离是一种分层架构,将计算 ...
- JNI编程之字符串处理
java中的字符串类型是String,对应的jni类型是jstring,由于jstring是引用类型,所以我们不能像基本数据类型那样去使用它,我们需要使用JNIEnv中的函数去处理jstring,下面 ...
- dig 简明教程
哈喽大家好,我是咸鱼 不知道大家在日常学习或者工作当中用 dig 命令多不多 dig 是 Domain Information Groper 的缩写,对于网络管理员和在域名系统(DNS)领域工作的小伙 ...
- NFT(数字藏品)热度没了?这玩意是机会还是泡沫?
感谢你阅读本文! 大家好,今天分享一下NFT(数字藏品)这个领域,虽然今天的NFT已经没有之前那么火热,不过市场上依旧还是有很多平台存在,有人离开,也有人不断进来,所以很有必要再分析一番. 需要注意的 ...
- P1119 floyd
最开始看错数据了没看到Q = 100 是50%的数据以为跑q遍floyd能过,结果只有30,其他全t 1.要注意题目中的条件,挖掘一些性质 2.本题的另一个关键的是要对floyd的过程原理比较熟悉,f ...
- AirSim 自动驾驶仿真 (6) 设置采集参数和属性
https://cloud.tencent.com/developer/article/2011384 1.配置文件在哪 默认情况下,文件位于用户目录下的AirSim文件夹,比如在Windows下,文 ...
- Python 如何实现合并 PDF 文件?
在处理多个 PDF 文档时,频繁地打开关闭文件会严重影响效率.因此,对于一大堆内容相关的 PDF 文件,我们可以先将这些 PDF 文件合并起来再操作,从而提高工作效率.比如,在传送大量的 PDF 文档 ...
- Android Gson 混淆问题
开发过程中遇到一个奇怪的问题. 有一个接口,debug 版本接收到云侧下发的字符串后可以通过 gson 将其转换为相应 bean 类,而 release 版本拿到的 bean 总是缺少一个关键的字段, ...
- 本地Stackedit Markdown编辑器设置远程访问
StackEdit是一个受欢迎的Markdown编辑器,在GitHub上拥有20.7k Star!,它支持将Markdown笔记保存到多个仓库,包括Gitee.GitHub和Gitea.此在线笔记工具 ...
- 使用Netty实现文件传输的HTTP服务器和客户端
现在我们来用netty实现文件传输的HTTP服务器和客户端 pom依赖文件: <?xml version="1.0" encoding="UTF-8"?& ...