amis InputQuarterRange 季度范围组件详解:配置属性、取值格式与源码实现
2026/9/13 18:11:40 网站建设 项目流程

amis InputQuarterRange 季度范围组件详解:配置属性、取值格式与源码实现

【免费下载链接】amis前端低代码框架,通过 JSON 配置就能生成各种页面。项目地址: https://gitcode.com/GitHub_Trending/am/amis

在 amis 低代码框架中,input-quarter-range(季度范围)控件是表单体系里面向财报、销售看板等按季度筛选场景的专用选择器:一次点击即可选定“开始季度—结束季度”区间,并以逗号分隔的字符串写入表单数据。本文基于仓库中的组件文档与实现源码,完整覆盖它的基本用法、内嵌模式、extraName双字段存储、全部配置属性、事件与动作机制,并深入DateRangePickerparseDuration等底层实现,帮助你既会用、又懂其原理。

组件定位:季度范围选择器

从源码结构看,input-quarter-range是日期范围控件族的一员。它在 InputQuarterRange.tsx 中定义,直接继承日期范围基类InputDateRange,并复用 amis-ui 的DateRangePicker,只是把弹层视图固定为viewMode="quarters"(季度视图):

// packages/amis/src/renderers/Form/InputQuarterRange.tsx export default class QuarterRangeControl extends InputDateRange { @supportStatic() render() { // ... return ( <div className={cx(`${ns}DateRangeControl`, className)}> <DateRangePicker viewMode="quarters" // ... /> </div> ); } } @FormItem({ type: 'input-quarter-range' }) export class QuarterRangeControlRenderer extends QuarterRangeControl { static defaultProps = { format: 'X', inputFormat: 'YYYY-[Q]Q', joinValues: true, delimiter: ',', /** shortcuts的兼容配置 */ ranges: 'thisquarter,prevquarter', shortcuts: 'thisquarter,prevquarter', animation: true }; }

这段defaultProps揭示了几个文档属性表里没有直接写明的默认值:存储格式默认X(Unix 时间戳秒)、显示格式默认YYYY-[Q]Q(即2024-Q1这种形式)、起止值默认用逗号joinValues + delimiter拼成一个字符串、并且默认开启季度相关的快捷键thisquarter,prevquarter。弹层中每个季度单元格(Q1~Q4)的渲染逻辑在 DateRangePicker.tsx 的renderQuarter中实现,它用moment().year(year).quarter(quarter)定位每个季度,并根据是否落在[startDate, endDate]区间内打上选中样式。

基本用法

在表单中声明一个input-quarter-range表单项即可使用,最简配置如下(文档示例,可直接在 表单组件 体系中运行):

{ "type": "form", "api": "/api/mock2/form/saveForm", "body": [ { "type": "input-quarter-range", "name": "quarter-range", "label": "季度范围" } ] }

选择完成后,表单数据中quarter-range的值为起止两个时间戳(按默认valueFormat: "X"存储)以逗号分隔的字符串。仓库中的单元测试 inputQuarterRange.test.tsx 验证了这条链路:点击输入框、在弹层中依次点选 Q1 和 Q4、确认之后,两个输入框的显示值分别是2024-Q1(当年)与2024-Q4

fireEvent.click(inputs[0]!); fireEvent.click( await within(document.querySelector('.cxd-DateRangePicker-start')!) .findByText('Q1') ); fireEvent.click( await within(document.querySelector('.cxd-DateRangePicker-start')!) .findByText('Q4') ); fireEvent.click(getByText('确认')); const thisYear = moment().format('YYYY'); expect((inputs[0] as HTMLInputElement).value).toEqual(`${thisYear}-Q1`); expect((inputs[1] as HTMLInputElement).value).toEqual(`${thisYear}-Q4`);

这个测试同时说明:显示格式(2024-Q1)与存储格式(时间戳)是两套独立的格式体系,分别由displayFormat/valueFormat控制。

内嵌模式(embed)

配置"embed": true后,季度面板不再以弹层形式出现,而是直接内联渲染在表单项位置,适合作为页面固定筛选区使用:

{ "type": "form", "api": "/api/mock2/form/saveForm", "debug": true, "body": [ { "type": "input-quarter-range", "name": "quarter-range", "label": "季度范围", "embed": true } ] }

测试用例 inputQuarterRange.test.tsx 对embed模式还叠加了自定义格式:valueFormat: "YYYY-MM"displayFormat: "YYYY/MM",并回显value: "2021-10,2021-12",断言内联面板中起止两侧的激活季度(rdtActive)均落在 Q4。需要注意内嵌模式与焦点事件的关系:文档事件表中明确focus/blur仅在非内嵌模式下触发。

存成两个字段(extraName)

默认情况下,季度范围只会写入name指定的一个字段,起止值用delimiter(默认逗号)拼接;若配置extraName,结束值会单独写入另一个字段。文档示例:

{ "type": "form", "debug": true, "api": "/api/mock2/form/saveForm", "body": [ { "type": "input-quarter-range", "name": "begin", "extraName": "end", "label": "季度范围" } ] }

这一行为不是季度组件独有的,而是表单控件包装层的通用机制。在 wrapControl.tsx 中可以看到:当model.extraName存在时,onChange 会把区间值的第二部分单独写入extraName对应的字段,即onChange(values[1], model.extraName, false, true)。因此表单提交后数据里会得到beginend两个独立字段,而不是一个逗号拼接的字符串,便于后端直接按字段接收起止时间。

属性表

除了支持 普通表单项属性表 中的配置以外,input-quarter-range还支持下面一些配置(完整继承自文档属性表):

属性名类型默认值说明版本
valueFormatstringX日期选择器值格式3.4.0
displayFormatstringYYYY-DD日期选择器显示格式3.4.0
placeholderstring"请选择季度范围"占位文本
minDatestring限制最小日期,用法同 限制范围
maxDatestring限制最大日期,用法同 限制范围
minDurationstring限制最小跨度,如: 2quarter
maxDurationstring限制最大跨度,如:4quarter
utcbooleanfalse保存 UTC 值
clearablebooleantrue是否可清除
embedbooleanfalse是否内联模式
animationbooleantrue是否启用游标动画2.2.0
extraNamestring是否存成两个字段3.3.0
popOverContainerSelectorstring弹层挂载位置选择器,会通过querySelector获取6.4.0

此外,由于它继承自日期范围基类AMISDateRangeSchemaBase(定义于 InputDateRange.tsx),还可使用基类声明的这些属性:delimiter(分隔符,默认逗号)、format/valueFormat(存储格式,format为旧版写法)、inputFormat/displayFormat(显示格式,旧版写作inputFormat)、joinValues(是否拼接值,默认true)、startPlaceholder/endPlaceholder(起止占位符)、borderModefull/half/none)、transform(日期数据处理函数,用于自定义处理选择后的值),以及ranges(3.1.0 起废弃,建议改用shortcuts)。

几个属性在源码中的解析路径值得展开:

  • minDate/maxDate:渲染前会先经过filterDate(minDate, data, valueFormat || format)(见 InputQuarterRange.tsx),即支持在限制值里写表达式引用表单数据(如${min}),并按存储格式解析为 moment 对象。
  • minDuration/maxDuration:由 date.ts 中的parseDuration解析,正则支持的单位包括secondminutehourdayweekmonthquarteryearweekdaymillisecond(可加复数s、可带+/-前缀、支持小数),最终生成moment.Duration传给弹层做跨度校验,所以2quarter这种写法就是被这条正则直接命中的。
  • popOverContainerSelector:透传给DateRangePicker后通过querySelector获取挂载节点;移动端(mobileUI)下弹层/弹窗统一挂载到env.getModalContainer,桌面端默认也是env.getModalContainer,该属性用于解决弹层被父容器overflow裁剪等问题。
  • 默认显示格式:属性表中标注displayFormat默认值为YYYY-DD,而从源码结构看,渲染时的取值链是displayFormat || inputFormat,且季度控件的defaultProps.inputFormatYYYY-[Q]Q,因此未显式配置displayFormat时实际呈现的是2024-Q1这类“年-季度”文本,与上文测试断言一致。

快捷键(shortcuts)

季度控件的defaultProps中默认配置了shortcuts: 'thisquarter,prevquarter',对应弹层左侧的快捷选项。这两个快捷项在 DateRangePicker.tsx 的availableShortcuts中定义:

  • thisquarter(本季度):startDate = now.startOf('quarter')endDate = now.endOf('quarter')
  • prevquarter(上季度):startDate = now.startOf('quarter').add(-1, 'quarter')endDate = now.startOf('quarter').add(-1, 'day').endOf('day')

此外,源码中还实现了可扩展的“高级范围”advancedRanges,支持按正则解析的动态快捷键,其中与季度直接相关的有两组:Nquartersago(N 个季度前到今天之前一天)与Nquarterslater(本季度起 N 个季度后)。也就是说除了默认两项,你还可以配置类似3quartersago的字符串快捷项,获得“最近 3 个季度”这类常用筛选。配置形式为逗号分隔字符串或ShortCuts数组(见 InputDateRange.tsx 的shortcuts声明)。

事件表

当前组件会对外派发以下事件,可以通过onEvent来监听这些事件,并通过actions来配置执行的动作,在actions中可以通过${事件参数名}${event.data.[事件参数名]}来获取事件产生的数据,详细请查看事件动作。

[name]表示当前组件绑定的名称,即name属性,如果没有配置name属性,则通过value取值。

事件名称事件参数说明
change[name]: string组件的值时间值变化时触发
focus[name]: string组件的值输入框获取焦点(非内嵌模式)时触发
blur[name]: string组件的值输入框失去焦点(非内嵌模式)时触发

从实现上看,change事件在基类 InputDateRange.tsx 的handleChange中派发:它先调用dispatchEvent('change', resolveEventData(this.props, {value: nextValue})),随后无条件执行props.onChange(nextValue)写入表单;focus/blur则分别在DateRangePickeronFocus/onBlur回调中通过dispatchEvent派发,事件参数里的值由resolveEventData结合name/value生成。

动作表

当前组件对外暴露以下特性动作,其他组件可以通过指定actionType: 动作名称componentId: 该组件id来触发这些动作,动作配置可以通过args: {动作配置项名称: xxx}来配置具体的参数,详细请查看事件动作。

动作名称动作配置说明
clear-清空
reset-将值重置为初始值。6.3.0 及以下版本为resetValue
setValuevalue: string更新的时间区间值,用,隔开更新数据,依赖格式format,例如 '1640966400,1664553600'

这三个动作的落点在基类中:

  • clear/reset:由 doAction 处理。clear直接调用弹层实例的this.dateRef?.clear()reset先从formStore.pristine(表单初始数据)或store.pristine中按name取出初始值,取不到则回退到resetValue属性,再调用this.dateRef?.reset(pristineVal)。这正是文档中“reset 将值重置为初始值”的含义——重置目标是表单 pristine 快照里该字段对应的值。
  • setValue:由 setData 处理。传入的字符串值先按delimiter拆成起止两段,各自经filterDate解析后交给DateRangePicker.formatValue统一格式化,最后调用onChange(value)写回表单。formatValue同样接收joinValues/delimiter/utc参数,所以setValue传入'1640966400,1664553600'(时间戳)时会按当前format/valueFormat归一化后存储。

移动端与静态渲染

两点补充行为同样来自源码:

  1. 移动端适配render中透传了mobileUI标志,移动端下DateRangePicker会把季度视图切换为日历弹层形态(DateRangePicker.tsx 中对['days', 'months', 'quarters']视图启用移动端日历),且弹层容器切换为env.getModalContainer
  2. 静态展示:组件类上标注了@supportStatic()(StaticHoc.tsx),当表单以静态模式渲染(如详情展示、只读态)时,季度范围会按纯文本形式展示起止季度,而不是可交互的弹层。

小结

input-quarter-range是 amis 表单中“按季度筛选”的标准答案:默认以X(时间戳)格式存一个逗号分隔的字段,可用valueFormat/displayFormat调整存储与展示格式,用extraName拆成两个字段,用minDate/maxDate(支持表达式)与minDuration/maxDuration(如2quarter,由parseDuration校验)约束可选范围,配合thisquarter/prevquarter/Nquartersago等快捷键覆盖常见报表场景,并通过change/focus/blur事件与clear/reset/setValue动作接入 amis 的事件动作体系。其实现位于 InputQuarterRange.tsx,底层依赖 DateRangePicker.tsx,行为验证见 inputQuarterRange.test.tsx;可视化配置则对应编辑器插件 InputQuarterRange.tsx。

【免费下载链接】amis前端低代码框架,通过 JSON 配置就能生成各种页面。项目地址: https://gitcode.com/GitHub_Trending/am/amis

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询