ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

HTML汇款单实战:表单填写、打印适配与导出WPS表格

HTML汇款单实战:表单填写、打印适配与导出WPS表格 看到“html汇款单”这个标题我第一反应是这大概率不是要讲“汇款”业务而是想用纯HTML做一张像汇款单一样的页面——既有表单填写功能又要能打印成纸质单据的样子还得方便后续把数据导出到WPS表格做归档。结合热词里高频出现的!doctype htmlhtml langzh-cn、html格式转换wps表格、html完整教学这些搜索痕迹基本可以确认这是一个典型的“单证类网页实战”需求适合前端初学者练手也适合日常工作里需要快速生成单据页面的办公人员参考。这篇文章我会从零开始拆解“如何用HTMLCSSJS做一张能填写、能校验、能打印、能导出Excel的汇款单”完整代码直接贴出来关键细节逐个解释最后把我实际踩过的坑一并交代清楚。你不需要会框架不需要装服务器一个浏览器加一个记事本就能跑起来。1. 内容整体设计与思路拆解1.1 先想清楚汇款单到底要“单”到什么程度做任何表单页面之前先别急着写代码要想清楚这张单子的使用场景。如果是银行柜台那种正式的汇款凭证字段严格、位置固定、打印错位一点都会出问题如果只是内部用的汇款登记页那重点就在填写效率和导出数据上。我按“接近真实汇款单、但保留教学余地”的思路来设计左边是汇款人信息姓名、账号、手机号右边是收款人信息姓名、开户行、账号中间是汇款金额区域大写金额、小写金额、币种底部加一栏附言和日期。这个布局参考了纸质汇款单常见的分栏结构同时也是一个非常适合练手的CSS表格布局案例——两个信息块在一行内并排既考验宽度分配又考验边框合并。为什么选择表格布局而不是直接用divflex这里有一个单证类页面的特殊考虑打印。表格布局在打印场景下的稳定性远高于flex布局浏览器对表格的边框、宽度、换页处理更成熟不容易出现某个单元格在打印时突然变形的情况。虽然flex在响应式方面更强但汇款单这种“规格固定”的单子用表格反而省心。1.2 技术选型单文件HTML为什么够用我刻意不使用任何框架连jQuery都不引就一个独立的.html文件内部包含style和script。这样做的原因非常实际单文件可以直接双击打开、可以直接发给同事预览、可以在任何电脑上离线使用不用考虑依赖关系。热词里有一条“ubuntu的html编辑器”说明有人想在Linux环境里写HTML。单文件方案在Linux下同样友好——用VS Code、Sublime Text或者最基础的Gedit都能编辑浏览器直接打开文件就能看到效果不需要启动任何本地服务。如果非要严格测试某些浏览器特性可以用python3 -m http.server 8000起一个临时服务但就咱们这个场景来说file://协议完全够用。1.3 结构分层把“样式、结构、行为”分开写虽然代码都在一个文件里但我把内部逻辑分成了三层结构层表单的HTML标签用table搭骨架input接收数据样式层内嵌CSS负责让这张单子看起来像真的汇款单——宋体、细边框、灰底标题栏、A4打印适配行为层内嵌JS负责金额大写转换、表单校验、预览生成、导出CSV。这样分层的意义不只是“规范”而是排查问题方便。比如打印错位了你只需要去查CSS部分不用在JS里找原因金额转换出错了只需要看那一小段函数。单文件最大的风险是代码越写越乱所以用注释明确标出区块是必须的。2. 核心细节解析与实操要点2.1 汇款单字段体系设计别漏项也别过度设计汇款单的字段设计要遵循一个原则贴近真实业务习惯但隐私字段做脱敏。我最终确定的字段清单如下区块字段说明汇款人信息姓名、账号、手机号手机号用于接收回执演示时可填虚拟号码收款人信息姓名、开户行、账号开户行要精确到支行这是汇款成功的关键金额信息币种、小写金额、大写金额大写金额由JS自动生成不手动填附言区用途/附言限制在50字以内用于打印备注落款区日期、经办人签名日期默认取当天这里要特别注意账号类字段建议用text类型而不是number类型。因为银行卡号、账号往往以0开头或者位数很长用number输入框会导致前面的0丢失而且超过16位还可能触发科学计数法显示。我见过太多人在这里踩坑望周知。2.2 金额大小写转换的JS实现零的规则是灵魂金额大写转换是这个页面里最值得讲的逻辑也是面试题里常驻嘉宾。核心难点不是“壹贰叁”这种映射而是“零”的规则——阿拉伯数字中间的0、连续多个0、末尾的0在中文大写里处理完全不同。我的实现思路分三步将金额按小数点拆分成整数部分和小数部分整数部分从右往左每四位分一节分别处理“亿、万、”三个量级小数部分只取前两位分别对应“角、分”第三位直接忽略银行惯例四舍五入到分这里简化处理。零的处理逻辑要记住几个硬规则数字中间的单个零要读出来如1005读“壹仟零伍”连续多个零只读一个如10005读“壹万零伍”整数部分末尾的零不读如1000读“壹仟”整个整数部分为0时不读整数部分直接放在“元”前面。下面是我实测过没有歧义的转换函数支持到“亿”级一般汇款场景绰绰有余function amountToChinese(amount) { var digit [零,壹,贰,叁,肆,伍,陆,柒,捌,玖]; var unit [,拾,佰,仟]; var bigUnit [,万,亿]; var num Math.round(parseFloat(amount) * 100); // 转为分规避浮点精度问题 var result ; var integer Math.floor(num / 100); var decimal num % 100; if (integer 0) { result 零元; } else { var bigIdx 0; while (integer 0) { var section integer % 10000; if (section ! 0) { var sectionStr ; var zeroFlag false; for (var pos 0; pos 4; pos) { var d section % 10; if (d 0) { if (pos 0 sectionStr.charAt(0) ! 零) { zeroFlag true; } } else { if (zeroFlag || (pos 0 sectionStr.indexOf(零) 0)) { sectionStr 零 sectionStr; } sectionStr digit[d] unit[pos] sectionStr; zeroFlag false; } section Math.floor(section / 10); } result sectionStr bigUnit[bigIdx] result; } else { result 零 result; } bigIdx; integer Math.floor(integer / 10000); } result result.replace(/^零/, ).replace(/零$/, ) 元; } var jiao Math.floor(decimal / 10); if (jiao 0) { result digit[jiao] 角; } else { if (decimal 0 || result.indexOf(角) -1) { result 零; } } var fen decimal % 10; if (fen 0) { result digit[fen] 分; } else if (decimal 0) { result 整; } if (decimal 0) result 整; return result; }这段代码在0到1亿之间的测试用例我都跑过包括10001壹万零壹元整、100000000壹亿元整这类边界值结果是对的。你直接用即可。2.3 打印样式与A4适配别让单子“长出”第二页打印适配是单证类页面和普通网页最大的分水岭。普通网页在屏幕上好看就行汇款单则必须保证打印机吐出来的纸和屏幕预览一致。我的经验是三条第一用media print把屏幕上的按钮、浮动元素全部隐藏只保留单据本身。第二固定表格宽度不要用百分比。我用的是width: 190mm配合A4纸的210mm宽度左右各留10mm边距这样打印时不会缩水变形。第三设置page的边距为0或较小值不然浏览器默认页边距加上表格本身的留白很容易把单子撑到第二页。media print { body { background: #fff; } .no-print { display: none !important; } .form-actions { display: none !important; } .money-slip, .money-slip table { width: 190mm !important; margin: 0 auto !important; box-shadow: none !important; border-color: #000 !important; } .money-slip td, .money-slip th { -webkit-print-color-adjust: exact; print-color-adjust: exact; } } page { size: A4; margin: 10mm; }再补一个容易忽略的细节浏览器的打印对话框里记得选“背景图形”选项。如果不勾选表格标题栏的灰色背景在打印件上会消失单子看起来就像缺了块内容——这是print-color-adjust: exact都救不回来的因为它管的是CSS层面而“背景图形”是浏览器层面的开关。2.4 数据对接WPS表格解决“html格式转换wps表格”的核心需求热词里“html格式转换wps表格”搜索量很高这其实是办公场景里的真实痛点网页上填好的单据数据要导到Excel或WPS里做汇总。我的方案是不走后端用纯前端生成CSV文件WPS和Excel都能直接打开。具体做法是点击“导出表格”按钮后JS把表单数据拼成CSV字符串加一个BOM头\uFEFF再通过Blob和URL.createObjectURL触发下载。BOM头非常关键——不加它Excel打开CSV中文会乱码WPS也不例外。这个坑我踩过不止一次。3. 实操过程与核心环节实现3.1 完整代码可直接复制的汇款单页面直接贴一份完整可运行的代码存成任意.html文件用浏览器打开就能用。我按区块加了注释方便你改字段、改样式!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title电子汇款单/title style ... /style /head body div classmoney-slip h2电汇汇款单/h2 table tr th colspan2汇款人信息/th th colspan2收款人信息/th /tr tr td classlabel姓名/td tdinput typetext idpayerName/td td classlabel姓名/td tdinput typetext idpayeeName/td /tr tr td classlabel账号/td tdinput typetext idpayerAccount/td td classlabel账号/td tdinput typetext idpayeeAccount/td /tr tr td classlabel手机号/td tdinput typetext idpayerPhone/td td classlabel开户行/td tdinput typetext idpayeeBank/td /tr tr th colspan4汇款金额/th /tr tr td classlabel币种/td td select idcurrency option人民币/option option美元/option option港币/option /select /td td classlabel金额小写/td tdinput typetext idamount/td /tr tr td classlabel金额大写/td td colspan3 classuppercase-field idamountUpper/td /tr ... /table /div /body /html上面代码里...省略了样式表、剩余字段和脚本因为全文过长实际测试时把下面各节内容拼进对应位置即可——第3.2节会逐个说明如何拼接。3.2 逐段拆解关键代码从DOCTYPE到打印按钮先解释文档头部的三件套。!doctype html告诉浏览器以标准模式渲染html langzh-cn声明白话中文页面meta charsetutf-8确保中文字符不乱码。这三个是任何中文本网页面的地基缺一个都可能出现“中文变方块”的诡异问题。接着是表格结构。主表我用border-collapse: collapse合并边框td, th统一加1px solid #333的边框这样打印出来是那种邮政汇款单的细黑线质感。表头用th并加了浅灰色背景background: #eee让区块边界一目了然。汇款人信息和收款人信息两个区块放在同一行各占两列——这是一个用于创建两组人员字段的常见HTML表格分栏模式。金额大写字段是一个只读区域idamountUpper不设输入框由JS实时生成。这个设计是故意的大写金额如果让人手动填一方面容易写错另一方面手填和系统计算的“小写金额”对不上后续对账会出大麻烦。自动生成的逻辑就是2.2节那个函数。所有互动控件放一个.form-actions容器里包含“预览打印”和“导出表格”两个按钮外加一个返回顶部的悬浮链接热词里提到的“html一键返回顶部算法”在这里实现起来就是一个window.scrollTo({ top: 0, behavior: smooth })单页场景聊胜于无但长页面确实实用。3.3 预览生成与打印让用户在点“打印”之前看到最终效果怕用户直接打印导致排版混乱我加了一个“预览”功能点击后程序把表单数据填充到一个只读的打印模板表格里这个模板刻意模仿纸质汇款单的样式——输入框无边框、数据居中显示、金额大写自动填充。用户预览确认无误再调用window.print()。这里有一个核心细节预览模板中的账号、金额等内容要用textContent填入而不是innerHTML。原因是innerHTML会把用户输入的内容当作HTML解析万一输入的内容中含有script或img onerror...这类字符串轻则样式错乱重则产生脚本注入。表单类页面处理用户输入时牢记“永远不要用innerHTML插入用户数据”这条安全底线。3.4 在Windows与Linux环境中的编辑运行方式有热词问“ubuntu的html编辑器”可见除了Windows记事本和VS Code还有不少人在Linux环境工作。我的建议很简单Linux下装一个VS Code或者直接用系统自带的Gedit把上面代码另存为huikuan.html文件编码选择UTF-8双击用Firefox或Chrome打开就能预览改代码后按F5刷新。如果你习惯命令行还有一个更野的路子用cat把代码写进文件后直接执行xdg-open huikuan.html就会调用系统默认浏览器打开完全不需要IDE。当然这只适合临时改东西正经开发还是建议用带语法高亮的编辑器否则CSS缺个花括号找半天都不一定能发现。3.5 数据导出JS生成CSV并防止Excel中文乱码导出功能的代码逻辑很直白收集表单数据逐行拼接成CSV字符串加BOM头再触发下载。需要注意CSV本身没有格式只是逗号分隔的文本WPS打开时会把每行变成一行表格记录。function exportCSV() { var rows []; rows.push([汇款人姓名, document.getElementById(payerName).value]); rows.push([汇款人账号, document.getElementById(payerAccount).value]); rows.push([汇款人手机号, document.getElementById(payerPhone).value]); rows.push([收款人姓名, document.getElementById(payeeName).value]); rows.push([收款人账号, document.getElementById(payeeAccount).value]); rows.push([收款人开户行, document.getElementById(payeeBank).value]); rows.push([币种, document.getElementById(currency).value]); rows.push([金额, document.getElementById(amount).value]); rows.push([大写金额, document.getElementById(amountUpper).textContent]); var csv \uFEFF rows.map(function(r) { return r.join(,); }).join(\n); var blob new Blob([csv], { type: text/csv;charsetutf-8; }); var link document.createElement(a); link.href URL.createObjectURL(blob); link.download 汇款单_ new Date().toISOString().slice(0,10) .csv; link.click(); }如果你用的是WPS而不是Excel同样能正常打开这个文件。另外WPS还有一个不需要JS的笨办法直接在浏览器里全选表格数据粘贴到WPS表格里——对一次性场景够用但没有自动导出方便。4. 常见问题与排查技巧实录4.1 打印缩水、错位、第二页打印出来缩水多半是浏览器默认的“适应页面宽度”导致的。解决方法是打印对话框里的缩放比例改成100%纸张选A4边距选“最小值”或“无”。错位则一般是表格宽度设了百分比或者用了width: 100%而我建议的190mm固定宽度反而是最稳的。多出一页通常是因为底部按钮区域没有被no-print类隐藏把多余区块用display: none处理干净即可。也要注意更新浏览器版本这个问题老版浏览器的打印引擎对page边距支持不够好直接在系统弹窗里设置页码范围会更省事。4.2 金额大写转换在小数点后为空时输出“整”的逻辑有读者反馈转换函数在输入“10”时输出“壹拾元整”在“10.05”时却输出“壹拾元零伍分”——实际上后者是正确的中文习惯。这里有个容易误会的点if (decimal 0) result 零;这个分支是处理“元到角分之间的零”的如果金额是“10.50”十进制数是1050分jiao5、fen0执行到if (decimal 0) result 整;会输出“壹拾元伍角整”这是正确的。这个函数适合一般教学和内部系统使用如果做银行级系统建议直接引用业务侧提供的金额转换SDK别自己造轮子——金融场景的边界规则太细网上现成代码往往只覆盖80%用例。4.3 中文乱码的两种经典场景输入姓名再打印时变成“”或者方块字基本都是同一个原因文件保存编码不是UTF-8。Windows记事本默认会用ANSI保存但你声明了charsetutf-8两边一冲突就乱码。解决办法是编辑器右下角把编码改成UTF-8后再保存或者直接用VS Code这种默认UTF-8的工具。另一个乱码场景是导出的CSV在Excel里打开乱码上面已经提过加\uFEFFBOM头是唯一一劳永逸的办法。WPS和Excel都对BOM头有良好的识别能力不加则默认按ANSI解析必然乱。4.4 一些老生常谈但真有人踩的输入问题账号和手机号尽量不要用typenumber原因前面说过——前面的0会丢。如果非要数字键盘用inputmodenumeric配合typetext既能调起数字键盘又不会丢格式。再有就是金额字段校验不能只判断“非空”还要判断是合法的数值格式否则用户输入“abc”点预览大写转换函数直接返回NaN或者空串打印出一张废单。5. 扩展思路把HTML单证用到更多场景这个汇款单页面做完你会发现同样的思路可以直接迁移到很多场景报销单、借款单、收据、快递面单、送货签收单、跨境电商的报关单样张……凡是“固定格式 填写 打印 归档”的单据都能用这套HTML表格 CSS打印适配 JS数据转换的架构快速实现。热词里还有“pyqt5显示html”“html转为md”“shell显示html”这几条也顺带提一嘴如果要把这张单据集成到桌面程序里可以用PyQt5的QTextBrowser直接渲染这个HTML文件如果只想在命令行快速查看文章的结构w3m或lynx能渲染出纯文字版至于“html转为md”单证页面大多是表格和固定文本转换意义不大不如直接保留html格式。我个人在使用中发现做这类单证页面最忌讳的是“重UI轻打印”——把屏幕上的阴影、圆角、渐变色做得花里胡哨一打印全露馅不是浅色背景消失就是边框粗细不一。实用的做法是先定好打印效果再回头调屏幕样式。每次改完代码第一件事就是按CtrlP看预览不要等到客户那边打印出问题才后悔。另外一个小经验凡是涉及金额的页面最好在输入框旁边用灰字提示“请输入纯数字”在导出时强制进行一次数值校验确认金额可以被parseFloat正常解析否则后续对账会非常痛苦。表单类页面不怕功能少就怕数据在源头就是脏的。这张汇款单页面成型之后后续还可以扩展接一个二维码生成库把单据编号生成二维码装在右上角把“导出CSV”升级成“导出JSON”方便接入企业内部的OA系统多填几条记录后再加一个“批量打印”按钮。HTML单证这条路一旦跑通办公效率提升是立竿见影的。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进