资讯动态

WPS JS宏批量合并Excel:三步搞定几十个表格汇总

发布时间:2026/10/23 1:17:15 来源:尧图企业网站定制
WPS JS宏批量合并Excel这件事听起来像是程序员才能碰的活但当手头有几十个门店的销售报表、几十个部门的日数据、或者一堆从系统导出的分表需要攒成一张总表时人力复制粘贴真的是最折磨人的操作。打开文件、全选、复制、切到汇总表、粘贴、关掉、打开下一个……机械重复半小时下来眼睛花了不说还容易漏行、串列更别说把别人手工备注的散碎内容一起带进来。我第一次被这种活逼到崩溃后直接用WPS自带的JS宏写了个脚本整个过程拆成三步几十个文件几秒钟全部合并完还自带空行过滤和来源追溯。这篇博文就把这套方案完整拆开讲三步合并法的思路、可以直接复制的完整代码、以及我在真实操作里踩过的各种坑。不管你是完全不懂代码的办公人员还是想找个批量处理范本的初学者都可以照着做。1. 什么情况下值得为Excel合并写一个脚本1.1 三个典型的合并场景基本覆盖掉日常大多数需求先说三个我实际碰到过、也帮周围人处理过的场景你看看是不是似曾相识。第一个是定期报表汇总。这类最常见公司下面有十几家门店或者几十个销售小组每个单位每周、每月发一张固定格式的Excel表报上来负责人需要把所有表按照相同列结构竖向堆到一起变成一张总明细。刚开始门店少手粘一下还能忍等到二十家以上就完全是体力活了。第二个是系统导出的分片数据。很多业务系统导出数据时会按时间、按地区、按订单来源切分文件。比如每天导出一个Excel文件月底要合并成一张全量明细表。这类文件列结构一模一样只是行数不同人工合并不仅慢还容易在反复切换窗口时丢失数据。第三个是数据补录和汇总分析。有时候你拿到的数据分散在多个工作簿里每张表又可能不止一个工作表分析前需要先把它们集中到一张表里统一处理。这种活通常还要反复做——比如每次跑数都要重新合并一次每次合并的操作方式完全一样。这些任务的共同特征很明确格式统一、规则简单、量大人烦。判断标准就一句话你操作时已经不需要思考只是机械重复那这件事就肯定可以交给脚本。1.2 为什么选JS宏而不是VBA或者Python有人可能会说合并Excel用Python写个pandas脚本不就行了确实行但放到真实办公环境里问题常常不在代码本身而在环境。我很直白地讲以下是我推荐WPS JS宏的理由对比项WPS JS宏VBAPython pandas运行环境WPS自带零安装WPS兼容模式下可用需要安装Python环境和库公司电脑经常不允许使用门槛懂一点JS或直接抄代码就能跑语法风格偏老空行、对象引用处理繁琐需要管理路径、虚拟环境、命令行上手门槛高与表格的联动直接操作当前打开的WPS表格同样可以直接操作通常要打开/保存外部文件操作不直观适合人群办公人员、非技术岗老Office开发者数据工程师、专业分析师更重要的是WPS新版本主推的宏语言就是JavaScript语法和前端JavaScript高度一致字符串处理、数组操作、正则表达式都比VBA顺手太多。比如判断一个单元格是否为空在JS里可以用String(v).trim()在VBA里则要处理各种Null和Trim的组合问题。对于只想要快速把工作搞定的人来说JS宏是学习成本和可使用度最平衡的选择。2. 动手前必须搞懂的JS宏对象模型和运行环境2.1 四个层级看懂所有Excel脚本操作写WPS的JS宏之前先不用急着背一堆API把对象模型搞清楚你就能看懂绝大部分代码。WPS的层级关系可以理解成下面这样ApplicationWPS程序本身 └ Workbooks所有打开的工作簿集合 └ Workbook一个Excel文件 └ Worksheets这个文件里的所有工作表集合 └ Worksheet某一张工作表 └ Range / Cells单元格区域 / 单元格用生活里的例子比喻工作簿就是桌面上的档案夹工作表是档案夹里的那张表格纸单元格是表格纸上一个一个的小格子。每次你操作Excel表格本质上都是顺着这条层级链找到某一个具体格子然后读它、写它或者改它的格式。日常写代码时你至少需要区分这几个对象ActiveWorkbook当前正在操作的工作簿。ThisWorkbook宏代码所在的那个工作簿。Workbooks.Open(路径)打开一个外部Excel文件并返回这个文件对应的Workbook对象。wb.Close(false)关闭打开的工作簿false表示不保存修改。Worksheets.Item(1)取第一张工作表也可以按名字取比如Worksheets.Item(数据)。Cells.Item(行, 列)按行号和列号定位单元格比如Cells.Item(2, 3)就是第2行第3列也就是C2。UsedRange一个工作表里所有被使用过的区域常用来快速算出数据的最后一行和最后一列。2.2 宏的入口、保存格式和第一次运行在WPS里写JS宏入口比较隐蔽这里直接按我习惯的路径走一遍。先打开任意一个WPS表格文件在菜单栏找到“开发工具”选项卡。如果你压根没看到这个选项卡就去“文件 → 选项 → 自定义功能区”在右侧勾选“开发工具”。勾完以后菜单栏就会出现这一步比较容易漏。点击“开发工具”里的“WPS宏编辑器”或直接按快捷键Alt F11就能进入宏编辑界面。左侧面板会显示当前工作簿的项目结构右侧就是代码输入区。写宏不是直接在原文件里保存而是要把文件“另存为”成启用宏的工作簿格式也就是.xlsm。如果你存成普通的.xlsx下次打开宏代码会直接丢失这个我一开始就吃过亏。第一次运行宏时WPS通常还会弹一个关于启用宏的安全提示。如果你看不到任何和宏相关的按钮、代码编辑窗口很大概率是宏被安全策略禁用了去“文件 → 选项 → 安全”或“信任中心”里找到“宏设置”选择“启用所有宏”或类似选项。2.3 写个最小测试宏先验证环境是不是通正式写大脚本之前强烈建议先写一个最小的测试宏确保宏编辑器、运行环境都正常。直接在代码区粘贴下面这段function 测试宏() { let sheet ActiveWorkbook.Worksheets.Item(1); sheet.Range(A1).Value2 宏脚本测试成功; 弹窗(当前工作表是 sheet.Name); } function 弹窗(text) { try { MsgBox(text); } catch (e) { alert(text); } }运行方式是光标放在测试宏函数里点击编辑器上的运行按钮或者直接按F5。运行后当前工作簿的第一张表A1单元格会写上“宏脚本测试成功”同时弹出一个消息框显示当前工作表名。这里顺手写了一个弹窗兼容函数是因为不同WPS版本里MsgBox和alert的支持情况不太一样两个都试一下保证在你环境里至少有一个能弹出来。这个最小宏跑通说明你的宏编辑器、执行权限、代码基础环境都没有问题。很多人装完环境就直接上大脚本结果核心代码还没跑就被环境问题卡住反而效率更低。3. 三步合并法的完整代码实现选文件、读数据、写汇总3.1 第1步弹窗选文件比写死路径可靠得多网上很多教程喜欢把要合并的文件路径写死在代码里比如“C:\某文件夹\1.xlsx”。但实际工作中文件路径隔一段时间就会变、文件夹名字可能改、月份目录也不一样写死路径意味着每次都要改代码反而更麻烦。我的做法是直接用文件选择对话框让用户手动多选文件。核心代码长这样let fd Application.FileDialog(3); // 3 代表文件选择对话框 fd.AllowMultiSelect true; // 允许按住Ctrl多选 fd.Title 请选择要合并的Excel文件; fd.InitialFileName ThisWorkbook.Path \\; if (fd.Show() 0) { return; // 用户点了取消直接退出 } let files fd.SelectedItems; // 用户选中的所有文件路径集合这里有一个新手非常容易踩的细节files虽然看起来像数组但它其实是一个Office风格的集合对象索引从1开始不是从0开始。后面写循环时要从i 1开始遍历而不是i 0。我先提一句后面第4章还会展开讲。另外fd.InitialFileName ThisWorkbook.Path \\的意思是打开文件选择框时默认定位到宏所在工作簿的目录。如果你已经把所有待合并文件都放在同一个文件夹里这个默认位置能省不少事。3.2 第2步逐文件打开并找出有效数据区域文件选择完毕真正的合并工作就开始了。核心逻辑是逐个打开Excel文件读取第一个工作表找到有效数据的最后一行和最后一列然后从第2行开始逐个单元格取值。判断有效区域用的是UsedRange它返回工作表里有内容的区域但理解它的计算逻辑很重要。UsedRange本身有4个关键属性Row有效区域的起始行号Column有效区域的起始列号Rows.Count有效区域的总行数Columns.Count有效区域的总列数所以有效区域的最后一行可以这样算let srcUsed sourceWs.UsedRange; let lastRow srcUsed.Row srcUsed.Rows.Count - 1; let lastCol srcUsed.Column srcUsed.Columns.Count - 1;为什么要减1因为Row Rows.Count - 1才是结束行。比如有效区域从第1行开始总共有5行那最后一行就是1 5 - 1 5。这个逻辑不弄清楚代码很容易在边界条件上出错。定位到有效区域之后从第2行开始遍历。因为绝大多数表第1行是表头我们要跳过表头只把数据行合并进去。遍历每一行时还要先判断这一行是不是空行。实际表格里经常有人无意中点到空格、设置了格式导致一行明明看着是空的却仍然被UsedRange计数。如果不做空行过滤合并结果会出现几百上千条空白行。3.3 第3步把数据写入汇总并收尾写入环节的核心是维护一个不断递增的targetRow变量。汇总表第1行是表头所以数据从targetRow 2开始写。每写完一行数据targetRow加1接着写下一行。写完后要调用wb.Close(false)关闭源工作簿。这里的false表示不保存修改因为你只是读取数据改动源文件没有任何意义。很多初学者只记得打开、忘了关闭导致脚本跑完以后系统里残留一大堆Excel进程第二次运行就明显卡顿。全部处理完用兼容弹窗函数提示“合并完成共汇总多少行”这样你心里有数能快速核验结果。3.4 可以直接复制的完整代码下面是完整版代码我已经把上面三步连贯起来了。把这段代码复制到WPS宏编辑器里保存成.xlsm运行批量合并Excel()函数即可。// // 功能批量合并Excel文件取每个文件的第一个工作表 // 用法将代码粘贴到WPS宏编辑器运行“批量合并Excel” // 说明所有源文件默认第1行是表头第2行起是数据 // function 弹窗(text) { try { MsgBox(text); } catch (e) { alert(text); } } function 批量合并Excel() { let startTime new Date().getTime(); // ---------- 第1步弹窗选择要合并的文件 ---------- let fd Application.FileDialog(3); // 3 表示文件选择对话框 fd.AllowMultiSelect true; fd.Title 请选择要合并的Excel文件可多选; fd.InitialFileName ThisWorkbook.Path \\; if (fd.Show() 0) { return; // 用户点击取消 } let files fd.SelectedItems; if (files.Count 0) { return; } // ---------- 第2步准备汇总表 ---------- let summarySheet ActiveWorkbook.Worksheets.Item(1); // 默认从汇总表第2行开始写。 // 如果这张表之前已经有合并结果建议先手动清空旧数据再运行。 let targetRow 2; // ---------- 第3步遍历文件读取数据写入汇总 ---------- for (let i 1; i files.Count; i) { let filePath files.Item(i); // 跳过宏所在工作簿自己避免把汇总表自身数据再合并进来 if (filePath.toLowerCase() ThisWorkbook.FullName.toLowerCase()) { continue; } let wb Workbooks.Open(filePath); let sourceWs wb.Worksheets.Item(1); // 取每个文件的第一个工作表 // 计算源文件有效区域的最后一行和最后一列 let srcUsed sourceWs.UsedRange; let lastRow srcUsed.Row srcUsed.Rows.Count - 1; let lastCol srcUsed.Column srcUsed.Columns.Count - 1; // 从第2行开始跳过表头 for (let r 2; r lastRow; r) { // 跳过整行都为空的脏数据行 let isEmptyRow true; for (let c 1; c lastCol; c) { let v sourceWs.Cells.Item(r, c).Value2; if (v ! null String(v).trim() ! ) { isEmptyRow false; break; } } if (isEmptyRow) { continue; } // 把这一行的每一列写入汇总表 for (let c 1; c lastCol; c) { summarySheet.Cells.Item(targetRow, c).Value2 sourceWs.Cells.Item(r, c).Value2; } targetRow; } wb.Close(false); // 数据已读完不保存关闭源文件 } let elapsed (new Date().getTime() - startTime) / 1000; 弹窗(合并完成共汇总 (targetRow - 2) 行耗时 elapsed.toFixed(1) 秒); }使用步骤我建议按下面这个顺序走在WPS里打开一个空白工作簿另存为.xlsm格式。按快捷键Alt F11打开宏编辑器新建一个脚本模块把上面代码粘贴进去。保存代码回到WPS表格界面。按Alt F8调出宏列表找到批量合并Excel点击运行。在弹出的文件选择框里选中所有要合并的文件点确定。第一次运行时先选两个小文件试跑确认输出结果符合预期后再真正处理全量文件。千万不要一上来就选几十个文件跑那样出了问题都不知道哪里错。4. 实测阶段踩过的坑集合下标、文件占用、脏数据一个不少4.1 集合下标从1开始别拿JavaScript的数组习惯硬套这是我第一次写WPS JS宏时实实在在卡住的问题。写惯了前端JavaScript的人遍历任何集合第一反应都是写for (let i 0; i files.Count; i)然后取files[i]。但WPS的SelectedItems是Office风格的集合对象不是原生数组它的第一项是files.Item(1)而不是files[0]。如果你从0开始循环轻则漏掉第一个文件重则直接抛“下标越界”。解决办法很简单统一用for (let i 1; i files.Count; i)并且用files.Item(i)来取值。这个习惯一旦养成后面接触Worksheets集合、Workbooks集合时就不会再犯同类错误。4.2 打开的工作簿一直不关第二次跑就卡得要死真实的办公电脑配置一般如果一次合并三四十个文件每个文件都通过Workbooks.Open打开而不调用wb.Close(false)关闭那么跑完一次脚本后任务管理器里会挂着一大串WPS后台进程。第二次再跑电脑直接卡到鼠标飘严重的时候还会提示内存不足。这个问题不是写代码时立刻暴露的而是跑了几次以后才逐渐明显。我的建议是在循环代码中保持wb.Close(false)与Workbooks.Open成对出现。更进一步可以用try...catch...finally结构把wb.Close(false)放到finally块里这样就算读取某一行时报错也能保证已经打开的工作簿被关闭不会发生“一个文件出错后面所有文件全卡死”的情况。大致的容错结构可以这样let wb null; try { wb Workbooks.Open(filePath); // 读取数据并写入汇总表 } catch (err) { 弹窗(处理失败 filePath 原因 err.message); } finally { if (wb ! null) { wb.Close(false); } }4.3 不清理旧汇总数据行数会越跑越多假设你跑了一次合并汇总表已经有500行数据。改了一点代码或者想重新算一遍又跑了一次脚本。由于脚本默认targetRow 2第二次运行会把新数据从第2行开始覆盖写入理论上不会出问题。但如果两次合并的文件数量不一样、源文件有新增内容或者上一次跑完以后有人在汇总表里手动加过内容就很容易出现新旧数据混在一起的情况。保险的做法是在运行前清空汇总表的数据区域。如果你已经确定汇总表结构可以直接手动清空旧数据再跑。如果想集成到代码里可以加一段自动清理逻辑从第2行扫到最后一个有内容的行然后清除内容。我这里给一个示例片段你可以自行嵌到主代码的“准备汇总表”部分let su summarySheet.UsedRange; let oldLastRow su.Row su.Rows.Count - 1; if (oldLastRow 2) { summarySheet.Range( summarySheet.Cells.Item(2, 1), summarySheet.Cells.Item(oldLastRow, 20) ).ClearContents(); }注意这个片段固定清理到第20列如果你的表格列数超过20需要把数字调大。更严谨的做法是先读取汇总表表头的列数再根据列数构造清理范围这里为了篇幅不展开了。4.4 源表里那些看不见的脏数据会让结果多出几千个空行合并脚本写好后我发现有一次跑出来的总表有将近3000行但源文件的数据加起来应该只有800多行。排查了很久最后定位到其中一个源文件。打开那个文件按Ctrl End光标直接跳到了第3000行但屏幕上明明只有几十行有数字。这就是典型的格式残留有人曾经在几百行里填过内容又删掉但格式、边框、空格符号还残留在单元格里UsedRange会把它们全部算进去。这类问题靠肉眼是发现不了的只有脚本跑完看汇总行数才会暴露。前面完整代码里的“空行过滤”已经能把大部分假空行挡掉但如果你按Ctrl End发现某张表的光标停在很后面的位置最好源头处理一下全选那些假空行右键“删除”而不是“清除内容”然后保存再重新合并。4.5 文件被占用、宏环境差异这些偶发问题怎么兜底办公环境里最现实的问题是你正跑着脚本结果某个Excel文件正在被另一个同事打开。这种情况下Workbooks.Open往往会以只读方式打开或者弹出一个文件占用提示导致脚本卡在那里不动。代码层面可以加一个简单的只读判断if (wb.ReadOnly) { 弹窗(注意 filePath 被只读打开请确认文件未占用); wb.Close(false); continue; }另一个偶发问题是不同WPS版本的API差异。有些旧版本对MsgBox、FileDialog的支持和预期不太一样。解决方案就是我前面说的尽量使用通用的数字枚举、写兼容函数不要在一个版本上跑通就以为所有环境都能通。至少在同一台电脑上部署前先用最小测试宏验证一遍基础环境。5. 给合并脚本做的升级改造来源文件名、Sheet匹配、批量提速5.1 在汇总表里加上“来源文件名”这一列合并完成以后最尴尬的问题就是某一行数据出错了想查它到底来自哪个文件结果所有数据混在一起根本分不清。如果合并源头有几十个文件想定位问题行都无从下手。所以我的建议是在汇总表右侧加一列“来源文件”每写完一行数据顺便把当前工作簿的文件名写进去。实际改造代码时你可以在写入每一行数据的内层循环后面加上这样一句let sourceFileName wb.Name; // 例如“门店A.xlsx” let sourceColumn lastCol 1; // 放在数据列后面单独一列 summarySheet.Cells.Item(targetRow, sourceColumn).Value2 sourceFileName;这样每一条汇总记录都能追溯到源文件后期核对数据、找异常都要方便得多。你还可以进一步写来源表名、来源行号但一般文件名就够了。5.2 不写死第一个Sheet按Sheet名找到真正的数据表基础版代码写的是“取每个文件的第一个工作表”。但在实际工作中有些文件的数据根本不在第一个Sheet或者第一个Sheet是封面、说明页真正数据在第二三张表里。如果一刀切取第一个Sheet合并出来的数据就是乱的。更稳妥的逻辑是优先查找名字匹配“数据”“明细”“本月”这类关键词的工作表找不到再退回第一个工作表。示例代码如下function 找数据表(wb) { let totalSheets wb.Worksheets.Count; for (let s 1; s totalSheets; s) { let sheetName wb.Worksheets.Item(s).Name; if (sheetName.indexOf(数据) -1 || sheetName.indexOf(明细) -1) { return wb.Worksheets.Item(s); } } return wb.Worksheets.Item(1); }然后在主循环里把wb.Worksheets.Item(1)替换成找数据表(wb)就行。注意indexOf匹配范围可以自己按实际表的名字调整核心思想是“智能匹配优先固定第一个Sheet兜底”。5.3 大数据量时的提速思路用Range批量读写基础版本里我用了两层循环逐格读取单元格外层遍历行、内层遍历列。这种写法对几千行、十几列的数据完全够用但如果你要合并的是几万行、几十列的大表就会发现速度明显变慢。原因很简单每次通过Cells.Item(r, c)访问单元格脚本引擎都要和表格对象做一次交互交互次数越多整体耗时越长。提速的基本思路是把整块区域一次性读成一个二维数组在内存里处理完再一次性写回汇总表。核心写法是let data sourceWs.Range( sourceWs.Cells.Item(2, 1), sourceWs.Cells.Item(lastRow, lastCol) ).Value2;这样拿到的data就是二维数组第一维是行第二维是列。之后遍历数组、过滤空行速度比逐个读写单元格快几个量级。写回汇总表时也可以先构造好目标区域再一次性赋值。不过要提醒一下不同WPS版本对Range.Value2返回数组的结构可能略有差异单行单列时尤其容易变成一维数组或标量。建议在小数据量上先验证确认取到的类型符合预期后再全量跑。如果你不需要追求极限性能基础版的逐格读写完全够用。提升性能之前先想想你的数据量是不是真的到了需要优化的程度。5.4 把脚本绑定到按钮一键运行脚本写好后别人不一定知道怎么打开宏编辑器、怎么运行宏。为了让整个流程傻瓜化我把这个合并功能绑定到了表格里的一个按钮上在WPS菜单的“开发工具”里选择“插入表单控件”或“插入形状”随便画一个矩形框右键点击矩形框选择“指定宏”然后选中批量合并Excel函数。保存后别人只需要打开这个工作簿点一下按钮整个合并过程就完成了完全不用看到一行代码。如果你的Excel汇总任务是要交给同事使用的这个按钮设计几乎是必须的。不然你写好的脚本只能在自己电脑上跑换到别人电脑上对方连宏编辑器在哪里都不知道。最后说点实际操作中的体验这个脚本我自己用了很长一段时间最大的感受是合并Excel这件事真正难的从来不是写代码而是把规则想清楚。比如要不要保留表头、遇到空行怎么处理、源文件格式不统一时怎么兜底、跑完以后怎么核验结果这些想明白了代码反而是水到渠成的事。最开始跑脚本之前我建议一定先备份原始数据和汇总表。脚本跑完后不要只盯着“共汇总多少行”那个提示花几分钟抽样看几条明细确认数据真的逐行对齐了。还有一个小技巧把汇总文件固定命名后放在固定目录每次只需要选择当月新增的文件运行不要总是全选整个文件夹这样既省时间也减少把无关文件合进来的概率。合并脚本这种东西属于写一次就能反复用的资产值得你花半小时把它打磨到顺手。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑