记录: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导入导出操作记录一下!以便以后使用 ...
随机推荐
- CF1526C2
与简单版的思路完全一致,只需要改一下范围. 可以去看我简单版本的博客. 题目简化和分析: 给您一个数组,在其中选择若干个数使得: 任意前缀和 \(\ge 0\) 数量尽可能的大 我们可以使用贪心策略, ...
- SpringBoot + 自定义注解 + AOP 高级玩法打造通用开关
前言 最近在工作中迁移代码的时候发现了以前自己写的一个通用开关实现,发现挺不错,特地拿出来分享给大家. 为了有良好的演示效果,我特地重新建了一个项目,把核心代码提炼出来加上了更多注释说明,希望xdm喜 ...
- 一个基于Vue模型的表单生成器
哈喽,我是老鱼,一名致力于在技术道路上的终身学习者.实践者.分享者! Vuetify Form Base是一个基于模型的表单生成器,目的是提供一个工具,以便以较少的努力从任何模型数据生成可编辑的表单, ...
- PCA主成分分析的理解
u |_matrix1x2_{{-0.70710678118654757};{-0.70710678118654735}} x^(1) |_matrix1x2_{{-0.51805350 ...
- Util应用框架基础(四) - 验证
本节介绍Util应用框架如何进行验证. 概述 验证是业务健壮性的基础. .Net 提供了一套称为 DataAnnotations 数据注解的方法,可以对属性进行一些基本验证,比如必填项验证,长度验证等 ...
- JavaScript高级程序设计笔记10 函数Function
函数 1.几种实例化函数对象的方式 以函数声明的方式定义 函数表达式 箭头函数(arrow function) 使用Function构造函数 接收任意多个字符串参数,最后一个参数始终会被当成函数体,而 ...
- 牛客多校第五场 K King of Range
题意: 给定一个\(n\)个数得序列\(a_i\),给定\(m\)个询问,每次给出一个\(k\),寻找有多少个区间\([l, r]\)中最大值与最小值之差严格大于\(k\). 思路: 可以发现,如果已 ...
- CART算法解密:从原理到Python实现
本文深入探讨了CART(分类与回归树)算法的核心原理.实现方法以及应用场景.文章首先介绍了决策树的基础知识,然后详细解析了CART算法的工作机制,包括特征选择和树的构建.接着,通过Python和PyT ...
- keil 5 安装教程
一.下载 keil 官网 二.安装教程 1.开始安装 双击安装包,开始安装,直接下一步 2.勾选同意,下一步 3.选择软件安装路径,下一步 4.填写信息 可以随意填写,下一步. 5.等待安装 6.安装 ...
- .NET 6 使用 LogDashboard 可视化日志
在上一篇中我使用Nlog记录日志到了数据库,接下来我们进行日志的可视化展示 1. 关于LogDashboard logdashboard是在github上开源的aspnetcore项目, 它旨在帮助开 ...