效果

前情提要

后端传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,处理合并的更多相关文章

  1. JeeSite中Excel导入导出

    在各种管理系统中,数据的导入导出是经常用到的功能,通常导入导出以Excel.CSV格式居多.如果是学习的过程中,最好是自己实现数据导入与导出的功能,然而在项目中,还是调用现成的功能比较好.近期一直使用 ...

  2. java简易excel导入导出工具(封装POI)

    Octopus 如何导入excel 如何导出excel github项目地址 Octopus Octopus 是一个简单的java excel导入导出工具. 如何导入excel 下面是一个excel文 ...

  3. Excel导入导出的业务进化场景及组件化的设计方案(上)

    1:前言 看过我文章的网友们都知道,通常前言都是我用来打酱油扯点闲情的. 自从写了上面一篇文章之后,领导就找我谈话了,怕我有什么想不开. 所以上一篇的(下)篇,目前先不出来了,哪天我异地二次回忆的时候 ...

  4. Excel导入导出帮助类

    /// <summary>    /// Excel导入导出帮助类    /// 记得引入 NPOI    /// 下载地址   http://npoi.codeplex.com/rele ...

  5. Octopus——excel导入导出工具

    Octopus Octopus是一个简易的Excel导入导出工具.目前主要就两个功能: 导入:将excel中一行数据转换为指定的java对象,并通过指定的正则表达式检查合法性. 导出:按照给定的xml ...

  6. TP5.0 excel 导入导出

    引第三方的phpexcel类库放到 ThinkPHP\Library\Vendor\demo下,自己建的文件夹demo 再将Excel.class放到ThinkPHP\Library\Org\clas ...

  7. 土制Excel导入导出及相关问题探讨

    转载请注明出处https://www.cnblogs.com/funnyzpc/p/10392085.html 新的一年,又一个开始,不见收获,却见年龄,好一个猪年,待我先来一首里尔克的诗: < ...

  8. Java 使用 Jxl 实现 Excel 导入导出

    开发过程中经常需要用到数据的导入导出功能,之前用的是POI,这次使用JXL,JXL相对于POI来说要轻量简洁许多,在数据量不大的情况下还是非常实用的.这里做一下使用JXL的学习记录.首先需要导入相应的 ...

  9. poi excel导入导出

    pom <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artif ...

  10. java jxl excel 导入导出的 总结(建立超链接,以及目录sheet的索引)

    最近项目要一个批量导出功能,而且要生成一个单独的sheet页,最后后面所有sheet的索引,并且可以点击进入连接.网上搜索了一下,找到一个方法,同时把相关的excel导入导出操作记录一下!以便以后使用 ...

随机推荐

  1. DHCP和PXE是怎么工作的

    dhcp(Dynamic Host Configuration Protocol):配置一段共享IP地址,为新上线的机器分配IP地址,回收下线机器的IP地址. 正常情况下主机(DHCP client) ...

  2. CF1343C

    题目简化和分析: 给您一个序列,您要在其中选择若干个数使得: 相邻两数异号 长度最大,总和最大 我们可以牢牢抓住长度且总和最大,这一特性. 说明我们必须在每一个连续的同号的子串中被迫选择最大的,以满足 ...

  3. Anaconda虚拟环境配置Python库与Spyder编译器

      本文介绍在Anaconda中,为Python的虚拟环境安装第三方库与Spyder等配套软件的方法.   在文章创建Anaconda虚拟Python环境的方法中,我们介绍了在Anaconda环境下, ...

  4. ESP32-MicroPython without Thonny

    why witout Thonny? 最近闲来在ESP32上用MicroPython捣鼓些小玩具,见很多教程推荐使用Thonny.欣然往之,竟是个这,实在不能认同.Thonny esp32-Micro ...

  5. ResNet详解:网络结构解读与PyTorch实现教程

    本文深入探讨了深度残差网络(ResNet)的核心概念和架构组成.我们从深度学习和梯度消失问题入手,逐一解析了残差块.初始卷积层.残差块组.全局平均池化和全连接层的作用和优点.文章还包含使用PyTorc ...

  6. RSA总结 From La神

    常用工具 分解大素数 factordb (http://www.factordb.com / API: http://factordb.com/api?query=) yafu (p q 相差过大或过 ...

  7. 前端脚手架CLI生成模版命令工具(包括,npm包的发布,脚手架的搭建,注意事项,优化等)

    写在前面 这是停更以后,续更的一篇文章. 为什么好长时间都没有更新,因为去其他平台更新了,包括掘金,思否,简书等. 在那些地方感觉没有归属感,有的平台原创审核很麻烦,简书号称可以获得打赏,可是码了几十 ...

  8. 手撕Vuex-实现mutations方法

    经过上一篇章介绍,完成了实现 getters 的功能,那么接下来本篇将会实现 mutations 的功能. 在实现之前我们先来回顾一下 mutations 的使用. 将官方的 Vuex 导入进来,因为 ...

  9. QGradient渐变填充

    QGradient渐变填充 QGradient (一)简介 (二)枚举类型 1.spread 2.CoordinateMode 3.type (三)常用函数 1.coordinateMode() 2. ...

  10. logback java动态配置【动态修改日志级别,动态修改appender】(转)

    logback 动态配置 写在前面在做java日志之前,强烈建议大家读一下这篇java日志的前世今生,对理清java日志框架很有帮助!1奉上地址: 一个著名的日志系统是怎么设计出来的 然后说一下,为啥 ...