Node.js处理学生EXCEL周报

247 阅读4分钟

图片.png

周报位于EXCEL中,不便于阅读。使用Node.js的exceljs模块和自定义 my.js模块处理。形成以个人为单位的周报,放入记事本中。

图片.png

image.png

一、基础调用

引入自定义模块my.jsexceljs 。读取工作簿、工作表。

//1.js
const my        = require('./my.js')   //引入自定义模块
const excelJS   = require('exceljs')   //引入第三方模块

let 工具 = new my()

读取EXCEL();

async function 读取EXCEL(){
    const 工作簿  = new excelJS.Workbook() //创建一个空的工作簿
    await 工作簿.xlsx.readFile('蔬菜周报1020.xlsx')   //读取EXCEL文件
    const 工作表  =  工作簿.worksheets[0]  //读取第一个工作表

    //使用工具类
    let arr =  工具.to_arr(工作表)
    console.log( arr.slice(0,3) )   //只看前两个 
    

}

二、进阶调用

自定义姓名 姓名分组(arr) 函数。

//新建2.js文件,将以下代码放进去 
const my = require('./my.js')   //引入自定义模块
const excelJS = require('exceljs')   //引入第三方模块

const fsPro = require('fs/promises')

let 工具 = new my()

读取EXCEL();    //工作簿 — 工作表 — 单元格
async function 读取EXCEL() {
    const 工作簿 = new excelJS.Workbook() //创建一个空的工作簿
    await 工作簿.xlsx.readFile('蔬菜周报1020.xlsx') //读取EXCEL

    const 工作表 = 工作簿.worksheets[0]  //读取第一个 工作表

    //使用 工具.方法()
    let arr = 工具.to_arr(工作表)    //以数组形式输出
    //console.log( arr )  //二维数组

    // 修改以下代码 只显示  数组的前三行 

    let data = 姓名分组(arr)

    for( let name in  data){

        let 周报 = data[name]
        let str = ''
        for(let w  of  周报){
            str += `
标题:${w.title}
内容:${w.text}
时间:${w.date}
            `;
        }//end for

        let path = `./周报/${name}.txt`
        await fsPro.writeFile(path,str)
        console.log(`生成 ${path}  成功 `)
        
    }



}//end 读取EXCEL




function 姓名分组(arr) {
    // 创建一个空对象用于存储分组后的数据
    const newData = {};

    // 遍历原始数据数组
    arr.forEach(item => {
        const alias = item[0]; // 获取当前条目的别名
        if (!newData[alias]) {
            // 如果该别名还未存在于newData中,则创建一个新的数组
            newData[alias] = [];
        }
        // 将当前条目添加到对应别名的数组中
        let one = {
            title: item[1],
            text: item[2],
            date: item[3]
        }
        newData[alias].push(one);

        
    });

    return newData;
}//end 姓名分组(arr)

这段代码定义了一个名为 姓名分组 的函数,它的作用是将一个包含多个条目的数组按照每个条目中的第一个元素(别名)进行分组。分组的结果是一个对象,其中每个键都是一个别名,而对应的值是一个包含所有具有相同别名的条目的数组。 设计的数据结构示例。


[
    '莲藕': [{},{},{},{}],
    '大蒜': [
    {
      title: '周报',
      text: '来这里一个月,从陌生到渐渐熟悉,这一步步的走来,说实话,确实不容易。实习可以让你学到学校学不到的实际操作经验。实习是学生从学生身份向职业者转变的一个过程,是一个人迈向社会的 很重要的一步。通过这次生产实习,我学到了很多书本上学不到的东西,也有了从无知到认知,到深入了 解公司和社会,开始的磕磕碰碰到后来的工作还算顺利的转变。',
      date: '2024-10-20 17:01:20'
    },
    {
      title: '周报',
      text: '这周在工作的时候 帮别人打印合同 可是他们很急 一直在旁边催我 导致我就很紧张 就把 内容给搞错了 当时没有发现 等到他们走了 我自己仔细看了一遍 才发现有错误 然后就只能和别人道歉  让别人把之前的签错的合同带过来 重新签对的合同 \n' +
        '不管别人怎么催 自己都不能乱',
      date: '2024-10-13 17:41:47'
    },
    {
      title: '周记',
      text: '又是平平无奇的一周 没有什么事情发生 感觉自己每天都跟机械 重复性的在做一些事情 很容易就会做的很烦躁 很不耐烦 但是也没办法 只能继续做\n' +
        '每天唯一会思考的事情就是吃什么 下班了就赶紧溜\n' +
        '感觉什么事都要来找我  就每天期盼着下班\n' +
        '\n',
      date: '2024-10-06 15:23:38'
    },
    {
      title: '周记',
      text: '这一周的实习生活比较顺利 没有什么大的事情发生 一切都在顺利的运行当中 我也在逐步 的上手中 和同事相处的也还不错 但是同一份工作做久了之后会显得有些许枯燥无聊 忙的时候很忙 无聊 的时候也很无聊 但是还是要坚持的下去 不能半途而废 ',
      date: '2024-09-29 18:53:18'
    },
    {
      title: '周报',
      text: '逐渐适应工作中 就是在面对他人的质疑的时候 还是会有点胆怯 会紧张 从而导致思考能力下降 给不出一个应对的方法\n' +
        '有时候有的客人突然就过来非常生气的指责你的问题 这个时候我就会变得特别懵 给不了他一个合理的解决方案 还是要调整自己 不要慌 要勇于面对',
      date: '2024-09-22 15:23:13'
    },
    {
      title: '周记',
      text: '在工作中遇到问题会很紧张 有时会不知所措  对于新的环境紧张又兴奋 在逐渐的适应工作环境  在人际交往方面需要更加的主动 \n' +
        '再遇到问题的时候要冷静下来 不要过于慌乱 因为是刚开始 所以会显得工作内容会有一些的枯 燥 但我也会尽快的融入 提升自己',
      date: '2024-09-15 12:37:38'
    },
    {
      title: '周记',
      text: '目前是还在找工作的状态当中 从上一份工作离职了 内心还是很纠结的状态 上一份工作也 收获了许多 特别是人际沟通方面 遇见各色各样的人 各种的事情 现在害怕自己找不到工作 放平心态 慢 慢来 过犹不及 希望自己能顺利找到工作 在试错中成长',
      date: '2024-09-08 12:37:11'
    }
  ]aa

三、进阶补充

增加自定义函数插入换行,方便阅读效果。

image.png

对应的代码,定义了一个名为 插入换行 的函数,它的作用是根据指定的最大长度 maxLength 来在字符串 str 中适当位置插入换行符 \n。这样可以确保每一行的字符数不会超过给定的最大长度,同时考虑到中文字符和英文字符(以及其他非中文字符)占用的空间不同:中文字符算作一个单位长度,而英文字符和其他字符算作半个单位长度。

image.png

//新建2.js文件,将以下代码放进去 
const my = require('./my.js')   //引入自定义模块
const excelJS = require('exceljs')   //引入第三方模块

const fsPro = require('fs/promises')

let 工具 = new my()

读取EXCEL();    //工作簿 — 工作表 — 单元格
async function 读取EXCEL() {
    const 工作簿 = new excelJS.Workbook() //创建一个空的工作簿
    await 工作簿.xlsx.readFile('学生周报1020.xlsx') //读取EXCEL

    const 工作表 = 工作簿.worksheets[0]  //读取第一个 工作表

    //使用 工具.方法()
    let arr = 工具.to_arr(工作表)    //以数组形式输出
    //console.log( arr )  //二维数组

    // 修改以下代码 只显示  数组的前三行 +

    let brr = 归类JSON(arr.slice(1))
    console.log(brr[1])

    for( let one of  brr){

        let wArr = one.data;
        let str = ''
        let n = 0 ;
        for(let w  of  wArr){
            w.text = 插入换行(w.text, 20)
            str += `
                标题:${w.title}
                    内容:${w.text}
                时间:${w.date}
            `;
            n++;
        }//end for
        str = ` ${one.name} 一共有 ${n} 篇周报。\r\n ${str} `
        let path = `./周报/${one.num.slice(-2)}_${one.name}.txt`
        await fsPro.writeFile(path,str)
        console.log(`生成 ${path}  成功 `)
        
    }//for 

}//end 读取EXCEL


function 归类JSON(arr) {
    // 创建一个空对象用于存储分组后的数据
    // brr = [  { name:'A',num:123 ,data:[]}  ];
    const brr = [];
    
    // 遍历原始数据数组
    arr.forEach(row => {
        const 姓名 = row[0]; // 获取当前条目的别名
        const 学号 = row[1]

        let 行 = brr.find(item => item.name === 姓名) 

        if ( !行 ) {    //不存在 对应名字

            let one = {
                name:姓名,
                num:学号,
                data:[{
                    title:  row[2],
                    text:   row[3],
                    date:   row[4],
                }]
            }
            brr.push(one)

        }else{
            let one = {
                title:  row[2],
                text:   row[3],
                date:   row[4],
            }
            行.data.push(one)
        }

    });

    return brr;
}//end 姓名分组(arr)



function 插入换行(str, maxLength) {
    let result = '';
    let currentLength = 0;
  
    // 按字符分割字符串
    const chars = str.split('');
  
    for (let char of chars) {
      // 计算当前字符的长度,中文字符长度为1,其他字符长度为0.5
      const charLength = /[\u4e00-\u9fa5]/.test(char) ? 1 : 0.5;
  
      // 更新当前行的长度
      currentLength += charLength;
  
      // 如果超过了最大长度,插入换行符并重置当前长度
      if (currentLength >= maxLength) {
        result += '\n';
        currentLength = 0;
      }
  
      // 添加字符到结果字符串
      result += char;
    }
  
    return result;
}
  
  

附件

1. my.js文件

这段代码定义了一个名为 my 的类,通过一系列方法来操作 Excel 文件,这些方法简化了从工作表中读取数据、将数据转换为数组或对象、生成列名数组、随机打乱数组元素以及设置特定单元格的样式和内容。

  • to_arr 方法:遍历工作表中的每一行和每个单元格,将它们的值收集到一个二维数组中,每一行的数据构成数组的一个元素。这使得可以方便地以编程方式处理表格数据。
  • to_cellObj 方法:同样遍历所有行和单元格,但这次是创建一个对象,其中键是由列字母和行号组成的字符串(如 "A1"),值是相应单元格的内容。这种方式有助于通过坐标快速查找特定单元格的值。
  • getColArr 方法:根据给定的数量生成一串列名,首先使用 A 到 Z,如果需要更多,则组合两个字母形成新的列名(如 AA, AB)。这对于处理具有大量列的工作表非常有用。
  • 打乱 方法:实现了一个简单的 Fisher-Yates 洗牌算法,用于随机打乱数组元素的顺序,常用于需要随机化数据的场景。
class my{

    to_arr(工作表) {  
        let arr = [] 
        工作表.eachRow((row, rowIndex) => {  
    
            let rowArr = [] 
            row.eachCell((cell, colIndex) => {  
                rowArr.push(cell.value) 
            }) 
            arr.push(rowArr) 
    
        }) 
        return arr 
    }

    to_cellObj(工作表){
        let OBJ = {}
        const colArr = ['占位','A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z']
    
        工作表.eachRow((row,rowIndex) => {
    
            row.eachCell((cell, colIndex) => {
                
                let 列名 = colArr[colIndex]
                OBJ[列名+rowIndex] = cell.value
            })
            
        })
        return OBJ
    }

    getColArr(n){
        let arr = []
        const ABC = [   //26个字母
            'A','B','C','D','E','F','G','H','I','J',
            'K','L','M','N','O','P','Q','R','S','T',
            'U','V','W','X','Y','Z'
        ]
    
        let tempArr = []
        for(let k = 0;k< n-26 ;k++){
            let i = parseInt( k/26 ) 
            let j = k%26
            let 值 = `${ABC[i]}${ABC[j]}`
            tempArr.push(值)
        }
    
        arr.unshift = ['占位']
        arr = arr.concat(ABC,tempArr)
        return arr;
    }

    
    打乱(array) {  
        let currentIndex = array.length, temporaryValue, randomIndex;  
      
        // 当还剩有元素未洗牌时  
        while (0 !== currentIndex) {  
            // 选取剩下的元素…  
            randomIndex = Math.floor(Math.random() * currentIndex);  
            currentIndex -= 1;  
      
            // 并与当前元素交换  
            temporaryValue = array[currentIndex];  
            array[currentIndex] = array[randomIndex];  
            array[randomIndex] = temporaryValue;  
        }  
      
        return array;  
    }  

    设格子(工作表,位置='A1',值='加油',color='FF0000',fgColor='FFFF00'   ){

        const 格子  = 工作表.getCell(位置)
        格子.value =  值
        格子.style = {       //红色字体 50大小  黄色背景
            font:{
                size: 50, 
                color: { argb: 'FF'+color },
            },
            fill: {             //填充  背景颜色
                type: 'pattern',
                pattern: 'solid',
                fgColor: { argb: 'FF'+fgColor },
                bgColor: { indexed: 64 }
            }
        }
    }

}


module.exports = my