ant-design/docs/blog/picker.zh-CN.md
二货爱吃白萝卜 e3063f244d
docs: blog picker (#50936)
* chore: blog picker

* docs: trigger update

* docs: add ref faq

* docs: update
2024-09-23 11:09:46 +08:00

100 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
title: 为什么禁用日期这么难?
date: 2024-09-23
author: zombieJ
---
在 antd 中DatePicker 的 issue 非常多。一来是由于日期选择需求的常见性,另一来是针对业务的需求会有各种各样的禁用选择组合。今天,我们就来聊聊 `disabledDate` 这个 API。
### disabledDate 不能控制时间
如其名,`disabledDate` 用于对日期进行禁用。所以当使用 DateTimePicker 时,时间部分并不会被 `disabledDate` 进行控制。而是需要通过 `disabledTime` 方法进行控制。这似乎有点反直觉,为什么需要用两个 API 管理呢?
#### 如何确定日期能选?
从直觉上看,一个日期是否禁用我们只需要将当前日期执行一次 `disabledDate` 即可知道。但是,如果当我们把面板切换成月份面板的时候,我们怎么知道当前月份是否可选呢?我们必须要对该月下的每个日期进行一次 `disabledDate` 才能确定该月下有可选日期,因此该月才能选择。
从目前看,这个逻辑似乎没问题。一个月 30 来天,月份面板 12 个月,最坏情况下,我们也只需要遍历 365 次就能知道 12 个月份是不可选的。但是以此类推,当面板切换为年、十年时。我们需要遍历的次数就会呈指数级增长,导致严重的性能问题([#39991](https://github.com/ant-design/ant-design/issues/39991))。
所以在 DatePicker 重构后,`disabledDate` 提供了额外的参数 `info.type`,告知提供的 date 对象来自于哪个 Panel。这样开发者就可以根据 Panel 来提供 `disabled` 信息,从而避免恐怖的调用循环。
但是作为兜底DatePicker 会对当前 Panel 单元的第一天和最后一天进行 `disabledDate` 的调用。这样可以确保在常见的范围禁用场景下,仍能满足需求。
#### disabledTime
回到时间禁用,这是相同的问题。如果我们通过 `disabledDate` 进行时间维度的禁用,那么在 DateTimePicker 下该天是否可选,我们就需要对该天的每一秒进行 `disabledDate` 校验。在最坏情况下,一天是否可选就需要校验 86400 次。Date Panel 就需要执行 ~200 万次。显然这是不可接受的。
所以对于时间维度,我们提供了 `disabledTime` 方法。这个方法相比 `disabledDate` 会要求提供更细细粒度的时间禁用信息:
```tsx
type DisabledTime = (now: Dayjs) => {
disabledHours?: () => number[];
disabledMinutes?: (selectedHour: number) => number[];
disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[];
disabledMilliseconds?: (
selectedHour: number,
selectedMinute: number,
selectedSecond: number,
) => number[];
};
```
(时间选择面板的每个单位都相当于日期面板的 Panel后者通过前者单位的信息来推出当前禁用单位
### 一些例子
在了解了上下文后,我们会发现 `disabledDate``disabledTime` 虽然设计是合理的,但是却有些偏底层。在业务中进行使用会比较麻烦,我们来看几个例子(当然,在业务中你需要考虑通过 HOC 来进行封装):
#### 工作时间
暂时不考虑节假日的情况,我们选择工作日的 9:00 ~ 17:00 为可选时间:
```tsx
const disabledDate = (date, info) => {
if (info.type === 'date') {
return date.day() === 0 || date.day() === 6;
}
return false;
};
const disabledTime = () => ({
disabledHours: () => {
return Array.from({ length: 24 }, (_, i) => i).filter((hour) => hour < 9 || hour > 17);
},
});
```
#### 时间日期范围
在 DatePicker 中有 `minDate``maxDate` 用于限制日期的选择范围,但是如果它们仅限于日期的限制。现在,假设我们有种场景需要带有时间的日期范围选择,比如 `2024-01-01 09:00:00` ~ `2024-01-02 17:00:00`,那么我们可以这样做:
```tsx
const disabledDate = (date, info) => {
if (info.type === 'date') {
return date.isBefore('2024-01-01', 'day') || date.isAfter('2024-01-02', 'day');
}
return !date.isSame('2024-01-01', info.type);
};
const disabledTime = (date) => {
if (date.isSame('2024-01-01', 'day')) {
return {
disabledHours: () => Array.from({ length: 24 }, (_, i) => i).filter((hour) => hour < 9),
};
}
if (date.isSame('2024-01-02', 'day')) {
return {
disabledHours: () => Array.from({ length: 24 }, (_, i) => i).filter((hour) => hour > 17),
};
}
// 只需要考虑开始和结束时间,范围外的本身已经被 `disabledDate` 禁用了
return {};
};
```
### 总结
通过 `disabledDate``disabledTime`,我们可以对日期和时间进行更细粒度的控制,以实现不同的业务需求。通过以上示例,相信你已经对这两个 API 有了更深入的了解。在实际业务中,你可以根据具体需求,结合这两个 API 来实现更多的功能。