
amis InputDateRange 日期范围控件详解默认值、快捷键、transform 处理函数与源码实现【免费下载链接】amis前端低代码框架通过 JSON 配置就能生成各种页面。项目地址: https://gitcode.com/GitHub_Trending/am/amis本文基于 amis 低代码框架的input-date-range表单控件文档系统讲解其默认值实际值、相对值、公式三种写法、快捷键配置、内嵌模式、双字段存储extraName以及transform日期处理函数等核心能力并结合开源仓库中 InputDateRange 渲染器、DateRangePicker 选择器 与 日期工具函数 的源码剖析每个配置项的底层处理逻辑帮助你在 amis 表单中正确构建可复用的日期范围选择方案。一、组件定位与基本用法input-date-range是 amis 表单中的日期范围输入控件用于让用户选择一段连续的日期区间。它的值本质上是一个开始时间,结束时间的字符串默认以 Unix 时间戳秒格式存储、以YYYY-MM-DD格式显示中间用分隔符delimiter默认逗号连接。从 InputDateRange.tsx 源码结构看该渲染器通过FormItem({type: input-date-range})注册进 amis 组件体系其默认属性定义在DateRangeControl.defaultProps中// packages/amis/src/renderers/Form/InputDateRange.tsx static defaultProps { format: X, // 存储格式默认为 Unix 时间戳 joinValues: true, // 是否拼接值 delimiter: ,, // 拼接分隔符 animation: true // 是否启用游标动画 };最基础的使用方式如下{ type: form, debug: true, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: select, label: 日期范围 } ] }同一套DateRangeControl基类还派生出了input-datetime-range日期时间范围inputFormat默认YYYY-MM-DD HH:mm和input-time-range时间范围viewMode为time两个变体详见 InputDateRange.tsx。本文聚焦input-date-range的日期场景。二、配置默认值实际值、相对值与公式通过value属性可以为日期范围设置默认值支持三种写法。2.1 直接指定实际值{ type: form, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: date, label: 日期范围, value: 1659283200,1661961599 } ] }两个时间戳分别对应区间开始与结束秒级 Unix 时间戳对应默认的format: X。2.2 使用相对值关键字{ type: form, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: date, label: 日期范围, value: today,1weeks } ] }支持的相对值关键字有today当前日期day或days天week或weeks周month或months月year或years年相对值的解析由 amis-core 的 filterDate 函数 完成其核心是一段正则// packages/amis-core/src/utils/date.ts export const relativeValueRe /^(.)?(\|-)(\d)(minute|min|hour|day|week|month|year|weekday|second|millisecond)s?$/i;1weeks匹配到该正则后会以当天 0 点为基准执行from.add(step, unit)若匹配的是分/秒/时级别的时间单位则基准会保留当前的时分秒。另外filterDate还支持now当前时刻与today今天 0 点两个特殊值并内置了${...}变量解析数据域中会自动注入now和today两个变量因此value也可以写成表达式动态取值。2.3 使用公式配置复杂区间对于本周一到本周日这类无法用单个相对值表达的场景可以使用 amis 表达式。注意 moment 默认一周从周日开始所以周一到周日需要STARTOF(NOW(), week)再加一天{ type: form, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: date, label: 日期范围, value: ${DATETOSTR(DATEMODIFY(STARTOF(NOW(), week), 1, day), X) , DATETOSTR(DATEMODIFY(ENDOF(NOW(), week), 1, day), X)} } ] }因为默认周日是第一天所以需要往后加一天。再看一个上月第一天到上月最后一天的例子{ type: form, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: date, label: 日期范围, value: ${DATETOSTR(STARTOF(DATEMODIFY(NOW(), -1, month), month), X) , DATETOSTR(ENDOF(DATEMODIFY(NOW(), -1, month), month), X)} } ] }这类公式由amis-formula包提供的NOW()、STARTOF()、ENDOF()、DATEMODIFY()、DATETOSTR()等函数组合完成X格式表示输出 Unix 时间戳与默认format: X保持一致。三、快捷键shortcutsshortcuts属性支持自定义快捷选择日期范围默认值为yesterday,7daysago,prevweek,thismonth,prevmonth,prevquarter见 DateRangePicker 默认属性。{ type: form, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: select, label: 日期范围, shortcuts: [ 7daysago, 15dayslater, 2weeksago, 1weekslater, thismonth, 2monthsago, 3monthslater ] } ] }3.1 内置快捷键与动态模式支持的快捷键有快捷键说明today今天yesterday昨天tomorrow明天prevweek上周thisweek本周thismonth本月prevmonth上个月prevquarter上个季度thisquarter本季度thisyear今年lastYear去年{n}daysago最近 n 天例如7daysago下面用法相同{n}dayslatern 天以内{n}weeksago最近 n 周{n}weekslatern 周以内{n}monthsago最近 n 月{n}monthslatern 月以内{n}quartersago最近 n 季度{n}quarterslatern 季度以内{n}yearsago最近 n 年{n}yearslatern 年以内源码中快捷键分为两层静态快捷键availableShortcutstoday、yesterday、prevweek、thismonth等固定键名直接查表命中每个键提供startDate/endDate两个基于now的 moment 计算函数。例如7daysago的结束时间是now.add(-1, days).endOf(day)即截至昨天 23:59:59.999这解释了为何最近 7 天不含今天。动态正则快捷键advancedRanges{n}daysago这类带数字的模式通过^(\d)daysago$等正则匹配后动态解析所以7daysago、30daysago、2weeksago都能工作无需逐一注册。renderShortcuts 方法 展示了完整的匹配顺序字符串键先查availableShortcuts未命中再逐个尝试advancedRanges正则string类型的shortcuts会被按逗号拆分后逐项处理。3.2 用表达式自定义快捷键3.1.0快捷键也支持对象数组写法startDate/endDate可以是 amis 表达式从而自定义任意区间{ type: form, debug: true, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: select, label: 日期范围, shortcuts: [ { label: 1天前, startDate: ${DATEMODIFY(NOW(), -1, day)}, endDate: ${NOW()} }, { label: 1个月前, startDate: ${DATEMODIFY(NOW(), -1, months)}, endDate: ${NOW()} }, { label: 本季度, startDate: ${STARTOF(NOW(), quarter)}, endDate: ${ENDOF(NOW(), quarter)} } ] } ] }该对象写法自3.1.0及以上版本支持。在源码层面renderShortcuts 对对象类型的条目会用isExpression()判断字段是否为${...}表达式是表达式则交给FormulaExec[formula]求值后再moment()解析是普通字符串则按valueFormat/format直接解析最后校验startDate.isValid()后才渲染。点击某个快捷键时selectShortcut 会以moment()为基准计算区间并与minDate/maxDate做moment.max/moment.min裁剪防止快捷方式越过日期限制。四、内嵌模式embed{ type: form, api: /api/mock2/form/saveForm, debug: true, body: [ { type: input-date-range, name: date, label: 日期范围, embed: true } ] }配置embed: true后日历面板不再以弹层形式展开而是内联嵌入页面。从 DateRangePicker.tsx 源码可以推断其交互差异handelEndDateChange在每次设置结束时间后会检查embed this.confirm()即内嵌模式下选完结束时间会立即触发确认而弹层模式需要用户点击确认按钮。focus/blur事件同样只在非内嵌模式下派发见第五节事件表。五、存成两个字段extraName / delimiter默认情况下日期范围存储为一个字段开始与结束时间用delimiter默认逗号拼接。如果配置extraName则会拆成两个字段分别存储{ type: form, debug: true, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: begin, extraName: end, label: 日期范围 } ] }此时表单数据中会同时存在begin与end两个键。extraName自3.3.0版本支持在 amis-core 的表单项渲染层 Item.tsx 中作为通用表单项属性声明表单取值时会将name/extraName分别映射到数据域。相关行为在测试用例 formitem.test.tsx 与 datetimeRange.test.tsx 中有覆盖。拼接与拆分的底层实现是 DateRangePicker.formatValue / unFormatValue 两个静态方法formatValue将startDate/endDate按valueFormat格式化后join(delimiter)unFormatValue则按delimiter拆分并调用filterDate解析对不合法的输入直接返回undefined注释中特别提到 moment 会把undefined当作now的历史坑因此需要结合原始值一起判断合法性。六、数据处理函数 transform3.5.06.1 时间精度规则默认情况下选择完成后开始时间对齐到当天 0 点moment().startOf(day)结束时间对齐到当天 23:59:59.999moment().endOf(day)。如果设置了timeFormat时间格式则会基于timeFormat决定最小时间单位不设置timeFormat默认按天day级处理moment().startOf(day); // 2008-08-08 00:00:00.000 moment().endOf(day); // 2008-08-08 23:59:59.999timeFormat为HH:mm:ss按秒second级处理moment().startOf(second); // 2008-08-08 08:08:08.000 moment().endOf(second); // 2008-08-08 08:08:08.999timeFormat为HH:mm按分钟minute级处理moment().startOf(minute); // 2008-08-08 08:08:00.000 moment().endOf(minute); // 2008-08-08 08:08:59.999timeFormat为HH按小时hour级处理moment().startOf(hour); // 2008-08-08 08:00:00.000 moment().endOf(hour); // 2008-08-08 08:59:59.999这与源码 filterDate 方法 中的判断分支一一对应先检查timeFormat是否包含ss秒、mm分、HH时、Q季度再对value执行startOf/endOf对齐未命中则回落为day级。6.2 transform 函数签名部分情况下即使配置timeFormat也无法满足需求例如结束时间的时分秒取当前时刻此时可以使用transform函数对时间值做进一步处理3.5.0及以上版本。函数签名如下interface TransFormFunc { ( /* 当前值Moment对象 */ value: moment.Moment, config: { /* 操作类型start起始时间end结束时间 */ type: start | end; /* 初始值最近一次选择的时间值 */ originValue: moment.Moment; /* 时间格式 */ timeFormat: string; }, /* 当前组件的属性 */ props: any, /* 当前组件数据域 */ data: any, /* moment函数 */ moment: moment ): moment.Moment; }6.3 示例结束时间设置为当前时间{ type: form, debug: true, api: /api/mock2/form/saveForm, body: [ { type: input-date-range, name: range1, label: 日期范围默认, valueFormat: YYYY-MM-DDTHH:mm:ss[Z] }, { type: input-date-range, name: range2, label: 日期范围使用transform函数, valueFormat: YYYY-MM-DDTHH:mm:ss[Z], transform: const now moment(); if (config.type end) {value.set({hours: now.hours(), minutes: now.minutes(), seconds: now.seconds(), milliseconds: now.milliseconds()})}; return value; } ] }上面示例使用transform函数将结束时间的值设置为当前时间function transform(value, config, props, data) { const now moment(); if (config.type end) { value.set({ hours: now.hours(), minutes: now.minutes(), seconds: now.seconds(), milliseconds: now.milliseconds() }); } return value; }实现上transform传入的是一段 JS 函数体字符串DateRangePicker 通过str2function(transform, value, config, props, data, moment)将其编译为真正的函数再依次以(value, {type, originValue, timeFormat}, props, data, moment)调用。注意transform执行在startOf/endOf对齐之后且返回值必须是一个moment.Moment对象。七、属性表除了支持 普通表单项属性表 中的配置以外input-date-range还支持下面一些配置属性名类型默认值说明版本valueFormatstringX日期选择器值格式3.4.0 版本后支持displayFormatstringYYYY-MM-DD日期选择器显示格式3.4.0 版本后支持placeholderstring请选择日期范围占位文本shortcutsstring \| string[] \| Array{label: string; startDate: string; endDate: string}yesterday,7daysago,prevweek,thismonth,prevmonth,prevquarter日期范围快捷键3.1.0版本后支持表达式minDatestring限制最小日期用法同 限制范围maxDatestring限制最大日期用法同 限制范围minDurationstring限制最小跨度如2daysmaxDurationstring限制最大跨度如1yearutcbooleanfalse保存 UTC 值clearablebooleantrue是否可清除embedbooleanfalse是否内联模式animationbooleantrue是否启用游标动画2.2.0extraNamestring是否存成两个字段3.3.0transformstring日期数据处理函数用来处理选择日期之后的值返回值为Moment对象3.5.0popOverContainerSelectorstring弹层挂载位置选择器会通过querySelector获取6.4.0几点源码层面的补充说明minDuration/maxDuration的字符串如2days、1year由 parseDuration 解析为moment.Duration支持 hour/day/week/month/quarter/year 等单位随后在 getEndDateByDuration / getStartDateByDuration 中用于钳制用户可选区间超出最大跨度的结束时间会被自动修正为startDate maxDuration。minDate/maxDate支持表达式如${NOW()}源码在 InputDateRange.tsx 中通过filterDate(minDate, data, valueFormat || format)做了实时解析并额外把原始字符串以minDateRaw/maxDateRaw传给选择器保证快捷键点击时能重新求值。弹层挂载非移动端下弹层容器默认取env.getModalContainer配置popOverContainerSelector后可通过querySelector指定挂载位置6.4.0起用于解决弹窗内嵌弹层被裁剪的问题。八、事件表当前组件会对外派发以下事件可以通过onEvent来监听这些事件并通过actions来配置执行的动作在actions中可以通过${事件参数名}或${event.data.[事件参数名]}来获取事件产生的数据详细请查看 事件动作。[name]表示当前组件绑定的名称即name属性如果没有配置name属性则通过value取值。事件名称事件参数说明change[name]: string组件的值时间值变化时触发focus[name]: string组件的值输入框获取焦点非内嵌模式时触发blur[name]: string组件的值输入框失去焦点非内嵌模式时触发对应 DateRangeControl 源码handleChange在调用onChange前会先dispatchEvent(change, ...)派发 change 事件onFocus/onBlur分别绑定focus/blur事件派发。九、动作表与触发示例当前组件对外暴露以下特性动作其他组件可以通过指定actionType: 动作名称、componentId: 该组件 id来触发这些动作动作配置可以通过args: {动作配置项名称: xxx}来配置具体的参数详细请查看 事件动作。动作名称动作配置说明clear-清空reset-将值重置为初始值。6.3.0 及以下版本为resetValuesetValuevalue: string更新的时间区间值用,隔开更新数据依赖格式format例如1650556800,1652889599三个动作的实现集中在 doAction 与 setData 方法clear直接调用选择器实例的clear()reset会优先从formStore.pristine表单初始值快照中按name取初始值取不到再回退resetValue属性setValue走setData先按delimiter拆分再经filterDate解析最终触发onChange。9.1 clear清空{ type: form, debug: true, body: [ { type: input-date-range, name: date, label: 日期, id: clear_text, value: 1714060800,1714319999 }, { type: button, label: 清空, onEvent: { click: { actions: [ { actionType: clear, componentId: clear_text } ] } } } ] }9.2 reset重置如果配置了resetValue则重置时使用resetValue的值否则使用初始值。{ type: form, debug: true, body: [ { type: input-date-range, name: date, label: 日期, id: reset_text, value: 1714060800,1714319999 }, { type: button, label: 重置, onEvent: { click: { actions: [ { actionType: reset, componentId: reset_text } ] } } } ] }9.3 setValue赋值{ type: form, debug: true, body: [ { type: input-date-range, name: date, label: 日期, id: setvalue_text, value: 1714060800,1714319999 }, { type: button, label: 赋值, onEvent: { click: { actions: [ { actionType: setValue, componentId: setvalue_text, args: { value: 1714060800,1714492799 } } ] } } } ] }十、调用链小结与测试验证综合上述源码input-date-range从用户操作到数据绑定的调用链是表单层DateRangeControl 注册表单项负责pristineValue初始化、事件派发change/focus/blur与特性动作clear/reset/setValue组件层DateRangePicker 管理日历弹层状态开始/结束时间、编辑态负责快捷键解析、跨度钳制、timeFormat精度对齐与transform执行工具层filterDate / parseDuration 完成相对值1weeks、表达式与时长字符串的解析。组件行为在 dateRange.test.tsx 与 inputDateRange.test.tsx 等测试用例中有覆盖可作为配置行为断言的参考。版本适用前提提示valueFormat/displayFormat需要 3.4.0extraName需要 3.3.0shortcuts表达式写法需要 3.1.0transform需要 3.5.0reset动作在 6.3.0 之前名为resetValuepopOverContainerSelector自 6.4.0 引入。在低版本中升级 schema 前请核对所用能力的版本门槛。【免费下载链接】amis前端低代码框架通过 JSON 配置就能生成各种页面。项目地址: https://gitcode.com/GitHub_Trending/am/amis创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考