目标
<input type="file">触发change事件。- 在监听器用 xlsx 把工作表当成 键值对数组 读取。
- 转成期望格式。
xlsx@0.18.5
Excel 工作表预览
读取
成品
键为姓名,值为年龄。
思路
- 得到行号数组,从首行行号(不算表头)到末行行号。
- 行号数组转换
- 键值对数组
[[sheet1.A1.v, sheet1.B1.v], [sheet1.A2.v, sheet1.B2.v] ...] Record<Name, Age>或{ name: string; age: number; }[]
- 键值对数组
行号数组
思路
- 末行行号
Sheet1['!ref']- 取末行行号字符串
- 转
Number
R.Range(start, end)
末行行号
const RowCount = R.compose( Number, LastRowNum, R.prop('!ref') );
取末行行号字符串
// 方案一 `R.slice()`
const LastRowNum = R.converge(
R.slice(R.__, Number.POSITIVE_INFINITY, R.__),
[
R.compose( R.add(2), R.indexOf(':') ),
R.identity
]
);
// 方案二 `R.match()`
const LastRowNum = R.compose( R.prop(1), R.match(/A\d+:B(\d+)/) );
行号数组转对象
转键值对数组
const PairOf = sheet1 => rowNum => R.map(
R.compose(
R.path(R.__, sheet1),
R.pair(R.__, 'v'),
R.concat(R.__, String(rowNum))
),
['A', 'B']
);
export const Pairs = R.converge(R.map, [
PairOf,
R.compose( R.range(2), R.inc, RowCount )
]);
设 rowNum = 2,转换流程如下:
-
R.concat(R.__, String(rowNum))['A', 'B']->['A2', 'B2']工作表单元格位置。 -
R.pair(R.__, 'v')[['A2', 'v'], ['B2', v]]sheet1 读属性路径。 -
R.path(R.__, sheet1)行号对应的键值对。
转对象
R.fromPairs() 可以当成 Object.fromEntries()。
R.compose( R.fromPairs, Pairs )(sheet1);
监听器
import { read } from 'xlsx/xlsx.mjs';
import { Pairs } from '@/util/xls.js';
async function handleChange(event) {
const elem = event.currentTarget;
const dic = await new Promise(resolve => {
const fr = new FileReader();
fr.readAsArrayBuffer(elem.files[0]);
fr.addEventListener('load',
R.compose(
resolve,
R.fromPairs,
Pairs,
R.path(['Sheets', 'Sheet1']),
read,
R.path(['target', 'result'])
)
);
});
elem.value = '';
}
行号数组转对象数组
只需换个函数,各键值对传入 R.zipObj()。
R.zipObj(['name', 'age'], ['赵志刚', 20]);
// {"age": 20, "name": "赵志刚"}
fr.addEventListener('load',
R.compose(
resolve,
- R.fromPairs,
+ R.map(R.zipObj(['name', 'age'])),
Pairs,
R.path(['Sheets', 'Sheet1']),
read,
R.path(['target', 'result'])
)
);