资讯动态

Handsontable Numeric 单元格类型完全指南:数字格式化、校验与编辑行为

发布时间:2026/9/20 16:56:27 来源:尧图企业网站定制
Handsontable Numeric 单元格类型完全指南数字格式化、校验与编辑行为【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址: https://gitcode.com/gh_mirrors/ha/handsontableNumeric 单元格类型是 Handsontable 中处理数字数据的主力方案它借助Intl.NumberFormat完成货币、百分比、单位、千位分隔等显示格式化自动将数字右对齐并对非数字输入执行内置校验。本文将以 Handsontable 官方指南numeric-cell-type.md为主体结合仓库源码逐一讲解如何配置 numeric 类型、如何用numericFormat精确控制显示格式、如何应对超出安全整数范围的数值以及编辑与校验环节的底层行为帮助你在数据表格中正确展示、排序和过滤数字。为什么需要 Numeric 单元格类型Handsontable 的默认单元格类型是text文本单元格的数据以string类型处理对应文本编辑器内部textarea元素的值。但在大量业务场景中单元格的值应当作为number类型参与运算、排序与格式化。Numeric 单元格类型正是为此设计——它允许你把显示出来的数字格式化得更美观并保证排序、过滤等操作按数值语义而非字符串语义进行。从源码看numeric 类型在 numericType.ts 中被注册为NumericCellType它组合了四个关键构件export const NumericCellType { CELL_TYPE, editor: NumericEditor, // 文本编辑器负责输入解析 renderer: numericRenderer, // 渲染器负责右对齐与格式化 validator: numericValidator,// 校验器负责判定输入是否为数字 dataType: number, valueSetter, // 负责把编辑后的字符串转回数值 valueFormatter, // 负责显示层的格式化 };也就是说一个 numeric 单元格由「编辑器 渲染器 校验器 值处理器」四部分协作完成完整闭环后续章节会逐一展开。使用 Numeric 单元格类型将type选项设置为numeric即可启用 numeric 单元格类型。它可以在三个粒度上配置整张表格JavaScript// 为整张表格的每个单元格设置 numeric 单元格类型 type: numeric, // 为单列的每个单元格设置 numeric 单元格类型 columns: [ { type: numeric, }, ] // 为单个单元格设置 numeric 单元格类型 cell: [ { row: 0, col: 0, type: numeric, } ],React// 为整张表格的每个单元格设置 numeric 单元格类型 type{numeric}, // 为单列的每个单元格设置 numeric 单元格类型 columns{[{ type: numeric, }]} // 为单个单元格设置 numeric 单元格类型 cell{[{ row: 0, col: 0, type: numeric, }]}Vue 3!-- 为整张表格的每个单元格设置 numeric 单元格类型 -- HotTable :settings{ type: numeric } / !-- 为单列的每个单元格设置 numeric 单元格类型 -- HotTable :settings{ columns: [{ type: numeric }] } / !-- 为单个单元格设置 numeric 单元格类型 -- HotTable :settings{ cell: [{ row: 0, col: 0, type: numeric }] } /Angular// 为整张表格的每个单元格设置 numeric 单元格类型 settings1 { type: numeric, }; // 为单列的每个单元格设置 numeric 单元格类型 settings2 { columns: [ { type: numeric, }, ], }; // 为单个单元格设置 numeric 单元格类型 settings3 { cell: [ { row: 0, col: 0, type: numeric, }, ], };数据源中的数字必须是 number 类型需要注意Handsontable 不会把字符串自动解析成数字。在你的数据源中numeric 单元格的值必须以number类型存储而不是字符串。例如data: [[7000]]会被正确当作数字处理而data: [[7000]]中的7000仍是字符串。安全整数边界JavaScript 的Number类型只能精确表示绝对值不超过 2⁵³即 ±9007199254740991的整数。任何对更大数字的计算都不会精确这是 JavaScript 语言本身的限制。关于如何显示超出该范围的精确值见下文「安全整数限制与 preserveNumericLiteral」一节。综合演示多种格式与 locale 切换官方指南配套了完整的交互演示。在 example1.js 中一个表格里同时使用了多种格式化风格example1.html 则在表格上方提供了 locale 下拉选择器Year基础数字格式化Price (USD)与Price (EUR)货币格式化style: currencyDistance单位格式化style: unitkilometer并启用千位分组Fuel单位格式化liter并保留 1 位小数Discount百分比格式化style: percentQuantity带千位分隔符的小数格式化style: decimaluseGrouping关键配置如下节选自 example1.jscolumns: [ { data: year, type: numeric }, { data: price_usd, type: numeric, numericFormat: { style: currency, currency: USD, minimumFractionDigits: 2 }, }, { data: price_eur, type: numeric, numericFormat: { style: currency, currency: EUR, minimumFractionDigits: 2 }, }, { data: distance_km, type: numeric, numericFormat: { style: unit, unit: kilometer, useGrouping: true }, }, { data: fuel_liters, type: numeric, numericFormat: { style: unit, unit: liter, minimumFractionDigits: 1, maximumFractionDigits: 1 }, }, { data: discount_percent, type: numeric, numericFormat: { style: percent, minimumFractionDigits: 0, maximumFractionDigits: 2 }, }, { data: quantity, type: numeric, numericFormat: { style: decimal, useGrouping: true, minimumFractionDigits: 0 }, }, ], columnSorting: true, filters: true, dropdownMenu: true,locale 选择器把选中的 locale如de-DE、ja-JP、fa-IR、zh-CN等 21 个选项见 example1.html通过hot.updateSettings({ locale: item.dataset.value })实时应用到表格你可以直观地看到同一数值在不同 locale 下的千位分隔符、小数点符号与货币符号差异。locale 与numericFormat是解耦的locale 通过独立的locale选项控制而numericFormat只负责格式样式。另一个独立的演示 example3.js 展示了同一份配置在ja-JP日文与tr-TR土耳其文两个 locale 下的 decimal 风格显示差异——同样的价格数据因 locale 不同而呈现不同的分组与小数点样式。格式化数字numericFormat 与 Intl.NumberFormat要在单元格渲染器中改变数字的显示外观使用numericFormat选项。自 Handsontable 17.0 起numericFormat支持原生的Intl.NumberFormatAPI它提供了更好的性能与更广泛的浏览器支持且无需任何外部依赖。numericFormat接受Intl.NumberFormatOptions的全部属性locale 则通过独立的locale选项控制。基本用法以下配置让同一份数据在en-US下显示为$7,000.00在de-DE下显示为7.000,00 €columns: [ { type: numeric, locale: en-US, numericFormat: { style: currency, currency: USD, minimumFractionDigits: 2 } }, { type: numeric, locale: de-DE, numericFormat: { style: currency, currency: EUR, minimumFractionDigits: 2 } } ]ReactHotTable columns{[{ type: numeric, locale: en-US, numericFormat: { style: currency, currency: USD, minimumFractionDigits: 2 } }, { type: numeric, locale: de-DE, numericFormat: { style: currency, currency: EUR, minimumFractionDigits: 2 } }]} /Vue 3HotTable :settings{ columns: [{ type: numeric, locale: en-US, numericFormat: { style: currency, currency: USD, minimumFractionDigits: 2 } }, { type: numeric, locale: de-DE, numericFormat: { style: currency, currency: EUR, minimumFractionDigits: 2 } }] } /Angularsettings { columns: [ { type: numeric, locale: en-US, numericFormat: { style: currency, currency: USD, minimumFractionDigits: 2 } }, { type: numeric, locale: de-DE, numericFormat: { style: currency, currency: EUR, minimumFractionDigits: 2 } } ] };常用格式化风格货币Currencystyle: currency配合currency属性如USD、EUR、PLN小数Decimalstyle: decimal配合useGrouping: true显示千位分隔符百分比Percentstyle: percent单位Unitstyle: unit配合unit属性如kilometer、liter要同时添加千位分隔符和固定小数位数组合使用useGrouping与minimumFractionDigits/maximumFractionDigitsnumericFormat: { style: decimal, useGrouping: true, // 添加千位分隔符例如 1,000,000 minimumFractionDigits: 2, // 始终显示两位小数 maximumFractionDigits: 2, }可用选项全集样式选项Style options属性可选值说明styledecimal默认、currency、percent、unit使用的格式化风格currencyISO 4217 货币代码如USD、EUR、PLN当style为currency时必填currencyDisplaysymbol默认、narrowSymbol、code、name货币的显示方式currencySignstandard默认、accounting会计格式下用括号表示负数unit单位标识符如kilometer、liter当style为unit时必填unitDisplayshort默认、narrow、long单位的显示方式记数法选项Notation options属性可选值说明notationstandard默认、scientific、engineering、compact使用的记数法compactDisplayshort默认、longcompact 记数法的显示风格如1.5M对比1.5 million符号与分组选项Sign and grouping options属性可选值说明signDisplayauto默认、never、always、exceptZero、negative何时显示正负号useGroupingtrue、false默认、always、auto、min2是否使用分组分隔符如1,000数字位选项Digit options属性可选值说明minimumIntegerDigits1到21整数位的最小数量不足补零minimumFractionDigits0到100小数位的最小数量maximumFractionDigits0到100小数位的最大数量minimumSignificantDigits1到21有效数字的最小数量maximumSignificantDigits1到21有效数字的最大数量舍入选项Rounding options属性可选值说明roundingModehalfExpand默认、ceil、floor、expand、trunc、halfCeil、halfFloor、halfTrunc、halfEven舍入算法roundingPriorityauto默认、morePrecision、lessPrecision小数位与有效数字之间的优先级roundingIncrement1、2、5、10、20、25、50、100、200、250、500、1000、2000、2500、5000舍入增量如按 5 分钱舍入trailingZeroDisplayauto默认、stripIfInteger整数时是否去除末尾零区域选项Locale options属性可选值说明localeMatcherbest fit默认、lookup区域匹配算法numberingSystemlatn、arab、hans、deva、thai等使用的数字系统源码视角格式化是如何完成的数值的显示格式化最终由 numericRenderer.ts 与 utils.ts 完成。渲染器对每个单元格先做两件事右对齐与添加htNumeric样式类——前提是单元格值确实是数字通过isNumeric判定并且你没有显式指定htLeft/htCenter/htRight/htJustify对齐类见 numericRenderer.ts。随后valueFormatter调用 utils.ts 中的intlFormatterexport function intlFormatter(value: unknown, cellProperties: Recordstring, unknown) { const { numericFormat, locale } cellProperties; const options (numericFormat ?? DEFAULT_INTL_FORMAT) as Intl.NumberFormatOptions; const cacheKey ${(locale as string) ?? }:${JSON.stringify(options)}; let formatter formatterCache.get(cacheKey); if (formatter undefined) { formatter new Intl.NumberFormat(locale as string, options); formatterCache.set(cacheKey, formatter); } return formatter.format(value as number); }两个值得注意的实现细节格式化器按「locale 配置」缓存formatterCache以${locale}:${JSON.stringify(options)}为键缓存Intl.NumberFormat实例。同一 locale 同一格式的多个单元格共享一个实例避免重复构造这是「更好的性能」在源码层面的直接体现。未配置numericFormat时的默认行为DEFAULT_INTL_FORMAT为{ useGrouping: false, maximumFractionDigits: 20 }见 utils.ts即默认不分组、最多保留 20 位小数。另外若你在numericFormat中使用了旧版 API 的pattern或culture属性渲染器会通过warn()输出一次性警告提示改用Intl.NumberFormat选项见 numericRenderer.ts——这是 17.0 迁移后的兼容性提示旧格式已不再受支持。安全整数限制与 preserveNumericLiteralJavaScript 将所有数字存为双精度浮点数因此大于Number.MAX_SAFE_INTEGER9007199254740991的整数在 Handsontable 见到它之前就已经被舍入。例如以数字字面量书写9007199254740993数据中实际已经是9007199254740992。要精确显示这类数值请以字符串形式提供data: [[9007199254740993]], columns: [ { type: numeric, preserveNumericLiteral: true, // 编辑后也保持原值精确 numericFormat: { style: decimal, useGrouping: true, }, } ],此时单元格渲染为9,007,199,254,740,993。你的numericFormat依然生效且不丢失任何数字——因为Intl.NumberFormat会把字符串当作精确十进制数读取而不是先转成 number。同样的值以数字形式存储时渲染结果为9,007,199,254,740,992。以字符串存在于数据源中的值会原样精确渲染。请同时设置preserveNumericLiteral: true这样用户编辑该单元格后值仍然是字符串——如果不设置编辑操作会把输入转换为数字精度在那一刻丢失。源码视角preserveNumericLiteral 的判定逻辑preserveNumericLiteral默认false自 18.1.0 引入见 metaSchema.ts的实际判定发生在 valueSetter.ts 中。当preserveNumericLiteral true且满足以下全部条件时值以「用户输入的原始字符串」保存输入不是千位分组形式7.000→7000的解析是预期行为不保留不是十六进制字面量0x1A这类输入按解析结果保存不发生数值溢出如1e400→Infinity、1e-400→0这类超出有限 double 范围的字面量按解析结果保存是普通点分十进制字面量isNumeric通过并且转换成 JS number 会有信息损失isLossyNumericConversion为真——即存在末尾小数零如9.0或超出安全整数范围。反之能无损转换的值如9、9.5、1000仍然以数字存储因此排序、过滤和公式计算不受影响。被保留的字面量在这些特性中仍表现为数字排序与过滤条件按数值比较公式引擎将其解析为数字SUM之类的函数依然会把它计入。唯一例外筛选菜单的 Filter by value 复选框列表是严格比较因此保留的字面量9.0与其纯数字9会显示为两个独立条目。编辑器行为numericFormat选项不会改变单元格编辑器对数字的呈现与解析方式。当你编辑 numeric 单元格时无论numericFormat如何配置被编辑的数字始终以点号.作为小数点显示且不带千位分隔符或货币符号。例如编辑$7,000.02时显示为7000.02。你可以用点号.**或逗号,**输入小数分隔符。对于小数分隔符为逗号的欧洲 locale如de-DE、fr-FR、es-ES可以输入点分千位格式的值如7.000或7.000,25Handsontable 分别解析为7000和7000.25。对于其他 locale编辑结束后会根据你的numericFormat配置自动添加千位分隔符。默认情况下输入9.0会存储为数字9下次打开编辑器时显示9超大数字也会丢失精度。要保留你输入的精确文本将preserveNumericLiteral设为true详见上一节。源码视角编辑输入的解析流程编辑输入的解析由 valueSetter.ts 完成。它首先通过getCellDecimalSeparator确定该单元格偏好的小数分隔符——优先从numericFormat.culture或locale出发用Intl.NumberFormat(locale).formatToParts(1.1)探测该 locale 的小数点符号locale 无效时再回退到旧的pattern字符串猜测见 valueSetter.ts。然后它借助 helpers/number.ts 中的四个判定函数识别各种输入形态isNumericLike兼容逗号小数分隔符的普通数字number.tsisCommaThousandsGroupedInteger当小数分隔符为点号时识别1,234,567这类逗号千位分组整数number.tsisDotThousandsGroupedInteger当小数分隔符为逗号时识别7.000这类点分千位整数number.tsisDotThousandsGroupedFloat识别7.000,25这类「点分千位 逗号小数」的浮点数number.ts。命中任一形态后getParsedNumber按探测到的分隔符完成解析最终返回数字或按preserveNumericLiteral逻辑返回原始字符串。这就是「编辑时输入7.000,25会被存为7000.25」的底层实现。校验数字Numeric 单元格类型内置校验器。当单元格被校验时任何非有效数字的值都会被标记为htInvalidCSS 类——在默认主题下渲染为红色单元格背景实际颜色由主题控制。校验在编辑单元格之后运行。要校验并标记数据源中已存在的值例如loadData()之后的数据请调用validateCells()方法。两个选项控制校验器如何处理值allowInvalid默认true无效值会被保留、保存到数据源并标记为无效。将allowInvalid设为false可拒绝无效输入并保持单元格编辑器打开直到输入有效数字为止。allowEmpty默认true空单元格通过校验。将allowEmpty设为false会将空单元格标记为无效。源码视角校验器实现内置校验器 numericValidator.ts 的实现非常直观export function numericValidator( this: CellProperties, value: unknown, callback: (valid: boolean) void): void { let valueToValidate value; if (valueToValidate null || valueToValidate undefined) { valueToValidate ; } if (this.allowEmpty valueToValidate ) { callback(true); } else if (valueToValidate ) { callback(false); } else { callback(isNumeric(value)); } }即null/undefined归一为空字符串空字符串的通过与否完全取决于allowEmpty其余值交给isNumeric判定number.ts。值得注意isNumeric的判定规则它接受0.001、.001、10000、1e26、22e-26、.45e26、0xabcdef十六进制等形式但不接受- 1000符号与数字间有空格或100 000空格分组这类输入。结果与键盘快捷键完成以上配置后numeric 单元格的值右对齐显示并使用你在numericFormat中定义的格式无效非数字值会被标记为无效底层数据源中存储的是原始数字或按preserveNumericLiteral保留的精确字面量。Numeric 单元格编辑器本质上是文本编辑器因此使用标准的编辑类键盘快捷键没有数字专属的键位绑定。输入小数点时只需键入点号.或逗号,——具体行为见上文「编辑器行为」。相关功能速查围绕 numeric 单元格类型你可以在当前仓库中进一步深入配置选项numericFormat与preserveNumericLiteral的完整 JSDoc 定义见 metaSchema.ts相关的locale、type、valueFormatter、validator、allowInvalid、allowEmpty均为全局核心选项。核心方法getCellMeta()、getCellsMeta()、getDataType()、validateCells()、setCellMeta()、setCellMetaObject()、removeCellMeta()等用于读写单元格元数据与触发校验。HooksafterGetCellMeta、afterSetCellMeta、beforeGetCellMeta、beforeSetCellMeta可在元数据读写时介入自定义行为。单元格类型总览cell-type 指南介绍了各内置类型的选择方法。渲染器与校验器的单元测试见 numericRenderer.spec.js 与 numericFormat.spec.js可据此验证各格式配置的实际输出。【免费下载链接】handsontableJavaScript Data Grid / Data Table with a Spreadsheet Look Feel. Works with React, Angular, and Vue. Supported by the Handsontable team ⚡项目地址: https://gitcode.com/gh_mirrors/ha/handsontable创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

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

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

免费获取报价