Handsontable 日期单元格类型(date / intl-date)完全指南:格式化、校验、排序与过滤

发布时间:2026/9/20 13:26:04
Handsontable 日期单元格类型(date / intl-date)完全指南:格式化、校验、排序与过滤 Handsontable 日期单元格类型date / intl-date完全指南格式化、校验、排序与过滤【免费下载链接】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导读本文以 Handsontable 官方文档 date-cell-type.md 为核心系统讲解日期单元格类型date/intl-date的配置与实战如何用Intl.DateTimeFormat选项对象控制显示格式、如何保证 ISO 8601 源数据与校验、原生日期选择器date picker的编辑行为以及排序、过滤如何依赖底层 ISO 值。文中结合当前仓库的源码如 intlDateType.ts、dateRenderer.ts、dateValidator.ts、dateEditor.ts逐层拆解实现原理读完你可以直接在项目中落地一个显示本地化、存储标准化的日期表格。日期单元格类型概述日期单元格类型date cell type让你的单元格值以日期的方式被对待按照配置格式化显示、校验输入合法性并在编辑时弹出交互式日期选择器。在 Handsontable 中日期相关的单元格类型有两个入口intl-date基于原生Intl.DateTimeFormatAPI 的推荐类型Handsontable 18.0 起主推。date与intl-date共享同一套渲染、校验、编辑与格式化逻辑的别名类型。两者配合 ISO 8601 日期字符串YYYY-MM-DD使用源数据必须是 ISO 8601 格式显示格式则由dateFormat对象独立控制。这一源数据标准化、显示本地化的设计让排序、过滤、导出等依赖底层值的功能始终稳定可靠。从源码结构看intl-date是一个典型的组合型单元格类型。在 intlDateType.ts 中可以看到它把编辑、渲染、校验三个环节组装在一起export const CELL_TYPE intl-date; export const IntlDateCellType { CELL_TYPE, editor: IntlDateEditor, renderer: intlDateRenderer, validator: intlDateValidator, sourceDataValidator, sourceDataWarningMessage: SOURCE_DATA_WARNING_MESSAGE, valueFormatter, };其中editor用于编辑、renderer负责显示格式化、validator与sourceDataValidator负责两套校验编辑时校验与批量源数据校验、valueFormatter负责把 ISO 值转成显示文本。date类型则对应 dateCellType 中的同名实现二者复用同一批底层组件。日期单元格类型演示官方文档提供了一个多列演示example1演示三种不同的格式化风格全部基于 ISO 8601 源数据Product date产品日期dateStyle: short短样式格式化Payment date付款日期自定义month/day/year组合格式化Registration date注册日期自定义包含weekday星期、month、day、year的完整格式化。以 JavaScript 版本的 example1.js 为例核心配置如下const data [ { car: Mercedes A 160, product_date: 2002-06-15, payment_date: 2002-05-20, registration_date: 2002-07-01, }, // ... 更多行 ]; const hot new Handsontable(container, { data, colHeaders: [Car, Product date, Payment date, Registration date], columns: [ { type: text, data: car }, { type: intl-date, data: product_date, dateFormat: { dateStyle: short }, }, { type: intl-date, data: payment_date, dateFormat: { month: long, day: numeric, year: numeric }, }, { type: intl-date, data: registration_date, dateFormat: { weekday: long, year: numeric, month: long, day: numeric }, }, ], columnSorting: true, filters: true, dropdownMenu: true, height: auto, licenseKey: non-commercial-and-evaluation, autoWrapRow: true, autoWrapCol: true, });注意演示中同时开启了columnSorting: true、filters: true与dropdownMenu: true配合日期列展示排序和过滤基于 ISO 底层值的行为。演示还包含一个语言locale切换下拉菜单点击菜单项后调用hot.updateSettings({ locale: item.dataset.value })即可实时切换整张表的显示语言例如en-US、de-DE这正是显示格式与源数据解耦的直观体现——切换 locale 只改变显示文本单元格底层值始终是 ISO 8601 字符串。该演示在不同框架下的等价实现分别位于 react/example1.jsx、angular/example1.ts、vue/example1.vue。使用日期单元格类型使用**对象风格object-style**配置把type设置为intl-date或date把dateFormat设置为一个对象。语言环境locale由独立的locale选项控制。日期类型可以在三个粒度上配置1. 整个表格grid 级type: intl-date, locale: en-US, dateFormat: { year: numeric, month: 2-digit, day: 2-digit },2. 单个列column 级columns: [ { type: intl-date, locale: en-US, dateFormat: { dateStyle: short } } ],3. 单个单元格cell 级cell: [ { row: 0, col: 2, type: intl-date, locale: en-US, dateFormat: { dateStyle: medium } } ],React 中使用 JSX 属性写法HotTable typeintl-date localeen-US dateFormat{{ year: numeric, month: 2-digit, day: 2-digit }} columns{[{ type: intl-date, locale: en-US, dateFormat: { dateStyle: short } }]} /源数据格式要求对于intl-date和date单元格源数据必须使用 ISO 8601 日期格式YYYY-MM-DD日期才能正常工作。dateFormat对象只影响显示排序和过滤依赖的是底层的 ISO 值而不是格式化后的显示文本。这一点在源码中得到严格印证dateValidator.ts 中的dateValidator直接调用isValidISODate(value)判定合法性export function dateValidator(this: CellMeta, value: unknown, callback: (valid: boolean) void): void { if (this.allowEmpty isEmpty(value)) { callback(true); return; } callback(isValidISODate(value)); }同时导出的sourceDataValidator用于批量源数据校验它除了放行allowEmpty的空值和 Formulas 插件的公式表达式以开头的字符串外同样要求isValidISODate(value)为真export function sourceDataValidator(value: unknown, cellMeta: CellMeta): boolean { if (cellMeta.allowEmpty isEmpty(value)) { return true; } if (typeof value string value.startsWith()) { return true; } return isValidISODate(value); }有趣的是该函数被标记为sourceDataValidator.rowIndependent true注释说明它的结果只依赖列级/全局 meta如allowEmpty从不依赖行级 meta因此源码数据校验运行器可以跨行复用同一个列级 meta 对象避免为每个单元格物化 meta——这是批量校验性能上的一个优化细节。校验失败时intlDateValidator.ts 会给出明确的警告文案SOURCE_DATA_WARNING_MESSAGE提示期望与 ISO 8601 日期格式YYYY-MM-DD兼容的值。另外isValidISODate与parseToLocalDate等日期解析工具集中在 helpers/dateTime.ts是渲染、校验、编辑共用的底层基础设施。格式化日期要控制日期在单元格渲染器中的显示效果使用dateFormat选项。从 Handsontable 18.0 开始intl-date和date单元格类型必须使用对象形式的dateFormat它基于原生Intl.DateTimeFormatAPI语言环境由独立的locale选项控制。::: tip 提示 与时间相关的dateFormat选项hour、minute、second、timeStyle、hour12、hourCycle、fractionalSecondDigits只影响显示。由于date/intl-date的源数据只含日期这些选项渲染出的时间永远是午夜00:00:00。如果需要编辑并存储日期 时间请使用日期时间单元格类型intl-datetime。这一约束在 metaSchema.ts 的dateFormat文档注释中也有明确说明。 :::渲染与格式化源码如何工作dateFormat在渲染阶段如何生效看 dateRenderer.ts 中的valueFormatter实现const DEFAULT_INTL_FORMAT: Intl.DateTimeFormatOptions { year: numeric, month: 2-digit, day: 2-digit, }; export function valueFormatter(value: unknown, cellProperties: CellProperties): unknown { const { dateFormat, locale, allowEmpty, instance } cellProperties; if (isEmpty(value)) { return allowEmpty ? value : BAD_VALUE_TEXT; // 空值允许为空则原样返回否则显示 #bad-value# } if (typeof dateFormat string) { // 字符串形式的 dateFormat 已不支持仅警告一次并原样返回 ... return value; } const date parseToLocalDate(value); if (date null) { return BAD_VALUE_TEXT; // 非法 ISO 值显示 #bad-value# } const intlFormat isObject(dateFormat) ? dateFormat as Intl.DateTimeFormatOptions : DEFAULT_INTL_FORMAT; return new Intl.DateTimeFormat(locale, intlFormat).format(date); }几个关键点默认格式即使不配置dateFormat也会使用{ year: numeric, month: 2-digit, day: 2-digit }作为兜底对应YYYY-MM-DD样式的本地化显示。字符串形式已废弃如果传入字符串形式的dateFormat渲染器会对每个实例仅警告一次请改用Intl.DateTimeFormatOptions对象然后原样返回值。非法值占位空值且不允许为空、或无法解析为日期时显示#bad-value#定义于 helpers/constants.ts 的BAD_VALUE_TEXT。intlDateRendererintlDateRenderer.ts直接委托给dateRenderer并把valueFormatter挂载为静态属性供编辑器等其他环节复用同一套格式化逻辑。使用 Intl.DateTimeFormat 选项dateFormat选项接受Intl.DateTimeFormatoptions 的全部属性配合type: intl-date或type: date使用。不同列可以搭配不同 locale 与格式columns: [ { type: intl-date, locale: en-US, dateFormat: { year: numeric, month: 2-digit, day: 2-digit } }, { type: intl-date, locale: de-DE, dateFormat: { dateStyle: long } } ]React 等价写法HotTable columns{[{ type: intl-date, locale: en-US, dateFormat: { year: numeric, month: 2-digit, day: 2-digit } }, { type: intl-date, locale: de-DE, dateFormat: { dateStyle: long } }]} /日期专用选项速查表样式快捷方式Style shortcuts属性可选值说明dateStylefull、long、medium、short日期格式化样式星期、日、月、年、纪元timeStylefull、long、medium、short时间部分样式时、分、秒、时区名用于日期 时间场景日期时间分量选项Date-time component options属性可选值说明weekdaylong、short、narrow星期的表示方式eralong、short、narrow纪元的表示方式yearnumeric、2-digit年份表示方式monthnumeric、2-digit、long、short、narrow月份表示方式daynumeric、2-digit日表示方式dayPeriodnarrow、short、long日周期例如 amhournumeric、2-digit小时若包含时间minutenumeric、2-digit分钟secondnumeric、2-digit秒fractionalSecondDigits1、2、3秒的小数位数timeZoneNamelong、short、shortOffset、longOffset、shortGeneric、longGeneric时区显示方式语言环境与其他选项Locale and other options属性可选值说明localeMatcherbest fit默认、lookup区域匹配算法calendarchinese、gregory、persian等使用的日历系统numberingSystemlatn、arab、hans等数字系统timeZoneIANA 时区如UTC、America/New_York格式化使用的时区hour12true、false12 小时制 vs 24 小时制hourCycleh11、h12、h23、h24小时周期formatMatcherbasic、best fit默认格式匹配算法完整的属性参考见dateFormatAPI 文档 与 MDN: Intl.DateTimeFormat。编辑器行为dateFormat控制的是单元格内的显示。编辑器日期选择器或文本输入可能以归一化形式展示该值对于intl-date和date底层值始终保持 ISO 8601 格式。这一点在 dateEditor.ts 中有非常清晰的实现DateEditor继承自TextEditor但其createElements()把文本域改造成原生日期输入createElements(type?: string): void { super.createElements(input); this.TEXTAREA.setAttribute(type, date); }prepare()阶段编辑器拿到的显示值已经过valueFormatter格式化但它会把originalValue替换为原始 ISO 源数据让原生日期输入框始终收到YYYY-MM-DD字符串prepare(row, col, prop, td, value, cellProperties) { super.prepare(row, col, prop, td, value, cellProperties); ... const physicalRow this.hot.toPhysicalRow(row); this.originalValue this.hot.getSourceDataAtCell(physicalRow, col); }setValue()在值为空时回退到defaultDate如果配置了并对非 ISO 值发出警告后清空输入setValue(value?: unknown): void { if (isEmpty(value)) { value this.cellProperties.defaultDate; } if (!isValidISODate(value)) { warn(DateEditor: value must be in ISO date format (YYYY-MM-DD) ...); super.setValue(); return; } super.setValue(value); }open()通过showPicker()程序化唤起浏览器的原生日期选择器focus()时全选输入内容方便直接键入。另外init()中注册了afterSetTheme钩子切换主题非首次运行时会关闭编辑器避免主题切换时编辑器状态错乱。defaultDate选项配置日期选择器在单元格为空时预选的日期例如defaultDate: 2015-02-02它只影响选择器的初始选中值不影响已填写单元格的值详见 metaSchema.ts 中defaultDate的说明。结果与行为验证完成日期单元格类型配置后单元格按你的dateFormat配置显示格式化后的日期文本点击intl-date或date单元格会打开浏览器的原生日期选择器无论显示格式如何源数据始终以 ISO 8601 格式YYYY-MM-DD存储。排序与过滤依赖 ISO 底层值文档明确指出排序和过滤依赖于底层的 ISO 值。源码同样印证列排序插件为intl-date提供了专门的比较函数工厂 intlDate.ts其compareFunctionFactory调用 columnSorting/utils.ts 中的createIntlDateCompareFunction——基于 ISO 日期构造可比较的值从而保证按真实时间顺序排序而不是按显示文本的字典序。过滤插件在 filters/constants.ts 中为intl-date注册了完整的条件集合before、after、between、today、yesterday、tomorrow等对应的单测位于 filters/tests/condition/intlDatefilters/sortComparators.ts则复用了与列排序一致的比较逻辑。因此只要源数据保持 ISO 8601无论显示成2/15/12还是February 15, 2012排序和过滤的结果都是正确的。导出与注册intl-date同样参与了导出XLSX与模块注册体系xlsx 导出类型映射见 plugins/exportFile/types/xlsx.ts模块全量注册测试见 registry/registerAllCellTypes.unit.js。如果你使用按需注册方式需要显式registerCellType(intlDateCellType)或调用registerAllModules()演示代码中即采用后者。键盘快捷键intl-date和date单元格编辑器打开的是浏览器原生日期选择器。选择器内部的键盘导航行为由浏览器提供因此在不同浏览器与操作系统之间存在差异。在日期选择器之外Handsontable 标准的编辑类键盘快捷键依然生效如 Enter 开始编辑、Esc 取消编辑、Tab 切换单元格等。相关资源相关指南单元格类型Cell type配置选项dateFormatlocaletypedefaultDatevalueFormattervalueParservalueSettervalueGetter核心方法getCellMeta()getCellMetaAtRow()getCellsMeta()getDataType()setCellMeta()setCellMetaObject()removeCellMeta()钩子HooksafterGetCellMetaafterSetCellMetabeforeGetCellMetabeforeSetCellMeta【免费下载链接】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),仅供参考