feat: Calendar 优化选中样式和滚动位置处理并支持屏蔽内置cell (#768)

This commit is contained in:
不如摸鱼去 2024-12-08 20:39:04 +08:00 committed by GitHub
parent 995a65f845
commit 97c40047e8
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
20 changed files with 319 additions and 203 deletions

View File

@ -137,14 +137,27 @@ function handleChange({ value }) {
设置 `formatter` 参数,其值为函数类型,接收一个 `object` 参数,返回一个对象,对象的属性保持跟入参的属性一致,其属性如下:
| 属性 | 类型 | 说明 | 最低版本 |
| ---------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| type | string | 日期类型,'selected' - 单日期选中,'start' - 范围开始日期,'end' - 范围结束日期,'middle' - 范围开始与结束之间的日期,'same' - 范围开始与结束日期同一天 | - |
| date | timestamp | 13 位的时间戳 | - |
| text | string | 日期文本内容 | - |
| topInfo | string | 上方提示信息 | - |
| bottomInfo | string | 下方提示信息 | - |
| disabled | boolean | 是否禁用 | - |
| 属性 | 类型 | 说明 | 最低版本 |
| ---------- | --------------- | ------------------------------------------- | -------- |
| type | CalendarDayType | 可选值见[CalendarDayType](#calendardaytype) | - |
| date | timestamp | 13 位的时间戳 | - |
| text | string | 日期文本内容 | - |
| topInfo | string | 上方提示信息 | - |
| bottomInfo | string | 下方提示信息 | - |
| disabled | boolean | 是否禁用 | - |
### CalendarDayType
| 类型 | 说明 |
| ----------------- | ------------------------------------ |
| selected | 单日期选中 |
| start | 范围开始日期 |
| end | 范围结束日期 |
| middle | 范围开始与结束之间的日期 |
| same | 范围开始与结束日期同一天 |
| current | 当前日期 |
| multiple-middle | 多日期范围选择,开始与结束之间的日期 |
| multiple-selected | 多日期范围选择,选中的日期 |
```html
<wd-calendar-view type="daterange" v-model="value" allow-same-day :formatter="formatter" @change="handleChange"></wd-calendar-view>
@ -218,23 +231,23 @@ function handleChange({ value }) {
## Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
| ----------------- | ---------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------- | --------------------- | -------- |
| v-model | 选中值,为 13 位时间戳或时间戳数组 | null / number / array | - | - | - |
| type | 日期类型 | string | date / dates / datetime / week / month / daterange / datetimerange / weekrange / monthrange | date | - |
| min-date | 最小日期,为 13 位时间戳 | number | - | 当前日期往前推 6 个月 | - |
| max-date | 最大日期,为 13 位时间戳 | number | - | 当前日期往后推 6 个月 | - |
| first-day-of-week | 周起始天 | number | - | 0 | - |
| formatter | 日期格式化函数 | function | - | - | - |
| max-range | type 为范围选择时有效,最大日期范围 | number | - | - | - |
| range-prompt | type 为范围选择时有效,选择超出最大日期范围时的错误提示文案 | string | - | 选择天数不能超过 x 天 | - |
| allow-same-day | type 为范围选择时有效,是否允许选择同一天 | boolean | - | false | - |
| show-panel-title | 是否展示面板标题,自动计算当前滚动的日期月份 | boolean | - | true | - |
| default-time | 选中日期所使用的当日内具体时刻 | string / array | - | 00:00:00 | - |
| panel-height | 可滚动面板的高度 | number | - | 378 | - |
| time-filter | type 为 'datetime' 或 'datetimerange' 时有效,用于过滤时间选择器的数据 | function | - | - | - |
| hide-second | type 为 'datetime' 或 'datetimerange' 时有效,是否不展示秒修改 | boolean | - | false | - |
| immediate-change | type 为 'datetime' 或 'datetimerange' 时有是否在手指松开时立即触发picker-view的 change 事件。若不开启则会在滚动动画结束后触发 change 事件1.2.25版本起提供,仅微信小程序和支付宝小程序支持。 | boolean | - | false | 1.2.25 |
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------- | --------------------- | -------- |
| v-model | 选中值,为 13 位时间戳或时间戳数组 | null / number / array | - | - | - |
| type | 日期类型 | string | date / dates / datetime / week / month / daterange / datetimerange / weekrange / monthrange | date | - |
| min-date | 最小日期,为 13 位时间戳 | number | - | 当前日期往前推 6 个月 | - |
| max-date | 最大日期,为 13 位时间戳 | number | - | 当前日期往后推 6 个月 | - |
| first-day-of-week | 周起始天 | number | - | 0 | - |
| formatter | 日期格式化函数 | function | - | - | - |
| max-range | type 为范围选择时有效,最大日期范围 | number | - | - | - |
| range-prompt | type 为范围选择时有效,选择超出最大日期范围时的错误提示文案 | string | - | 选择天数不能超过 x 天 | - |
| allow-same-day | type 为范围选择时有效,是否允许选择同一天 | boolean | - | false | - |
| show-panel-title | 是否展示面板标题,自动计算当前滚动的日期月份 | boolean | - | true | - |
| default-time | 选中日期所使用的当日内具体时刻 | string / array | - | 00:00:00 | - |
| panel-height | 可滚动面板的高度 | number | - | 378 | - |
| time-filter | type 为 'datetime' 或 'datetimerange' 时有效,用于过滤时间选择器的数据 | function | - | - | - |
| hide-second | type 为 'datetime' 或 'datetimerange' 时有效,是否不展示秒修改 | boolean | - | false | - |
| immediate-change | type 为 'datetime' 或 'datetimerange' 时有,是否在手指松开时立即触发 picker-view 的 change 事件。若不开启则会在滚动动画结束后触发 change 事件1.2.25 版本起提供,仅微信小程序和支付宝小程序支持。 | boolean | - | false | 1.2.25 |
## Events

View File

@ -145,14 +145,27 @@ function handleConfirm({ value }) {
设置 `formatter` 参数,其值为函数类型,接收一个 `object` 参数,返回一个对象,对象的属性保持跟入参的属性一致,其属性如下:
| 属性 | 类型 | 说明 | 最低版本 |
| ---------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- |
| type | string | 日期类型,'selected' - 单日期选中,'start' - 范围开始日期,'end' - 范围结束日期,'middle' - 范围开始与结束之间的日期,'same' - 范围开始与结束日期同一天 | - |
| date | timestamp | 13 位的时间戳 | - |
| text | string | 日期文本内容 | - |
| topInfo | string | 上方提示信息 | - |
| bottomInfo | string | 下方提示信息 | - |
| disabled | boolean | 是否禁用 | - |
| 属性 | 类型 | 说明 | 最低版本 |
| ---------- | --------------- | ------------------------------------------- | -------- |
| type | CalendarDayType | 可选值见[CalendarDayType](#calendardaytype) | - |
| date | timestamp | 13 位的时间戳 | - |
| text | string | 日期文本内容 | - |
| topInfo | string | 上方提示信息 | - |
| bottomInfo | string | 下方提示信息 | - |
| disabled | boolean | 是否禁用 | - |
### CalendarDayType
| 类型 | 说明 | 最低版本 |
| ----------------- | ------------------------------------ | ---------------- |
| selected | 单日期选中 | - |
| start | 范围开始日期 | - |
| end | 范围结束日期 | - |
| middle | 范围开始与结束之间的日期 | - |
| same | 范围开始与结束日期同一天 | - |
| current | 当前日期 | - |
| multiple-middle | 多日期范围选择,开始与结束之间的日期 | $LOWEST_VERSION$ |
| multiple-selected | 多日期范围选择,选中的日期 | $LOWEST_VERSION$ |
```html
<wd-calendar type="daterange" v-model="value" allow-same-day :formatter="formatter" @confirm="handleConfirm" />
@ -210,7 +223,7 @@ const formatter = (day) => {
```html
<wd-calendar
label="快捷选项"
:shortcuts ="shortcuts "
:shortcuts="shortcuts "
:on-shortcuts-click="onShortcutsClick"
type="daterange"
v-model="value"
@ -243,7 +256,6 @@ const onShortcutsClick = ({ item }) => {
return [startDate, endDate]
}
function handleConfirm({ value }) {
console.log(value)
}
@ -319,7 +331,6 @@ const beforeConfirm = ({ value, resolve }) => {
function handleConfirm({ value }) {
console.log(value)
}
```
## 最大范围限制
@ -340,7 +351,7 @@ function handleConfirm({ value }) {
```html
<view style="margin-bottom: 10px;">当前选中日期:{{ formatValue }}</view>
<wd-calendar use-default-slot v-model="value" @confirm="handleConfirm4">
<wd-calendar v-model="value" @confirm="handleConfirm4">
<wd-button>选择日期</wd-button>
</wd-calendar>
```
@ -352,70 +363,99 @@ const formatValue = ref<string>('')
function handleConfirm4({ value }) {
formatValue.value = new Date(value).toString()
}
```
## 使用组件实例方法
通过 ref 可以获取到 Calendar 实例并调用实例方法,通过 `with-cell` 可以屏蔽组件内部的 cell 选择器。
```html
<wd-button @click="openCalendar">打开日历</wd-button>
<wd-calendar ref="calendar" :with-cell="false" v-model="value" @confirm="handleConfirm" />
```
```typescript
import { ref } from 'vue'
import type { CalendarInstance } from '@/uni_modules/wot-design-uni/components/wd-calendar/types'
const calendar = ref<CalendarInstance>()
const value = ref<number>(Date.now())
function openCalendar() {
calendar.value?.open()
}
function closeCalendar() {
calendar.value?.close()
}
function handleConfirm({ value }) {
console.log(value)
}
```
## Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
| ---------------------- | --------------------------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------- | --------------------- | -------- |
| v-model | 选中值,为 13 位时间戳或时间戳数组 | null / number / array | - | - | - |
| type | 日期类型 | string | date / dates / datetime / week / month / daterange / datetimerange / weekrange / monthrange | date | - |
| min-date | 最小日期,为 13 位时间戳 | number | - | 当前日期往前推 6 个月 | - |
| max-date | 最大日期,为 13 位时间戳 | number | - | 当前日期往后推 6 个月 | - |
| first-day-of-week | 周起始天 | number | - | 0 | - |
| formatter | 日期格式化函数 | function | - | - | - |
| max-range | type 为范围选择时有效,最大日期范围 | number | - | - | - |
| range-prompt | type 为范围选择时有效,选择超出最大日期范围时的错误提示文案 | string | - | 选择天数不能超过 x 天 | - |
| allow-same-day | type 为范围选择时有效,是否允许选择同一天 | boolean | - | false | - |
| default-time | 选中日期所使用的当日内具体时刻 | string / array | - | 00:00:00 | - |
| time-filter | type 为 'datetime' 或 'datetimerange' 时有效,用于过滤时间选择器的数据 | function | - | - | - |
| hide-second | type 为 'datetime' 或 'datetimerange' 时有效,是否不展示秒修改 | boolean | - | false | - |
| show-confirm | 是否显示确定按钮 | boolean | - | true | - |
| show-type-switch | 是否显示类型切换功能 | boolean | - | false | - |
| shortcuts | 快捷选项,为对象数组,其中对象的 `text` 必传 | array | - | - | - |
| title | 弹出层标题 | string | - | 选择日期 | - |
| label | 选择器左侧文案 | string | - | - | - |
| placeholder | 选择器占位符 | string | - | 请选择 | - |
| disabled | 禁用 | boolean | - | fasle | - |
| readonly | 只读 | boolean | - | false | - |
| display-format | 自定义展示文案的格式化函数,返回一个字符串 | function | - | - | - |
| inner-display-format | 自定义范围选择类型的面板内部回显,返回一个字符串 | function | - | - | - |
| size | 设置选择器大小 | string | large | - | - |
| label-width | 设置左侧标题宽度 | string | - | 33% | - |
| error | 是否为错误状态,错误状态时右侧内容为红色 | boolean | - | false | - |
| required | 必填样式 | boolean | - | false | - |
| center | 是否垂直居中 | boolean | - | false | - |
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
| align-right | 选择器的值靠右展示 | boolean | - | false | - |
| before-confirm | 确定前校验函数,接收 { value, resolve } 参数,通过 resolve 继续执行resolve 接收 1 个 boolean 参数 | function | - | - | - |
| use-default-slot | 使用默认插槽时设置该选项 | boolean | - | false | - |
| use-label-slot | 使用 label 插槽时设置该选项 | boolean | - | false | - |
| close-on-click-modal | 点击遮罩是否关闭 | boolean | - | true | - |
| z-index | 弹窗层级 | number | - | 15 | - |
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离iphone X 类型的机型) | boolean | - | true | - |
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
| rules | 表单验证规则,结合`wd-form`组件使用 | `FormItemRule []` | - | `[]` | - |
| immediate-change | type 为 'datetime' 或 'datetimerange' 时有是否在手指松开时立即触发picker-view的 change 事件。若不开启则会在滚动动画结束后触发 change 事件1.2.25版本起提供,仅微信小程序和支付宝小程序支持。 | boolean | - | false | 1.2.25 |
| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 |
| ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------- | --------------------- | ---------------- |
| v-model | 选中值,为 13 位时间戳或时间戳数组 | null / number / array | - | - | - |
| type | 日期类型 | string | date / dates / datetime / week / month / daterange / datetimerange / weekrange / monthrange | date | - |
| min-date | 最小日期,为 13 位时间戳 | number | - | 当前日期往前推 6 个月 | - |
| max-date | 最大日期,为 13 位时间戳 | number | - | 当前日期往后推 6 个月 | - |
| first-day-of-week | 周起始天 | number | - | 0 | - |
| formatter | 日期格式化函数 | function | - | - | - |
| max-range | type 为范围选择时有效,最大日期范围 | number | - | - | - |
| range-prompt | type 为范围选择时有效,选择超出最大日期范围时的错误提示文案 | string | - | 选择天数不能超过 x 天 | - |
| allow-same-day | type 为范围选择时有效,是否允许选择同一天 | boolean | - | false | - |
| default-time | 选中日期所使用的当日内具体时刻 | string / array | - | 00:00:00 | - |
| time-filter | type 为 'datetime' 或 'datetimerange' 时有效,用于过滤时间选择器的数据 | function | - | - | - |
| hide-second | type 为 'datetime' 或 'datetimerange' 时有效,是否不展示秒修改 | boolean | - | false | - |
| show-confirm | 是否显示确定按钮 | boolean | - | true | - |
| show-type-switch | 是否显示类型切换功能 | boolean | - | false | - |
| shortcuts | 快捷选项,为对象数组,其中对象的 `text` 必传 | array | - | - | - |
| title | 弹出层标题 | string | - | 选择日期 | - |
| label | 选择器左侧文案 | string | - | - | - |
| placeholder | 选择器占位符 | string | - | 请选择 | - |
| disabled | 禁用 | boolean | - | false | - |
| readonly | 只读 | boolean | - | false | - |
| display-format | 自定义展示文案的格式化函数,返回一个字符串 | function | - | - | - |
| inner-display-format | 自定义范围选择类型的面板内部回显,返回一个字符串 | function | - | - | - |
| size | 设置选择器大小 | string | large | - | - |
| label-width | 设置左侧标题宽度 | string | - | 33% | - |
| error | 是否为错误状态,错误状态时右侧内容为红色 | boolean | - | false | - |
| required | 必填样式 | boolean | - | false | - |
| center | 是否垂直居中 | boolean | - | false | - |
| ellipsis | 是否超出隐藏 | boolean | - | false | - |
| align-right | 选择器的值靠右展示 | boolean | - | false | - |
| before-confirm | 确定前校验函数,接收 { value, resolve } 参数,通过 resolve 继续执行resolve 接收 1 个 boolean 参数 | function | - | - | - |
| <s>use-default-slot</s> | <s>使用默认插槽时设置该选项</s>,已废弃直接使用默认插槽即可。 | boolean | - | false | - |
| <s>use-label-slot</s> | <s>使用 label 插槽时设置该选项</s>,已废弃直接使用 label 插槽即可。 | boolean | - | false | - |
| close-on-click-modal | 点击遮罩是否关闭 | boolean | - | true | - |
| z-index | 弹窗层级 | number | - | 15 | - |
| safe-area-inset-bottom | 弹出面板是否设置底部安全距离iphone X 类型的机型) | boolean | - | true | - |
| prop | 表单域 `model` 字段名,在使用表单校验功能的情况下,该属性是必填的 | string | - | - | - |
| rules | 表单验证规则,结合`wd-form`组件使用 | `FormItemRule []` | - | `[]` | - |
| immediate-change | type 为 'datetime' 或 'datetimerange' 时有,是否在手指松开时立即触发 picker-view 的 change 事件。若不开启则会在滚动动画结束后触发 change 事件1.2.25 版本起提供,仅微信小程序和支付宝小程序支持。 | boolean | - | false | 1.2.25 |
| with-cell | 是否使用内置 cell 选择器 | boolean | - | true | $LOWEST_VERSION$ |
### FormItemRule 数据结构
| 键名 | 说明 | 类型 |
| --- | --- | --- |
| required | 是否为必选字段 | `boolean` |
| message | 错误提示文案 | `string` |
| 键名 | 说明 | 类型 |
| --------- | ------------------------------------------------------- | ------------------------------------- |
| required | 是否为必选字段 | `boolean` |
| message | 错误提示文案 | `string` |
| validator | 通过函数进行校验,可以返回一个 `Promise` 来进行异步校验 | `(value, rule) => boolean \| Promise` |
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
| pattern | 通过正则表达式进行校验,正则无法匹配表示校验不通过 | `RegExp` |
## Events
| 事件名称 | 说明 | 参数 | 最低版本 |
| -------- | ------------------------------------ | ------------------------ | -------- |
| confirm | 绑定值变化时触发 | `{ value }` | - |
| change | 点击面板日期时触发 | `{ value }` | - |
| cancel | 点击关闭按钮或者蒙层时触发 | -|-|
| open | 日历打开时触发 | -|-|
| 事件名称 | 说明 | 参数 | 最低版本 |
| -------- | -------------------------- | ----------- | -------- |
| confirm | 绑定值变化时触发 | `{ value }` | - |
| change | 点击面板日期时触发 | `{ value }` | - |
| cancel | 点击关闭按钮或者蒙层时触发 | - | - |
| open | 日历打开时触发 | - | - |
## Methods

View File

@ -630,7 +630,7 @@ const columnChange = ({ selectedItem, resolve, finish }) => {
| title | 弹出层标题 | string | - | - | - |
| label | 选择器左侧文案 | string | - | - | - |
| placeholder | 选择器占位符 | string | - | 请选择 | - |
| disabled | 禁用 | boolean | - | fasle | - |
| disabled | 禁用 | boolean | - | false | - |
| readonly | 只读 | boolean | - | false | - |
| display-format | 自定义展示文案的格式化函数,返回一个字符串 | function | - | - | - |
| column-change | 接收当前列的选中项 item、当前列下标、当前列选中项下标下一列数据处理函数 resolve、结束选择 finish | function | - | - | - |

View File

@ -268,7 +268,7 @@ const displayFormatTabLabel = (items) => {
| confirm-button-text | 确认按钮文案 | string | - | 完成 | - |
| label | 选择器左侧文案label可以不传 | string | - | - | - |
| placeholder | 选择器占位符 | string | - | 请选择 | - |
| disabled | 禁用 | boolean | - | fasle | - |
| disabled | 禁用 | boolean | - | false | - |
| readonly | 只读 | boolean | - | false | - |
| display-format | 自定义展示文案的格式化函数,返回一个字符串 | function | - | - | - |
| formatter | 自定义弹出层选项文案的格式化函数,返回一个字符串 | function | - | - | - |

View File

@ -252,7 +252,7 @@ function handleConfirm({ value }) {
| confirm-button-text | 确认按钮文案 | string | - | 完成 | - |
| label | 选择器左侧文案 | string | - | - | - |
| placeholder | 选择器占位符 | string | - | 请选择 | - |
| disabled | 禁用 | boolean | - | fasle | - |
| disabled | 禁用 | boolean | - | false | - |
| readonly | 只读 | boolean | - | false | - |
| display-format | 自定义展示文案的格式化函数,返回一个字符串 | function | - | - | - |
| column-change | 接收 pickerView 实例、选中项、当前修改列的下标、resolve 作为入参,根据选中项和列下标进行判断,通过 pickerView 实例暴露出来的 `setColumnData` 方法修改其他列的数据源。 | function | - | - | - |

View File

@ -330,7 +330,7 @@ function handleConfirm({ value, selectedItems }) {
| title | 弹出层标题 | string | - | - | - |
| label | 选择器左侧文案 | string | - | - | - |
| placeholder | 选择器占位符 | string | - | 请选择 | - |
| disabled | 禁用 | boolean | - | fasle | - |
| disabled | 禁用 | boolean | - | false | - |
| loading | 加载中 | boolean | - | false | - |
| loading-color | 加载的颜色,只能使用十六进制的色值写法,且不能使用缩写 | String | - | #4D80F0 | - |
| readonly | 只读 | boolean | - | false | - |

View File

@ -44,8 +44,9 @@
</wd-calendar>
</view>
</demo-block>
<demo-block transparent title="open事件">
<wd-calendar v-model="value17" @open="handleOpen" />
<demo-block title="组件实例事件">
<wd-button @click="openCalendar">打开日历</wd-button>
<wd-calendar ref="calendarRef" v-model="value17" :with-cell="false" @confirm="handleConfirm5" />
</demo-block>
</page-wraper>
<wd-message-box />
@ -54,10 +55,8 @@
import { useToast } from '@/uni_modules/wot-design-uni'
import { dayjs } from '@/uni_modules/wot-design-uni'
import type { CalendarDayItem, CalendarFormatter } from '@/uni_modules/wot-design-uni/components/wd-calendar-view/types'
import type { CalendarOnShortcutsClickOption } from '@/uni_modules/wot-design-uni/components/wd-calendar/types'
import type { CalendarInstance, CalendarOnShortcutsClickOption } from '@/uni_modules/wot-design-uni/components/wd-calendar/types'
import { ref } from 'vue'
import { useMessage } from '@/uni_modules/wot-design-uni'
const message = useMessage()
const minDate = ref<number>(new Date(new Date().getFullYear() - 20, new Date().getMonth() - 6, new Date().getDate()).getTime())
@ -78,6 +77,13 @@ const value14 = ref<number | null>(null)
const value15 = ref<number | null>(null)
const value16 = ref<number>(Date.now())
const value17 = ref<number>(Date.now())
const calendarRef = ref<CalendarInstance>()
function openCalendar() {
calendarRef.value?.open()
}
const formatValue = ref<string>('')
const formatter: CalendarFormatter = (day: CalendarDayItem) => {
const date = new Date(day.date)
@ -171,8 +177,9 @@ function handleConfirm4({ value }: any) {
console.log(new Date(value).toString())
formatValue.value = new Date(value).toString()
}
function handleOpen() {
message.alert('打开日历')
function handleConfirm5({ value }: any) {
toast.success('已选择' + dayjs(value).format('YYYY年MM月DD日'))
}
</script>
<style lang="scss" scoped></style>

View File

@ -218,10 +218,13 @@ $-calendar-day-fw: var(--wot-calendar-day-fw, 500) !default;
$-calendar-day-height: var(--wot-calendar-day-height, 64px) !default;
$-calendar-month-width: var(--wot-calendar-month-width, 50px) !default;
$-calendar-active-color: var(--wot-calendar-active-color, $-color-theme) !default;
$-calendar-selected-color: var(--wot-calendar-selected-color, $-color-white) !default;
$-calendar-disabled-color: var(--wot-calendar-disabled-color, rgba(0, 0, 0, 0.25)) !default;
$-calendar-range-color: var(--wot-calendar-range-color, rgba(#4d80f0, 0.09)) !default;
$-calendar-active-border: var(--wot-calendar-active-border, 8px) !default;
$-calendar-info-fs: var(--wot-calendar-info-fs, 10px) !default;
$-calendar-item-margin-bottom: var(--wot-calendar-item-margin-bottom, 4px) !default;
/* checkbox */
$-checkbox-margin: var(--wot-checkbox-margin, 10px) !default; // 多个复选框距离

View File

@ -44,6 +44,7 @@
height: $-calendar-day-height;
line-height: $-calendar-day-height;
text-align: center;
margin-bottom: $-calendar-item-margin-bottom;
@include when(disabled) {
.wd-month__day-text {
@ -55,11 +56,11 @@
color: $-calendar-active-color;
}
@include when(selected) {
@include when(selected, multiple-selected) {
.wd-month__day-container {
border-radius: $-calendar-active-border;
background: $-calendar-active-color;
color: #fff;
color: $-calendar-selected-color;
}
}
@ -68,6 +69,12 @@
background: $-calendar-range-color;
}
}
@include when(multiple-middle) {
.wd-month__day-container {
background: $-calendar-active-color;
color: $-calendar-selected-color;
}
}
@include when(start) {
&::after {
@ -87,8 +94,8 @@
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
border-radius: $-calendar-active-border;
color: $-calendar-selected-color;
border-radius: $-calendar-active-border 0 0 $-calendar-active-border;
}
}
@ -106,18 +113,22 @@
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
border-radius: $-calendar-active-border;
color: $-calendar-selected-color;
border-radius: 0 $-calendar-active-border $-calendar-active-border 0;
}
}
@include when(same) {
.wd-month__day-container {
background: $-calendar-active-color;
color: #fff;
color: $-calendar-selected-color;
border-radius: $-calendar-active-border;
}
}
@include when(last-row){
margin-bottom: 0;
}
}
@include e(day-container) {

View File

@ -3,13 +3,15 @@
<wd-toast selector="wd-month" />
<view class="month">
<view class="wd-month">
<view class="wd-month__title">{{ monthTitle(date) }}</view>
<view class="wd-month__title" v-if="showTitle">{{ monthTitle(date) }}</view>
<view class="wd-month__days">
<view
v-for="(item, index) in days"
:key="index"
:class="`wd-month__day ${item.disabled ? 'is-disabled' : ''} ${item.type ? itemClass(item.type, value!, type) : ''}`"
:style="firstDayStyle(index, item.date, firstDayOfWeek)"
:class="`wd-month__day ${item.disabled ? 'is-disabled' : ''} ${item.isLastRow ? 'is-last-row' : ''} ${
item.type ? dayTypeClass(item.type) : ''
}`"
:style="index === 0 ? firstDayStyle : ''"
@click="handleDateClick(index)"
>
<view class="wd-month__day-container">
@ -38,22 +40,23 @@ export default {
<script lang="ts" setup>
import wdToast from '../../wd-toast/wd-toast.vue'
import { computed, ref, watch } from 'vue'
import { computed, ref, watch, type CSSProperties } from 'vue'
import {
compareDate,
formatMonthTitle,
getDateByDefaultTime,
getDayByOffset,
getDayOffset,
getFirstDayStyle,
getItemClass,
getMonthEndDay,
getNextDay,
getPrevDay,
getWeekRange
} from '../utils'
import { useToast } from '../../wd-toast'
import { deepClone, isArray, isFunction } from '../../common/util'
import { deepClone, isArray, isFunction, objToStyle } from '../../common/util'
import { useTranslate } from '../../composables/useTranslate'
import type { CalendarDayItem, CalendarDayType, CalendarType } from '../types'
import type { CalendarDayItem, CalendarDayType } from '../types'
import { monthProps } from './types'
const props = defineProps(monthProps)
@ -65,9 +68,15 @@ const days = ref<Array<CalendarDayItem>>([])
const toast = useToast('wd-month')
const itemClass = computed(() => {
return (monthType: CalendarDayType, value: number | (number | null)[], type: CalendarType) => {
return getItemClass(monthType, value, type)
const offset = computed(() => {
const firstDayOfWeek = props.firstDayOfWeek >= 7 ? props.firstDayOfWeek % 7 : props.firstDayOfWeek
const offset = (7 + new Date(props.date).getDay() - firstDayOfWeek) % 7
return offset
})
const dayTypeClass = computed(() => {
return (monthType: CalendarDayType) => {
return getItemClass(monthType, props.value, props.type)
}
})
@ -76,12 +85,21 @@ const monthTitle = computed(() => {
return formatMonthTitle(date)
}
})
const firstDayStyle = computed(() => {
return (index: number, date: number, firstDayOfWeek: number) => {
return getFirstDayStyle(index, date, firstDayOfWeek)
}
const dayStyle: CSSProperties = {}
dayStyle.marginLeft = `${(100 / 7) * offset.value}%`
return objToStyle(dayStyle)
})
const isLastRow = (date: number) => {
const currentDate = new Date(date)
const currentDay = currentDate.getDate()
const daysInMonth = getMonthEndDay(currentDate.getFullYear(), currentDate.getMonth() + 1)
const totalDaysShown = offset.value + daysInMonth
const totalRows = Math.ceil(totalDaysShown / 7)
return Math.ceil((offset.value + currentDay) / 7) === totalRows
}
watch(
[() => props.type, () => props.date, () => props.value, () => props.minDate, () => props.maxDate, () => props.formatter],
() => {
@ -141,17 +159,29 @@ function getDateType(date: number): CalendarDayType {
}
function getDatesType(date: number): CalendarDayType {
if (!isArray(props.value)) return ''
const { value } = props
let type: CalendarDayType = ''
props.value.some((item) => {
if (compareDate(date, item) === 0) {
type = 'selected'
return true
}
return false
})
if (!isArray(value)) return type
const isSelected = (day: number) => {
return value.some((item) => compareDate(day, item) === 0)
}
if (isSelected(date)) {
const prevDay = getPrevDay(date)
const nextDay = getNextDay(date)
const prevSelected = isSelected(prevDay)
const nextSelected = isSelected(nextDay)
if (prevSelected && nextSelected) {
type = 'multiple-middle'
} else if (prevSelected) {
type = 'end'
} else if (nextSelected) {
type = 'start'
} else {
type = 'multiple-selected'
}
}
return type
}
@ -251,16 +281,12 @@ function handleDateChange(date: CalendarDayItem) {
}
function handleDatesChange(date: CalendarDayItem) {
if (date.disabled) return
const value = deepClone(isArray(props.value) ? props.value : [])
if (date.type !== 'selected') {
value.push(getDate(date.date))
} else {
value.splice(value.indexOf(date.date), 1)
}
emit('change', {
value
})
const currentValue = deepClone(isArray(props.value) ? props.value : [])
const dateIndex = currentValue.findIndex((item) => item && compareDate(item, date.date) === 0)
const value = dateIndex === -1 ? [...currentValue, getDate(date.date)] : currentValue.filter((_, index) => index !== dateIndex)
emit('change', { value })
}
function handleDateRangeChange(date: CalendarDayItem) {
if (date.disabled) return
@ -345,7 +371,8 @@ function getFormatterDate(date: number, day: string | number, type?: CalendarDay
topInfo: '',
bottomInfo: '',
type,
disabled: compareDate(date, props.minDate) === -1 || compareDate(date, props.maxDate) === 1
disabled: compareDate(date, props.minDate) === -1 || compareDate(date, props.maxDate) === 1,
isLastRow: isLastRow(date)
}
if (props.formatter) {
if (isFunction(props.formatter)) {

View File

@ -15,5 +15,6 @@ export const monthProps = {
allowSameDay: makeBooleanProp(false),
defaultTime: {
type: [Array] as PropType<Array<number[]>>
}
},
showTitle: makeBooleanProp(true)
}

View File

@ -26,6 +26,7 @@
:range-prompt="rangePrompt"
:allow-same-day="allowSameDay"
:default-time="defaultTime"
:showTitle="index !== 0"
@change="handleDateChange"
/>
</view>
@ -131,17 +132,18 @@ const weekLabel = computed(() => {
//
const scrollHeight = computed(() => {
const scrollHeight: number = timeType.value ? (props.panelHeight || 378) - 125 : props.panelHeight || 378
const scrollHeight: number = timeType.value ? props.panelHeight - 125 : props.panelHeight
return scrollHeight
})
//
const months = computed<MonthInfo[]>(() => {
return getMonths(props.minDate, props.maxDate).map((month) => {
return getMonths(props.minDate, props.maxDate).map((month, index) => {
const offset = (7 + new Date(month).getDay() - props.firstDayOfWeek) % 7
const totalDay = getMonthEndDay(new Date(month).getFullYear(), new Date(month).getMonth() + 1)
const rows = Math.ceil((offset + totalDay) / 7)
return {
height: (offset + totalDay > 35 ? 64 * 6 : 64 * 5) + 45,
height: rows * 64 + (rows - 1) * 4 + (index === 0 ? 0 : 45), // 64px,4px margin,45px
date: month
}
})
@ -190,7 +192,9 @@ async function scrollIntoView() {
await pause()
let activeDate: number | null = 0
if (isArray(props.value)) {
activeDate = props.value![0]
// ,
const sortedValue = [...props.value].sort((a, b) => (a || 0) - (b || 0))
activeDate = sortedValue[0]
} else if (isNumber(props.value)) {
activeDate = props.value
}
@ -200,16 +204,28 @@ async function scrollIntoView() {
}
let top: number = 0
let activeMonthIndex = -1
for (let index = 0; index < months.value.length; index++) {
if (compareMonth(months.value[index].date, activeDate) === 0) {
activeMonthIndex = index
// ,
const date = new Date(activeDate)
const day = date.getDate()
const firstDay = new Date(date.getFullYear(), date.getMonth(), 1)
const offset = (7 + firstDay.getDay() - props.firstDayOfWeek) % 7
const row = Math.floor((offset + day - 1) / 7)
// 64px,4px margin
top += row * 64 + row * 4
break
}
top += months.value[index] ? Number(months.value[index].height) : 0
}
scrollTop.value = 0
//
await pause()
scrollTop.value = top
if (top > 0) {
await pause()
// 45
scrollTop.value = top + (activeMonthIndex > 0 ? 45 : 0)
}
}
/**
* 获取时间 picker 的数据
@ -341,7 +357,7 @@ function doSetSubtitle(scrollTop: number) {
let height: number = 0 //
for (let index = 0; index < months.value.length; index++) {
height = height + months.value[index].height
if (scrollTop < height + 45) {
if (scrollTop < height) {
scrollIndex.value = index
return
}

View File

@ -70,7 +70,7 @@ export const calendarViewProps = {
export type CalendarViewProps = ExtractPropTypes<typeof calendarViewProps>
export type CalendarDayType = '' | 'start' | 'middle' | 'end' | 'selected' | 'same' | 'current'
export type CalendarDayType = '' | 'start' | 'middle' | 'end' | 'selected' | 'same' | 'current' | 'multiple-middle' | 'multiple-selected'
export type CalendarDayItem = {
date: number
@ -79,6 +79,7 @@ export type CalendarDayItem = {
bottomInfo?: string
type?: CalendarDayType
disabled?: boolean
isLastRow?: boolean
}
export type CalendarFormatter = (day: CalendarDayItem) => CalendarDayItem

View File

@ -116,24 +116,6 @@ export function getWeekLabel(index: number) {
return weeks.value[index]
}
/**
*
* @param {number} index
* @param {timestamp} date
* @param {number} firstDayOfWeek
*/
export function getFirstDayStyle(index: number, date: number, firstDayOfWeek: number) {
if (firstDayOfWeek >= 7) {
firstDayOfWeek = firstDayOfWeek % 7
}
if (index !== 0) return ''
const offset = (7 + new Date(date).getDay() - firstDayOfWeek) % 7
return 'margin-left: ' + (100 / 7) * offset + '%'
}
/**
*
* @param {timestamp} date
@ -222,6 +204,9 @@ export function getDayByOffset(date: number, offset: number) {
return dateValue.getTime()
}
export const getPrevDay = (date: number) => getDayByOffset(date, -1)
export const getNextDay = (date: number) => getDayByOffset(date, 1)
/**
*
* @param {timestamp} date1
@ -431,7 +416,7 @@ export function getWeekNumber(date: number | Date) {
return 1 + Math.round(((date.getTime() - week.getTime()) / 86400000 - 3 + ((week.getDay() + 6) % 7)) / 7)
}
export function getItemClass(monthType: CalendarDayType, value: number | (number | null)[], type: CalendarType) {
export function getItemClass(monthType: CalendarDayType, value: number | null | (number | null)[], type: CalendarType) {
const classList = ['is-' + monthType]
if (type.indexOf('range') > -1 && isArray(value)) {

View File

@ -45,6 +45,7 @@
height: $-calendar-day-height;
line-height: $-calendar-day-height;
text-align: center;
margin-bottom: $-calendar-item-margin-bottom;
@include when(disabled) {
.wd-year__month-text {
@ -70,7 +71,7 @@
}
@include when(start) {
color: #fff;
color: $-calendar-selected-color;
&::after {
position: absolute;
@ -84,7 +85,7 @@
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
border-radius: $-calendar-active-border 0 0 $-calendar-active-border;
}
&.is-without-end::after {
@ -93,7 +94,7 @@
}
@include when(end) {
color: #fff;
color: $-calendar-selected-color;
&::after {
position: absolute;
@ -107,18 +108,21 @@
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
border-radius: 0 $-calendar-active-border $-calendar-active-border 0;
}
}
@include when(same) {
color: #fff;
color: $-calendar-selected-color;
.wd-year__month-text {
background: $-calendar-active-color;
border-radius: $-calendar-active-border;
}
}
@include when(last-row){
margin-bottom: 0;
}
}
@include e(month-text) {

View File

@ -15,5 +15,6 @@ export const yearProps = {
allowSameDay: makeBooleanProp(false),
defaultTime: {
type: [Array] as PropType<Array<number[]>>
}
},
showTitle: makeBooleanProp(true)
}

View File

@ -2,12 +2,14 @@
<wd-toast selector="wd-year" />
<view class="wd-year year">
<view class="wd-year__title">{{ yearTitle(date) }}</view>
<view class="wd-year__title" v-if="showTitle">{{ yearTitle(date) }}</view>
<view class="wd-year__months">
<view
v-for="(item, index) in months"
:key="index"
:class="`wd-year__month ${item.disabled ? 'is-disabled' : ''} ${item.type ? itemClass(item.type, value!, type) : ''}`"
:class="`wd-year__month ${item.disabled ? 'is-disabled' : ''} ${item.isLastRow ? 'is-last-row' : ''} ${
item.type ? monthTypeClass(item.type) : ''
}`"
@click="handleDateClick(index)"
>
<view class="wd-year__month-top">{{ item.topInfo }}</view>
@ -36,7 +38,7 @@ import { useToast } from '../../wd-toast'
import { useTranslate } from '../../composables/useTranslate'
import { dayjs } from '../../common/dayjs'
import { yearProps } from './types'
import type { CalendarDayItem, CalendarDayType, CalendarType } from '../types'
import type { CalendarDayItem, CalendarDayType } from '../types'
const props = defineProps(yearProps)
const emit = defineEmits(['change'])
@ -46,9 +48,9 @@ const { translate } = useTranslate('calendar-view')
const months = ref<CalendarDayItem[]>([])
const itemClass = computed(() => {
return (monthType: CalendarDayType, value: number | (number | null)[], type: CalendarType) => {
return getItemClass(monthType, value, type)
const monthTypeClass = computed(() => {
return (monthType: CalendarDayType) => {
return getItemClass(monthType, props.value, props.type)
}
})
@ -179,8 +181,10 @@ function getFormatterDate(date: number, month: number, type?: CalendarDayType) {
topInfo: '',
bottomInfo: '',
type,
disabled: compareMonth(date, props.minDate) === -1 || compareMonth(date, props.maxDate) === 1
disabled: compareMonth(date, props.minDate) === -1 || compareMonth(date, props.maxDate) === 1,
isLastRow: month >= 8
}
if (props.formatter) {
if (isFunction(props.formatter)) {
monthObj = props.formatter(monthObj)

View File

@ -14,6 +14,7 @@
:range-prompt="rangePrompt"
:allow-same-day="allowSameDay"
:default-time="defaultTime"
:showTitle="index !== 0"
@change="handleDateChange"
/>
</view>
@ -45,16 +46,16 @@ const scrollIndex = ref<number>(0) // 当前显示的年份索引
//
const scrollHeight = computed(() => {
const scrollHeight: number = (props.panelHeight || 378) + (props.showPanelTitle ? 26 : 16)
const scrollHeight: number = props.panelHeight + (props.showPanelTitle ? 26 : 16)
return scrollHeight
})
//
const years = computed<YearInfo[]>(() => {
return getYears(props.minDate, props.maxDate).map((year) => {
return getYears(props.minDate, props.maxDate).map((year, index) => {
return {
date: year,
height: 237
height: index === 0 ? 200 : 245
}
})
})
@ -89,8 +90,10 @@ async function scrollIntoView() {
top += years.value[index] ? Number(years.value[index].height) : 0
}
scrollTop.value = 0
await pause()
scrollTop.value = top
if (top > 0) {
await pause()
scrollTop.value = top + 45
}
}
const yearScroll = (event: { detail: { scrollTop: number } }) => {
@ -109,7 +112,7 @@ function doSetSubtitle(scrollTop: number) {
let height: number = 0 //
for (let index = 0; index < years.value.length; index++) {
height = height + years.value[index].height
if (scrollTop < height + 45) {
if (scrollTop < height) {
scrollIndex.value = index
return
}

View File

@ -1,13 +1,13 @@
/*
* @Author: weisheng
* @Date: 2024-03-15 20:40:34
* @LastEditTime: 2024-06-09 14:38:57
* @LastEditTime: 2024-12-08 19:13:33
* @LastEditors: weisheng
* @Description:
* @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-calendar/types.ts
*
*/
import type { PropType } from 'vue'
import type { ComponentPublicInstance, ExtractPropTypes, PropType } from 'vue'
import { baseProps, makeArrayProp, makeBooleanProp, makeNumberProp, makeRequiredProp, makeStringProp } from '../common/props'
import type { CalendarFormatter, CalendarTimeFilter, CalendarType } from '../wd-calendar-view/types'
import type { FormItemRule } from '../wd-form/types'
@ -72,14 +72,6 @@ export const calendarProps = {
*
*/
labelWidth: String,
/**
* 使 label
*/
useLabelSlot: makeBooleanProp(false),
/**
* 使
*/
useDefaultSlot: makeBooleanProp(false),
/**
*
*/
@ -184,7 +176,12 @@ export const calendarProps = {
/**
* picker-view的 change change 1.2.25
*/
immediateChange: makeBooleanProp(false)
immediateChange: makeBooleanProp(false),
/**
* 使
* true使
*/
withCell: makeBooleanProp(true)
}
export type CalendarDisplayFormat = (value: number | number[], type: CalendarType) => string
@ -211,3 +208,7 @@ export type CalendarExpose = {
/** 打开时间选择器弹窗 */
open: () => void
}
export type CalendarProps = ExtractPropTypes<typeof calendarProps>
export type CalendarInstance = ComponentPublicInstance<CalendarExpose, CalendarProps>

View File

@ -1,7 +1,7 @@
<template>
<view :class="`wd-calendar ${cell.border.value ? 'is-border' : ''} ${customClass}`">
<view class="wd-calendar__field" @click="open">
<slot v-if="useDefaultSlot"></slot>
<view class="wd-calendar__field" @click="open" v-if="withCell">
<slot v-if="$slots.default"></slot>
<view
v-else
:class="`wd-calendar__cell ${disabled ? 'is-disabled' : ''} ${readonly ? 'is-readonly' : ''} ${alignRight ? 'is-align-right' : ''} ${
@ -9,12 +9,11 @@
} ${size ? 'is-' + size : ''} ${center ? 'is-center' : ''}`"
>
<view
v-if="label || useLabelSlot"
v-if="label || $slots.label"
:class="`wd-calendar__label ${isRequired ? 'is-required' : ''} ${customLabelClass}`"
:style="labelWidth ? 'min-width:' + labelWidth + ';max-width:' + labelWidth + ';' : ''"
>
<block v-if="label">{{ label }}</block>
<slot v-else name="label"></slot>
<slot name="label">{{ label }}</slot>
</view>
<view class="wd-calendar__body">
<view class="wd-calendar__value-wraper">