——————————☆☆☆——————————

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 等内容,冲鸭~

五 参考文献

  • nodejs 实现导出 excel 报表
  • GitHub:SheetJS
  • GitHub:node-xlsx

jsliang 的文档库由 梁峻荣 采纳 常识共享 署名-非商业性应用-雷同形式共享 4.0 国内 许可协定 进行许可。<br/>基于 https://github.com/LiangJunrong/document-library 上的作品创作。<br/>本许可协定受权之外的应用权限能够从 https://creativecommons.org/licenses/by-nc-sa/2.5/cn/ 处取得。