# DateTimeRangePicker

基于 [Element Date Picker 日期选择器](https://element-plus.org/zh-CN/component/datetime-picker.html)

## 背景

为什么要封装这个组件？

新版的 ant 的日期时间范围选择器改变了用户体验，详情看 ant 官网 issue 相关反馈:

[DatePicker.RangePicker 的 onChange 事件的不一致和不正确的行为 #27779](https://github.com/ant-design/ant-design/issues/27779)，[作者的相关回复](https://github.com/ant-design/ant-design/issues/27779#issuecomment-728840416)

由于我们使用 Vue3，Vue3 对标的就是新的 ant 库，无法返回到旧版。

对于回到旧版体验，已经封装过一个组件，[DateTimeRangePicker](https://ktcode.keytop.cn/front-framework/kt/unity/-/tree/master/packages/components/src/DateTimeRangePicker)，

但这个是基于 antdv 的日期组件的魔改，用户体验反馈上还是稍差。

我们参考了 [Element 的日期范围选择器](https://element-plus.org/zh-CN/component/datetime-picker.html)，Element 在之前的收费系统就是用这个组件的，也养成了用户习惯。所以我们把矛头指向 Element 的日期范围选择器，但是想把 Element 的组件混入到 antd 组件库里，为了业务项目的不修改（或少修改），我们会完完全全保留之前封装的 [DateTimeRangePicker](https://ktcode.keytop.cn/front-framework/kt/unity/-/tree/master/packages/components/src/DateTimeRangePicker) 组件的所有 props、event、methods、slot 风格，需要注意以下这几个点：

## Props 对比（少频用到的 props 这里不再展示，详情看 [ant](https://antdv.com/components/date-picker-cn#%E5%85%B1%E5%90%8C%E7%9A%84-API)，[el](https://element-plus.org/zh-CN/component/datetime-picker.html#attributes)）

> tips：开启一个带有时间的日期范围组件，ant 使用 `<a-range-picker show-time />`，el-plus 使用 `<el-date-picker type="datetimerange"/>`

> tips：以下比较的 “✅” 符号为使用这个 API 风格

| 属性 vs                                                                                                                                                                                                                                                                                                                                                                                | ant（v3.2.13）                                                                  | el-plus（v2.3.1）                                                                                                   |
| -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- |
| v-mode:value（绑定值）                                                                                                                                                                                                                                                                                                                                                                 | ✅ v-mode:value（dayjs[]）                                                      | v-model（Date / number / string / Array）                                                                           |
| readonly（只读）                                                                                                                                                                                                                                                                                                                                                                       | ✅ inputReadOnly，与 el 概念有点不一样                                          | readonly                                                                                                            |
| disabled（禁用）                                                                                                                                                                                                                                                                                                                                                                       | 一致                                                                            | 一致                                                                                                                |
| editable（文本框可输入）                                                                                                                                                                                                                                                                                                                                                               | -                                                                               | ✅ editable                                                                                                         |
| allow-clear（是否显示清除按钮 ）                                                                                                                                                                                                                                                                                                                                                       | ✅ allow-clear                                                                  | clearable                                                                                                           |
| size（尺寸）                                                                                                                                                                                                                                                                                                                                                                           | ✅ size（large \| middle \| small）                                             | size（large \| default \| small）                                                                                   |
| placeholder（输入框提示文字 ）                                                                                                                                                                                                                                                                                                                                                         | ✅ placeholder（string \| [string, string]）                                    | placeholder，非范围选择时的占位内容，<br>el 使用 start-placeholder & end-placeholder 来分别表示开始和结束两个时间框 |
| format（显示在输入框中的格式）                                                                                                                                                                                                                                                                                                                                                         | 一致                                                                            | 一致                                                                                                                |
| dropdown-class-name<br>（日期范围选择器下拉框的类名 ）                                                                                                                                                                                                                                                                                                                                 | ✅ dropdown-class-name                                                          | popper-class                                                                                                        |
| separator（开始结束之间的分隔符）                                                                                                                                                                                                                                                                                                                                                      | ✅ separator（string \| v-slot:separator）                                      | range-separator（string）                                                                                           |
| default-value（选择器打开时默认显示的时间 ）TODO:                                                                                                                                                                                                                                                                                                                                      | default-picker-value（dayjs[]）                                                 | default-value （Date \| [Date, Date]），⚠️ el 目前有 bug，暂不处理                                                  |
| show-time（选择日期后的默认时间值<br>默认设置了区间 `00:00:00 ~ 23:59:59`，如不需要，请传入 `:show-time="{ defaultValue: null }"`                                                                                                                                                                                                                                                      | ✅ show-time（Object \| boolean），showTime.defaultValue                        | default-time（Date \| [Date, Date]）                                                                                |
| value-format（可选，绑定值的格式。 不指定则绑定值为 Date 对象）                                                                                                                                                                                                                                                                                                                        | 一致                                                                            | 一致                                                                                                                |
| id（等价于原生 input id 属性），<br>⚠️ 如果之前为了迎合自动化测试添加了 id 属性的话，需要更改为`[xxx,xxx]`，<br>这样 el 会为开始和结束 input 框添加对应的 id                                                                                                                                                                                                                           | 一致                                                                            | 一致                                                                                                                |
| ranges（预设时间范围快捷选择 ），默认设置了今天，昨天，本周，上周，本月，上月，近两月，如不需要，请传入 `:ranges="false"`                                                                                                                                                                                                                                                              | ✅ ranges（{ [range: string]: dayjs[] } \| { [range: string]: () => dayjs[] }） | shortcuts（[{ text: string, value: date \| function }] ）                                                           |
| disabled-date（不可选择的日期 ）                                                                                                                                                                                                                                                                                                                                                       | 一致                                                                            | 一致                                                                                                                |
| render-extra-footer（() => VNode \| v-slot:renderExtraFooter ）（这是产品提出的的一个需求，需要在下拉框底部新增提示或者其他，<br>这里可以是一个 props 传入的方式（props 传入的方式，需要注意返回的是一个 VNode，可以使用 [h 方法](https://cn.vuejs.org/api/render-function.html#h)，或者 [jsx](https://cn.vuejs.org/guide/extras/render-function.html#jsx-tsx)），也可以是一个 slot ） | -                                                                               | -                                                                                                                   |

## Event 对比

| vs                               | ant                                                                                                                                                                     | el-plus                                 |
| -------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------- |
| change（用户确认选定的值时触发） | ✅ change，change 回调有两个参数，分别为：<br>dates（[dayjs, dayjs] \| [string, string]）（受 value-format 影响）<br> dateStrings（[string, string]）（受 format 影响） | change，回调只有一个参数                |
| calendar-change                  | ✅ calendar-change，ant 的设计下，一般不会使用到                                                                                                                        | calendarChange，参照 ant 回调前两个参数 |
| 确定                             | ✅ ok，ant 的设计下，一般不会使用到                                                                                                                                     | 无，需实现，实现了 ok，但是意义不大     |
| 下拉面板打开隐藏                 | ✅ open-change                                                                                                                                                          | visible-change                          |

## “披着 ant-date-picker 皮的 el-date-picker”

保持 ant 外观风格，使 `<el-date-picker/>` 组件看着更和谐，做了以下样式/dom 处理

- 中间分隔符，使用 ant 的图标进行渲染
- 去掉头部图标
- 加上尾部图标
- hover 样式
- ...

# Usage

1. 新增依赖包 `@kt/unity-date-time-range-picker`

2.

main.ts

```ts
// 如果使用了旧版，请删除旧版的引用方式
- import { DateTimeRangePicker } from '@kt/unity-components';
+ import DateTimeRangePicker from '@kt/unity-date-time-range-picker';

/* ... */

// ✅ 推荐全局安装
app.use(DateTimeRangePicker);

// ✨ 全局 props 配置
// showTime：true， 默认开启 Start date time 00:00:00, end date time 23:59:59，showTime = { defaultValue: null } 关闭
// ranges：true，默认开启预设时间范围快捷选择，ranges = false 关闭
app.use(DateTimeRangePicker, {
  props: { showTime: { defaultValue: null }, ranges: false }
});
```

业务中使用

```html
<script lang="ts" setup>
  import dayjs, { Dayjs } from 'dayjs';
  const value = ref<[Dayjs, Dayjs]>();
</script>

<template>
  <date-time-range-picker v-model:value="value" />
</template>
```

3. i18n 处理（⚠️ 必要的处理，否则 el 默认为英语）

App.vue

```html
<script lang="ts" setup>
  // 引入 el-config-provider 类似 a-config-provider 的全局配置，底层的 provide key 采用 symbol() 所以两个框架互不干扰，可以放心使用
  import { ElConfigProvider } from '@kt/unity-date-time-range-picker';

  // 引入 el 提供的 i18n 相关资源包，目前我们业务项目需要实现 4 种语言
  import {
    zh_CN,
    en_US,
    es_ES,
    th_TH
  } from '@kt/unity-date-time-range-picker/locale';

  const EL_LOCALE_MAP: Record<string, any> = {
    zh_CN,
    en_US,
    es_ES,
    th_TH
  };

  const elLocale = ref(zh_CN);

  // 根据业务需求来实现切换，这里没有标准
  // elLocale.value = EL_LOCALE_MAP[val];
</script>

<template>
  <a-config-provider>
    <el-config-provider :locale="elLocale">
      <!-- ... -->
      <!-- ... -->
      <!-- ... -->
    </el-config-provider>
  </a-config-provider>
</template>
```

# 注意

旧版替换，目前只需要处理 Usage 这几个步骤即可，不需要操作其他。如果有遇到问题，请及时反馈或提交 [issue](https://ktcode.keytop.cn/front-framework/kt/unity/-/issues/new?issue%5Bassignee_id%5D=&issue%5Bmilestone_id%5D=)
