——————————☆☆☆——————————
Node 系列相应地址:
- 代码仓库:https://github.com/LiangJunro...
- 文章仓库:https://github.com/LiangJunro...系列-前端资料/Node
——————————☆☆☆——————————
在通过 Puppeteer 操作浏览器下载到 Excel 之后,我们终于可以将预备将多语言的操作玩出花来了。
本篇我们将通过 node-xlsx
,对 Excel 进行多语言导入导出的操作。
一 前言
在服务端的工作中,生成报表并送给运营、产品进行分析应该是一门简单手艺。
但是在前端中,能这样耍的机会并不多,所以多语言操作是个好玩的点(没接触过的会觉得比较新鲜)。
当然,既然服务端可以,对 Node.js 来说,提供这种功能也无可厚非。
jsliang 非常懒,所以直奔主题打开 GitHub:
那就第 1 个了,不要搞什么调研不调研的,对于非生产数据来说,我就是玩~
看第一行简介:Excel file parser/builder that relies on js-xlsx.
js-xlsx
?这个我知道啊,在 2021.06.03
这一刻有 25.7k
Star 的仓库地址:https://github.com/SheetJS/sheetjs
其实一开始试了下它关于 Node 的,enm...一时半会没入门!
但是,我还是用我的 node-xlsx
吧,毕竟例子都在它仓库的 README.md 贴出来了!
二 快速开始
- 安装包:
npm i node-xlsx -S
- 安装 TypeScript:
npm i @types/node-xlsx -D
2.1 测试导入
src/index.ts
import program from 'commander';
import common from './common';
import './base/console';
import xlsx from 'node-xlsx';
import fs from 'fs';
program
.version('0.0.1')
.description('工具库')
program
.command('jsliang')
.description('jsliang 帮助指令')
.action(() => {
common();
});
program
.command('test')
.description('测试频道')
.action(async () => {
// 测试新功能的时候使用
// 以 buffer 形式导入
const workSheetsFromBuffer = xlsx.parse(fs.readFileSync(`${__dirname}/common/dist/Excel 试用文件.xlsx`));
console.log(JSON.stringify(workSheetsFromBuffer, null, 2));
// 以文件形式导入
const workSheetsFromFile = xlsx.parse(`${__dirname}/common/dist/Excel 试用文件.xlsx`);
console.log(JSON.stringify(workSheetsFromFile, null, 2));
});
program.parse(process.argv);
执行 npm run test
,控制台打印如下:
---1---
[
{
"name": "Sheet1",
"data": [
[
"key",
"zh-CN",
"en-US",
"zh-TW",
"zh-GZ"
],
[
"noMoney",
"我没钱啦!",
"I have no money",
"我沒錢啦!",
"我冇钱啦!"
]
]
}
]
---2---
[
{
"name": "Sheet1",
"data": [
[
"key",
"zh-CN",
"en-US",
"zh-TW",
"zh-GZ"
],
[
"noMoney",
"我没钱啦!",
"I have no money",
"我沒錢啦!",
"我冇钱啦!"
]
]
}
]
OK,都能正常导入~
2.2 测试导出
import program from 'commander';
import common from './common';
import './base/console';
import xlsx from 'node-xlsx';
import fs from 'fs';
program
.version('0.0.1')
.description('工具库')
program
.command('jsliang')
.description('jsliang 帮助指令')
.action(() => {
common();
});
program
.command('test')
.description('测试频道')
.action(async () => {
// 测试新功能的时候使用
// 导出数据
const data = [
[1, 2, 3],
[true, false, null, 'sheetjs'],
['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'],
['baz', null, 'qux'],
];
const buffer = xlsx.build([{ name: "jsliang", data: data }]); // 拿到文件 buffer
// 写入文件
fs.writeFileSync(`${__dirname}/common/dist/test-sheet.xlsx`, Buffer.from(buffer));
});
program.parse(process.argv);
执行 npm run test
后,目录变成:
打开这个 Excel 文件,可以看到:
好的,导出也 OK 了~
2.3 测试定制宽度
当然,有时候产品非常懒,需要我们将表格宽度给做好成每一列都能宽一点,那就要定制下页面宽度:
index.ts
import program from 'commander';
import common from './common';
import './base/console';
import xlsx from 'node-xlsx';
import fs from 'fs';
program
.version('0.0.1')
.description('工具库')
program
.command('jsliang')
.description('jsliang 帮助指令')
.action(() => {
common();
});
program
.command('test')
.description('测试频道')
.action(async () => {
// 测试新功能的时候使用
// 导出数据
const data = [
['key', 'zh-CN', 'en-US', 'zh-TW', 'zh-GZ'],
['noMoney', '我没钱啦!', 'I have no money', '我沒錢啦!', '我冇钱啦!'],
];
// 列宽设置
const options = {
'!cols': [
{ wch: 10 },
{ wch: 15 },
{ wch: 15 },
{ wch: 15 },
{ wch: 15 },
]
}
// 生成 buffer
const buffer = xlsx.build([{ name: "jsliang", data: data }], options); // 拿到文件 buffer
// 写入文件
fs.writeFileSync(`${__dirname}/common/dist/Excel 导出文件.xlsx`, Buffer.from(buffer));
});
program.parse(process.argv);
执行 npm run test
,看到 dist
目录生成:
然后点开「Excel 导出文件.xlsx」,里面内容为:
安逸,满屏飘满 no money~
三 多语言操作
在我们简单了解 node-xlsx
之后,我们就可以通过它完成多语言的导入导出,以及下一章会讲解如何获取需要的资源。
3.1 导入
接「006 - Puppeteer」,我们在上一篇文章已经完成了资源的下载,实际上我们应该一条龙服务,从下载到导入统统给安排了。
那么,我们当前的目录需要改造一番:
- src
+ base
- common
- language
+ dist
- download.ts
- export.ts
- import.ts
- source.json
- index.ts
- questionList.ts
- sortCatalog.ts
- index.ts
文字目录好像没那么清晰,还是贴个图吧:
那么,开始写代码:
questionList.ts - 先明确自己的提问路线
// common 板块的问题咨询路线
export const questionList = {
'公共服务': { // q0
'文件排序': { // q1
'需要排序的文件夹': 'Work 工作', // q2
},
},
'多语言': { // q0
'下载多语言资源': { // q3
'下载地址': 'Work 工作', // q4
},
'导入多语言资源': { // q3
'下载地址': 'Work 工作', // q4
},
'导出多语言资源': { // q3
'导出全量资源': 'Work 工作',
'导出单门资源': 'Work 工作',
}
},
};
index.ts
import { inquirer } from '../base/inquirer';
import { Result } from '../base/interface';
import { sortCatalog } from './sortCatalog';
import { downLoadExcel } from './language/download';
import { importLanguage } from './language/import';
import { exportLanguage } from './language/export';
// 问题记录器
const answers = {
q0: '',
q1: '',
q2: '',
q3: '',
q4: '',
};
const common = (): void => {
// 问题路线:看 questionList.ts
const questionList = [
// q0
{
type: 'list',
message: '请问需要什么服务?',
choices: ['公共服务', '多语言']
},
// q1
{
type: 'list',
message: '当前公共服务有:',
choices: ['文件排序']
},
// q2
{
type: 'input',
message: '需要排序的文件夹为?(绝对路径)',
},
// q3
{
type: 'list',
message: '请问多语言需要什么支持?',
choices: [
'下载多语言资源',
'导入多语言资源',
'导出多语言资源',
],
},
// q4
{
type: 'input',
message: '资源下载地址(HTTP)?',
default: 'https://www.kdocs.cn/l/sdwvJUKBzkK2',
}
];
const answerList = [
// q0 - 请问需要什么服务?
async (result: Result, questions: any) => {
answers.q0 = result.answer;
switch (result.answer) {
case '公共服务':
questions[1]();
break;
case '多语言':
questions[3]();
break;
default: break;
}
},
// q1 - 当前公共服务有:
async (result: Result, questions: any) => {
answers.q1 = result.answer;
if (result.answer === '文件排序') {
questions[2]();
}
},
// q2 - 需要排序的文件夹为?(绝对路径)
async (result: Result, _questions: any, prompts: any) => {
answers.q2 = result.answer;
const sortResult = await sortCatalog(result.answer);
if (sortResult) {
console.log('排序成功!');
prompts.complete();
}
},
// q3 - 请问多语言需要什么支持?
async (result: Result, questions: any, prompts: any) => {
answers.q3 = result.answer;
switch (result.answer) {
case '下载多语言资源':
case '导入多语言资源':
questions[4]();
break;
case '导出多语言资源':
const exportResult = await exportLanguage();
if (exportResult) {
console.log('导出成功!');
prompts.complete();
}
default: break;
}
},
// q4 - 资源下载地址(HTTP)?
async (result: Result) => {
answers.q4 = result.answer;
const download = async (): Promise<any> => {
const downloadResult = await downLoadExcel(result.answer);
if (downloadResult) {
console.log('下载成功!');
return true;
}
};
switch (answers.q3) {
case '下载多语言资源':
await download();
break;
case '导入多语言资源':
await download();
const importResult = await importLanguage();
if (importResult) {
console.log('导入完毕!');
}
default:
break;
}
},
];
inquirer(questionList, answerList);
};
export default common;
需要注意的是,我们如果要导入的话,肯定有个对应的资源文件,这边就用 source.json
演示:
source.json
{
"zh-CN": {
},
"en-US": {
},
"zh-TW": {
},
"zh-GZ": {
}
}
简版内容如下,通过 import.ts
导入资源并填充里面内容:
import.ts
import xlsx from 'node-xlsx';
import fs from 'fs';
import path from 'path';
export const importLanguage = async (): Promise<boolean> => {
const language = JSON.parse(fs.readFileSync(path.join(__dirname, './source.json'), 'utf8'));
const workSheetsFromBuffer = xlsx.parse(
fs.readFileSync(
path.join(__dirname, '/dist/Excel 试用文件.xlsx'),
),
);
const sheet1Data = workSheetsFromBuffer[0].data.map(i => i.map(j => String(j)));
// 获取头部数据
const header = sheet1Data[0];
// 查找 key 对应列
let keyIndex = 0;
for (let i = 0; i < header.length; i++) {
if (header[i] === 'key') {
keyIndex = i;
break;
}
}
if (keyIndex < 0) {
console.error('未找到 key 对应列!');
return false;
}
// 设置资源内容
const fullLanguage: any[] = [...Object.keys(language), ...header.filter((item: any) => item !== 'key')];
const filterFullLanguage = new Set();
for (let i = 0; i < fullLanguage.length; i++) {
if (!filterFullLanguage.has(fullLanguage[i])) {
filterFullLanguage.add(fullLanguage[i]);
// 如果没有该种语言,则新增
if (!language[fullLanguage[i]]) {
language[fullLanguage[i]] = {};
}
}
}
// 获取内容数据
const body = sheet1Data.slice(1);
for (let i = 0; i < body.length; i++) {
for (let j = 0; j < body[i].length; j++) {
if (j !== keyIndex) {
const nowLanguage = language[header[j]]; // 一个损耗性能的操作,每次都会读取新列表,但是我不想优化
const nowKey = body[i][keyIndex]; // 获取这一行的 key
nowLanguage[nowKey] = body[i][j]; // 替换 key
}
}
}
fs.writeFileSync(path.join(__dirname, './source.json'), JSON.stringify(language, null, 2), 'utf8');
return true;
};
export.ts
export const exportLanguage = async (): Promise<boolean> => {
// 详细内容待补充
return await true;
};
编写完毕,执行 npm run jsliang
,按照提问逐个回车:
然后代码跑起来(姿势很帅),成功导入:
这样导入流程就完毕了。
当然,导入的过程中,还需要修复对齐 key(即某中文 key 情况下,其他资源未翻译;或者删除 key 资源),这些就不哆嗦列举了,需要的时候补充写一写,也不难~
3.2 导出
导入尚且如此,导出就更轻松了:
export.ts
import xlsx from 'node-xlsx';
import fs from 'fs';
import path from 'path';
export const exportLanguage = async (): Promise<boolean> => {
const languageData = JSON.parse(fs.readFileSync(path.join(__dirname, './source.json'), 'utf8'));
// 组装头部数据
const header = Object.keys(languageData);
// 组装内容数据
const chineseKeyList = Object.keys(languageData['zh-CN']);
const body: any[] = [];
for (let i = 0; i < chineseKeyList.length; i++) {
const nowKey = chineseKeyList[i];
const nowFloor = [nowKey];
console.log(nowFloor, nowKey);
for (let j = 0; j < header.length; j++) {
const nowLanguage = header[j];
nowFloor.push(languageData[nowLanguage][nowKey]);
}
body.push(nowFloor);
}
// 导出数据
const data = [
['keys', ...header],
...body,
];
const buffer = xlsx.build([{ name: "jsliang", data: data }]); // 拿到文件 buffer
// 写入文件
fs.writeFileSync(path.join(__dirname, './dist/Excel 导出文件.xlsx'), Buffer.from(buffer));
return await true;
};
执行 npm run jsliang
,按流程点点:
然后就看下 dist
目录有没有对应的文件:
在打开文件看看:
OK,搞定,收工~
四 后续
那么,Excel 的操作流程我们就安排得明明白白了。
再往下一章,jsliang 可能开启 Node 服务,完成简单网站的搭建,不过 jsliang 于 2018 年写过一篇 Node 从 0 基础到企业官网的文章了,所以咱们尝试搞个小游戏吧,嘿嘿~
当前阶段算上 Node 初篇完毕,主要也没啥内容,后续会补充开启服务,WebSocket 等内容,冲鸭~
五 参考文献
jsliang 的文档库由 梁峻荣 采用 知识共享 署名-非商业性使用-相同方式共享 4.0 国际 许可协议 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/>本许可协议授权之外的使用权限可以从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处获得。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。