前言
原本项目要求前端把excel中的数据导入指定表格中,并把指定表格的数据导出到excel中,单纯的数据导入导出功能直接使用 js-xlsx 则满足要求。
后续要求导出的excel文件带样式,使用js-xlsx无法满足,加上 xlsx-style 则可设置excel样式。
现在要求根据模板生成excel文件,即把数据插入预设好的excel文件中,由于 js-xlsx 加上XLSX.read(file, {..., cellStyles: true}) 读取excel模板时样式不完整,最终无法生成与excel模板样式一样的文件。查了一下是免费的js-xlsx(SheetJS)不能获取完整样式,专业版还是可以。最后移除 js-xlsx + xlsx-style,使用 ExcelJs 实现。
注意:SheetJS不能导入 .xls 的文件,如需要导入.xls,需继续保留 js-xlsx
使用js-xlsx实现Excel数据的导入导出
-
安装
// 安装
npm install xlsx
// 引入使用
import XLSX from "xlsx";
-
基本操作
1、excel工作簿
const workbook = {
SheetNames: [], //工作表名数组
Sheets: {} //工作表对象 键名对应SheetNames的key
}
//读取(本地或网络)文件,并创建一个工作薄
const workbook = XLSX.read(data, opts);
//创建一个新的工作簿
const workbook = XLSX.utils.book_new();
//通过抓取页面中的 HTML TABLE 创建工作表
const workbook = XLSX.utils.table_to_book(dom_element, opts);
2、工作表
const worksheet = {
'!ref': 'A1:C2', //工作表的范围 必须 否则不显示
'!margins': [ //合并单元格列表
{
s: { c: 1, r: 0 }, //s start 开始;c cols 开始列;r rows 开始行
e: { c: 4, r: 0 }, //e end 结束;c cols 结束列;r rows 结束行
}
],
A1: {t: 's', v: 'A'}, //单元格
A2: {t: 'n', v: 1},
}
const rows = [
{ A: 1, B: 2, C: 3, D: 4 },
{ A: 11, B: 22, C: 33, D: 44 }
]
// 数组对象转为sheet表
const worksheet = XLSX.utils.json_to_sheet(rows);
const rows = [
[1, 2, 3, 4, 5],
[11, 22, 33, 44, 55]
]
// 将数组转为sheet
const worksheet = XLSX.utils.aoa_to_sheet(rows);
//接受一个表 DOM 元素并返回一个类似于输入表的工作薄
const worksheet = XLSX.utils.table_to_sheet(DOM);
4、将工作表附加到工作簿
XLSX.utils.book_append_sheet(workbook, worksheet, sheet_name);
// 第四个参数为true时,该函数将从指定的工作表名称开始。
// 如果工作簿中存在工作表名称,则将通过查找名称主干并递增计数器来选择新的工作表名称。
XLSX.utils.book_append_sheet(workbook, sheetA, "Sheet2", true); // Sheet2
XLSX.utils.book_append_sheet(workbook, sheetB, "Sheet2", true); // Sheet3
5、写入文件
XLSX.write(workbook, options)
XLSX.writeFile(workbook, filename, options)
options
参数
名称 | 描述 | 默认值 |
---|---|---|
type | 输出数据编码,必须,值:base64、binary、string、buffer、array、file | |
cellDates | 存储日期为d类型(默认为n) | false |
bookSST | 生成共享字符串表 | false |
bookType | 文档格式类型,必须,值:xlsx、xlsm、xlsb、biff8、biff5、biff4、biff3、biff2、xlml、numbers、ods、fods、wk3、csv、txt、sylk、html、dif、dbf、wk1、rtf、prn、eth | "xlsx" |
sheet | 单页格式的工作表名称 | "" |
compression | 对基于ZIP的格式使用ZIP压缩 | false |
Props | 重写工作簿时重写工作簿属性 |
4、读取文件
XLSX.read(data, options)
XLSX.readFile(filename, options)
options
参数
名称 | 描述 | 默认值 |
---|---|---|
type | 输入数据编码,必须,值:base64、binary、string、buffer、array、file | |
raw | 如果为 true,纯文本解析将不会解析值 | false |
codepage | 代码页,如果指定,则在适当时使用代码页 | |
sheetStubs | 为存根单元创建 z 类型的单元对象 | false |
sheetRows | 工作表行数,If >0、读取前 sheetRows 行 | 0 |
bookProps | 如果为 true,则仅解析足以获取图书元数据 | false |
password | 如果已定义且文件已加密,则使用密码 | "" |
sheets | 如果指定,则仅解析指定的工作表 | |
xlfn | 如果为 true,则在公式中保留 _xlfn. 前缀 | alse |
5、sheet生成对应格式的数据
let svg = XLSX.utils.sheet_to_csv(worksheet)
let html = XLSX.utils.sheet_to_html(worksheet)
let json = XLSX.utils.sheet_to_json(worksheet)
-
封装excel文件的导入导出
/* eslint-disable */
require('script-loader!file-saver');
require('./Blob.js');
require('script-loader!xlsx/dist/xlsx.core.min');
function generateArray(table) {
var out = [];
var rows = table.querySelectorAll('tr');
var ranges = [];
for (var R = 0; R < rows.length; ++R) {
var outRow = [];
var row = rows[R];
var columns = row.querySelectorAll('td');
for (var C = 0; C < columns.length; ++C) {
var cell = columns[C];
var colspan = cell.getAttribute('colspan');
var rowspan = cell.getAttribute('rowspan');
var cellValue = cell.innerText;
if (cellValue !== "" && cellValue == +cellValue) cellValue = +cellValue;
//Skip ranges
ranges.forEach(function (range) {
if (R >= range.s.r && R <= range.e.r && outRow.length >= range.s.c && outRow.length <= range.e.c) {
for (var i = 0; i <= range.e.c - range.s.c; ++i) outRow.push(null);
}
});
//Handle Row Span
if (rowspan || colspan) {
rowspan = rowspan || 1;
colspan = colspan || 1;
ranges.push({ s: { r: R, c: outRow.length }, e: { r: R + rowspan - 1, c: outRow.length + colspan - 1 } });
}
;
//Handle Value
outRow.push(cellValue !== "" ? cellValue : null);
//Handle Colspan
if (colspan) for (var k = 0; k < colspan - 1; ++k) outRow.push(null);
}
out.push(outRow);
}
return [out, ranges];
};
function datenum(v, date1904) {
if (date1904) v += 1462;
var epoch = Date.parse(v);
return (epoch - new Date(Date.UTC(1899, 11, 30))) / (24 * 60 * 60 * 1000);
}
function sheet_from_array_of_arrays(data, opts) {
var ws = {};
var range = { s: { c: 10000000, r: 10000000 }, e: { c: 0, r: 0 } };
for (var R = 0; R != data.length; ++R) {
for (var C = 0; C != data[R].length; ++C) {
if (range.s.r > R) range.s.r = R;
if (range.s.c > C) range.s.c = C;
if (range.e.r < R) range.e.r = R;
if (range.e.c < C) range.e.c = C;
var cell = { v: data[R][C] };
if (cell.v == null) continue;
var cell_ref = XLSX.utils.encode_cell({ c: C, r: R });
if (typeof cell.v === 'number') cell.t = 'n';
else if (typeof cell.v === 'boolean') cell.t = 'b';
else if (cell.v instanceof Date) {
cell.t = 'n';
cell.z = XLSX.SSF._table[14];
cell.v = datenum(cell.v);
}
else cell.t = 's';
ws[cell_ref] = cell;
}
}
if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
return ws;
}
function Workbook() {
if (!(this instanceof Workbook)) return new Workbook();
this.SheetNames = [];
this.Sheets = {};
}
function s2ab(s) {
var buf = new ArrayBuffer(s.length);
var view = new Uint8Array(buf);
for (var i = 0; i != s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
return buf;
}
export function export_table_to_excel(id) {
var theTable = document.getElementById(id);
var oo = generateArray(theTable);
var ranges = oo[1];
/* original data */
var data = oo[0];
var ws_name = "SheetJS";
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
/* add ranges to worksheet */
// ws['!cols'] = ['apple', 'banan'];
ws['!merges'] = ranges;
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' });
saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), "test.xlsx")
}
function formatJson(jsonData) {
}
export function export_json_to_excel(th, jsonData, defaultTitle) {
/* original data */
var data = jsonData;
data.unshift(th);
var ws_name = "SheetJS";
var wb = new Workbook(), ws = sheet_from_array_of_arrays(data);
/* add worksheet to workbook */
wb.SheetNames.push(ws_name);
wb.Sheets[ws_name] = ws;
var wbout = XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' });
var title = defaultTitle || '列表'
saveAs(new Blob([s2ab(wbout)], { type: "application/octet-stream" }), title + ".xlsx")
}
使用插件xlsx-style解决导出excel文件带样式
-
安装
npm install xlsx-style --save
-
报错
This relative module was not found: * ./cptable in ./node_modules/xlsx-style/dist/cpexcel.js
找到在\node_modules\xlsx-style\dist\cpexcel.js 807行的var cpt = require('./cpt' + 'able');
更换成var cpt = cptable;
保存继续。
-
使用
import XLSXSTYLE from 'xlsx-style'
// 设置某个单元格样式
worksheet.B3.s = {
// 字体
font: {
name: '仿宋',
sz: 14,
bold: true
},
// 居中
alignment: {
horizontal: 'center',
vertical: 'center',
wrapText: true
},
// 单元格边框
border: {
top: {
style: 'thin'
},
bottom: {
style: 'thin'
},
left: {
style: 'thin'
},
right: {
style: 'thin'
}
}
}
// XLSX.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' });
// 更改为
XLSXSTYLE.write(wb, { bookType: 'xlsx', bookSST: false, type: 'binary' , cellStyles: true });
导出excel自定义公式失效
一般情况下添加 cellFormula 则可使用自定义公式,XLSX.write(workbook, {..., cellFormula: true}) ,如果版本过低或其他情况下不起作用,可找到在\node_modules\xlsx-style\xlsx.js或\node_modules\xlsx-style\xlsx.js 中 function write_ws_xml_cell() 修改
if (cell.v === undefined && cell.s === undefined && !cell.f) return "";
var o = { r: ref }, v = cell.f ? writetag('f', escapexml(cell.f)) : writetag('v', escapexml(vv));
使用ExcelJs实现Excel导入导出并带样式配置
-
安装
// 安装
npm install exceljs
// 引入使用
import ExcelJS from "exceljs";
-
基本操作
1、新建一个excel工作簿
const workbook = new ExcelJS.Workbook();
2、在工作簿中新建一张工作表
const worksheet = workbook.addWorksheet('Sheet1');
3、在工作簿中删除一张工作表
workbook.removeWorksheet(worksheet.id);
4、获取工作簿中的某一张工作表
// 遍历所有工作表
workbook.worksheets.forEach(worksheet => {
console.log(worksheet);
})
// 注意: workbook.worksheets.forEach 仍然是可以正常运行的, 但是以下的方式更好
workbook.eachSheet((worksheet, sheetId) => {
console.log(worksheet, sheetId);
});
// 按 name 提取工作表
const worksheet = workbook.getWorksheet('Sheet1');
// 按 id 提取工作表
const worksheet = workbook.getWorksheet(1);
5、获取工作表中的所有数据集
const sheetData = worksheet.getSheetValues();
6、获取工作表中某列数据
const cB = worksheet.getColumn('B');
const c1 = worksheet.getColumn(1);
// 迭代每列中单元格的数据
c1.eachCell(c => {
console.log(c.value);
});
// 指定列的全部数据
console.log(c1.values);
7、获取工作表中某行数据
const r3 = worksheet.getRow(3);
// 迭代每行中单元格的数据
r3.eachCell(r => {
console.log(r.value);
});
// 指定行的全部数据
console.log(r3.values);
8、向指定单元格写入数据
worksheet.getCell('A1').value = '插入指定值';
worksheet.getCell(3, 3).value = '根据行列插入值';
9、向指定列中写入一组数据
const c3 = worksheet.getColumn(3);
c3.values = [1, 2, 3, 4, 5, 6];
10、向指定行中写入一组数据
const r3 = worksheet.getRow(3);
r3.values = [1, 2, 3, 4, 5, 6];
11、插入行
// 按键值插入行,每次向下移动行
worksheet.insertRow(1, { id: 1, name: 'John Doe', dob: new Date(1970, 1, 1) });
// 通过连续数组插入一行(分配给列A, B, C)
worksheet.insertRow(1, [3, 'Sam', new Date()]);
// 插入多行数组,如 rows两行,将当前位置1后面的行向下移动2行
var rows = [
[5, 'Bob', new Date()],
{ id: 6, name: 'Barbara', dob: new Date() }
];
worksheet.insertRows(1, rows, 'o');
参数 | 描述 | 默认值 |
---|---|---|
位置 | 要插入的行号,从那里向下推所有行 | |
值/秒 | 新行/秒值 | |
风格 | ‘i’ 代表从上面的行继承,’i+’ 包括空单元格,’o’ 代表原始样式,’o+’ 包括空单元格,’n’ 代表无 | ‘n’ |
12、复制行
worksheet.duplicateRow(1, 2, false);
参数 | 描述 | 默认值 |
---|---|---|
开始 | 要复制的行号 | |
数量 | 您想要复制该行的次数 | 1 |
插入 | true:插入的重复新行,之前的所有行会向下推; false:复制的行数直接替换原本的行; | true |
13、合并单元格
// 合并一组单元格
worksheet.mergeCells('A4:B5');
// 从左上到右下进行合并
worksheet. mergeCells('K10', 'M12');
// 按开始行,开始列,结束行,结束列合并(相当于K10:M12)
worksheet.mergeCells(10, 11, 12, 13);
14、取消单元格合并
worksheet.unMergeCells (A4);
-
封装excel文件的导入导出
import ExcelJS from "exceljs";
import JSZipUtils from "jszip-utils";
function getZipFileBinaryContent(fileUrl) {
return new Promise((resolve) => {
JSZipUtils.getBinaryContent(fileUrl, (err, data) => {
if (err) {
// 抛出异常
throw err;
} else {
resolve(data);
}
});
});
}
function getColHeader(worksheet, opts) {
let type = opts ? opts.colHeaderType : null; // 列头类型
if (type == null) {
return null; // 无列头类型列头不设置
}
let colHeader = {};
const header = opts.header; // 表头
// 表头是指定行
if (type == 'headerRow') {
let hRow = parseInt(header);
if (isNaN(hRow) || hRow < 1) {
hRow = 1; // 默认第一行
}
colHeader = worksheet.getRow(hRow).values;
}
// 表头是数组
if (type == 'headerArray' && Array.isArray(header)) {
header.forEach((name, index) => {
colHeader[index + 1] = name;
});
}
// 表头是列属性
if (type == 'headerColType') {
const colKey = header || 'number'; // 列属性 无指定默认使用 number
const keys = opts.targetKeys; // 目标
worksheet.columns.forEach(col => {
let cn = col.number;
let ck = col[colKey];
if (ck && keys && keys[ck]) {
colHeader[cn] = keys[ck];
} else if (!keys && ck) {
colHeader[cn] = ck;
}
});
}
return colHeader
}
/**
* 导入excel表指定工作表数据
* @param {File|Blob} file 导入xlsx文件
* @param {String|Number} [sheetName=1] 工作表名/id
* @param {Object} [s2JOpts] 数据转换参数
* @param [s2JOpts.defval] 默认值
* @param [s2JOpts.colHeaderType] {'headerRow'|'headerArray'|'headerColType'} 列头类型
* @param [s2JOpts.header] {number|string|array} 表头
* @param [s2JOpts.targetKeys] {array} 列头目标对应关系,colHeaderType== 'headerColType' 时使用
* @returns
*/
export async function import_excel_to_json(file, sheetName, s2JOpts) {
const workbook = new ExcelJS.Workbook();
// 读取excel文件
await workbook.xlsx.load(file);
// 获取指定工作表,无指定默认获取第一张
if (!sheetName) sheetName = 1;
const worksheet = workbook.getWorksheet(sheetName);
// 获取工作表中的数据集(二维数组,注意下标是从 1 开始)
const sheetData = worksheet.getSheetValues();
// 设置数据默认值
if (s2JOpts && s2JOpts.defval != undefined) {
for (let i = 1; i <= worksheet.rowCount; i++) {
if (!Array.isArray(sheetData[i])) {
sheetData[i] = [];
}
for (let j = 1; j <= worksheet.columnCount; j++) {
if ([null, undefined].includes(sheetData[i][j])) {
sheetData[i][j] = s2JOpts.defval
}
}
}
}
// 自定义列头
let colHeader = getColHeader(worksheet, s2JOpts);
// 表格列头不为空需要重置工作表数据
if (colHeader) {
for (let i = 1; i <= worksheet.rowCount; i++) {
// 定义临时对象存储每一行内容
let tempObj = {};
if (Array.isArray(sheetData[i])) {
for (let n in colHeader) {
tempObj[colHeader[n]] = sheetData[i][n];
}
}
// 替换重置数据
sheetData[i] = tempObj;
}
}
return sheetData;
}
export async function import_excel_to_worksheet(file, valueType) {
const workbook = new ExcelJS.Workbook();
// 读取excel文件
await workbook.xlsx.load(file);
if (valueType == 'sheetNames') {
let names = []
workbook.eachSheet((worksheet, sheetId) => {
names.push(worksheet.name)
});
return names
}
return;
}
/**
* 导出json数据到excel表
* @param {[{header: string, key:string}]} tableHeader 表头
* @param tableHeader[].header 表头显示值 例:名称
* @param tableHeader[].key 表头key 例:name
* @param {[{string}]|[[]]} jsonArray 导出数据
* @param jsonArray[].string 导出数据 例:name: '001'
* @param {string} [fileName='导出数据'] 文件名
* @param {string} [sheetName='SheetJS'] 工作表面
*/
export async function export_json_to_excel(tableHeader, jsonArray, fileName, sheetName) {
const workbook = new ExcelJS.Workbook();
// 新建工作表
if (!sheetName) sheetName = 'SheetJS';
const worksheet = workbook.addWorksheet(sheetName);
if (!tableHeader || tableHeader.length == 0) {
tableHeader = Object.keys(jsonArray[0]).map(item => ({ header: item, key: item }));
}
// 往表中插入表头
worksheet.columns = tableHeader;
// 往表中添加行
worksheet.addRows(jsonArray);
// 重新生成 Excel 文件下载
const newFile = await workbook.xlsx.writeBuffer();
saveAs(new Blob([newFile], { type: "application/octet-stream" }), `${fileName || '导出数据'}.xlsx`);
}
/**
* 导出excel模板组合文档
* @param {string} demoUrl excle模板路径
* @param {{string: {string}}} exportData 导出数据
* @param exportData.string 导出数据-工作表名
* @param exportData.string.string 导出数据-工作表名-指定单元格值 例: A1: '测试'
* @param {string} [fileName='导出模板'] 文件名
*/
export async function export_template_to_excel(demoUrl, exportData, fileName) {
// 读取并获得模板文件的二进制内容
const fileContent = await getZipFileBinaryContent(demoUrl);
const workbook = new ExcelJS.Workbook();
// 加载 workbook 信息
await workbook.xlsx.load(fileContent);
//遍历所有工作表
workbook.eachSheet((worksheet, sheetId) => {
const sheetData = exportData[worksheet.name];
let insertList = {}; // 要插入数据集
for (let key in sheetData) {
const keyCell = worksheet.getCell(key)
let keyValue = sheetData[key];
// 先插入非数组类型(即当个单元格的值),避免插入数组类型(即在指定行插入多行)时向下推导致插入单元格的值不正确
if (!Array.isArray(keyValue)) {
// 在指定单元格插入内容
keyCell.value = resetCustomCellValue(keyValue, keyCell);
} else if (Array.isArray(keyValue) && keyValue.length > 0) {
const start = keyCell.row; // 复制开始行数
if (!Array.isArray(insertList[start])) {
// 开发复制行还未有数据集,直接赋值
insertList[start] = keyValue;
} else {
// 复制行已有数据集,获取最大数据数,逐行赋值
const maxNum = insertList[start].length > keyValue.length ? insertList[start].length : keyValue.length;
for (let i = 0; i < maxNum; i++) {
if (!insertList[start][i]) {
insertList[start][i] = keyValue[i];
} else {
for (let k in keyValue[i]) {
insertList[start][i][k] = keyValue[i][k]
}
}
}
}
}
}
/**
* 注意:使用duplicateRow、insertRows、spliceRows 方法在指定行插入多行数据,从插入行以下的合并单元格会失效,公式不会自动下移
* 解决方案:
* 获取原始合并单元格信息,把插入行下的合并单元格向下推,最后取消原本的再重新合并
* 获取原行号,插入行后使其行号向下推,再替换掉原来的
*/
// 获取所有合并单元格信息
let mergeList = new Array();
for (let key in worksheet._merges) {
const m = worksheet._merges[key];
// 按开始行,开始列,结束行,结束列 存储
mergeList.push([m.top, m.left, m.bottom, m.right]);
}
// 删除之前所有合并单元格
for (let key in worksheet._merges) {
worksheet.unMergeCells(key);
}
// 记录插入行后行号移动后值
let moveRows = { maxMoveAmount: 0 };
for (let i = 1; i <= worksheet.rowCount; i++) {
moveRows[i] = i;
}
// 开始插入数据
for (let row in insertList) {
// row 为原始行,即未插入任何数据时的行
let start = parseInt(row); // 开始复制行
for (let i in insertList) {
// 在row之上有插入数据,row开始行需要向下推
if (i < row) {
start += insertList[i].length - 1;
}
}
const amount = insertList[row].length - 1; // 复制数量
if (amount > 0) {
let newMerges = new Array();
mergeList.forEach(m => {
if (m[0] == start && m[2] == start) {
// 复制开始行内有合并单元格,所有复制行都需要加上
for (let i = 0; i < amount; i++) {
const num = start + i + 1;
newMerges.push([num, m[1], num, m[3]])
}
} else if (m[0] == start && m[2] > start) {
// 开始行是复制开始行,结束行大于复制开始行,合并单元格需要向下合并复制数量行
m[2] += amount;
} else if (m[0] > start) {
// 开始行在复制开始行之下,需要向下推移复制数量行
m[0] += amount;
m[2] += amount;
}
})
mergeList = mergeList.concat(newMerges);
// 插入复制行
worksheet.duplicateRow(start, amount, true);
moveRows.maxMoveAmount += amount;
for (let r in moveRows) {
// 把在原始行下的行号下移复制行数
if (r != 'maxMoveAmount' && r.indexOf('#') == -1 && r > row) {
moveRows[r] += amount;
}
}
}
// 插入数组数据
let kCol = {};
insertList[row].forEach((item, index) => {
// 获取插入行数
const kRow = start + index;
if (index != 0) {
moveRows[kRow + '#' + start] = kRow;
}
for (let k in item) {
if (!kCol[k]) {
kCol[k] = worksheet.getCell(k).col;
}
const kCell = worksheet.getCell(kRow, kCol[k]);
kCell.value = resetCustomCellValue(item[k], kCell);;
}
})
}
// 重置所有合并单元格
mergeList.forEach(m => {
worksheet.mergeCells(m[0], m[1], m[2], m[3]);
})
// 处理自定义公式
const sheetValues = worksheet.getSheetValues();
sheetValues.forEach((values, r) => {
if (Array.isArray(values)) {
const params = { moveRows: moveRows, curRow: r };
values.forEach((val, c) => {
if (val && val.formula) {
worksheet.getCell(r, c).value = resetCustomCellValue(val, null, 'formula', params)
}
})
}
})
});
// 重新生成 Excel 文件下载
const newFile = await workbook.xlsx.writeBuffer();
saveAs(new Blob([newFile], { type: "application/octet-stream" }), `${fileName || '导出模板'}.xlsx`);
}
/**
* 重置自定义单元格值
* @param {*} oldVal 单元格值
* @param {object} [cell] 单元格数据
* @param {string} [type] 值处理类型
* @param {object} [params] 指定值处理类型所需参数
* --- type: 'formula', params: {moveRows: 原始行移动后行数信息, curRow: 当元格所在行 }
* @returns
*/
function resetCustomCellValue(oldVal, cell, type, params) {
let newVal = oldVal;
if (type == 'formula') {
let formula = oldVal.formula;
const { moveRows, curRow } = params;
const regex = new RegExp('[A-Z]+[0-9]+', 'g');
let cellKeys = formula.match(regex);
if (Array.isArray(cellKeys) && moveRows && curRow) {
cellKeys.sort((a, b) => {
// 倒叙排列,使其单元格由后往前处理,避免出错
return b.replace(new RegExp('[A-Z]+'), '') - a.replace(new RegExp('[A-Z]+'), '');
}).forEach(k => {
let row = k.replace(new RegExp('[A-Z]+'), '');
let col = k.replace(new RegExp('[0-9]+'), '');
let newRow = moveRows[curRow + '#' + row] || moveRows[row] || moveRows.maxMoveAmount + parseInt(row);
formula = formula.replace(k, col + newRow);
})
}
newVal = { formula: formula };
} else if (cell && cell.numFmt && typeof oldVal != 'number') {
// 设置数字属性,非数字类型值需要转换
let num = parseFloat(oldVal);
if (!isNaN(num)) {
newVal = num;
}
}
return newVal;
}
使用
const { import_excel_to_json, export_json_to_excel, export_template_to_excel } = require('./Export2Excel');
或
import { import_excel_to_json, export_json_to_excel, export_template_to_excel } from '@./tableDesign';
// ===================导入=====================
// colHeaderType 未指定,不对数据重构,直接得到一个二维数组
// colHeaderType = 'headerRow' 拿指定行数据作为表头,重构对象数组
// 例:r1 = ['key1','key2'] jsonData = [{key1:'A1,key2:'B1'},{key1:'A2',key2:'B2'} ...]
let sheet2JSONOpts1 = { colHeaderType: 'headerRow', header: 1 };
// colHeaderType = 'headerArray' 拿指定表头,重构对象数组
// 例:r1 = ['id','name'] jsonData = [{id:'A1,name:'B1'},{id:'A2',name:'B2'} ...]
let sheet2JSONOpts2 = { colHeaderType: 'headerArray', header: ['id', 'name'] };
// colHeaderType = 'headerColType' 拿列指定属性值作为表头,重构对象数组
// 例:未设置targetKeys jsonData = [{A:'A1,B:'B1'},{A:'A2',B:'B2'} ...]
// 例:targetKeys = { A: 'id', B: 'name' } jsonData = [{id:'A1,name:'B1'},{id:'A2',name:'B2'} ...]
let sheet2JSONOpts3 = { colHeaderType: 'headerColType', header: 'letter', targetKeys: { A: 'id', B: 'name' } };
const jsonData = await import_excel_to_json(file, 1, sheet2JSONOpts3);
// ===================导出=====================
let tHeader = [
{ header: '流程名称', key: 'flowName' },
{ header: '流程分类', key: 'catename' },
];
let dataList = [
{ flowName: '报销申请', catename: '财务' },
{ flowName: '财务管理', catename: '财务' },
]
export_json_to_excel(tHeader, dataList);
// ===================导出模板=====================
const fileUrl = 'xlsxtemplate.xlsx';
const exportData = {
Sheet1: {
A1: '测试xlsx模板标题',
B3: '姓名',
D4: 34,
A6: [
{ A6: 1, B6: 2, C6: 3, D6: 5, E6: 9 },
{ A6: 2, B6: 2, C6: 3, D6: 5, E6: 9 },
{ A6: 3, B6: 2, C6: 3, D6: 5, E6: 9 },
{ A6: 4, B6: 2, C6: 3, D6: 5, E6: 9 }
]
},
Sheet2: {
A1: '测试xlsx模板标题11111'
}
}
export_template_to_excel(fileUrl, exportData);