feat: DatetimePicker 支持time和date-time类型下配置选择到秒 (#1117)

 Closes: #844
This commit is contained in:
不如摸鱼去 2025-06-17 13:39:51 +08:00 committed by GitHub
parent 60261374ac
commit f2e8fdad80
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
18 changed files with 545 additions and 141 deletions

View File

@ -64,10 +64,30 @@ const value = ref<number>(Date.now())
const value4 = ref<string>('11:12') const value4 = ref<string>('11:12')
``` ```
## time 类型(带秒)
`time` 类型设置 `use-second` 属性可以展示时分秒,绑定值为 `HH:mm:ss` 格式。
```html
<wd-datetime-picker-view type="time" v-model="value" label="时分秒" use-second />
```
```typescript
const value = ref<string>('11:12:30')
```
## datetime 类型(带秒)
`datetime` 类型设置 `use-second` 属性可以展示年月日时分秒,绑定值为时间戳。
```html
<wd-datetime-picker-view type="datetime" v-model="value" label="年月日时分秒" use-second />
```
```typescript
const value = ref<number>(Date.now())
```
## 修改内部格式 ## 修改内部格式
`formatter` 属性传入一个函数,接收 `type``value` 值,返回展示的文本内容。`type``year``month``date``hour``minute` 类型,`value``number` 类型。 `formatter` 属性传入一个函数,接收 `type``value` 值,返回展示的文本内容。`type``year``month``date``hour``minute` 类型,`value``number` 类型。
使用自定义`formatter`会关闭内置的默认`display-format`函数。 使用自定义`formatter`会关闭内置的默认`display-format`函数。
@ -133,6 +153,8 @@ const filter = (type, values) => {
| minMinute | 最小分钟time类型时生效 | number | - | 0 | - | | minMinute | 最小分钟time类型时生效 | number | - | 0 | - |
| maxMinute | 最大分钟time类型时生效 | number | - | 59 | - | | maxMinute | 最大分钟time类型时生效 | number | - | 59 | - |
| immediate-change | 是否在手指松开时立即触发picker-view的 change 事件。若不开启则会在滚动动画结束后触发 change 事件1.2.25版本起提供,仅微信小程序和支付宝小程序支持。 | boolean | - | false | 1.2.25 | | immediate-change | 是否在手指松开时立即触发picker-view的 change 事件。若不开启则会在滚动动画结束后触发 change 事件1.2.25版本起提供,仅微信小程序和支付宝小程序支持。 | boolean | - | false | 1.2.25 |
| use-second | 是否显示秒选择,仅在 time 和 datetime 类型下生效 | boolean | - | false | $LOWEST_VERSION$ |
## Events ## Events
| 事件名称 | 说明 | 参数 | 最低版本 | | 事件名称 | 说明 | 参数 | 最低版本 |

View File

@ -78,6 +78,28 @@ const value = ref<number>(Date.now())
const value4 = ref<string>('09:20') const value4 = ref<string>('09:20')
``` ```
## time 类型(带秒)
`time` 类型设置 `use-second` 属性可以展示时分秒,绑定值为 `HH:mm:ss` 格式。
```html
<wd-datetime-picker type="time" v-model="value" label="时分秒" use-second />
```
```typescript
const value = ref<string>('09:20:30')
```
## datetime 类型(带秒)
`datetime` 类型设置 `use-second` 属性可以展示年月日时分秒,绑定值为时间戳。
```html
<wd-datetime-picker type="datetime" v-model="value" label="年月日时分秒" use-second />
```
```typescript
const value = ref<number>(Date.now())
```
## 修改展示格式 ## 修改展示格式
@ -295,6 +317,7 @@ const displayFormatTabLabel = (items) => {
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - | | prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
| rules | 表单验证规则,结合`wd-form`组件使用 | `FormItemRule []` | - | `[]` | - | | rules | 表单验证规则,结合`wd-form`组件使用 | `FormItemRule []` | - | `[]` | - |
| immediate-change | 是否在手指松开时立即触发picker-view的 change 事件。若不开启则会在滚动动画结束后触发 change 事件1.2.25版本起提供,仅微信小程序和支付宝小程序支持。 | boolean | - | false | 1.2.25 | | immediate-change | 是否在手指松开时立即触发picker-view的 change 事件。若不开启则会在滚动动画结束后触发 change 事件1.2.25版本起提供,仅微信小程序和支付宝小程序支持。 | boolean | - | false | 1.2.25 |
| use-second | 是否显示秒选择,仅在 time 和 datetime 类型下生效 | boolean | - | false | $LOWEST_VERSION$ |
### FormItemRule 数据结构 ### FormItemRule 数据结构

View File

@ -65,6 +65,28 @@ const value = ref<number>(Date.now())
const value4 = ref<string>('11:12') const value4 = ref<string>('11:12')
``` ```
## Time Type (with Seconds)
`time` type with `use-second` property displays hour, minute and second, the binding value is in `HH:mm:ss` format.
```html
<wd-datetime-picker-view type="time" v-model="value" label="Hour Minute Second" use-second />
```
```typescript
const value = ref<string>('11:12:30')
```
## Datetime Type (with Seconds)
`datetime` type with `use-second` property displays year, month, day, hour, minute and second, the binding value is timestamp.
```html
<wd-datetime-picker-view type="datetime" v-model="value" label="Year Month Day Hour Minute Second" use-second />
```
```typescript
const value = ref<number>(Date.now())
```
## Modify Internal Format ## Modify Internal Format
Pass a function to the `formatter` property, which receives `type` and `value` values and returns the display text content. `type` can be `year`, `month`, `date`, `hour`, `minute`, and `value` is of type `number`. Pass a function to the `formatter` property, which receives `type` and `value` values and returns the display text content. `type` can be `year`, `month`, `date`, `hour`, `minute`, and `value` is of type `number`.
@ -131,6 +153,7 @@ const filter = (type, values) => {
| minMinute | Minimum minute, effective for time type | number | - | 0 | - | | minMinute | Minimum minute, effective for time type | number | - | 0 | - |
| maxMinute | Maximum minute, effective for time type | number | - | 59 | - | | maxMinute | Maximum minute, effective for time type | number | - | 59 | - |
| immediate-change | Whether to trigger the picker-view's change event immediately when the finger is released. If not enabled, the change event will be triggered after the scrolling animation ends. Available from version 1.2.25, only supported on WeChat Mini Program and Alipay Mini Program. | boolean | - | false | 1.2.25 | | immediate-change | Whether to trigger the picker-view's change event immediately when the finger is released. If not enabled, the change event will be triggered after the scrolling animation ends. Available from version 1.2.25, only supported on WeChat Mini Program and Alipay Mini Program. | boolean | - | false | 1.2.25 |
| use-second | Whether to display the second selection, only effective for time and datetime types | boolean | - | false | $LOWEST_VERSION$ |
## Events ## Events

View File

@ -78,6 +78,28 @@ const value = ref<number>(Date.now())
const value4 = ref<string>('09:20') const value4 = ref<string>('09:20')
``` ```
## Time Type (with Seconds)
`time` type with `use-second` property displays hour, minute and second, the binding value is in `HH:mm:ss` format.
```html
<wd-datetime-picker type="time" v-model="value" label="Hour Minute Second" use-second />
```
```typescript
const value = ref<string>('09:20:30')
```
## Datetime Type (with Seconds)
`datetime` type with `use-second` property displays year, month, day, hour, minute and second, the binding value is timestamp.
```html
<wd-datetime-picker type="datetime" v-model="value" label="Year Month Day Hour Minute Second" use-second />
```
```typescript
const value = ref<number>(Date.now())
```
## Modify Display Format ## Modify Display Format
Pass a function to the `display-format` property, which receives an array of all selected items and returns the display text content. Pass a function to the `display-format` property, which receives an array of all selected items and returns the display text content.
@ -290,6 +312,7 @@ const displayFormatTabLabel = (items) => {
| prop | Form field `model` field name, required when using form validation | string | - | - | - | | prop | Form field `model` field name, required when using form validation | string | - | - | - |
| rules | Form validation rules, used with `wd-form` component | `FormItemRule []` | - | `[]` | - | | rules | Form validation rules, used with `wd-form` component | `FormItemRule []` | - | `[]` | - |
| immediate-change | Whether to trigger the picker-view's change event immediately when the finger is released. If not enabled, the change event will be triggered after the scrolling animation ends. Available from version 1.2.25, only supported on WeChat Mini Program and Alipay Mini Program. | boolean | - | false | 1.2.25 | | immediate-change | Whether to trigger the picker-view's change event immediately when the finger is released. If not enabled, the change event will be triggered after the scrolling animation ends. Available from version 1.2.25, only supported on WeChat Mini Program and Alipay Mini Program. | boolean | - | false | 1.2.25 |
| use-second | Whether to display the second selection, only effective for time and datetime types | boolean | - | false | $LOWEST_VERSION$ |
### FormItemRule Data Structure ### FormItemRule Data Structure

View File

@ -969,6 +969,7 @@
"ri-qi-xuan-ze-1": "Date selection", "ri-qi-xuan-ze-1": "Date selection",
"ri-qi-xuan-ze-2": "Date selection", "ri-qi-xuan-ze-2": "Date selection",
"ri-qi-xuan-ze-3": "Date selection", "ri-qi-xuan-ze-3": "Date selection",
"ri-qi-xuan-ze-dai-miao": "Date selection (with seconds)",
"ri-zhou-yue-qie-huan": "Day Week Month Switching", "ri-zhou-yue-qie-huan": "Day Week Month Switching",
"ring-lei-xing-loading": "Ring type loading", "ring-lei-xing-loading": "Ring type loading",
"ru-ding-dan-chu-yu-zan-ting-zhuang-tai-jin-ru-wo-de-ding-dan-ye-mian-zhao-dao-yao-qu-xiao-de-ding-dan-dian-ji-qu-xiao-ding-dan-an-niu-xuan-ze-ding-dan-qu-xiao-yuan-yin-hou-dian-ji-xia-yi-bu-ti-jiao-shen-qing-ji-ke": "If the order is in a suspended state, go to the \"My Orders\" page, find the order you want to cancel, and click the \"Cancel Order\" button; After selecting the reason for canceling the order, click \"Next\" to submit the application.", "ru-ding-dan-chu-yu-zan-ting-zhuang-tai-jin-ru-wo-de-ding-dan-ye-mian-zhao-dao-yao-qu-xiao-de-ding-dan-dian-ji-qu-xiao-ding-dan-an-niu-xuan-ze-ding-dan-qu-xiao-yuan-yin-hou-dian-ji-xia-yi-bu-ti-jiao-shen-qing-ji-ke": "If the order is in a suspended state, go to the \"My Orders\" page, find the order you want to cancel, and click the \"Cancel Order\" button; After selecting the reason for canceling the order, click \"Next\" to submit the application.",
@ -1035,6 +1036,7 @@
"shi-jian-fan-wei-yi-nian": "Time Range: One Year", "shi-jian-fan-wei-yi-nian": "Time Range: One Year",
"shi-jian-he-di-zhi": "Time and Address", "shi-jian-he-di-zhi": "Time and Address",
"shi-jian-lei-xing": "TIME", "shi-jian-lei-xing": "TIME",
"shi-jian-xuan-ze-dai-miao": "Time selection (with seconds)",
"shi-jiao-xiao-yan": "Out of focus verification", "shi-jiao-xiao-yan": "Out of focus verification",
"shi-jiao-xiao-yan-0": "Out of focus verification", "shi-jiao-xiao-yan-0": "Out of focus verification",
"shi-jing-shan-qu": "SHIJINGSHAN", "shi-jing-shan-qu": "SHIJINGSHAN",

View File

@ -969,6 +969,7 @@
"ri-qi-xuan-ze-1": "日期选择", "ri-qi-xuan-ze-1": "日期选择",
"ri-qi-xuan-ze-2": "日期选择", "ri-qi-xuan-ze-2": "日期选择",
"ri-qi-xuan-ze-3": "日期选择", "ri-qi-xuan-ze-3": "日期选择",
"ri-qi-xuan-ze-dai-miao": "日期选择(带秒)",
"ri-zhou-yue-qie-huan": "日周月切换", "ri-zhou-yue-qie-huan": "日周月切换",
"ring-lei-xing-loading": "ring类型loading", "ring-lei-xing-loading": "ring类型loading",
"ru-ding-dan-chu-yu-zan-ting-zhuang-tai-jin-ru-wo-de-ding-dan-ye-mian-zhao-dao-yao-qu-xiao-de-ding-dan-dian-ji-qu-xiao-ding-dan-an-niu-xuan-ze-ding-dan-qu-xiao-yuan-yin-hou-dian-ji-xia-yi-bu-ti-jiao-shen-qing-ji-ke": "如订单处于暂停状态,进入“我的订单”页面,找到要取消的订单,点击“取消订单”按钮;选择订单取消原因后,点击“下一步”提交申请即可。", "ru-ding-dan-chu-yu-zan-ting-zhuang-tai-jin-ru-wo-de-ding-dan-ye-mian-zhao-dao-yao-qu-xiao-de-ding-dan-dian-ji-qu-xiao-ding-dan-an-niu-xuan-ze-ding-dan-qu-xiao-yuan-yin-hou-dian-ji-xia-yi-bu-ti-jiao-shen-qing-ji-ke": "如订单处于暂停状态,进入“我的订单”页面,找到要取消的订单,点击“取消订单”按钮;选择订单取消原因后,点击“下一步”提交申请即可。",
@ -1035,6 +1036,7 @@
"shi-jian-fan-wei-yi-nian": "时间范围一年", "shi-jian-fan-wei-yi-nian": "时间范围一年",
"shi-jian-he-di-zhi": "时间和地址", "shi-jian-he-di-zhi": "时间和地址",
"shi-jian-lei-xing": "时间类型", "shi-jian-lei-xing": "时间类型",
"shi-jian-xuan-ze-dai-miao": "时间选择(带秒)",
"shi-jiao-xiao-yan": "失焦校验", "shi-jiao-xiao-yan": "失焦校验",
"shi-jiao-xiao-yan-0": "失焦校验", "shi-jiao-xiao-yan-0": "失焦校验",
"shi-jing-shan-qu": "石景山区", "shi-jing-shan-qu": "石景山区",

View File

@ -4,10 +4,12 @@
<demo-block transparent> <demo-block transparent>
<wd-cell-group border> <wd-cell-group border>
<wd-datetime-picker :label="$t('ri-qi-xuan-ze')" v-model="value1" @confirm="handleConfirm1" /> <wd-datetime-picker :label="$t('ri-qi-xuan-ze')" v-model="value1" @confirm="handleConfirm1" />
<wd-datetime-picker :label="$t('ri-qi-xuan-ze-dai-miao')" use-second v-model="value18" />
<wd-datetime-picker :label="$t('nian-yue-ri')" v-model="value2" type="date" @confirm="handleConfirm2" /> <wd-datetime-picker :label="$t('nian-yue-ri')" v-model="value2" type="date" @confirm="handleConfirm2" />
<wd-datetime-picker :label="$t('nian-yue')" v-model="value3" type="year-month" @confirm="handleConfirm3" /> <wd-datetime-picker :label="$t('nian-yue')" v-model="value3" type="year-month" @confirm="handleConfirm3" />
<wd-datetime-picker :label="$t('nian')" v-model="value16" type="year" @confirm="handleConfirm16" /> <wd-datetime-picker :label="$t('nian')" v-model="value16" type="year" @confirm="handleConfirm16" />
<wd-datetime-picker :label="$t('shi-fen')" v-model="value4" type="time" @confirm="handleConfirm4" /> <wd-datetime-picker :label="$t('shi-fen')" v-model="value4" type="time" @confirm="handleConfirm4" />
<wd-datetime-picker :label="$t('shi-jian-xuan-ze-dai-miao')" v-model="value19" type="time" use-second />
<wd-datetime-picker :label="$t('zhan-shi-ge-shi')" v-model="value5" :display-format="displayFormat" @confirm="handleConfirm5" /> <wd-datetime-picker :label="$t('zhan-shi-ge-shi')" v-model="value5" :display-format="displayFormat" @confirm="handleConfirm5" />
<wd-datetime-picker :label="$t('nei-bu-ge-shi')" v-model="value6" :formatter="formatter" @confirm="handleConfirm6" /> <wd-datetime-picker :label="$t('nei-bu-ge-shi')" v-model="value6" :formatter="formatter" @confirm="handleConfirm6" />
<wd-datetime-picker :label="$t('guo-lv-xuan-xiang')" v-model="value7" :filter="filter" @confirm="handleConfirm7" /> <wd-datetime-picker :label="$t('guo-lv-xuan-xiang')" v-model="value7" :filter="filter" @confirm="handleConfirm7" />
@ -34,7 +36,13 @@
<wd-datetime-picker :label="$t('ri-qi-xuan-ze-1')" align-right v-model="value13" @confirm="handleConfirm13" /> <wd-datetime-picker :label="$t('ri-qi-xuan-ze-1')" align-right v-model="value13" @confirm="handleConfirm13" />
</demo-block> </demo-block>
<demo-block :title="$t('qu-yu-xuan-ze')" transparent> <demo-block :title="$t('qu-yu-xuan-ze')" transparent>
<wd-datetime-picker :label="$t('ri-qi-xuan-ze-2')" :title="$t('qing-xuan-ze-qu-jian')" v-model="value14" @confirm="handleConfirm14" /> <wd-datetime-picker
:label="$t('ri-qi-xuan-ze-2')"
:title="$t('qing-xuan-ze-qu-jian')"
v-model="value14"
use-second
@confirm="handleConfirm14"
/>
</demo-block> </demo-block>
<demo-block :title="$t('fan-wei-tab-zhan-shi-ge-shi')" transparent> <demo-block :title="$t('fan-wei-tab-zhan-shi-ge-shi')" transparent>
<wd-datetime-picker <wd-datetime-picker
@ -76,7 +84,8 @@ const value14 = ref<any[]>(['', ''])
const value15 = ref<any[]>(['', Date.now()]) const value15 = ref<any[]>(['', Date.now()])
const value16 = ref(Date.now()) const value16 = ref(Date.now())
const value17 = ref(Date.now()) const value17 = ref(Date.now())
const value18 = ref(Date.now())
const value19 = ref('09:20:26')
const minDate = ref<number>(Date.now()) const minDate = ref<number>(Date.now())
const maxDate = ref<number>(new Date(new Date().getFullYear() + 1, new Date().getMonth(), new Date().getDate()).getTime()) const maxDate = ref<number>(new Date(new Date().getFullYear() + 1, new Date().getMonth(), new Date().getDate()).getTime())

View File

@ -5,6 +5,10 @@
<wd-datetime-picker-view v-model="value1" @change="onChange1" /> <wd-datetime-picker-view v-model="value1" @change="onChange1" />
</demo-block> </demo-block>
<demo-block :title="$t('ri-qi-xuan-ze-dai-miao')" transparent>
<wd-datetime-picker-view v-model="value8" use-second />
</demo-block>
<demo-block :title="$t('nian-yue-ri')" transparent> <demo-block :title="$t('nian-yue-ri')" transparent>
<wd-datetime-picker-view type="date" v-model="value2" @change="onChange2" /> <wd-datetime-picker-view type="date" v-model="value2" @change="onChange2" />
</demo-block> </demo-block>
@ -21,6 +25,10 @@
<wd-datetime-picker-view type="time" v-model="value4" @change="onChange4" /> <wd-datetime-picker-view type="time" v-model="value4" @change="onChange4" />
</demo-block> </demo-block>
<demo-block :title="$t('shi-jian-xuan-ze-dai-miao')" transparent>
<wd-datetime-picker-view type="time" v-model="value9" use-second @change="onChange4" />
</demo-block>
<demo-block :title="$t('nei-bu-ge-shi')" transparent> <demo-block :title="$t('nei-bu-ge-shi')" transparent>
<wd-datetime-picker-view v-model="value5" :formatter="formatter" @change="onChange5" /> <wd-datetime-picker-view v-model="value5" :formatter="formatter" @change="onChange5" />
</demo-block> </demo-block>
@ -44,6 +52,9 @@ const value4 = ref<string>('11:12')
const value5 = ref<number>(Date.now()) const value5 = ref<number>(Date.now())
const value6 = ref<number>(Date.now()) const value6 = ref<number>(Date.now())
const value7 = ref<string>('') const value7 = ref<string>('')
const value8 = ref<number>(Date.now())
const value9 = ref<string>('11:12:13')
const formatter: DatetimePickerViewFormatter = (type, value) => { const formatter: DatetimePickerViewFormatter = (type, value) => {
switch (type) { switch (type) {
case 'year': case 'year':

View File

@ -84,6 +84,7 @@ $-fw-semibold: var(--wot-fw-semibold, 600) !default; // PingFangSC-Semibold
/* 尺寸 */ /* 尺寸 */
$-size-side-padding: var(--wot-size-side-padding, 15px) !default; // 屏幕两边留白 $-size-side-padding: var(--wot-size-side-padding, 15px) !default; // 屏幕两边留白
$-size-side-padding-small: var(--wot-size-side-padding-small, 6px) !default; // 屏幕两边留白小值
/*-------------------------------- Theme color application size. end --------------------------------*/ /*-------------------------------- Theme color application size. end --------------------------------*/
@ -438,7 +439,7 @@ $-picker-column-height: var(--wot-picker-column-height, 210px) !default; // 列
$-picker-column-item-height: var(--wot-picker-column-item-height, 35px) !default; // 列高 滚筒外部的高度 $-picker-column-item-height: var(--wot-picker-column-item-height, 35px) !default; // 列高 滚筒外部的高度
$-picker-column-select-bg: var(--wot-picker-column-select-bg, #f5f5f5) !default; $-picker-column-select-bg: var(--wot-picker-column-select-bg, #f5f5f5) !default;
$-picker-loading-button-color: var(--wot-picker-loading-button-color, rgba(0, 0, 0, 0.25)) !default; // loading 背景颜色 $-picker-loading-button-color: var(--wot-picker-loading-button-color, rgba(0, 0, 0, 0.25)) !default; // loading 背景颜色
$-picker-column-padding: var(--wot-picker-column-padding, 0 $-size-side-padding) !default; // 选项内间距 $-picker-column-padding: var(--wot-picker-column-padding, 0 $-size-side-padding-small) !default; // 选项内间距
$-picker-column-disabled-color: var(--wot-picker-column-disabled-color, rgba(0, 0, 0, 0.25)) !default; // 选择器选项禁用的颜色 $-picker-column-disabled-color: var(--wot-picker-column-disabled-color, rgba(0, 0, 0, 0.25)) !default; // 选择器选项禁用的颜色
$-picker-mask: var(--wot-picker-mask, linear-gradient(180deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.25))) $-picker-mask: var(--wot-picker-mask, linear-gradient(180deg, hsla(0, 0%, 100%, 0.9), hsla(0, 0%, 100%, 0.25)))

View File

@ -3,29 +3,6 @@ import { baseProps, makeBooleanProp, makeNumberProp, makeRequiredProp, makeStrin
export type DateTimeType = 'date' | 'year-month' | 'time' | 'datetime' | 'year' export type DateTimeType = 'date' | 'year-month' | 'time' | 'datetime' | 'year'
/**
* @description 便 pickerView
* @param value
* @param type picker类型
* @return {Array} pickerValue
*/
export function getPickerValue(value: string | number, type: DateTimeType) {
const values: number[] = []
const date = new Date(value)
if (type === 'time') {
const pair = String(value).split(':')
values.push(parseInt(pair[0]), parseInt(pair[1]))
} else {
values.push(date.getFullYear(), date.getMonth() + 1)
if (type === 'date') {
values.push(date.getDate())
} else if (type === 'datetime') {
values.push(date.getDate(), date.getHours(), date.getMinutes())
}
}
return values
}
export const datetimePickerViewProps = { export const datetimePickerViewProps = {
...baseProps, ...baseProps,
/** /**
@ -44,7 +21,13 @@ export const datetimePickerViewProps = {
* picker内部滚筒高 * picker内部滚筒高
*/ */
columnsHeight: makeNumberProp(217), columnsHeight: makeNumberProp(217),
/**
* key
*/
valueKey: makeStringProp('value'), valueKey: makeStringProp('value'),
/**
* label
*/
labelKey: makeStringProp('label'), labelKey: makeStringProp('label'),
/** /**
* date / year-month / time * date / year-month / time
@ -86,6 +69,18 @@ export const datetimePickerViewProps = {
* time类型时生效 * time类型时生效
*/ */
maxMinute: makeNumberProp(59), maxMinute: makeNumberProp(59),
/**
* time datetime
*/
useSecond: makeBooleanProp(false),
/**
* time datetime
*/
minSecond: makeNumberProp(0),
/**
* time datetime
*/
maxSecond: makeNumberProp(59),
/** /**
* picker-view的 change change 1.2.25 * picker-view的 change change 1.2.25
*/ */
@ -93,7 +88,7 @@ export const datetimePickerViewProps = {
startSymbol: makeBooleanProp(false) startSymbol: makeBooleanProp(false)
} }
export type DatetimePickerViewColumnType = 'year' | 'month' | 'date' | 'hour' | 'minute' export type DatetimePickerViewColumnType = 'year' | 'month' | 'date' | 'hour' | 'minute' | 'second'
export type DatetimePickerViewOption = { export type DatetimePickerViewOption = {
label: string label: string

View File

@ -0,0 +1,27 @@
import { type DateTimeType } from './types'
/**
* @description 便 pickerView
* @param value
* @param type picker类型
* @return {Array} pickerValue
*/
export function getPickerValue(value: string | number, type: DateTimeType) {
const values: number[] = []
const date = new Date(value)
if (type === 'time') {
const pair = String(value).split(':')
values.push(parseInt(pair[0]), parseInt(pair[1]))
if (pair[2]) {
values.push(parseInt(pair[2]))
}
} else {
values.push(date.getFullYear(), date.getMonth() + 1)
if (type === 'date') {
values.push(date.getDate())
} else if (type === 'datetime') {
values.push(date.getDate(), date.getHours(), date.getMinutes(), date.getSeconds())
}
}
return values
}

View File

@ -1,21 +1,19 @@
<template> <template>
<view> <wd-picker-view
<wd-picker-view ref="datePickerview"
ref="datePickerview" :custom-class="customClass"
:custom-class="customClass" :custom-style="customStyle"
:custom-style="customStyle" :immediate-change="immediateChange"
:immediate-change="immediateChange" v-model="pickerValue"
v-model="pickerValue" :columns="columns"
:columns="columns" :columns-height="columnsHeight"
:columns-height="columnsHeight" :columnChange="columnChange"
:columnChange="columnChange" :loading="loading"
:loading="loading" :loading-color="loadingColor"
:loading-color="loadingColor" @change="onChange"
@change="onChange" @pickstart="onPickStart"
@pickstart="onPickStart" @pickend="onPickEnd"
@pickend="onPickEnd" ></wd-picker-view>
></wd-picker-view>
</view>
</template> </template>
<script lang="ts"> <script lang="ts">
export default { export default {
@ -30,14 +28,9 @@ export default {
import wdPickerView from '../wd-picker-view/wd-picker-view.vue' import wdPickerView from '../wd-picker-view/wd-picker-view.vue'
import { getCurrentInstance, onBeforeMount, ref, watch } from 'vue' import { getCurrentInstance, onBeforeMount, ref, watch } from 'vue'
import { debounce, isFunction, isDef, padZero, range, isArray, isString } from '../common/util' import { debounce, isFunction, isDef, padZero, range, isArray, isString } from '../common/util'
import { import { datetimePickerViewProps, type DatetimePickerViewColumnType, type DatetimePickerViewOption, type DatetimePickerViewExpose } from './types'
getPickerValue,
datetimePickerViewProps,
type DatetimePickerViewColumnType,
type DatetimePickerViewOption,
type DatetimePickerViewExpose
} from './types'
import type { PickerViewInstance } from '../wd-picker-view/types' import type { PickerViewInstance } from '../wd-picker-view/types'
import { getPickerValue } from './util'
// //
/** @description 判断时间戳是否合法 */ /** @description 判断时间戳是否合法 */
@ -78,20 +71,11 @@ const innerValue = ref<null | string | number>(null)
const columns = ref<DatetimePickerViewOption[][]>([]) const columns = ref<DatetimePickerViewOption[][]>([])
// pickerViewvalue // pickerViewvalue
const pickerValue = ref<string | number | boolean | string[] | number[] | boolean[]>([]) const pickerValue = ref<string | number | boolean | string[] | number[] | boolean[]>([])
// created hook //
const created = ref<boolean>(false) const created = ref<boolean>(false)
const { proxy } = getCurrentInstance() as any const { proxy } = getCurrentInstance() as any
defineExpose<DatetimePickerViewExpose>({
updateColumns,
setColumns,
getSelects,
correctValue,
getPickerValue,
getOriginColumns,
...props
})
/** /**
* @description updateValue 防抖函数的占位符 * @description updateValue 防抖函数的占位符
*/ */
@ -171,8 +155,10 @@ watch(
() => props.minHour, () => props.minHour,
() => props.maxHour, () => props.maxHour,
() => props.minMinute, () => props.minMinute,
() => props.minMinute, () => props.maxMinute,
() => props.maxMinute () => props.minSecond,
() => props.maxSecond,
() => props.useSecond
], ],
() => { () => {
updateValue() updateValue()
@ -260,7 +246,7 @@ function getOriginColumns() {
*/ */
function getRanges(): Array<{ type: DatetimePickerViewColumnType; range: number[] }> { function getRanges(): Array<{ type: DatetimePickerViewColumnType; range: number[] }> {
if (props.type === 'time') { if (props.type === 'time') {
return [ const result: Array<{ type: DatetimePickerViewColumnType; range: number[] }> = [
{ {
type: 'hour', type: 'hour',
range: [props.minHour, props.maxHour] range: [props.minHour, props.maxHour]
@ -270,10 +256,17 @@ function getRanges(): Array<{ type: DatetimePickerViewColumnType; range: number[
range: [props.minMinute, props.maxMinute] range: [props.minMinute, props.maxMinute]
} }
] ]
if (props.useSecond) {
result.push({
type: 'second',
range: [props.minSecond, props.maxSecond]
})
}
return result
} }
const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = getBoundary('max', innerValue.value as number) const { maxYear, maxDate, maxMonth, maxHour, maxMinute, maxSecond } = getBoundary('max', innerValue.value as number)
const { minYear, minDate, minMonth, minHour, minMinute } = getBoundary('min', innerValue.value as number) const { minYear, minDate, minMonth, minHour, minMinute, minSecond } = getBoundary('min', innerValue.value as number)
const result: Array<{ type: DatetimePickerViewColumnType; range: number[] }> = [ const result: Array<{ type: DatetimePickerViewColumnType; range: number[] }> = [
{ {
@ -298,6 +291,13 @@ function getRanges(): Array<{ type: DatetimePickerViewColumnType; range: number[
} }
] ]
if (props.type === 'datetime' && props.useSecond) {
result.push({
type: 'second',
range: [minSecond, maxSecond]
})
}
if (props.type === 'date') result.splice(3, 2) if (props.type === 'date') result.splice(3, 2)
if (props.type === 'year-month') result.splice(2, 3) if (props.type === 'year-month') result.splice(2, 3)
if (props.type === 'year') result.splice(1, 4) if (props.type === 'year') result.splice(1, 4)
@ -316,15 +316,19 @@ function correctValue(value: string | number | Date): string | number {
value = props.minDate value = props.minDate
} else if (!isDateType && !value) { } else if (!isDateType && !value) {
// Date使 // Date使
value = `${padZero(props.minHour)}:00` value = props.useSecond ? `${padZero(props.minHour)}:00:00` : `${padZero(props.minHour)}:00`
} }
// typetime // typetime
if (!isDateType) { if (!isDateType) {
// Date // Date
let [hour, minute] = (isString(value) ? value : value.toString()).split(':') let [hour, minute, second = '00'] = (isString(value) ? value : value.toString()).split(':')
hour = padZero(range(Number(hour), props.minHour, props.maxHour)) hour = padZero(range(Number(hour), props.minHour, props.maxHour))
minute = padZero(range(Number(minute), props.minMinute, props.maxMinute)) minute = padZero(range(Number(minute), props.minMinute, props.maxMinute))
if (props.useSecond) {
second = padZero(range(Number(second), props.minSecond, props.maxSecond))
return `${hour}:${minute}:${second}`
}
return `${hour}:${minute}` return `${hour}:${minute}`
} }
@ -347,12 +351,14 @@ function getBoundary(type: 'min' | 'max', innerValue: number) {
let date: number = 1 let date: number = 1
let hour: number = 0 let hour: number = 0
let minute: number = 0 let minute: number = 0
let second: number = 0
if (type === 'max') { if (type === 'max') {
month = 12 month = 12
date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1) date = getMonthEndDay(value.getFullYear(), value.getMonth() + 1)
hour = 23 hour = 23
minute = 59 minute = 59
second = 59
} }
if (value.getFullYear() === year) { if (value.getFullYear() === year) {
@ -363,6 +369,9 @@ function getBoundary(type: 'min' | 'max', innerValue: number) {
hour = boundary.getHours() hour = boundary.getHours()
if (value.getHours() === hour) { if (value.getHours() === hour) {
minute = boundary.getMinutes() minute = boundary.getMinutes()
if (value.getMinutes() === minute) {
second = boundary.getSeconds()
}
} }
} }
} }
@ -372,7 +381,8 @@ function getBoundary(type: 'min' | 'max', innerValue: number) {
[`${type}Month`]: month, [`${type}Month`]: month,
[`${type}Date`]: date, [`${type}Date`]: date,
[`${type}Hour`]: hour, [`${type}Hour`]: hour,
[`${type}Minute`]: minute [`${type}Minute`]: minute,
[`${type}Second`]: second
} }
} }
@ -401,13 +411,17 @@ function updateColumnValue(value: string | number) {
* @return {date} innerValue * @return {date} innerValue
*/ */
function updateInnerValue() { function updateInnerValue() {
const { type } = props const { type, useSecond } = props
let innerValue: string | number = '' let innerValue: string | number = ''
const pickerVal = datePickerview.value?.getValues() || [] const pickerVal = datePickerview.value?.getValues() || []
const values = isArray(pickerVal) ? pickerVal : [pickerVal] const values = isArray(pickerVal) ? pickerVal : [pickerVal]
if (type === 'time') { if (type === 'time') {
innerValue = `${padZero(values[0])}:${padZero(values[1])}` if (useSecond) {
innerValue = `${padZero(values[0])}:${padZero(values[1])}:${padZero(values[2])}`
} else {
innerValue = `${padZero(values[0])}:${padZero(values[1])}`
}
return innerValue return innerValue
} }
@ -425,15 +439,19 @@ function updateInnerValue() {
date = (Number(values[2]) && parseInt(String(values[2]))) > maxDate ? maxDate : values[2] && parseInt(String(values[2])) date = (Number(values[2]) && parseInt(String(values[2]))) > maxDate ? maxDate : values[2] && parseInt(String(values[2]))
} }
// 34 // 345
let hour = 0 let hour = 0
let minute = 0 let minute = 0
let second = 0
if (type === 'datetime') { if (type === 'datetime') {
hour = Number(values[3]) && parseInt(values[3]) hour = Number(values[3]) && parseInt(values[3])
minute = Number(values[4]) && parseInt(values[4]) minute = Number(values[4]) && parseInt(values[4])
if (useSecond) {
second = Number(values[5]) && parseInt(values[5])
}
} }
const value = new Date(Number(year), Number(month) - 1, Number(date), hour, minute).getTime() const value = new Date(Number(year), Number(month) - 1, Number(date), hour, minute, second).getTime()
innerValue = correctValue(value) innerValue = correctValue(value)
return innerValue return innerValue
@ -456,11 +474,15 @@ function columnChange(picker: PickerViewInstance) {
date = date > maxDate ? maxDate : date date = date > maxDate ? maxDate : date
let hour: number = 0 let hour: number = 0
let minute: number = 0 let minute: number = 0
let second: number = 0
if (props.type === 'datetime') { if (props.type === 'datetime') {
hour = Number(values[3]) hour = Number(values[3])
minute = Number(values[4]) minute = Number(values[4])
if (props.useSecond) {
second = Number(values[5])
}
} }
const value = new Date(year, month - 1, date, hour, minute).getTime() const value = new Date(year, month - 1, date, hour, minute, second).getTime()
/** 根据计算选中项的时间戳,重新计算所有的选项列表 */ /** 根据计算选中项的时间戳,重新计算所有的选项列表 */
// //
innerValue.value = correctValue(value) innerValue.value = correctValue(value)
@ -497,8 +519,13 @@ function getSelects() {
if (isArray(pickerVal)) return pickerVal if (isArray(pickerVal)) return pickerVal
return [pickerVal] return [pickerVal]
} }
</script>
<style lang="scss" scoped> defineExpose<DatetimePickerViewExpose>({
@import './index.scss'; updateColumns,
</style> setColumns,
getSelects,
correctValue,
getPickerValue,
getOriginColumns
})
</script>

View File

@ -53,14 +53,6 @@ export const datetimePickerProps = {
* *
*/ */
labelWidth: makeStringProp('33%'), labelWidth: makeStringProp('33%'),
/**
* 使
*/
useDefaultSlot: makeBooleanProp(false),
/**
* label 使
*/
useLabelSlot: makeBooleanProp(false),
/** /**
* *
*/ */
@ -85,7 +77,13 @@ export const datetimePickerProps = {
* picker内部滚筒高 * picker内部滚筒高
*/ */
columnsHeight: makeNumberProp(217), columnsHeight: makeNumberProp(217),
/**
* key
*/
valueKey: makeStringProp('value'), valueKey: makeStringProp('value'),
/**
* label
*/
labelKey: makeStringProp('label'), labelKey: makeStringProp('label'),
/** /**
* type time type Array * type time type Array
@ -119,6 +117,18 @@ export const datetimePickerProps = {
* time类型时生效 * time类型时生效
*/ */
maxMinute: makeNumberProp(59), maxMinute: makeNumberProp(59),
/**
* time datetime
*/
useSecond: makeBooleanProp(false),
/**
* time datetime
*/
minSecond: makeNumberProp(0),
/**
* time datetime
*/
maxSecond: makeNumberProp(59),
/** /**
* *
*/ */

View File

@ -101,6 +101,9 @@
:min-date="minDate" :min-date="minDate"
:max-minute="maxMinute" :max-minute="maxMinute"
:min-minute="minMinute" :min-minute="minMinute"
:use-second="useSecond"
:min-second="minSecond"
:max-second="maxSecond"
:start-symbol="true" :start-symbol="true"
:immediate-change="immediateChange" :immediate-change="immediateChange"
@change="onChangeStart" @change="onChangeStart"
@ -128,6 +131,9 @@
:min-date="minDate" :min-date="minDate"
:max-minute="maxMinute" :max-minute="maxMinute"
:min-minute="minMinute" :min-minute="minMinute"
:use-second="useSecond"
:min-second="minSecond"
:max-second="maxSecond"
:start-symbol="false" :start-symbol="false"
:immediate-change="immediateChange" :immediate-change="immediateChange"
@change="onChangeEnd" @change="onChangeEnd"
@ -158,7 +164,6 @@ import { computed, getCurrentInstance, nextTick, onBeforeMount, onMounted, ref,
import { deepClone, isArray, isDef, isEqual, isFunction, padZero } from '../common/util' import { deepClone, isArray, isDef, isEqual, isFunction, padZero } from '../common/util'
import { useCell } from '../composables/useCell' import { useCell } from '../composables/useCell'
import { import {
getPickerValue,
type DatetimePickerViewInstance, type DatetimePickerViewInstance,
type DatetimePickerViewColumnFormatter, type DatetimePickerViewColumnFormatter,
type DatetimePickerViewColumnType type DatetimePickerViewColumnType
@ -168,6 +173,7 @@ import { useParent } from '../composables/useParent'
import { useTranslate } from '../composables/useTranslate' import { useTranslate } from '../composables/useTranslate'
import { datetimePickerProps, type DatetimePickerExpose } from './types' import { datetimePickerProps, type DatetimePickerExpose } from './types'
import { dayjs } from '../common/dayjs' import { dayjs } from '../common/dayjs'
import { getPickerValue } from '../wd-datetime-picker-view/util'
const props = defineProps(datetimePickerProps) const props = defineProps(datetimePickerProps)
const emit = defineEmits(['change', 'open', 'toggle', 'cancel', 'confirm', 'update:modelValue']) const emit = defineEmits(['change', 'open', 'toggle', 'cancel', 'confirm', 'update:modelValue'])
@ -334,11 +340,11 @@ function handleBoundaryValue(
currentArray: number[], currentArray: number[],
boundary: number[] boundary: number[]
): boolean { ): boolean {
const { type } = props const { type, useSecond } = props
switch (type) { switch (type) {
case 'datetime': { case 'datetime': {
const [year, month, date, hour, minute] = boundary const [year, month, date, hour, minute, second] = boundary
if (columnType === 'year') { if (columnType === 'year') {
return isStart ? value > year : value < year return isStart ? value > year : value < year
} }
@ -354,6 +360,17 @@ function handleBoundaryValue(
if (columnType === 'minute' && currentArray[0] === year && currentArray[1] === month && currentArray[2] === date && currentArray[3] === hour) { if (columnType === 'minute' && currentArray[0] === year && currentArray[1] === month && currentArray[2] === date && currentArray[3] === hour) {
return isStart ? value > minute : value < minute return isStart ? value > minute : value < minute
} }
if (
useSecond &&
columnType === 'second' &&
currentArray[0] === year &&
currentArray[1] === month &&
currentArray[2] === date &&
currentArray[3] === hour &&
currentArray[4] === minute
) {
return isStart ? value > second : value < second
}
break break
} }
case 'year-month': { case 'year-month': {
@ -387,13 +404,16 @@ function handleBoundaryValue(
break break
} }
case 'time': { case 'time': {
const [hour, minute] = boundary const [hour, minute, second] = boundary
if (columnType === 'hour') { if (columnType === 'hour') {
return isStart ? value > hour : value < hour return isStart ? value > hour : value < hour
} }
if (columnType === 'minute' && currentArray[0] === hour) { if (columnType === 'minute' && currentArray[0] === hour) {
return isStart ? value > minute : value < minute return isStart ? value > minute : value < minute
} }
if (useSecond && columnType === 'second' && currentArray[0] === hour && currentArray[1] === minute) {
return isStart ? value > second : value < second
}
break break
} }
} }
@ -725,9 +745,9 @@ function defaultDisplayFormat(items: Record<string, any>[], tabLabel: boolean =
if (props.formatter) { if (props.formatter) {
const typeMaps = { const typeMaps = {
year: ['year'], year: ['year'],
datetime: ['year', 'month', 'date', 'hour', 'minute'], datetime: props.useSecond ? ['year', 'month', 'date', 'hour', 'minute', 'second'] : ['year', 'month', 'date', 'hour', 'minute'],
date: ['year', 'month', 'date'], date: ['year', 'month', 'date'],
time: ['hour', 'minute'], time: props.useSecond ? ['hour', 'minute', 'second'] : ['hour', 'minute'],
'year-month': ['year', 'month'] 'year-month': ['year', 'month']
} }
return items return items
@ -745,9 +765,11 @@ function defaultDisplayFormat(items: Record<string, any>[], tabLabel: boolean =
case 'year-month': case 'year-month':
return `${items[0].label}-${items[1].label}` return `${items[0].label}-${items[1].label}`
case 'time': case 'time':
return `${items[0].label}:${items[1].label}` return props.useSecond ? `${items[0].label}:${items[1].label}:${items[2].label}` : `${items[0].label}:${items[1].label}`
case 'datetime': case 'datetime':
return `${items[0].label}-${items[1].label}-${items[2].label} ${items[3].label}:${items[4].label}` return props.useSecond
? `${items[0].label}-${items[1].label}-${items[2].label} ${items[3].label}:${items[4].label}:${items[5].label}`
: `${items[0].label}-${items[1].label}-${items[2].label} ${items[3].label}:${items[4].label}`
} }
} }

View File

@ -1,7 +1,10 @@
import { mount } from '@vue/test-utils' import { mount } from '@vue/test-utils'
import WdDatetimePickerView from '@/uni_modules/wot-design-uni/components/wd-datetime-picker-view/wd-datetime-picker-view.vue' import WdDatetimePickerView from '@/uni_modules/wot-design-uni/components/wd-datetime-picker-view/wd-datetime-picker-view.vue'
import { describe, expect, test, vi } from 'vitest' import { describe, expect, test, vi } from 'vitest'
import { DatetimePickerViewFilter, DatetimePickerViewFormatter } from '@/uni_modules/wot-design-uni/components/wd-datetime-picker-view/types' import {
type DatetimePickerViewFilter,
type DatetimePickerViewFormatter
} from '@/uni_modules/wot-design-uni/components/wd-datetime-picker-view/types'
import { nextTick } from 'vue' import { nextTick } from 'vue'
describe('WdDatetimePickerView 日期时间选择器视图', () => { describe('WdDatetimePickerView 日期时间选择器视图', () => {
@ -314,4 +317,108 @@ describe('WdDatetimePickerView 日期时间选择器视图', () => {
expect(wrapper.props('type')).toBe('year-month') expect(wrapper.props('type')).toBe('year-month')
}) })
test('useSecond 属性 - 时间类型', async () => {
const wrapper = mount(WdDatetimePickerView, {
props: {
modelValue: '12:30:45',
type: 'time',
useSecond: true
}
})
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('type')).toBe('time')
expect(wrapper.props('modelValue')).toBe('12:30:45')
})
test('useSecond 属性 - 日期时间类型', async () => {
const now = Date.now()
const wrapper = mount(WdDatetimePickerView, {
props: {
modelValue: now,
type: 'datetime',
useSecond: true
}
})
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('type')).toBe('datetime')
expect(wrapper.props('modelValue')).toBe(now)
})
test('useSecond 属性 - 时间范围限制', async () => {
const wrapper = mount(WdDatetimePickerView, {
props: {
modelValue: '12:30:45',
type: 'time',
useSecond: true,
minSecond: 0,
maxSecond: 30
}
})
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('minSecond')).toBe(0)
expect(wrapper.props('maxSecond')).toBe(30)
})
test('useSecond 属性 - 日期时间范围限制', async () => {
const now = Date.now()
const wrapper = mount(WdDatetimePickerView, {
props: {
modelValue: now,
type: 'datetime',
useSecond: true,
minSecond: 0,
maxSecond: 30
}
})
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('minSecond')).toBe(0)
expect(wrapper.props('maxSecond')).toBe(30)
})
test('useSecond 属性 - 时间格式化', async () => {
const formatter: DatetimePickerViewFormatter = (type, value) => {
if (type === 'second') {
return value + '秒'
}
return value
}
const wrapper = mount(WdDatetimePickerView, {
props: {
modelValue: '12:30:45',
type: 'time',
useSecond: true,
formatter
}
})
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('formatter')).toBe(formatter)
})
test('useSecond 属性 - 时间过滤', async () => {
const filter: DatetimePickerViewFilter = (type, values) => {
if (type === 'second') {
return values.filter((value) => value % 5 === 0)
}
return values
}
const wrapper = mount(WdDatetimePickerView, {
props: {
modelValue: '12:30:45',
type: 'time',
useSecond: true,
filter
}
})
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('filter')).toBe(filter)
})
}) })

View File

@ -668,4 +668,148 @@ describe('WdDatetimePicker 日期时间选择器', () => {
expect(wrapper.props('prop')).toBe('date') expect(wrapper.props('prop')).toBe('date')
expect(wrapper.props('rules')).toEqual(rules) expect(wrapper.props('rules')).toEqual(rules)
}) })
test('useSecond 属性 - 时间类型', async () => {
const wrapper = mount(WdDatetimePicker, {
props: {
modelValue: '12:30:45',
type: 'time',
useSecond: true
},
global: {
components: globalComponents
}
})
await wrapper.vm.$nextTick()
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('type')).toBe('time')
expect(wrapper.props('modelValue')).toBe('12:30:45')
expect(wrapper.find('.wd-picker__value').text()).toBe('12:30:45')
})
test('useSecond 属性 - 日期时间类型', async () => {
const now = Date.now()
const wrapper = mount(WdDatetimePicker, {
props: {
modelValue: now,
type: 'datetime',
useSecond: true
},
global: {
components: globalComponents
}
})
await wrapper.vm.$nextTick()
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('type')).toBe('datetime')
expect(wrapper.props('modelValue')).toBe(now)
})
test('useSecond 属性 - 时间范围限制', async () => {
const wrapper = mount(WdDatetimePicker, {
props: {
modelValue: '12:30:45',
type: 'time',
useSecond: true,
minSecond: 0,
maxSecond: 30
},
global: {
components: globalComponents
}
})
await wrapper.vm.$nextTick()
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('minSecond')).toBe(0)
expect(wrapper.props('maxSecond')).toBe(30)
})
test('useSecond 属性 - 日期时间范围限制', async () => {
const now = Date.now()
const wrapper = mount(WdDatetimePicker, {
props: {
modelValue: now,
type: 'datetime',
useSecond: true,
minSecond: 0,
maxSecond: 30
},
global: {
components: globalComponents
}
})
await wrapper.vm.$nextTick()
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('minSecond')).toBe(0)
expect(wrapper.props('maxSecond')).toBe(30)
})
test('useSecond 属性 - 自定义显示格式', async () => {
const displayFormat = vi.fn((items) => {
return `${items[0].label}${items[1].label}${items[2].label}`
})
const wrapper = mount(WdDatetimePicker, {
props: {
modelValue: '12:30:45',
type: 'time',
useSecond: true,
displayFormat
},
global: {
components: globalComponents
}
})
await wrapper.vm.$nextTick()
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('displayFormat')).toBe(displayFormat)
expect(displayFormat).toHaveBeenCalled()
})
test('useSecond 属性 - 范围选择', async () => {
const startDate = new Date(2024, 0, 1, 12, 30, 45).getTime()
const endDate = new Date(2024, 0, 1, 13, 30, 45).getTime()
const wrapper = mount(WdDatetimePicker, {
props: {
modelValue: [startDate, endDate],
type: 'datetime',
useSecond: true
},
global: {
components: globalComponents
}
})
await wrapper.vm.$nextTick()
expect(wrapper.props('useSecond')).toBe(true)
expect(Array.isArray(wrapper.props('modelValue'))).toBe(true)
expect(wrapper.props('modelValue')).toEqual([startDate, endDate])
})
test('useSecond 属性 - 表单验证', async () => {
const rules = [{ required: true, message: '请选择时间' }]
const wrapper = mount(WdDatetimePicker, {
props: {
modelValue: '',
type: 'time',
useSecond: true,
prop: 'time',
rules
},
global: {
components: globalComponents
}
})
await wrapper.vm.$nextTick()
expect(wrapper.props('useSecond')).toBe(true)
expect(wrapper.props('prop')).toBe('time')
expect(wrapper.props('rules')).toEqual(rules)
})
}) })

View File

@ -355,50 +355,6 @@ describe('useUpload', () => {
}) })
}) })
// 测试选择媒体文件
it('should choose media files', async () => {
const mockChooseMedia = vi.fn().mockImplementation((options) => {
options.success({
tempFiles: [
{
fileType: 'image',
tempFilePath: 'temp/image.jpg',
size: 1024,
duration: 0
},
{
fileType: 'video',
tempFilePath: 'temp/video.mp4',
thumbTempFilePath: 'temp/thumb.jpg',
size: 10240,
duration: 15
}
]
})
})
;(global as any).uni.chooseMedia = mockChooseMedia
const files = await chooseFile({
accept: 'media',
multiple: true,
maxCount: 9,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
compressed: true,
maxDuration: 60,
camera: 'back'
})
expect(files).toHaveLength(2)
expect(files[1]).toEqual({
type: 'video',
path: 'temp/video.mp4',
thumb: 'temp/thumb.jpg',
size: 10240,
duration: 15
})
})
// 测试选择文件失败的情况 // 测试选择文件失败的情况
it('should handle choose file failure', async () => { it('should handle choose file failure', async () => {
const mockChooseImage = vi.fn().mockImplementation((options) => { const mockChooseImage = vi.fn().mockImplementation((options) => {