{ "schemaVersion": 1, "sourceKind": "authored-spec", "provenance": "authored-in-repo", "specFile": "spec/移动端规格.md", "specSection": "4 · 下拉刷新 PullRefresh", "confidence": "high", "slug": "pullrefresh", "name": "下拉刷新 PullRefresh", "semanticTypeCandidates": [ "pull-refresh", "gesture", "list-viewport" ], "variantDimensions": [ { "name": "state", "values": [ "pull", "ready", "refreshing", "done" ] }, { "name": "threshold", "values": [ "60" ] } ], "representativeVariants": [ { "state": "pull", "threshold": "60", "label": "下拉中" }, { "state": "ready", "threshold": "60", "label": "已达阈值" }, { "state": "refreshing", "threshold": "60", "label": "刷新中" }, { "state": "done", "threshold": "60", "label": "完成提示" } ], "anatomy": { "viewport": "包裹滚动内容的容器,负责手势", "indicator": "下拉指示区,含箭头或旋转图标与状态文字", "content": "业务内容" }, "structurePatterns": { "state": "pull(下拉中)/ ready(已达阈值)/ refreshing(刷新中)/ done(完成提示)", "threshold": "触发阈值,默认 60px" }, "usageHints": [ "列表顶部下拉手势触发刷新,移动端最常见的列表刷新入口", "手势使用 Pointer Events,位移以纵向为主;横向位移更大时让位给页面横滑", "达到阈值后松手进入 refreshing;未达阈值松手回弹", "刷新期间再次下拉不重复触发", "指示区 role=status + aria-live=polite,状态文字变化被读屏播报", "需保留一个非手势的等价入口(如列表底部的刷新按钮)" ], "doNotInvent": [ "惯性与阻尼曲线", "与页面整体下拉(浏览器级)的竞争规则" ], "unknowns": [ "刷新超时的提示形式", "完成提示的停留时长" ], "interaction": [ "手势使用 Pointer Events,位移以纵向为主;横向位移更大时让位给页面横滑", "达到阈值后松手进入 refreshing;未达阈值松手回弹", "刷新期间再次下拉不重复触发" ], "accessibility": [ "指示区 role=status + aria-live=polite,状态文字变化被读屏播报", "需保留一个非手势的等价入口(如列表底部的刷新按钮)" ], "api": { "source": "implementation", "note": "props / events / slots 为 6 端实现的公共接口(说明文字取自规格对应小节)。字段名与各端源码逐名核对:node tools/verify-mobile-docs.mjs", "props": [ { "name": "threshold", "type": "number", "default": "60", "desc": "触发阈值:浏览器端(css/h5/react/vue2/vue3)单位为 px,默认 60;uni-app 端单位为 rpx,默认 120(≈ 60px @375pt)", "required": false }, { "name": "refreshing", "type": "boolean", "default": "false", "desc": "刷新中状态;由业务侧在 refresh 事件后置位(规格 §4.4)", "required": false }, { "name": "done", "type": "boolean", "default": "false", "desc": "完成提示状态(规格 §4.4)", "required": false }, { "name": "fallbackLabel", "type": "string", "default": "'刷新列表'", "desc": "非手势等价入口的文案(规格 §4.6)", "required": false } ], "events": [ { "name": "refresh", "params": "—", "desc": "松手达到阈值时触发(手动点等价入口同样触发)" } ], "slots": [ { "name": "default", "desc": "列表内容;组件在外层包裹手势与指示区(规格 §4.2)" } ], "requiredNote": "「必传」按严格定义:实现里**没有默认值**时才为 Y(本门禁逐条核对 props 与各端源码的默认值,防止契约与实现脱节)。" }, "variantClasses": { "state": { "pull": [ ".is-pulling" ], "ready": [ ".is-ready" ], "refreshing": [ ".is-refreshing" ], "done": [ ".is-done" ] }, "threshold": { "60": [ "--kole-m-pullrefresh-threshold" ] } }, "demos": [ { "id": "pull", "group": "01 组件类型", "title": "下拉中", "desc": "未达阈值:指示器随位移旋转,提示「下拉即可刷新」。", "variant": "state=pull" }, { "id": "ready", "group": "01 组件类型", "title": "已达阈值", "desc": "达到 60px 提示改为「松开立即刷新」,松手即进入刷新。", "variant": "state=ready" }, { "id": "refreshing", "group": "01 组件类型", "title": "刷新中", "desc": "指示器持续旋转、下拉不回弹;期间再次下拉不重复触发。", "variant": "state=refreshing" }, { "id": "done", "group": "01 组件类型", "title": "完成提示", "desc": "完成态短暂停留后复位(停留时长规格未定,见「规格未定」)。", "variant": "state=done" } ], "related": [ { "slug": "swipecell", "why": "同属手势交互:下拉是纵向、滑动是横向,两者靠方向判定让位" }, { "slug": "navbar", "why": "刷新常与顶栏配合(刷新后更新标题或角标)" } ] }