diff --git a/.github/workflows/auto-release.yml b/.github/workflows/auto-release.yml index bd1561885..cf1d33cd7 100644 --- a/.github/workflows/auto-release.yml +++ b/.github/workflows/auto-release.yml @@ -64,9 +64,7 @@ jobs: BODY: ${{ github.event.comment.body }} run: | txt=$(cat packages/tdesign-miniprogram/CHANGELOG.md) - chat_txt=$(cat packages/tdesign-miniprogram-chat/CHANGELOG.md) echo "${txt%%##*}$BODY${txt##*---}" > packages/tdesign-miniprogram/CHANGELOG.md - echo "${chat_txt%%##*}$BODY${chat_txt##*---}" > packages/tdesign-miniprogram-chat/CHANGELOG.md git add . git config --local user.email "github-actions[bot]@users.noreply.github.com" git config --local user.name "github-actions[bot]" diff --git a/.github/workflows/pr-spell-check.yml b/.github/workflows/pr-spell-check.yml new file mode 100644 index 000000000..23aa30d6b --- /dev/null +++ b/.github/workflows/pr-spell-check.yml @@ -0,0 +1,6 @@ +name: pr-spell-check +on: [pull_request] + +jobs: + run: + uses: TDesignOteam/workflows/.github/workflows/spell-check.yml@main diff --git a/.github/workflows/pr-spelling.template.yml b/.github/workflows/pr-spelling.template.yml deleted file mode 100644 index 47f3069eb..000000000 --- a/.github/workflows/pr-spelling.template.yml +++ /dev/null @@ -1,13 +0,0 @@ -name: pr-spell-check -on: [pull_request] - -jobs: - run: - name: Spell Check with Typos - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - name: Check spelling - uses: crate-ci/typos@master - with: - config: .github/workflows/typos-config.toml \ No newline at end of file diff --git a/.github/workflows/tag-push.yml b/.github/workflows/tag-push.yml index 634f6ad74..87a1a3d3f 100644 --- a/.github/workflows/tag-push.yml +++ b/.github/workflows/tag-push.yml @@ -55,5 +55,5 @@ jobs: git config --local user.name "github-actions[bot]" git status git fetch origin - git merge origin/develop + git merge origin/develop -X theirs git push origin main diff --git a/packages/common b/packages/common index 23ecc659a..e81f31dd2 160000 --- a/packages/common +++ b/packages/common @@ -1 +1 @@ -Subproject commit 23ecc659a8b58a619651c8d5f600f9c02a5699ca +Subproject commit e81f31dd25e9cb8bcb06031c38ac0fb33a4e7a32 diff --git a/packages/components/calendar/README.en-US.md b/packages/components/calendar/README.en-US.md index f40175b8d..c251de2fa 100644 --- a/packages/components/calendar/README.en-US.md +++ b/packages/components/calendar/README.en-US.md @@ -8,21 +8,22 @@ name | type | default | description | required -- | -- | -- | -- | -- style | Object | - | CSS(Cascading Style Sheets) | N custom-style | Object | - | CSS(Cascading Style Sheets),used to set style on virtual component | N +allow-same-day | Boolean | false | `1.11.2` | N auto-close | Boolean | true | `0.34.0` | N confirm-btn | String / Object | '' | [see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/calendar/type.ts) | N first-day-of-week | Number | 0 | \- | N -format | Function | - | Typescript:`CalendarFormatType ` `type CalendarFormatType = (day: TDate) => TDate` `type TDateType = 'selected' \| 'disabled' \| 'start' \| 'centre' \| 'end' \| ''` `interface TDate { date: Date; day: number; type: TDateType; className?: string; prefix?: string; suffix?: string;}`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/calendar/type.ts) | N -locale-text | Object | - | Typescript:`CalendarLocaleText` `interface CalendarLocaleText {title?: string; weekdays?: string[]; monthTitle?: string; months?: string[]; confirm?: string;}`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/calendar/type.ts) | N +format | Function | - | Typescript: `CalendarFormatType ` `type CalendarFormatType = (day: TDate) => TDate` `type TDateType = 'selected' \| 'disabled' \| 'start' \| 'centre' \| 'end' \| ''` `interface TDate { date: Date; day: number; type: TDateType; className?: string; prefix?: string; suffix?: string;}`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/calendar/type.ts) | N +locale-text | Object | - | Typescript: `CalendarLocaleText` `interface CalendarLocaleText {title?: string; weekdays?: string[]; monthTitle?: string; months?: string[]; confirm?: string;}`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/calendar/type.ts) | N max-date | Number | - | \- | N min-date | Number | - | \- | N readonly | Boolean | - | `1.9.7` | N switch-mode | String | none | `1.8.2`。options: none/month/year-month | N title | String | - | \- | N -type | String | 'single' | options: single/multiple/range | N +type | String | single | options: single/multiple/range | N use-popup | Boolean | true | `0.32.0` | N using-custom-navbar | Boolean | false | \- | N -value | Number / Array | - | Typescript:`number \| number[]` | N -default-value | Number / Array | undefined | uncontrolled property。Typescript:`number \| number[]` | N +value | Number / Array | - | Typescript: `number \| number[]` | N +default-value | Number / Array | undefined | uncontrolled property。Typescript: `number \| number[]` | N visible | Boolean | false | \- | N ### Calendar Events diff --git a/packages/components/calendar/README.md b/packages/components/calendar/README.md index 734e21496..f9a22cca6 100644 --- a/packages/components/calendar/README.md +++ b/packages/components/calendar/README.md @@ -74,6 +74,7 @@ isComponent: true -- | -- | -- | -- | -- style | Object | - | 样式 | N custom-style | Object | - | 样式,一般用于开启虚拟化组件节点场景 | N +allow-same-day | Boolean | false | `1.11.2`。是否允许区间选择日历的起止时间相同,仅当 `type='range'` 时有效 | N auto-close | Boolean | true | `0.34.0`。自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭,不需要手动设置 visible | N confirm-btn | String / Object | '' | 确认按钮。值为 null 则不显示确认按钮。值类型为字符串,则表示自定义按钮文本,值类型为 Object 则表示透传 Button 组件属性。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/calendar/type.ts) | N first-day-of-week | Number | 0 | 第一天从星期几开始,默认 0 = 周日 | N @@ -84,7 +85,7 @@ min-date | Number | - | 最小可选的日期,不传则默认今天 | N readonly | Boolean | - | `1.9.7`。是否只读,只读状态下不能选择日期 | N switch-mode | String | none | `1.8.2`。切换模式。 `none` 表示平铺展示所有月份; `month` 表示支持按月切换, `year-month` 表示既按年切换,也支持按月切换。可选项:none/month/year-month | N title | String | - | 标题,不传默认为“请选择日期” | N -type | String | 'single' | 日历的选择类型,single = 单选;multiple = 多选; range = 区间选择。可选项:single/multiple/range | N +type | String | single | 日历的选择类型,single = 单选;multiple = 多选; range = 区间选择。可选项:single/multiple/range | N use-popup | Boolean | true | `0.32.0`。是否使用弹出层包裹日历 | N using-custom-navbar | Boolean | false | 是否使用了自定义导航栏 | N value | Number / Array | - | 当前选择的日期,不传则选用 minDate 属性值或今天,优先级:minDate > today。当 type = multiple 或 range 时传入数组。TS 类型:`number \| number[]` | N diff --git a/packages/components/calendar/calendar.less b/packages/components/calendar/calendar.less index baa1a740c..047bd9d7e 100644 --- a/packages/components/calendar/calendar.less +++ b/packages/components/calendar/calendar.less @@ -139,6 +139,7 @@ } &--selected, + &--start-end, &--start, &--end { background: @calendar-active-color; diff --git a/packages/components/calendar/calendar.ts b/packages/components/calendar/calendar.ts index 46db08e15..9f3ac4efd 100644 --- a/packages/components/calendar/calendar.ts +++ b/packages/components/calendar/calendar.ts @@ -86,6 +86,10 @@ export default class Calendar extends SuperComponent { this.base.type = v; }, + allowSameDay(v) { + this.base.allowSameDay = v; + }, + confirmBtn(v) { if (typeof v === 'string') { this.setData({ innerConfirmBtn: v === 'slot' ? 'slot' : { content: v } }); diff --git a/packages/components/calendar/props.ts b/packages/components/calendar/props.ts index 3daf0b66b..af09b4fc0 100644 --- a/packages/components/calendar/props.ts +++ b/packages/components/calendar/props.ts @@ -6,6 +6,11 @@ import { TdCalendarProps } from './type'; const props: TdCalendarProps = { + /** 是否允许区间选择日历的起止时间相同,仅当 `type='range'` 时有效 */ + allowSameDay: { + type: Boolean, + value: false, + }, /** 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭,不需要手动设置 visible */ autoClose: { type: Boolean, diff --git a/packages/components/calendar/type.ts b/packages/components/calendar/type.ts index a19e7aa82..7d5000317 100644 --- a/packages/components/calendar/type.ts +++ b/packages/components/calendar/type.ts @@ -7,6 +7,14 @@ import { ButtonProps } from '../button/index'; export interface TdCalendarProps { + /** + * 是否允许区间选择日历的起止时间相同,仅当 `type='range'` 时有效 + * @default false + */ + allowSameDay?: { + type: BooleanConstructor; + value?: boolean; + }; /** * 自动关闭;在点击关闭按钮、确认按钮、遮罩层时自动关闭,不需要手动设置 visible * @default true @@ -83,7 +91,7 @@ export interface TdCalendarProps { }; /** * 日历的选择类型,single = 单选;multiple = 多选; range = 区间选择 - * @default 'single' + * @default single */ type?: { type: StringConstructor; diff --git a/packages/components/cascader/README.en-US.md b/packages/components/cascader/README.en-US.md index f204e1dee..6f5a7fc8d 100644 --- a/packages/components/cascader/README.en-US.md +++ b/packages/components/cascader/README.en-US.md @@ -10,10 +10,10 @@ style | Object | - | CSS(Cascading Style Sheets) | N custom-style | Object | - | CSS(Cascading Style Sheets),used to set style on virtual component | N check-strictly | Boolean | false | \- | N close-btn | Boolean | true | \- | N -keys | Object | - | Typescript:`KeysType`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts) | N -options | Array | [] | Typescript:`Array` | N +keys | Object | - | Typescript: `CascaderKeysType` `type CascaderKeysType = TreeKeysType`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/cascader/type.ts) | N +options | Array | [] | Typescript: `Array` | N placeholder | String | 选择选项 | \- | N -sub-titles | Array | [] | Typescript:`Array` | N +sub-titles | Array | [] | Typescript: `Array` | N theme | String | step | options: step/tab | N title | String | - | \- | N value | String / Number | null | \- | N @@ -53,6 +53,6 @@ Name | Default Value | Description --td-cascader-step-dot-size | 16rpx | - --td-cascader-step-height | 88rpx | - --td-cascader-title-color | @text-color-primary | - +--td-cascader-title-font-size | 36rpx | - --td-cascader-title-height | 26rpx | - ---td-cascader-title-padding | @spacer-2 | - ---td-cascader-title-font-size | 36rpx | - \ No newline at end of file +--td-cascader-title-padding | @spacer-2 | - \ No newline at end of file diff --git a/packages/components/cascader/README.md b/packages/components/cascader/README.md index 65ea276fb..f0a5dd999 100644 --- a/packages/components/cascader/README.md +++ b/packages/components/cascader/README.md @@ -67,7 +67,7 @@ style | Object | - | 样式 | N custom-style | Object | - | 样式,一般用于开启虚拟化组件节点场景 | N check-strictly | Boolean | false | 父子节点选中状态不再关联,可各自选中或取消 | N close-btn | Boolean | true | 关闭按钮 | N -keys | Object | - | 用来定义 value / label 在 `options` 中对应的字段别名。TS 类型:`KeysType`。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts) | N +keys | Object | - | 用来定义 value / label / children / disabled 在 `options` 中对应的字段别名。TS 类型:`CascaderKeysType` `type CascaderKeysType = TreeKeysType`。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/cascader/type.ts) | N options | Array | [] | 可选项数据源。TS 类型:`Array` | N placeholder | String | 选择选项 | 未选中时的提示文案 | N sub-titles | Array | [] | 每级展示的次标题。TS 类型:`Array` | N @@ -110,6 +110,6 @@ title | 自定义 `title` 显示内容 --td-cascader-step-dot-size | 16rpx | - --td-cascader-step-height | 88rpx | - --td-cascader-title-color | @text-color-primary | - +--td-cascader-title-font-size | 36rpx | - --td-cascader-title-height | 26rpx | - ---td-cascader-title-padding | @spacer-2 | - ---td-cascader-title-font-size | 36rpx | - \ No newline at end of file +--td-cascader-title-padding | @spacer-2 | - \ No newline at end of file diff --git a/packages/components/cascader/cascader.ts b/packages/components/cascader/cascader.ts index 660411c39..ac13625f3 100644 --- a/packages/components/cascader/cascader.ts +++ b/packages/components/cascader/cascader.ts @@ -15,11 +15,13 @@ type KeysType = TdCascaderProps['keys']['value']; function parseOptions(options: OptionsType, keys: KeysType) { const label = keys?.label ?? 'label'; const value = keys?.value ?? 'value'; + const disabled = keys?.disabled ?? 'disabled'; return options.map((item) => { return { [label]: item[label], [value]: item[value], + [disabled]: item[disabled], }; }); } @@ -278,7 +280,7 @@ export default class Cascader extends SuperComponent { item = item[keys?.children ?? 'children'][index]; } - if (item.disabled) { + if (item[keys?.disabled ?? 'disabled']) { return; } this.triggerEvent('pick', { diff --git a/packages/components/cascader/props.ts b/packages/components/cascader/props.ts index 450dbe096..5221fd1bf 100644 --- a/packages/components/cascader/props.ts +++ b/packages/components/cascader/props.ts @@ -16,7 +16,7 @@ const props: TdCascaderProps = { type: Boolean, value: true, }, - /** 用来定义 value / label 在 `options` 中对应的字段别名 */ + /** 用来定义 value / label / children / disabled 在 `options` 中对应的字段别名 */ keys: { type: Object, }, diff --git a/packages/components/cascader/type.ts b/packages/components/cascader/type.ts index 76bd1288b..27ef49224 100644 --- a/packages/components/cascader/type.ts +++ b/packages/components/cascader/type.ts @@ -4,7 +4,7 @@ * 该文件为脚本自动生成文件,请勿随意修改。如需修改请联系 PMC * */ -import { TreeOptionData, KeysType } from '../common/common'; +import { TreeOptionData, TreeKeysType } from '../common/common'; export interface TdCascaderProps { /** @@ -24,11 +24,11 @@ export interface TdCascaderProps TDate; constructor(options = {}) { @@ -54,7 +56,7 @@ export default class TCalendar { getMonths() { const ans = []; const selectedDate = this.getTrimValue(); - const { minDate, maxDate, type, format } = this; + const { minDate, maxDate, type, allowSameDay, format } = this; const minDateRect = getDateRect(minDate); let { year: minYear, month: minMonth } = minDateRect; const { time: minTime } = minDateRect; @@ -74,9 +76,12 @@ export default class TCalendar { if (type === 'range' && selectedDate) { if (Array.isArray(selectedDate)) { const [startDate, endDate] = selectedDate; + const compareWithStart = startDate && isSameDate({ year, month, date }, startDate); + const compareWithEnd = endDate && isSameDate({ year, month, date }, endDate); - if (startDate && isSameDate({ year, month, date }, startDate)) return 'start'; - if (endDate && isSameDate({ year, month, date }, endDate)) return 'end'; + if (compareWithStart && compareWithEnd && allowSameDay) return 'start-end'; + if (compareWithStart) return 'start'; + if (compareWithEnd) return 'end'; if (startDate && endDate && curDate.getTime() > startDate.getTime() && curDate.getTime() < endDate.getTime()) return 'centre'; } @@ -124,7 +129,7 @@ export default class TCalendar { this.value = selected; if (type === 'range' && Array.isArray(selectedDate)) { - if (selectedDate.length === 1 && selected > selectedDate[0]) { + if (selectedDate.length === 1 && selected >= selectedDate[0]) { this.value = [selectedDate[0], selected]; } else { this.value = [selected]; diff --git a/packages/components/common/shared/calendar/type.ts b/packages/components/common/shared/calendar/type.ts index 365a11100..0fd05cdb1 100644 --- a/packages/components/common/shared/calendar/type.ts +++ b/packages/components/common/shared/calendar/type.ts @@ -1,6 +1,6 @@ export type TCalendarValue = number | Date; -export type TDateType = 'selected' | 'disabled' | 'start' | 'centre' | 'end' | ''; +export type TDateType = 'selected' | 'disabled' | 'start-end' | 'start' | 'centre' | 'end' | ''; export type TCalendarType = 'single' | 'multiple' | 'range'; diff --git a/packages/components/common/shared/date.ts b/packages/components/common/shared/date.ts index e3d2542c1..c4e3cb6c8 100644 --- a/packages/components/common/shared/date.ts +++ b/packages/components/common/shared/date.ts @@ -12,7 +12,7 @@ export const getDateRect = (date: Date | number) => { }; }; -export const isSameDate = (date1: CompareDate, date2: CompareDate) => { +export const isSameDate = (date1: CompareDate, date2: CompareDate): Boolean => { if (date1 instanceof Date || typeof date1 === 'number') date1 = getDateRect(date1); if (date2 instanceof Date || typeof date2 === 'number') date2 = getDateRect(date2); const keys = ['year', 'month', 'date']; diff --git a/packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap b/packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap index 523c9131e..2c89aac5e 100644 --- a/packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap +++ b/packages/components/date-time-picker/__test__/__snapshots__/index.test.js.snap @@ -61,7 +61,7 @@ exports[`date-time-picker :base 1`] = ` > diff --git a/packages/components/dropdown-menu/__test__/__snapshots__/index.test.js.snap b/packages/components/dropdown-menu/__test__/__snapshots__/index.test.js.snap index ac9cf9e91..8ba1d2ccb 100644 --- a/packages/components/dropdown-menu/__test__/__snapshots__/index.test.js.snap +++ b/packages/components/dropdown-menu/__test__/__snapshots__/index.test.js.snap @@ -552,7 +552,7 @@ exports[`dropdown-menu :base 1`] = ` ariaDisabled="{{true}}" ariaLabel="禁用选项" ariaRole="radio" - class="t-radio t-radio--left t-radio--block class t-class" + class="t-radio t-radio--left t-radio--block t-radio--disabled class t-class" disabled="{{true}}" id="" style="" diff --git a/packages/components/picker-item/picker-item.less b/packages/components/picker-item/picker-item.less index f8938cb27..987082893 100644 --- a/packages/components/picker-item/picker-item.less +++ b/packages/components/picker-item/picker-item.less @@ -2,7 +2,6 @@ @picker: ~'@{prefix}-picker'; @item: ~'@{picker}-item'; -@picker-group-height: var(--td-picker-group-height, 400rpx); @picker-item-color: var(--td-picker-item-color, @text-color-secondary); @picker-item-active-color: var(--td-picker-item-active-color, @text-color-primary); @picker-item-font-size: var(--td-picker-item-font-size, @font-size-m); @@ -14,14 +13,14 @@ .@{item} { &__group { - height: @picker-group-height; overflow: hidden; flex: 1; z-index: 1; } &__wrapper { - padding: 144rpx 0; + // 虚拟滚动性能优化:使用 will-change 提示浏览器 + will-change: transform; } &__item { @@ -30,6 +29,8 @@ align-items: center; color: @picker-item-color; font-size: @picker-item-font-size; + // 虚拟滚动性能优化:隔离渲染上下文 + contain: layout style paint; &-icon { font-size: 36rpx; diff --git a/packages/components/picker-item/picker-item.ts b/packages/components/picker-item/picker-item.ts index e60a3d143..d65c7d8aa 100644 --- a/packages/components/picker-item/picker-item.ts +++ b/packages/components/picker-item/picker-item.ts @@ -13,6 +13,16 @@ const INERTIA_TIME = 300; // 且距离大于`MOMENTUM_DISTANCE`时,执行惯性滚动 const INERTIA_DISTANCE = 15; +// 虚拟滚动配置 +const VIRTUAL_SCROLL_CONFIG = { + ENABLE_THRESHOLD: 100, // 超过100个选项启用虚拟滚动 + // VISIBLE_COUNT: 5, // 可见区域显示5个选项,使用 visibleItemCount 属性代替 + BUFFER_COUNT: 8, // 上下各缓冲8个选项(增加缓冲区,防止快速滑动时空白) + THROTTLE_TIME: 16, // 节流时间(60fps,提高更新频率) + FAST_SCROLL_BUFFER: 12, // 快速滑动时的额外缓冲区 + FAST_SCROLL_THRESHOLD: 50, // 判定为快速滑动的速度阈值(px/frame) +}; + const range = function (num: number, min: number, max: number) { return Math.min(Math.max(num, min), max); }; @@ -70,6 +80,17 @@ export default class PickerItem extends SuperComponent { columnIndex: 0, pickerKeys: { value: 'value', label: 'label', icon: 'icon' }, formatOptions: props.options.value, + // 虚拟滚动相关 + enableVirtualScroll: false, // 是否启用虚拟滚动 + visibleOptions: [], // 可见的选项列表 + virtualStartIndex: 0, // 虚拟滚动起始索引 + virtualOffsetY: 0, // 虚拟滚动偏移量 + totalHeight: 0, // 总高度(用于占位) + + // 动态属性(由父组件传递) + itemHeight: 40, // 单个选项高度 + visibleItemCount: 5, // 可视区域内的选项个数 + wrapperPaddingY: 72, // wrapper的上下padding }; lifetimes = { @@ -77,17 +98,33 @@ export default class PickerItem extends SuperComponent { this.StartY = 0; this.StartOffset = 0; this.startTime = 0; + this._moveTimer = null; + this._animationTimer = null; // 动画期间更新虚拟滚动的定时器 + this._lastOffset = 0; // 上一次的偏移量(用于计算滑动速度) + this._lastMoveTime = 0; // 上一次移动的时间 + this._scrollDirection = 0; // 滑动方向:1向下,-1向上,0静止 + }, + detached() { + // 清理定时器,防止内存泄漏 + if (this._moveTimer) { + clearTimeout(this._moveTimer); + this._moveTimer = null; + } + if (this._animationTimer) { + clearInterval(this._animationTimer); + this._animationTimer = null; + } }, }; methods = { onClickItem(event: WechatMiniprogram.TouchEvent) { const { index: clickIndex } = event.currentTarget.dataset; - const { pickItemHeight } = this.data; + const { itemHeight } = this.data; const index = range(clickIndex, 0, this.getCount() - 1); if (index !== this._selectedIndex) { - this.setData({ offset: -index * pickItemHeight, curIndex: index, duration: 200 }); + this.setData({ offset: -index * itemHeight, curIndex: index, duration: 200 }); } this.updateSelected(index, true); @@ -104,17 +141,61 @@ export default class PickerItem extends SuperComponent { onTouchMove(event) { const { StartY, StartOffset } = this; - const { pickItemHeight } = this.data; + const { itemHeight, enableVirtualScroll } = this.data; + const currentTime = Date.now(); + // 偏移增量 const deltaY = event.touches[0].clientY - StartY; - const newOffset = range(StartOffset + deltaY, -(this.getCount() * pickItemHeight), 0); - this.setData({ - offset: newOffset, - }); + const newOffset = range(StartOffset + deltaY, -(this.getCount() * itemHeight), 0); + + // 计算滑动速度和方向 + const offsetDelta = newOffset - this._lastOffset; + const timeDelta = currentTime - this._lastMoveTime || 16; + const scrollSpeed = Math.abs(offsetDelta / timeDelta) * 16; // 转换为 px/frame + + // 计算滑动方向(避免嵌套三元表达式) + if (offsetDelta > 0) { + this._scrollDirection = 1; // 向下滑动 + } else if (offsetDelta < 0) { + this._scrollDirection = -1; // 向上滑动 + } else { + this._scrollDirection = 0; // 静止 + } + + // 判断是否为快速滑动 + const isFastScroll = scrollSpeed > VIRTUAL_SCROLL_CONFIG.FAST_SCROLL_THRESHOLD; + + // 优化节流策略:快速滑动时立即更新,慢速滑动时节流 + if (!this._moveTimer || isFastScroll) { + if (this._moveTimer) { + clearTimeout(this._moveTimer); + this._moveTimer = null; + } + + this.setData({ offset: newOffset }); + + // 虚拟滚动:更新可见范围(快速滑动时使用更大的缓冲区) + if (enableVirtualScroll) { + this.updateVisibleOptions(newOffset, isFastScroll); + } + + this._moveTimer = setTimeout(() => { + this._moveTimer = null; + }, VIRTUAL_SCROLL_CONFIG.THROTTLE_TIME); + } + + // 记录当前状态 + this._lastOffset = newOffset; + this._lastMoveTime = currentTime; }, onTouchEnd(event) { - const { offset, pickItemHeight } = this.data; + if (this._moveTimer) { + clearTimeout(this._moveTimer); + this._moveTimer = null; + } + + const { offset, itemHeight } = this.data; const { startTime } = this; if (offset === this.StartOffset) { return; @@ -129,13 +210,66 @@ export default class PickerItem extends SuperComponent { } // 调整偏移量 - const newOffset = range(offset + distance, -this.getCount() * pickItemHeight, 0); - const index = range(Math.round(-newOffset / pickItemHeight), 0, this.getCount() - 1); - this.setData({ - offset: -index * pickItemHeight, - duration: ANIMATION_DURATION, - curIndex: index, - }); + const newOffset = range(offset + distance, -this.getCount() * itemHeight, 0); + const index = range(Math.round(-newOffset / itemHeight), 0, this.getCount() - 1); + + // 判断是否为快速惯性滚动 + const isFastInertia = Math.abs(distance) > itemHeight * 3; + + // 立即更新虚拟滚动视图(修复惯性滚动后空白问题,快速滚动时使用更大缓冲区) + if (this.data.enableVirtualScroll) { + this.updateVisibleOptions(-index * itemHeight, isFastInertia); + } + + // 清除之前的动画更新定时器 + if (this._animationTimer) { + clearInterval(this._animationTimer); + this._animationTimer = null; + } + + // 在动画执行期间定期更新虚拟滚动视图(确保动画过程流畅) + if (this.data.enableVirtualScroll && Math.abs(distance) > 0) { + const startOffset = offset; + const endOffset = -index * itemHeight; + const startTime = Date.now(); + + this._animationTimer = setInterval(() => { + const elapsed = Date.now() - startTime; + const progress = Math.min(elapsed / ANIMATION_DURATION, 1); + + // 使用缓动函数计算当前偏移量 + const easeOutCubic = 1 - (1 - progress) ** 3; + const currentOffset = startOffset + (endOffset - startOffset) * easeOutCubic; + + // 快速惯性滚动时使用更大的缓冲区 + this.updateVisibleOptions(currentOffset, isFastInertia); + + if (progress >= 1) { + clearInterval(this._animationTimer); + this._animationTimer = null; + } + }, 16); // 约60fps + } + + this.setData( + { + offset: -index * itemHeight, + duration: ANIMATION_DURATION, + curIndex: index, + }, + () => { + // 动画结束后清除定时器并最终更新虚拟滚动视图 + if (this._animationTimer) { + clearInterval(this._animationTimer); + this._animationTimer = null; + } + if (this.data.enableVirtualScroll) { + // 动画结束后使用正常缓冲区(不再是快速滚动状态) + this.updateVisibleOptions(-index * itemHeight, false); + } + }, + ); + if (index === this._selectedIndex) return; this.updateSelected(index, true); }, @@ -164,27 +298,126 @@ export default class PickerItem extends SuperComponent { // 刷新选中状态 update() { - const { options, value, pickerKeys, pickItemHeight, format, columnIndex } = this.data; + const { options, value, pickerKeys, format, columnIndex, itemHeight, visibleItemCount } = this.data; const formatOptions = this.formatOption(options, columnIndex, format); + const optionsCount = formatOptions.length; + + // 判断是否启用虚拟滚动 + const enableVirtualScroll = optionsCount >= VIRTUAL_SCROLL_CONFIG.ENABLE_THRESHOLD; - const index = formatOptions.findIndex((item: PickerItemOption) => item[pickerKeys?.value] === value); + // 大数据量优化:使用 Map 快速查找索引 + let index: number = -1; + if (optionsCount > 500) { + // 构建临时 Map(只在查找时构建,不缓存) + const valueMap = new Map(formatOptions.map((item, idx) => [item[pickerKeys?.value], idx])); + index = valueMap.get(value) ?? -1; + } else { + index = formatOptions.findIndex((item: PickerItemOption) => item[pickerKeys?.value] === value); + } const selectedIndex = index > 0 ? index : 0; - this.setData( - { - formatOptions, - offset: -selectedIndex * pickItemHeight, - curIndex: selectedIndex, - }, - () => { - this.updateSelected(selectedIndex, false); - }, - ); + // 计算wrapper的padding,确保选中项居中显示 + const wrapperPaddingY = ((visibleItemCount - 1) / 2) * itemHeight; + + const updateData: any = { + formatOptions, + offset: -selectedIndex * itemHeight, + curIndex: selectedIndex, + enableVirtualScroll, + totalHeight: optionsCount * itemHeight, + wrapperPaddingY, + }; + + // 如果启用虚拟滚动,计算可见选项 + if (enableVirtualScroll) { + const visibleRange = this.computeVirtualRange(-selectedIndex * itemHeight, optionsCount, itemHeight); + updateData.visibleOptions = formatOptions.slice(visibleRange.startIndex, visibleRange.endIndex); + updateData.virtualStartIndex = visibleRange.startIndex; + updateData.virtualOffsetY = visibleRange.startIndex * itemHeight; + } else { + // 不启用虚拟滚动时,visibleOptions 等于 formatOptions + updateData.visibleOptions = formatOptions; + updateData.virtualStartIndex = 0; + updateData.virtualOffsetY = 0; + } + + this.setData(updateData, () => { + this.updateSelected(selectedIndex, false); + }); + }, + + /** + * 计算虚拟滚动的可见范围 + * @param offset 当前滚动偏移量 + * @param totalCount 总选项数量 + * @param itemHeight 单个选项高度 + * @param isFastScroll 是否为快速滑动 + */ + computeVirtualRange(offset: number, totalCount: number, itemHeight: number, isFastScroll = false) { + const scrollTop = Math.abs(offset); + const { BUFFER_COUNT, FAST_SCROLL_BUFFER } = VIRTUAL_SCROLL_CONFIG; + const { visibleItemCount } = this.data; + + // 根据滑动速度动态调整缓冲区大小 + const dynamicBuffer = isFastScroll ? FAST_SCROLL_BUFFER : BUFFER_COUNT; + + // 根据滑动方向调整缓冲区分配 + // 向上滑动(_scrollDirection = -1):增加顶部缓冲区 + // 向下滑动(_scrollDirection = 1):增加底部缓冲区 + const topBuffer = this._scrollDirection === -1 ? dynamicBuffer + 2 : dynamicBuffer; + const bottomBuffer = this._scrollDirection === 1 ? dynamicBuffer + 2 : dynamicBuffer; + + // 计算当前可见区域的中心索引 + const centerIndex = Math.floor(scrollTop / itemHeight); + + // 计算起始索引(减去顶部缓冲区) + const startIndex = Math.max(0, centerIndex - topBuffer); + // 计算结束索引(加上可见数量和底部缓冲区) + const endIndex = Math.min(totalCount, centerIndex + visibleItemCount + bottomBuffer); + + return { startIndex, endIndex }; + }, + + /** + * 更新虚拟滚动的可见选项 + * @param offset 当前滚动偏移量(可选,不传则使用 data.offset) + * @param isFastScroll 是否为快速滑动 + */ + updateVisibleOptions(offset?: number, isFastScroll = false) { + const { formatOptions, itemHeight, enableVirtualScroll } = this.data; + + if (!enableVirtualScroll) return; + + const currentOffset = offset !== undefined ? offset : this.data.offset; + const visibleRange = this.computeVirtualRange(currentOffset, formatOptions.length, itemHeight, isFastScroll); + + // 只有当可见范围发生变化时才更新 + if ( + visibleRange.startIndex !== this.data.virtualStartIndex || + visibleRange.endIndex !== this.data.virtualStartIndex + this.data.visibleOptions.length + ) { + this.setData({ + visibleOptions: formatOptions.slice(visibleRange.startIndex, visibleRange.endIndex), + virtualStartIndex: visibleRange.startIndex, + virtualOffsetY: visibleRange.startIndex * itemHeight, + }); + } }, getCount() { return this.data?.options?.length; }, + + getCurrentSelected() { + const { offset, itemHeight, formatOptions, pickerKeys } = this.data; + const currentIndex = Math.max(0, Math.min(Math.round(-offset / itemHeight), this.getCount() - 1)); + + return { + index: currentIndex, + value: formatOptions[currentIndex]?.[pickerKeys?.value], + label: formatOptions[currentIndex]?.[pickerKeys?.label], + }; + }, }; } diff --git a/packages/components/picker-item/picker-item.wxml b/packages/components/picker-item/picker-item.wxml index 6d65994bc..18151ce2a 100644 --- a/packages/components/picker-item/picker-item.wxml +++ b/packages/components/picker-item/picker-item.wxml @@ -1,7 +1,7 @@ - - - {{option[pickerKeys.label]}} - + + + + + + + {{option[pickerKeys.label]}} + + + + + + + + + {{option[pickerKeys.label]}} + + + diff --git a/packages/components/picker/README.en-US.md b/packages/components/picker/README.en-US.md index 8f47b8bc0..512a2cf5e 100644 --- a/packages/components/picker/README.en-US.md +++ b/packages/components/picker/README.en-US.md @@ -9,18 +9,19 @@ name | type | default | description | required style | Object | - | CSS(Cascading Style Sheets) | N custom-style | Object | - | CSS(Cascading Style Sheets),used to set style on virtual component | N auto-close | Boolean | true | \- | N -cancel-btn | String / Boolean | true | Typescript:`boolean \| string` | N -confirm-btn | String / Boolean | true | Typescript:`boolean \| string` | N +cancel-btn | String / Boolean | true | Typescript: `boolean \| string` | N +confirm-btn | String / Boolean | true | Typescript: `boolean \| string` | N header | Boolean | true | \- | N -item-height | Number | 80 | \- | N -keys | Object | - | Typescript:`KeysType`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts) | N -popup-props | Object | {} | popup properties。Typescript:`PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N +item-height | Number | 40 | \- | N +keys | Object | - | Typescript: `KeysType`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts) | N +popup-props | Object | {} | popup properties。Typescript: `PopupProps`,[Popup API Documents](./popup?tab=api)。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N title | String | '' | \- | N use-popup | Boolean | true | \- | N using-custom-navbar | Boolean | false | \- | N -value | Array | - | Typescript:`Array` `type PickerValue = string \| number`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N -default-value | Array | undefined | uncontrolled property。Typescript:`Array` `type PickerValue = string \| number`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N +value | Array | - | Typescript: `Array` `type PickerValue = string \| number`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N +default-value | Array | undefined | uncontrolled property。Typescript: `Array` `type PickerValue = string \| number`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N visible | Boolean | false | \- | N +visible-item-count | Number | 5 | \- | N ### Picker Events @@ -48,8 +49,8 @@ name | type | default | description | required -- | -- | -- | -- | -- style | Object | - | CSS(Cascading Style Sheets) | N custom-style | Object | - | CSS(Cascading Style Sheets),used to set style on virtual component | N -format | Function | - | Typescript:`(option: PickerItemOption, columnIndex: number) => PickerItemOption` | N -options | Array | [] | Typescript:`PickerItemOption[]` `interface PickerItemOption { label: string; value: string \| number; icon?: string }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker-item/type.ts) | N +format | Function | - | Typescript: `(option: PickerItemOption, columnIndex: number) => PickerItemOption` | N +options | Array | [] | Typescript: `PickerItemOption[]` `interface PickerItemOption { label: string; value: string \| number; icon?: string }`。[see more ts definition](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker-item/type.ts) | N ### PickerItem Slots @@ -69,15 +70,12 @@ Name | Default Value | Description --td-picker-confirm-color | @brand-color | - --td-picker-indicator-bg-color | @bg-color-secondarycontainer | - --td-picker-indicator-border-radius | 12rpx | - ---td-picker-mask-color-bottom | hsla(0, 0%, 100%, 0.4) | - ---td-picker-mask-color-top | hsla(0, 0%, 100%, 0.92) | - --td-picker-title-color | @text-color-primary | - --td-picker-title-font-size | 36rpx | - --td-picker-title-font-weight | 600 | - --td-picker-title-line-height | 52rpx | - --td-picker-toolbar-height | 116rpx | - --td-picker-transparent-color | --td-picker-transparent-color | - ---td-picker-group-height | 400rpx | - --td-picker-item-active-color | @text-color-primary | - --td-picker-item-color | @text-color-secondary | - --td-picker-item-font-size | @font-size-m | - \ No newline at end of file diff --git a/packages/components/picker/README.md b/packages/components/picker/README.md index 3f2f43d16..c86bf7127 100644 --- a/packages/components/picker/README.md +++ b/packages/components/picker/README.md @@ -57,7 +57,7 @@ auto-close | Boolean | true | 自动关闭;在确认、取消、点击遮罩 cancel-btn | String / Boolean | true | 取消按钮文字。TS 类型:`boolean \| string` | N confirm-btn | String / Boolean | true | 确定按钮文字。TS 类型:`boolean \| string` | N header | Boolean | true | 头部内容。值为 true 显示空白头部,值为 false 不显示任何内容 | N -item-height | Number | 80 | PickerItem 的子项高度,单位 rpx | N +item-height | Number | 40 | PickerItem 的子项高度,单位 `px` | N keys | Object | - | 用来定义 value / label / icon 在 `options` 中对应的字段别名。TS 类型:`KeysType`。[通用类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/common/common.ts) | N popup-props | Object | {} | 透传 Popup 组件全部属性。TS 类型:`PopupProps`,[Popup API Documents](./popup?tab=api)。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N title | String | '' | 标题 | N @@ -66,6 +66,7 @@ using-custom-navbar | Boolean | false | 是否使用了自定义导航栏 | N value | Array | - | 选中值。TS 类型:`Array` `type PickerValue = string \| number`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N default-value | Array | undefined | 选中值。非受控属性。TS 类型:`Array` `type PickerValue = string \| number`。[详细类型定义](https://github.com/Tencent/tdesign-miniprogram/blob/develop/packages/components/picker/type.ts) | N visible | Boolean | false | 是否显示 | N +visible-item-count | Number | 5 | 可视区域 PickerItem 的子项个数 | N ### Picker Events @@ -114,15 +115,12 @@ label-suffix-index | 列表子项后置插槽,用于自定义标签文本之 --td-picker-confirm-color | @brand-color | - --td-picker-indicator-bg-color | @bg-color-secondarycontainer | - --td-picker-indicator-border-radius | 12rpx | - ---td-picker-mask-color-bottom | hsla(0, 0%, 100%, 0.4) | - ---td-picker-mask-color-top | hsla(0, 0%, 100%, 0.92) | - --td-picker-title-color | @text-color-primary | - --td-picker-title-font-size | 36rpx | - --td-picker-title-font-weight | 600 | - --td-picker-title-line-height | 52rpx | - --td-picker-toolbar-height | 116rpx | - --td-picker-transparent-color | --td-picker-transparent-color | - ---td-picker-group-height | 400rpx | - --td-picker-item-active-color | @text-color-primary | - --td-picker-item-color | @text-color-secondary | - --td-picker-item-font-size | @font-size-m | - \ No newline at end of file diff --git a/packages/components/picker/__test__/__snapshots__/index.test.js.snap b/packages/components/picker/__test__/__snapshots__/index.test.js.snap index 4f23c0c19..a6d0f9160 100644 --- a/packages/components/picker/__test__/__snapshots__/index.test.js.snap +++ b/packages/components/picker/__test__/__snapshots__/index.test.js.snap @@ -55,7 +55,7 @@ exports[`picker :base 1`] = ` diff --git a/packages/components/picker/picker.less b/packages/components/picker/picker.less index 49286d728..fc6913f44 100644 --- a/packages/components/picker/picker.less +++ b/packages/components/picker/picker.less @@ -16,8 +16,6 @@ @picker-bg-color: var(--td-picker-bg-color, @bg-color-container); @picker-mask-color: var(--td-picker-transparent-color); -@picker-mask-color-top: var(--td-picker-mask-color-top, hsla(0, 0%, 100%, 0.92)); -@picker-mask-color-bottom: var(--td-picker-mask-color-bottom, hsla(0, 0%, 100%, 0.4)); @picker-indicator-bg-color: var(--td-picker-indicator-bg-color, @bg-color-secondarycontainer); @picker-indicator-border-radius: var(--td-picker-indicator-border-radius, 12rpx); @@ -100,7 +98,6 @@ position: absolute; left: 32rpx; right: 32rpx; - top: 144rpx; pointer-events: none; background-color: @picker-indicator-bg-color; border-radius: @picker-indicator-border-radius; diff --git a/packages/components/picker/picker.ts b/packages/components/picker/picker.ts index ce80eaea1..e58fb53ea 100644 --- a/packages/components/picker/picker.ts +++ b/packages/components/picker/picker.ts @@ -1,5 +1,4 @@ import { SuperComponent, wxComponent, RelationsOptions } from '../common/src/index'; -import { rpx2px } from '../common/utils'; import config from '../common/config'; import props from './props'; import useCustomNavbar from '../mixins/using-custom-navbar'; @@ -29,16 +28,15 @@ export default class Picker extends SuperComponent { }; observers = { - 'value, visible'() { - this.updateChildren(); + 'value, visible'(value, visible) { + // 只在打开弹窗或value变化时更新,关闭时不更新避免滚动 + if (visible) { + this.updateChildren(); + this.updateIndicatorPosition(); + } }, - }; - - lifetimes = { - attached() { - this.setData({ - pickItemHeight: rpx2px(this.properties.itemHeight), - }); + 'itemHeight, visibleItemCount'() { + this.updateIndicatorPosition(); }, }; @@ -47,19 +45,19 @@ export default class Picker extends SuperComponent { classPrefix: name, defaultPopUpProps: {}, defaultPopUpzIndex: 11500, - pickItemHeight: 0, + indicatorTop: 72, // 默认indicator位置,会动态计算 }; methods = { updateChildren() { - const { pickItemHeight } = this.data; - const { value, defaultValue } = this.properties; + const { value, defaultValue, itemHeight, visibleItemCount } = this.properties; this.$children.forEach((child, index) => { child.setData({ value: value?.[index] ?? defaultValue?.[index] ?? '', columnIndex: index, - pickItemHeight, + itemHeight, + visibleItemCount, }); child.update(); }); @@ -82,8 +80,21 @@ export default class Picker extends SuperComponent { }, onConfirm() { - const [value, label] = this.getSelectedValue(); - const columns = this.getColumnIndexes(); + // 获取当前实际显示的选中值 + const value = []; + const label = []; + const columns = []; + + this.$children.forEach((child, columnIndex) => { + const current = child.getCurrentSelected(); + + value.push(current.value); + label.push(current.label); + columns.push({ + column: columnIndex, + index: current.index, + }); + }); this.close('confirm-btn'); this.triggerEvent('confirm', { value, label, columns }); @@ -115,9 +126,16 @@ export default class Picker extends SuperComponent { } this.triggerEvent('close', { trigger }); }, + + updateIndicatorPosition() { + const { itemHeight, visibleItemCount } = this.properties; + const indicatorTop = ((visibleItemCount - 1) / 2) * itemHeight; + this.setData({ indicatorTop }); + }, }; ready() { this.$children.map((column, index) => (column.columnIndex = index)); + this.updateIndicatorPosition(); } } diff --git a/packages/components/picker/props.ts b/packages/components/picker/props.ts index 2039737a2..fc116c3b4 100644 --- a/packages/components/picker/props.ts +++ b/packages/components/picker/props.ts @@ -26,10 +26,10 @@ const props: TdPickerProps = { type: Boolean, value: true, }, - /** PickerItem 的子项高度,单位 rpx */ + /** PickerItem 的子项高度,单位 `px` */ itemHeight: { type: Number, - value: 80, + value: 40, }, /** 用来定义 value / label / icon 在 `options` 中对应的字段别名 */ keys: { @@ -69,6 +69,11 @@ const props: TdPickerProps = { type: Boolean, value: false, }, + /** 可视区域 PickerItem 的子项个数 */ + visibleItemCount: { + type: Number, + value: 5, + }, }; export default props; diff --git a/packages/components/picker/template.wxml b/packages/components/picker/template.wxml index b2093c2f0..b294acd19 100644 --- a/packages/components/picker/template.wxml +++ b/packages/components/picker/template.wxml @@ -18,7 +18,7 @@ - + diff --git a/packages/components/picker/type.ts b/packages/components/picker/type.ts index ca570576e..1f8e40101 100644 --- a/packages/components/picker/type.ts +++ b/packages/components/picker/type.ts @@ -41,8 +41,8 @@ export interface TdPickerProps { value?: boolean; }; /** - * PickerItem 的子项高度,单位 rpx - * @default 80 + * PickerItem 的子项高度,单位 `px` + * @default 40 */ itemHeight?: { type: NumberConstructor; @@ -109,6 +109,14 @@ export interface TdPickerProps { type: BooleanConstructor; value?: boolean; }; + /** + * 可视区域 PickerItem 的子项个数 + * @default 5 + */ + visibleItemCount?: { + type: NumberConstructor; + value?: number; + }; } export type PickerValue = string | number; diff --git a/packages/components/radio/radio.wxml b/packages/components/radio/radio.wxml index eb3ebeada..e97d68879 100644 --- a/packages/components/radio/radio.wxml +++ b/packages/components/radio/radio.wxml @@ -3,7 +3,7 @@ import('@docs/getting-started.md'), }, - { - title: '更新日志', - titleEn: 'CHANGELOG', - name: 'changelog', - meta: { docType: 'explain' }, - path: '/miniprogram-chat/changelog', - component: () => import('@tdesign-miniprogram-chat/CHANGELOG.md'), - }, { title: '什么是流式输出', name: 'sse', diff --git a/packages/tdesign-miniprogram/CHANGELOG.md b/packages/tdesign-miniprogram/CHANGELOG.md index cb31723d1..09f07e97a 100644 --- a/packages/tdesign-miniprogram/CHANGELOG.md +++ b/packages/tdesign-miniprogram/CHANGELOG.md @@ -6,6 +6,16 @@ docClass: timeline --- +## 🌈 1.11.2 `2025-11-12` +### 🚀 Features +- `Picker`: + - ⚠️ `itemHeight` 默认单位改用 `px`,避免单位转换带来的精度问题 @anlyyao ([#4052](https://github.com/Tencent/tdesign-miniprogram/pull/4052)) + - 新增 `visibleItemCount` 属性,可自定义可视区域 `PickerItem` 的子项个数 @anlyyao ([#4052](https://github.com/Tencent/tdesign-miniprogram/pull/4052)) + - 优化大量数据时列表滚动性能 @jarmywang ([#4014](https://github.com/Tencent/tdesign-miniprogram/pull/4014)) +- `Calendar`: 新增 `allowSameDay` 属性,允许 `type='range'` 场景的起始时间相同 @anlyyao ([#4045](https://github.com/Tencent/tdesign-miniprogram/pull/4045)) +- `Cascader`: 支持通过 `keys` 属性定义 `children / disabled` 在 `options` 中对应的字段别名 @anlyyao ([#4044](https://github.com/Tencent/tdesign-miniprogram/pull/4044)) + + ## 🌈 1.11.1 `2025-11-06` ### 🐞 Bug Fixes - `Calendar`: 修复 `value[]` 结合 `swich-mode` 时,初始化错误 @anlyyao ([#4005](https://github.com/Tencent/tdesign-miniprogram/pull/4005)) diff --git a/packages/tdesign-miniprogram/package.json b/packages/tdesign-miniprogram/package.json index a73a4bffa..031cbb823 100644 --- a/packages/tdesign-miniprogram/package.json +++ b/packages/tdesign-miniprogram/package.json @@ -1,6 +1,6 @@ { "name": "tdesign-miniprogram", - "version": "1.11.1", + "version": "1.11.2", "title": "tdesign-miniprogram", "description": "TDesign Component for miniprogram", "main": "miniprogram_dist/index.js", diff --git a/packages/tdesign-miniprogram/site/app.vue b/packages/tdesign-miniprogram/site/app.vue index d4fcdc524..e5a2d90e1 100644 --- a/packages/tdesign-miniprogram/site/app.vue +++ b/packages/tdesign-miniprogram/site/app.vue @@ -1,7 +1,10 @@