合并
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
const XLSX = require('xlsx');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
/**
|
||||
* 合并「团队开票申请表」脚本
|
||||
*
|
||||
* 目录结构假设:
|
||||
* <根目录>/
|
||||
* 7.xxx/
|
||||
* *团队开票申请表*.xlsx (含4个sheet,其中一个含"商旅",一个含"吉智")
|
||||
* 其它两个表格...
|
||||
* 7.yyy/
|
||||
* ...
|
||||
*
|
||||
* 用法:
|
||||
* node merge_kaipiao.js <根目录> [输出文件路径]
|
||||
*
|
||||
* 例如:
|
||||
* node merge_kaipiao.js "D:\\某个目录" "D:\\合并结果.xlsx"
|
||||
*/
|
||||
|
||||
// ---------- 参数 ----------
|
||||
const rootDir = process.argv[2];
|
||||
const outFile = process.argv[3] || path.join(process.cwd(), '团队开票申请表-合并.xlsx');
|
||||
|
||||
if (!rootDir) {
|
||||
console.error('用法: node merge_kaipiao.js <根目录> [输出文件路径]');
|
||||
process.exit(1);
|
||||
}
|
||||
if (!fs.existsSync(rootDir) || !fs.statSync(rootDir).isDirectory()) {
|
||||
console.error(`根目录不存在或不是文件夹: ${rootDir}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ---------- 工具函数 ----------
|
||||
|
||||
// 判断是否为 Excel 文件(排除临时文件 ~$ 开头)
|
||||
function isExcelFile(name) {
|
||||
return /\.(xlsx|xls)$/i.test(name) && !name.startsWith('~$');
|
||||
}
|
||||
|
||||
// 在一个工作簿里按关键字查找 sheet 名
|
||||
function findSheetName(workbook, keyword) {
|
||||
return workbook.SheetNames.find(n => n.includes(keyword));
|
||||
}
|
||||
|
||||
// 把一个 sheet 读成二维数组(含表头行)
|
||||
function sheetToRows(sheet) {
|
||||
return XLSX.utils.sheet_to_json(sheet, { header: 1, defval: null, blankrows: false });
|
||||
}
|
||||
|
||||
// ---------- 收集子文件夹 ----------
|
||||
const subDirs = fs.readdirSync(rootDir, { withFileTypes: true })
|
||||
.filter(d => d.isDirectory() && d.name.startsWith('7.'))
|
||||
.map(d => d.name)
|
||||
.sort();
|
||||
|
||||
if (subDirs.length === 0) {
|
||||
console.error('未找到以 "7." 开头的子文件夹');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
console.log(`找到 ${subDirs.length} 个 "7." 子文件夹`);
|
||||
|
||||
// 合并结果:分别累积 商旅 / 吉智 两组数据
|
||||
let shangluHeader = null;
|
||||
const shangluRows = [];
|
||||
let jizhiHeader = null;
|
||||
const jizhiRows = [];
|
||||
|
||||
let processedCount = 0;
|
||||
|
||||
for (const sub of subDirs) {
|
||||
const subPath = path.join(rootDir, sub);
|
||||
|
||||
// 找到名字含「团队开票申请表」的表格
|
||||
const files = fs.readdirSync(subPath).filter(isExcelFile);
|
||||
const targetFile = files.find(f => f.includes('团队开票申请表'));
|
||||
|
||||
if (!targetFile) {
|
||||
console.warn(` [跳过] ${sub}: 未找到含"团队开票申请表"的表格`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const filePath = path.join(subPath, targetFile);
|
||||
let workbook;
|
||||
try {
|
||||
workbook = XLSX.readFile(filePath);
|
||||
} catch (e) {
|
||||
console.warn(` [跳过] ${sub}: 读取失败 - ${e.message}`);
|
||||
continue;
|
||||
}
|
||||
|
||||
const shangluName = findSheetName(workbook, '商旅');
|
||||
const jizhiName = findSheetName(workbook, '吉智');
|
||||
|
||||
if (!shangluName) console.warn(` [警告] ${sub}: 未找到含"商旅"的sheet`);
|
||||
if (!jizhiName) console.warn(` [警告] ${sub}: 未找到含"吉智"的sheet`);
|
||||
|
||||
// 处理 商旅
|
||||
if (shangluName) {
|
||||
const rows = sheetToRows(workbook.Sheets[shangluName]);
|
||||
if (rows.length > 0) {
|
||||
const [header, ...body] = rows;
|
||||
if (!shangluHeader) shangluHeader = header;
|
||||
shangluRows.push(...body);
|
||||
}
|
||||
}
|
||||
|
||||
// 处理 吉智
|
||||
if (jizhiName) {
|
||||
const rows = sheetToRows(workbook.Sheets[jizhiName]);
|
||||
if (rows.length > 0) {
|
||||
const [header, ...body] = rows;
|
||||
if (!jizhiHeader) jizhiHeader = header;
|
||||
jizhiRows.push(...body);
|
||||
}
|
||||
}
|
||||
|
||||
processedCount++;
|
||||
console.log(` [完成] ${sub}: ${targetFile}`);
|
||||
}
|
||||
|
||||
if (processedCount === 0) {
|
||||
console.error('没有可合并的数据');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ---------- 生成输出工作簿 ----------
|
||||
const outWb = XLSX.utils.book_new();
|
||||
|
||||
if (shangluHeader) {
|
||||
const data = [shangluHeader, ...shangluRows];
|
||||
const ws = XLSX.utils.aoa_to_sheet(data);
|
||||
XLSX.utils.book_append_sheet(outWb, ws, '商旅');
|
||||
} else {
|
||||
console.warn('未收集到任何"商旅"数据');
|
||||
}
|
||||
|
||||
if (jizhiHeader) {
|
||||
const data = [jizhiHeader, ...jizhiRows];
|
||||
const ws = XLSX.utils.aoa_to_sheet(data);
|
||||
XLSX.utils.book_append_sheet(outWb, ws, '吉智');
|
||||
} else {
|
||||
console.warn('未收集到任何"吉智"数据');
|
||||
}
|
||||
|
||||
if (outWb.SheetNames.length === 0) {
|
||||
console.error('没有生成任何 sheet,未写出文件');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
XLSX.writeFile(outWb, outFile);
|
||||
|
||||
console.log('\n合并完成:');
|
||||
console.log(` 商旅: ${shangluRows.length} 行数据`);
|
||||
console.log(` 吉智: ${jizhiRows.length} 行数据`);
|
||||
console.log(` 输出文件: ${outFile}`);
|
||||
Reference in New Issue
Block a user