效果

前情提要

后端传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. nfls10.1

    T1 大水题,用位运算更加便捷求解. T2 看出来有环了,但是没往基环树上想,寄. 暴力分,有部分分是基础树,可以跑一遍深搜,根节点的选择是 k 种颜色,剩下的是 k - 1 种颜色.还有暴力是可以二 ...

  2. 前端web页面支持MQTT消息推送

    MQTT服务一般用直接下载mosquitto,安装后启动服务即可.方便可靠. 但是默认情况下只开通了1883的tcp访问,用html的web页面上调用就不行了. 其实mosquitto是支持多端口的, ...

  3. z函数|exkmp|拓展kmp 笔记+图解

    题外话,我找个什么时间把kmp也加一下图解 z函数|exkmp 别担心 这个exkmp和kmp没毛点关系,请放心食用. 本文下标以1开始,为什么?因为1开始就不需要进行长度和下标的转换,长度即下标. ...

  4. Android-Java-反序列化JSON

    import com.google.gson.Gson; import com.google.gson.reflect.TypeToken; String jsonStr= WebAPIOperato ...

  5. CF1333A [Little Artem]

    Problem 题目简述 给你一个 \(n \times m\) 的方格,构造一个方案,使得方案中 \(B = W + 1\). \(B\):相邻的格子有至少一个白色格子的黑色格子的个数. \(W\) ...

  6. [vue]精宏技术部试用期学习笔记 I

    精宏技术部试用期学习笔记(vue) 什么是vue? 我个人对 vue 的理解 是把 html\css\js 三件套融合起来的结构,同时用组件化的思维把一个页面装填起来 同时让页面形成树状结构 优点是方 ...

  7. offscreenCanvas+worker+IndexedDB实现无感大量图片缓存

    一个有必要实现的需求 因为项目中需要使用canvasTexture(一个threejs3d引擎中的材质类型),绘制大量的图片,每次使用都会请求大量的oss图片资源,虽然重复请求会有磁盘缓存但毕竟这个磁 ...

  8. centos 7.9安装Prometheus

    一.Prometheus功能 Prometheus 在系统监控和警报方面非常强大,它适用于多种应用场景.以下是一些常见的 Prometheus 应用场景,以及具体的例子: 性能监控:Prometheu ...

  9. 搭建 MongoDB (v6.0) 副本集记录

    副本集概述 副本集(Replica Set)是一组带有故障转移的 MongoDB 实例组成的集群,由一个主(Primary)服务器和多个从(Secondary)服务器构成.通过Replication, ...

  10. Flask SocketIO 实现动态绘图

    Flask-SocketIO 是基于 Flask 的一个扩展,用于简化在 Flask 应用中集成 WebSocket 功能.WebSocket 是一种在客户端和服务器之间实现实时双向通信的协议,常用于 ...