diff --git a/docs/component/pull-refresh.md b/docs/component/pull-refresh.md new file mode 100644 index 00000000..ccaf042a --- /dev/null +++ b/docs/component/pull-refresh.md @@ -0,0 +1,189 @@ +# PullRefresh 下拉刷新 + +用于提供下拉刷新的交互操作。 + +## 基础用法 + +下拉刷新时会触发 `refresh` 事件,在事件的回调函数中可以进行同步或异步操作,操作完成后将 `v-model` 设置为 `false`,表示加载完成。 + +```html + + + + + +``` + +```typescript +import { ref } from 'vue' + +const loading = ref(false) +const list = ref([1, 2, 3, 4, 5]) + +function onRefresh() { + setTimeout(() => { + list.value = [1, 2, 3, 4, 5, 6] + loading.value = false + }, 2000) +} +``` + +## 自定义文案 + +通过 `pulling-text`、`loosing-text`、`loading-text`、`success-text` 属性可以自定义不同状态下的文案。 + +```html + + + + + +``` + +## 成功提示 + +通过 `success-text` 可以设置刷新成功后的提示文案,通过 `success-duration` 可以设置提示展示时长。 + +```html + + + + + +``` + +## 自定义插槽 + +通过插槽可以自定义下拉刷新过程中的提示内容。 + +```html + + + + + + + + + + + + + +``` + +## 禁用状态 + +通过 `disabled` 属性可以禁用下拉刷新。 + +```html + + + + + +``` + +## API + +### Props + +| 参数 | 说明 | 类型 | 可选值 | 默认值 | 最低版本 | +|------|------|------|-------|--------|---------| +| v-model | 是否处于加载中状态 | `boolean` | - | `false` | - | +| disabled | 是否禁用下拉刷新 | `boolean` | - | `false` | - | +| pulling-text | 下拉过程提示文案 | `string` | - | `'下拉即可刷新...'` | - | +| loosing-text | 释放过程提示文案 | `string` | - | `'释放即可刷新...'` | - | +| loading-text | 加载过程提示文案 | `string` | - | `'加载中...'` | - | +| success-text | 刷新成功提示文案 | `string` | - | `''` | - | +| success-duration | 刷新成功提示展示时长(ms) | `number \| string` | - | `500` | - | +| animation-duration | 动画时长(ms) | `number \| string` | - | `300` | - | +| head-height | 顶部内容高度 | `number \| string` | - | `50` | - | +| pull-distance | 触发下拉刷新的距离 | `number \| string` | - | 与 `head-height` 相同 | - | + +### Events + +| 事件名 | 说明 | 参数 | 最低版本 | +|--------|------|------|---------| +| refresh | 下拉刷新时触发 | - | - | +| change | 拖拽时或状态改变时触发 | `{ status: PullRefreshStatus, distance: number }` | - | + +### Slots + +| 名称 | 说明 | 参数 | 最低版本 | +|------|------|------|---------| +| default | 内容区 | - | - | +| normal | 非下拉状态时顶部内容 | - | - | +| pulling | 下拉过程中顶部内容 | `{ distance: number }` | - | +| loosing | 释放过程中顶部内容 | `{ distance: number }` | - | +| loading | 加载过程中顶部内容 | `{ distance: number }` | - | +| success | 刷新成功时顶部内容 | - | - | + +### Methods + +通过 ref 可以获取到 PullRefresh 实例并调用实例方法。 + +| 方法名 | 说明 | 参数 | 返回值 | 最低版本 | +|--------|------|------|--------|---------| +| finish | 结束刷新状态 | - | - | - | + +### 类型定义 + +组件导出以下类型定义: + +```typescript +import type { PullRefreshProps, PullRefreshStatus } from '@/uni_modules/wot-design-uni' + +type PullRefreshStatus = 'normal' | 'pulling' | 'loosing' | 'loading' | 'success' +``` + +## 常见问题 + +### 在某些情况下拖拽不生效? + +请检查是否在页面滚动容器上设置了 `overflow: hidden` 或其他影响滚动的样式。 + +### 如何实现上拉加载更多? + +可以结合 `wd-loadmore` 组件实现上拉加载更多功能。 + +```html + + + + + + +``` \ No newline at end of file diff --git a/docs/en-US/component/pull-refresh.md b/docs/en-US/component/pull-refresh.md new file mode 100644 index 00000000..e1cb1a06 --- /dev/null +++ b/docs/en-US/component/pull-refresh.md @@ -0,0 +1,189 @@ +# PullRefresh + +Provides pull-to-refresh interaction. + +## Basic Usage + +The `refresh` event will be triggered when pull-to-refresh, you can perform synchronous or asynchronous operations in the event callback function. After the operation is completed, set `v-model` to `false` to indicate that the loading is complete. + +```html + + + + + +``` + +```typescript +import { ref } from 'vue' + +const loading = ref(false) +const list = ref([1, 2, 3, 4, 5]) + +function onRefresh() { + setTimeout(() => { + list.value = [1, 2, 3, 4, 5, 6] + loading.value = false + }, 2000) +} +``` + +## Custom Text + +You can customize the text in different states through `pulling-text`, `loosing-text`, `loading-text`, and `success-text` properties. + +```html + + + + + +``` + +## Success Tip + +You can set the prompt text after successful refresh through `success-text`, and set the prompt display duration through `success-duration`. + +```html + + + + + +``` + +## Custom Slots + +You can customize the prompt content during the pull-to-refresh process through slots. + +```html + + + + + + + + + + + + + +``` + +## Disabled State + +You can disable pull-to-refresh through the `disabled` property. + +```html + + + + + +``` + +## API + +### Props + +| Parameter | Description | Type | Optional Values | Default Value | Minimum Version | +|-----------|-------------|------|----------------|---------------|----------------| +| v-model | Whether it is in loading state | `boolean` | - | `false` | - | +| disabled | Whether to disable pull-to-refresh | `boolean` | - | `false` | - | +| pulling-text | Text during pulling process | `string` | - | `'Pull to refresh...'` | - | +| loosing-text | Text during releasing process | `string` | - | `'Release to refresh...'` | - | +| loading-text | Text during loading process | `string` | - | `'Loading...'` | - | +| success-text | Text for successful refresh | `string` | - | `''` | - | +| success-duration | Display duration of success tip (ms) | `number \| string` | - | `500` | - | +| animation-duration | Animation duration (ms) | `number \| string` | - | `300` | - | +| head-height | Height of top content | `number \| string` | - | `50` | - | +| pull-distance | Distance to trigger pull-to-refresh | `number \| string` | - | Same as `head-height` | - | + +### Events + +| Event Name | Description | Parameters | Minimum Version | +|------------|-------------|------------|----------------| +| refresh | Triggered when pull-to-refresh | - | - | +| change | Triggered when dragging or status changes | `{ status: PullRefreshStatus, distance: number }` | - | + +### Slots + +| Name | Description | Parameters | Minimum Version | +|------|-------------|------------|----------------| +| default | Content area | - | - | +| normal | Top content when not pulling | - | - | +| pulling | Top content during pulling | `{ distance: number }` | - | +| loosing | Top content during releasing | `{ distance: number }` | - | +| loading | Top content during loading | `{ distance: number }` | - | +| success | Top content when refresh succeeds | - | - | + +### Methods + +You can get the PullRefresh instance through ref and call instance methods. + +| Method Name | Description | Parameters | Return Value | Minimum Version | +|-------------|-------------|------------|--------------|----------------| +| finish | End refresh state | - | - | - | + +### Type Definitions + +The component exports the following type definitions: + +```typescript +import type { PullRefreshProps, PullRefreshStatus } from '@/uni_modules/wot-design-uni' + +type PullRefreshStatus = 'normal' | 'pulling' | 'loosing' | 'loading' | 'success' +``` + +## FAQ + +### Dragging doesn't work in some cases? + +Please check if `overflow: hidden` or other styles that affect scrolling are set on the page scroll container. + +### How to implement pull-up to load more? + +You can combine with the `wd-loadmore` component to implement pull-up to load more functionality. + +```html + + + + + + +``` \ No newline at end of file diff --git a/src/locale/en-US.json b/src/locale/en-US.json index a6a5e41a..f291dcbc 100644 --- a/src/locale/en-US.json +++ b/src/locale/en-US.json @@ -864,6 +864,7 @@ "progress-title": "Progress", "pu-tong-an-niu": "Normal button", "pu-tong-shu-zhi": "Ordinary", + "pullrefresh-xia-la-shua-xin": "", "q1-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo": "Q1: The seven day no reason return and exchange policy allows customers to accept returns and exchanges for all products within 7 days (based on the delivery receipt) without affecting secondary sales. The seven day no reason return and exchange policy allows customers to accept returns and exchanges for all products within 7 days (based on the delivery receipt) without affecting secondary sales. The seven day no reason return and exchange policy allows customers to accept returns and exchanges for all products within 7 days (based on the delivery receipt) without affecting secondary sales. The seven day no reason return and exchange policy allows customers to accept returns and exchanges for all products within 7 days (based on the delivery receipt) without affecting secondary sales. The seven day no reason return and exchange policy allows customers to accept returns and exchanges for all products within 7 days (based on the delivery receipt) without affecting secondary sales. The seven day no reason return and exchange policy allows customers to accept returns and exchanges for all products within 7 days (based on the delivery receipt) without affecting secondary sales.", "q1-you-hui-quan-shi-yong-xiang-qing-xiang-qing-ye-mian-wo-de-wo-de-you-hui-you-hui-quan-gui-ze-shuo-ming": "Q1: What are the details of coupon usage? Details page 【 My 】 - 【 My Discounts 】 - 【 Coupon Rules Explanation 】.", "qi-ta-xin-xi": "Other information", diff --git a/src/locale/zh-CN.json b/src/locale/zh-CN.json index 0e8bfbd6..b705ac80 100644 --- a/src/locale/zh-CN.json +++ b/src/locale/zh-CN.json @@ -864,6 +864,7 @@ "progress-title": "Progress 进度条", "pu-tong-an-niu": "普通按钮", "pu-tong-shu-zhi": "普通数值", + "pullrefresh-xia-la-shua-xin": "Pullrefresh 下拉刷新", "q1-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo-qi-tian-wu-li-you-tui-huan-huo-zhi-du-suo-you-shang-pin-zai-bu-ying-xiang-er-ci-xiao-shou-de-qing-kuang-xia-7-tian-nei-yi-kuai-di-dan-qian-shou-wei-zhun-jun-jie-shou-ke-hu-tui-huan-huo": "Q1:七天无理由退换货制度,所有商品在不影响二次销售的情况下7天内(以快递单签收为准)均接受客户退换货。七天无理由退换货制度,所有商品在不影响二次销售的情况下7天内(以快递单签收为准)均接受客户退换货。七天无理由退换货制度,所有商品在不影响二次销售的情况下7天内(以快递单签收为准)均接受客户退换货。七天无理由退换货制度,所有商品在不影响二次销售的情况下7天内(以快递单签收为准)均接受客户退换货。七天无理由退换货制度,所有商品在不影响二次销售的情况下7天内(以快递单签收为准)均接受客户退换货。七天无理由退换货制度,所有商品在不影响二次销售的情况下7天内(以快递单签收为准)均接受客户退换货。", "q1-you-hui-quan-shi-yong-xiang-qing-xiang-qing-ye-mian-wo-de-wo-de-you-hui-you-hui-quan-gui-ze-shuo-ming": "Q1:优惠券使用详情?详情页面【我的】-【我的优惠】-【优惠券规则说明】。", "qi-ta-xin-xi": "其他信息", diff --git a/src/pages.json b/src/pages.json index 6224104c..75b48e4f 100644 --- a/src/pages.json +++ b/src/pages.json @@ -1326,6 +1326,21 @@ "navigationBarTitleText": "%rootportal-title%" // #endif } + }, + { + "path": "pullRefresh/Index", + "name": "pullRefresh", + "style": { + "mp-alipay": { + "allowsBounceVertical": "NO" + }, + // #ifdef MP + "navigationBarTitleText": "PullRefresh 下拉刷新", + // #endif + // #ifndef MP + "navigationBarTitleText": "%pullrefresh-title%" + // #endif + } } ] } diff --git a/src/pages/index/Index.vue b/src/pages/index/Index.vue index ba5149d2..c7cdd47d 100644 --- a/src/pages/index/Index.vue +++ b/src/pages/index/Index.vue @@ -319,6 +319,10 @@ const list = computed(() => [ { id: 'numberKeyboard', name: t('numberkeyboard-shu-zi-jian-pan') + }, + { + id: 'pullRefresh', + name: t('pullrefresh-xia-la-shua-xin') } ] }, diff --git a/src/subPages/pullRefresh/Index.vue b/src/subPages/pullRefresh/Index.vue new file mode 100644 index 00000000..38744aa0 --- /dev/null +++ b/src/subPages/pullRefresh/Index.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/src/uni_modules/wot-design-uni/components/common/abstracts/variable.scss b/src/uni_modules/wot-design-uni/components/common/abstracts/variable.scss index bdbb88e1..77d7a62e 100644 --- a/src/uni_modules/wot-design-uni/components/common/abstracts/variable.scss +++ b/src/uni_modules/wot-design-uni/components/common/abstracts/variable.scss @@ -658,7 +658,14 @@ $-toast-loading-margin-bottom: var(--wot-toast-loading-margin-bottom, 16px) !def $-toast-box-shadow: var(--wot-toast-box-shadow, 0px 6px 16px 0px rgba(0, 0, 0, 0.08)) !default; // 外部阴影 /* loading */ -$-loading-size: var(--wot-loading-size, 32px) !default; // loading 大小 +$-loading-size: var(--wot-loading-size, 30px) !default; // loading图标大小 + +/* pull-refresh */ +$-pull-refresh-head-height: var(--wot-pull-refresh-head-height, 50px) !default; // 头部高度 +$-pull-refresh-head-font-size: var(--wot-pull-refresh-head-font-size, 14px) !default; // 头部字体大小 +$-pull-refresh-head-color: var(--wot-pull-refresh-head-color, rgba(0, 0, 0, 0.6)) !default; // 头部字体颜色 +$-pull-refresh-loading-size: var(--wot-pull-refresh-loading-size, 20px) !default; // 加载图标大小 +$-pull-refresh-animation-duration: var(--wot-pull-refresh-animation-duration, 300ms) !default; // 动画持续时间 大小 /* tooltip */ $-tooltip-bg: var(--wot-tooltip-bg, rgba(38, 39, 40, 0.8)) !default; // 背景色 @@ -970,5 +977,16 @@ $-signature-border: var(--wot-signature-border, 1px solid $-color-gray-5) !defau $-signature-footer-margin-top: var(--wot-signature-footer-margin-top, 8px) !default; // 底部按钮上边距 $-signature-button-margin-left: var(--wot-signature-button-margin-left, 8px) !default; // 底部按钮左边距 +/* pull-refresh */ +$-pull-refresh-head-height: var(--wot-pull-refresh-head-height, 50px) !default; // 头部高度 +$-pull-refresh-head-bg: var(--wot-pull-refresh-head-bg, $-color-white) !default; // 头部背景色 +$-pull-refresh-content-bg: var(--wot-pull-refresh-content-bg, $-color-white) !default; // 内容背景色 +$-pull-refresh-text-fs: var(--wot-pull-refresh-text-fs, $-fs-content) !default; // 文字字体大小 +$-pull-refresh-text-color: var(--wot-pull-refresh-text-color, $-color-secondary) !default; // 文字颜色 +$-pull-refresh-text-line-height: var(--wot-pull-refresh-text-line-height, 1.5) !default; // 文字行高 +$-pull-refresh-loading-margin: var(--wot-pull-refresh-loading-margin, 8px) !default; // 加载状态文字左边距 +$-pull-refresh-loading-size: var(--wot-pull-refresh-loading-size, 16px) !default; // 加载图标大小 +$-pull-refresh-animation-duration: var(--wot-pull-refresh-animation-duration, 300ms) !default; // 动画持续时间 + diff --git a/src/uni_modules/wot-design-uni/components/wd-pull-refresh/index.scss b/src/uni_modules/wot-design-uni/components/wd-pull-refresh/index.scss new file mode 100644 index 00000000..f1f0a213 --- /dev/null +++ b/src/uni_modules/wot-design-uni/components/wd-pull-refresh/index.scss @@ -0,0 +1,42 @@ +@import "../common/abstracts/_mixin.scss"; +@import "../common/abstracts/variable.scss"; + +@include b(pull-refresh) { + position: relative; + overflow: hidden; + user-select: none; + + @include e(head) { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: $-pull-refresh-head-height; + display: flex; + align-items: center; + justify-content: center; + background-color: $-color-white; + } + + @include e(content) { + position: relative; + background-color: $-pull-refresh-content-bg; + } + + @include e(text) { + font-size: $-pull-refresh-text-fs; + color: $-pull-refresh-text-color; + line-height: $-pull-refresh-text-line-height; + } + + @include e(loading) { + display: flex; + align-items: center; + justify-content: center; + flex-direction: row; + + .wd-pull-refresh__text { + margin-left: $-pull-refresh-loading-margin; + } + } +} \ No newline at end of file diff --git a/src/uni_modules/wot-design-uni/components/wd-pull-refresh/types.ts b/src/uni_modules/wot-design-uni/components/wd-pull-refresh/types.ts new file mode 100644 index 00000000..1f748c78 --- /dev/null +++ b/src/uni_modules/wot-design-uni/components/wd-pull-refresh/types.ts @@ -0,0 +1,68 @@ +/* + * @Author: Solo Coding + * @Date: 2024-12-19 + * @LastEditTime: 2024-12-19 + * @LastEditors: Solo Coding + * @Description: PullRefresh 下拉刷新组件类型定义 + * @FilePath: /wot-design-uni/src/uni_modules/wot-design-uni/components/wd-pull-refresh/types.ts + */ +import type { ExtractPropTypes } from 'vue' +import { baseProps, makeBooleanProp, makeStringProp, makeNumericProp } from '../common/props' + +export type PullRefreshStatus = 'normal' | 'pulling' | 'loosing' | 'loading' | 'success' + +export type ScrollMode = 'scroll-view' | 'page' + +export const pullRefreshProps = { + ...baseProps, + /** + * 是否处于加载中状态 + */ + modelValue: makeBooleanProp(false), + /** + * 是否禁用下拉刷新 + */ + disabled: makeBooleanProp(false), + /** + * 下拉过程提示文案 + */ + pullingText: makeStringProp('下拉即可刷新...'), + /** + * 释放过程提示文案 + */ + loosingText: makeStringProp('释放即可刷新...'), + /** + * 加载过程提示文案 + */ + loadingText: makeStringProp('加载中...'), + /** + * 刷新成功提示文案 + */ + successText: makeStringProp(''), + /** + * 刷新成功提示展示时长(ms) + */ + successDuration: makeNumericProp(300), + /** + * 动画时长(ms) + */ + animationDuration: makeNumericProp(300), + /** + * 顶部内容高度 + */ + headHeight: makeNumericProp(50), + /** + * 触发下拉刷新的距离 + */ + pullDistance: makeNumericProp(''), + /** + * 滚动模式:scroll-view(局部滚动) | page(页面滚动) + */ + scrollMode: makeStringProp('page'), + /** + * scroll-view 模式下的固定高度 + */ + height: makeNumericProp(400) +} + +export type PullRefreshProps = ExtractPropTypes diff --git a/src/uni_modules/wot-design-uni/components/wd-pull-refresh/wd-pull-refresh.vue b/src/uni_modules/wot-design-uni/components/wd-pull-refresh/wd-pull-refresh.vue new file mode 100644 index 00000000..e59098a2 --- /dev/null +++ b/src/uni_modules/wot-design-uni/components/wd-pull-refresh/wd-pull-refresh.vue @@ -0,0 +1,273 @@ + + + + + diff --git a/src/uni_modules/wot-design-uni/global.d.ts b/src/uni_modules/wot-design-uni/global.d.ts index 94014a34..47217a05 100644 --- a/src/uni_modules/wot-design-uni/global.d.ts +++ b/src/uni_modules/wot-design-uni/global.d.ts @@ -40,6 +40,7 @@ declare module 'vue' { WdPopover: typeof import('./components/wd-popover/wd-popover.vue')['default'] WdPopup: typeof import('./components/wd-popup/wd-popup.vue')['default'] WdProgress: typeof import('./components/wd-progress/wd-progress.vue')['default'] + WdPullRefresh: typeof import('./components/wd-pull-refresh/wd-pull-refresh.vue')['default'] WdRadio: typeof import('./components/wd-radio/wd-radio.vue')['default'] WdRadioGroup: typeof import('./components/wd-radio-group/wd-radio-group.vue')['default'] WdRate: typeof import('./components/wd-rate/wd-rate.vue')['default'] @@ -95,6 +96,7 @@ declare module 'vue' { WdFloatingPanel: typeof import('./components/wd-floating-panel/wd-floating-panel.vue')['default'] WdSignature: typeof import('./components/wd-signature/wd-signature.vue')['default'] WdRootPortal: typeof import('./components/wd-root-portal/wd-root-portal.vue')['default'] + WdPullRefresh: typeof import('./components/wd-pull-refresh/wd-pull-refresh.vue')['default'] } } diff --git a/tests/components/wd-pull-refresh.test.ts b/tests/components/wd-pull-refresh.test.ts new file mode 100644 index 00000000..a35e1f37 --- /dev/null +++ b/tests/components/wd-pull-refresh.test.ts @@ -0,0 +1,121 @@ +import { mount } from '@vue/test-utils' +import WdPullRefresh from '@/uni_modules/wot-design-uni/components/wd-pull-refresh/wd-pull-refresh.vue' +import { describe, expect, test, vi } from 'vitest' +import { nextTick } from 'vue' + +describe('WdPullRefresh', () => { + test('should render correctly', () => { + const wrapper = mount(WdPullRefresh, { + props: { + modelValue: false + }, + slots: { + default: '
Content
' + } + }) + + expect(wrapper.find('.wd-pull-refresh').exists()).toBe(true) + expect(wrapper.find('.wd-pull-refresh__content').exists()).toBe(true) + expect(wrapper.find('.wd-pull-refresh__head').exists()).toBe(true) + }) + + test('should support scroll mode prop', async () => { + const wrapper = mount(WdPullRefresh, { + props: { + modelValue: false, + scrollMode: 'page' + }, + slots: { + default: '
Content
' + } + }) + + await nextTick() + + expect(wrapper.vm.scrollMode).toBe('page') + }) + + test('should handle page scroll correctly', async () => { + const wrapper = mount(WdPullRefresh, { + props: { + modelValue: false, + scrollMode: 'page' + }, + slots: { + default: '
Content
' + } + }) + + await nextTick() + + // 验证组件正常渲染 + expect(wrapper.vm.scrollMode).toBe('page') + }) + + test('should render scroll-view mode correctly', async () => { + const wrapper = mount(WdPullRefresh, { + props: { + modelValue: false, + scrollMode: 'scroll-view' + }, + slots: { + default: '
Content
' + } + }) + + await nextTick() + + // 验证 scroll-view 模式下的渲染 + expect(wrapper.find('.wd-pull-refresh__scroll-view').exists()).toBe(true) + expect(wrapper.vm.scrollMode).toBe('scroll-view') + }) + + test('should use default scroll mode', async () => { + const wrapper = mount(WdPullRefresh, { + props: { + modelValue: false + }, + slots: { + default: '
Content
' + } + }) + + await nextTick() + // 默认模式应该是 page + expect(wrapper.vm.scrollMode).toBe('page') + }) + + test('should expose correct methods', () => { + const wrapper = mount(WdPullRefresh, { + props: { + modelValue: false + }, + slots: { + default: '
Content
' + } + }) + + // 检查暴露的方法 + expect(typeof wrapper.vm.finish).toBe('function') + expect(wrapper.vm.scrollMode).toBeDefined() + }) + + test('should handle refresh correctly', async () => { + const onRefresh = vi.fn() + const wrapper = mount(WdPullRefresh, { + props: { + modelValue: false, + scrollMode: 'page' + }, + slots: { + default: '
Content
' + } + }) + + wrapper.vm.$emit('refresh') + await nextTick() + + // 检查是否可以正常触发刷新事件 + expect(wrapper.emitted('refresh')).toBeTruthy() + }) +})