最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • Node 系列 - 007 - node-xlsx

    正文概述 掘金(jsliang)   2021-07-13   442

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

    Node 系列相应地址:

    • 代码仓库:github.com/LiangJunron…
    • 文章仓库:github.com/LiangJunron…

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

    在通过 Puppeteer 操作浏览器下载到 Excel 之后,我们终于可以将预备将多语言的操作玩出花来了。

    本篇我们将通过 node-xlsx,对 Excel 进行多语言导入导出的操作。

    一 目录

    不折腾的前端,和咸鱼有什么区别

    目录
    一 目录二 前言三 快速开始 3.1 测试导入 3.2 测试导出 3.3 测试定制宽度四 多语言操作 4.1 导入 4.2 导出五 后续六 参考文献

    二 前言

    在服务端的工作中,生成报表并送给运营、产品进行分析应该是一门简单手艺。

    但是在前端中,能这样耍的机会并不多,所以多语言操作是个好玩的点(没接触过的会觉得比较新鲜)。

    当然,既然服务端可以,对 Node.js 来说,提供这种功能也无可厚非。

    jsliang 非常懒,所以直奔主题打开 GitHub:

    Node 系列 - 007 - node-xlsx

    那就第 1 个了,不要搞什么调研不调研的,对于非生产数据来说,我就是玩~

    看第一行简介:Excel file parser/builder that relies on js-xlsx.

    js-xlsx?这个我知道啊,在 2021.06.03 这一刻有 25.7k Star 的仓库地址:https://github.com/SheetJS/sheetjs

    但是,我还是用我的 node-xlsx 吧,毕竟例子都在它仓库的 README.md 贴出来了!

    三 快速开始

    • 安装包:npm i node-xlsx -S
    • 安装 TypeScript:npm i @types/node-xlsx -D

    3.1 测试导入

    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,都能正常导入~

    3.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 后,目录变成:

    Node 系列 - 007 - node-xlsx

    打开这个 Excel 文件,可以看到:

    Node 系列 - 007 - node-xlsx

    好的,导出也 OK 了~

    3.3 测试定制宽度

    当然,有时候产品非常懒,需要我们将表格宽度给做好成每一列都能宽一点,那就要定制下页面宽度:

    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 目录生成:

    Node 系列 - 007 - node-xlsx

    然后点开「Excel 导出文件.xlsx」,里面内容为:

    Node 系列 - 007 - node-xlsx

    安逸,满屏飘满 no money~

    四 多语言操作

    在我们简单了解 node-xlsx 之后,我们就可以通过它完成多语言的导入导出,以及下一章会讲解如何获取需要的资源。

    4.1 导入

    接「006 - Puppeteer」,我们在上一篇文章已经完成了资源的下载,实际上我们应该一条龙服务,从下载到导入统统给安排了。

    那么,我们当前的目录需要改造一番:

    - src
      + base
      - common
        - language
          + dist
          - download.ts
          - export.ts
          - import.ts
          - source.json
        - index.ts
        - questionList.ts
        - sortCatalog.ts
      - index.ts
    

    文字目录好像没那么清晰,还是贴个图吧:

    Node 系列 - 007 - node-xlsx

    那么,开始写代码:

    // common 板块的问题咨询路线
    export const questionList = {
      '公共服务': { // q0
        '文件排序': { // q1
          '需要排序的文件夹': 'Work 工作', // q2
        },
      },
      '多语言': { // q0
        '下载多语言资源': { // q3
          '下载地址': 'Work 工作', // q4
        },
        '导入多语言资源': { // q3
          '下载地址': 'Work 工作', // q4
        },
        '导出多语言资源': { // q3
          '导出全量资源': 'Work 工作',
          '导出单门资源': 'Work 工作',
        }
      },
    };
    
    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 演示:

    {
      "zh-CN": {
    
      },
      "en-US": {
    
      },
      "zh-TW": {
    
      },
      "zh-GZ": {
    
      }
    }
    

    简版内容如下,通过 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 const exportLanguage = async (): Promise<boolean> => {
      // 详细内容待补充
      return await true;
    };
    

    编写完毕,执行 npm run jsliang,按照提问逐个回车:

    Node 系列 - 007 - node-xlsx

    然后代码跑起来(姿势很帅),成功导入:

    Node 系列 - 007 - node-xlsx

    Node 系列 - 007 - node-xlsx

    这样导入流程就完毕了。

    4.2 导出

    导入尚且如此,导出就更轻松了:

    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,按流程点点:

    Node 系列 - 007 - node-xlsx

    然后就看下 dist 目录有没有对应的文件:

    Node 系列 - 007 - node-xlsx

    在打开文件看看:

    Node 系列 - 007 - node-xlsx

    OK,搞定,收工~

    五 后续

    那么,Excel 的操作流程我们就安排得明明白白了。

    再往下一章,jsliang 可能开启 Node 服务,完成简单网站的搭建,不过 jsliang 于 2018 年写过一篇 Node 从 0 基础到企业官网的文章了,所以咱们尝试搞个小游戏吧,嘿嘿~

    六 参考文献

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


    起源地下载网 » Node 系列 - 007 - node-xlsx

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元