Skip to content

RequestList 列表组件系统

强大的 React 列表组件库,支持无限滚动、虚拟滚动、分页、选择、分组、排序等功能。

导入

tsx
// 核心组件
import { InfiniteList, PaginatedList, RequestList } from '@xcloud/ui-mobile'

// 增强组件
import {
  VirtualInfiniteList,
  SelectableList,
  GroupedList,
  SortableList,
  PullToRefresh,
  SwipeableListItem,
} from '@xcloud/ui-mobile'

// Hooks
import {
  useInfiniteList,
  usePaginatedList,
  useListSelection,
  useListFilters,
  useListMutations,
} from '@xcloud/ui-mobile'

核心组件

InfiniteList - 无限滚动列表

基础的无限滚动列表组件,自动触底加载更多数据:

tsx
import { InfiniteList } from '@xcloud/ui-mobile'

<InfiniteList
  queryKey={['products']}
  queryFn={async ({ pageParam = 1 }) => {
    const res = await fetch(`/api/products?page=${pageParam}`)
    return res.json()
  }}
  renderItem={(product) => <ProductCard product={product} />}
  emptyText="暂无商品"
/>

PaginatedList - 分页列表

传统的页码分页列表:

tsx
<PaginatedList
  queryKey={['orders']}
  queryFn={fetchOrders}
  pageSize={20}
  renderItem={(order) => <OrderItem order={order} />}
  showPagination
/>

增强组件

VirtualInfiniteList - 虚拟滚动列表

使用虚拟滚动技术,高性能渲染大量数据。基于 @tanstack/react-virtual 实现:

tsx
import { VirtualInfiniteList } from '@xcloud/ui-mobile'

<VirtualInfiniteList
  queryKey={['products']}
  queryFn={fetchProducts}
  height={600}
  estimateSize={72}
  overscan={5}
  renderItem={(product) => <ProductCard product={product} />}
/>

主要特性:

  • 🚀 只渲染可见区域,支持数千条数据流畅滚动
  • 📏 自动测量每个项目的实际高度
  • 🔄 结合无限滚动,自动加载更多数据

SelectableList - 可选择列表

支持单选/多选、全选、批量操作的列表组件:

tsx
import { SelectableList } from '@xcloud/ui-mobile'

<SelectableList
  queryKey={['products']}
  queryFn={fetchProducts}
  selectionOptions={{
    getItemId: (item) => item.id,
    multiple: true,
    maxSelection: 20,
  }}
  batchActions={[
    {
      key: 'delete',
      label: '删除',
      icon: '🗑️',
      danger: true,
      onAction: async (items) => {
        await deleteProducts(items.map(i => i.id))
      },
    },
  ]}
  renderItem={(product, index, { isSelected, onToggle }) => (
    <div onClick={onToggle}>
      <input type="checkbox" checked={isSelected} />
      <span>{product.name}</span>
    </div>
  )}
  onSelectionChange={(ids, items) => {
    console.log('选中:', ids)
  }}
/>

主要特性:

  • ☑️ 支持单选和多选模式,可设置最大选择数量
  • ✅ 全选功能,显示选中数量和半选状态
  • 🎯 自定义批量操作,如删除、导出等

GroupedList - 分组列表

支持按规则分组显示数据,支持吸顶标题和可折叠分组:

tsx
import { GroupedList } from '@xcloud/ui-mobile'

<GroupedList
  queryKey={['contacts']}
  queryFn={fetchContacts}
  groupBy={(contact) => contact.name[0].toUpperCase()}
  renderGroupHeader={(groupKey, items) => (
    <div>{groupKey} ({items.length})</div>
  )}
  renderItem={(contact) => <ContactCard contact={contact} />}
  stickyHeader
  collapsible
  defaultExpandedGroups={['A', 'B', 'C']}
/>

主要特性:

  • 📋 通过 groupBy 函数自动将数据分组显示
  • 📌 滚动时分组标题自动吸附在顶部
  • 🎯 支持折叠/展开分组,节省空间

使用场景: 联系人列表(按首字母)、订单列表(按日期)、商品列表(按分类)等

SortableList - 可排序列表

支持拖拽排序的列表组件,基于 @dnd-kit 实现:

tsx
import { SortableList } from '@xcloud/ui-mobile'

const [tasks, setTasks] = useState([...])

<SortableList
  items={tasks}
  getItemId={(task) => task.id}
  onReorder={(newTasks) => {
    setTasks(newTasks)
  }}
  showDragHandle
  disabled={false}
  renderItem={(task) => (
    <div>
      <input type="checkbox" checked={task.completed} />
      <span>{task.title}</span>
    </div>
  )}
/>

主要特性:

  • 🎯 基于 @dnd-kit,支持鼠标和触摸拖拽
  • ☰ 可选的拖拽手柄,避免误触
  • 🔒 支持动态启用/禁用拖拽功能

辅助组件

PullToRefresh - 下拉刷新

移动端下拉刷新组件,支持触摸手势:

tsx
import { PullToRefresh, InfiniteList } from '@xcloud/ui-mobile'
import { useQueryClient } from '@tanstack/react-query'

const queryClient = useQueryClient()

<PullToRefresh
  onRefresh={async () => {
    await queryClient.invalidateQueries({ queryKey: ['products'] })
  }}
  threshold={80}
  maxPullDistance={150}
>
  <InfiniteList
    queryKey={['products']}
    queryFn={fetchProducts}
    renderItem={(item) => <ProductCard item={item} />}
  />
</PullToRefresh>

主要特性:

  • 📱 原生触摸事件支持,流畅的手势体验
  • 🎨 平滑的阻尼动画,符合物理直觉
  • ⚙️ 自定义触发阈值、文本和指示器

SwipeableListItem - 滑动操作列表项

iOS/Android 风格的左右滑动操作:

tsx
import { SwipeableListItem, InfiniteList } from '@xcloud/ui-mobile'

<InfiniteList
  queryKey={['emails']}
  queryFn={fetchEmails}
  renderItem={(email) => (
    <SwipeableListItem
      leftActions={[
        {
          key: 'star',
          label: '收藏',
          icon: '⭐',
          color: '#ff9800',
          onAction: () => handleStar(email.id),
        },
      ]}
      rightActions={[
        {
          key: 'delete',
          label: '删除',
          icon: '🗑️',
          color: '#f44336',
          onAction: () => handleDelete(email.id),
        },
      ]}
      threshold={50}
    >
      <div>{email.subject}</div>
    </SwipeableListItem>
  )}
/>

主要特性:

  • ⬅️➡️ 支持左滑和右滑,可配置不同操作
  • 🎨 自定义按钮颜色、图标和文字
  • 📱 流畅的触摸交互,符合原生应用习惯

使用场景: 邮件列表、聊天消息、待办事项、购物车等

使用 Hooks

useInfiniteList

无限滚动列表的底层 Hook:

tsx
import { useInfiniteList } from '@xcloud/ui-mobile'

const { data, loadMore, hasMore, isLoading, refresh } = useInfiniteList({
  queryKey: ['products'],
  queryFn: fetchProducts,
  enabled: true,
})

useListSelection

列表选择状态管理 Hook:

tsx
import { useListSelection } from '@xcloud/ui-mobile'

const {
  selectedIds,
  selectedCount,
  isSelected,
  toggleSelect,
  selectAll,
  clearSelection,
  isAllSelected,
  isIndeterminate,
} = useListSelection({
  getItemId: (item) => item.id,
  multiple: true,
  maxSelection: 100,
  onSelectionChange: (ids) => {
    console.log('选中的 IDs:', ids)
  },
})

useListFilters

列表筛选和搜索 Hook:

tsx
import { useListFilters } from '@xcloud/ui-mobile'

const {
  search,
  setSearch,
  sort,
  setSort,
  filters,
  setFilters,
  reset,
} = useListFilters({
  searchKeys: ['name', 'description'],
  defaultSort: { field: 'createdAt', order: 'desc' },
})

useListMutations

列表 CRUD 操作 Hook:

tsx
import { useListMutations } from '@xcloud/ui-mobile'

const {
  create,
  update,
  remove,
  isCreating,
  isUpdating,
  isDeleting,
} = useListMutations({
  queryKey: ['products'],
  createFn: async (data) => {
    await fetch('/api/products', { method: 'POST', body: JSON.stringify(data) })
  },
  updateFn: async (id, data) => {
    await fetch(`/api/products/${id}`, { method: 'PUT', body: JSON.stringify(data) })
  },
  deleteFn: async (id) => {
    await fetch(`/api/products/${id}`, { method: 'DELETE' })
  },
  onSuccess: () => console.log('操作成功'),
})

与 @xcloud/request 集成

RequestList 组件提供了与 @xcloud/request 包的无缝集成,通过适配器自动处理分页、搜索和过滤。

安装

bash
pnpm add @xcloud/request @xcloud/ui-mobile

设置 RequestProvider

首先,在应用根组件中设置 RequestProvider

tsx
import { RequestProvider } from '@xcloud/request'
import { createClient } from '@xcloud/request'

// 创建全局请求客户端实例
const requestClient = createClient({
  baseURL: '/api',
  // 其他配置...
})

function App() {
  return (
    <RequestProvider client={requestClient}>
      {/* 你的应用组件 */}
    </RequestProvider>
  )
}

使用 Hook 版本适配器(推荐)

使用 useRequestAdapteruseSearchableRequestAdapter 自动从 RequestProvider 获取 client:

tsx
import { useRequestAdapter } from '@xcloud/ui-mobile'
import { RequestList } from '@xcloud/ui-mobile'

interface User {
  id: string
  name: string
  email: string
}

function UserList() {
  // 自动从 RequestProvider 获取 client
  const queryFn = useRequestAdapter<User>({
    url: '/users',
    method: 'GET', // 可选,默认 GET
  })

  return (
    <RequestList
      queryKey={['users']}
      queryFn={queryFn}
      mode="pagination"
      renderItem={(user) => (
        <div className="p-4 border-b">
          <h3>{user.name}</h3>
          <p>{user.email}</p>
        </div>
      )}
    />
  )
}

使用可搜索适配器(Hook 版本)

tsx
import { useSearchableRequestAdapter } from '@xcloud/ui-mobile'
import { RequestList } from '@xcloud/ui-mobile'
import { useState } from 'react'

function UserList() {
  const [search, setSearch] = useState('')
  const [role, setRole] = useState<string>()

  // 自动从 RequestProvider 获取 client
  const queryFn = useSearchableRequestAdapter<User>({
    url: '/users',
    searchFields: ['name', 'email'], // 搜索字段
    filterFields: ['role', 'status'], // 过滤字段
  })

  return (
    <>
      <input
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="搜索用户..."
      />
      <RequestList
        queryKey={['users', { search, role }]}
        queryFn={queryFn}
        mode="pagination"
        renderItem={(user) => <UserCard user={user} />}
      />
    </>
  )
}

使用 useRequestClient Hook(工厂函数版本)

在组件中使用 useRequestClient 获取客户端实例:

tsx
import { useRequestClient } from '@xcloud/request'
import { RequestList, createRequestAdapter } from '@xcloud/ui-mobile'

interface User {
  id: string
  name: string
  email: string
}

function UserList() {
  // 从 Context 获取客户端实例
  const client = useRequestClient()

  // 创建适配器
  const queryFn = createRequestAdapter<User>({
    url: '/users',
    client, // 使用 Context 中的客户端
    method: 'GET',
  })

  return (
    <RequestList
      queryKey={['users']}
      queryFn={queryFn}
      mode="pagination"
      renderItem={(user) => (
        <div className="p-4 border-b">
          <h3>{user.name}</h3>
          <p>{user.email}</p>
        </div>
      )}
    />
  )
}

基础用法

不使用 useRequestClient 时,可以直接传入客户端实例:

tsx
import { RequestList, createRequestAdapter } from '@xcloud/ui-mobile'
import { createClient } from '@xcloud/request'

// 创建请求客户端实例
const client = createClient({
  baseURL: '/api',
})

interface User {
  id: string
  name: string
  email: string
  role: 'admin' | 'user'
}

function UserList() {
  // 创建适配器
  const queryFn = createRequestAdapter<User>({
    url: '/users',
    client,
    method: 'GET',
  })

  return (
    <RequestList
      queryKey={['users']}
      queryFn={queryFn}
      mode="pagination"
      renderItem={(user) => (
        <div className="p-4 border-b">
          <h3>{user.name}</h3>
          <p>{user.email}</p>
          <span>{user.role}</span>
        </div>
      )}
    />
  )
}

搜索和过滤

使用 createSearchableRequestAdapter 支持搜索和多字段过滤:

tsx
import { useState } from 'react'
import { RequestList, createSearchableRequestAdapter } from '@xcloud/ui-mobile'
import { createClient } from '@xcloud/request'

const client = createClient()

function UserListWithSearch() {
  const [search, setSearch] = useState('')
  const [role, setRole] = useState<string | undefined>()

  const queryFn = createSearchableRequestAdapter<User>({
    url: '/users',
    client,
    searchFields: ['name', 'email'], // 搜索这些字段
    filterFields: ['role', 'status'], // 过滤这些字段
  })

  return (
    <div>
      {/* 搜索框 */}
      <input
        value={search}
        onChange={(e) => setSearch(e.target.value)}
        placeholder="搜索用户..."
      />

      {/* 过滤器 */}
      <select value={role} onChange={(e) => setRole(e.target.value)}>
        <option value="">全部角色</option>
        <option value="admin">管理员</option>
        <option value="user">普通用户</option>
      </select>

      {/* 列表 - queryKey 包含搜索和过滤参数 */}
      <RequestList
        queryKey={['users', { search, role }]}
        queryFn={queryFn}
        mode="pagination"
        renderItem={(user) => (
          <div>{user.name}</div>
        )}
      />
    </div>
  )
}

自定义参数转换

如果你的 API 使用不同的参数格式:

tsx
import { createRequestAdapter, extractQueryParams } from '@xcloud/ui-mobile'

const queryFn = createRequestAdapter<User>({
  url: '/users',
  client,
  // 自定义参数格式
  transformParams: (context) => {
    const params = extractQueryParams(context.queryKey)
    return {
      pageNumber: context.pageParam, // API 使用 pageNumber
      limit: 20,                      // API 使用 limit
      keyword: params.search,         // API 使用 keyword
      ...params,
    }
  },
  // 如果响应格式不同,也可以自定义转换
  transformResponse: (response) => ({
    data: response.list,
    hasMore: response.hasMore,
    total: response.total,
    currentPage: response.page,
    pageSize: response.pageSize,
    nextPage: response.hasMore ? response.page + 1 : undefined,
  }),
})

POST 请求

默认使用 GET,也可以使用 POST:

tsx
const queryFn = createRequestAdapter<User>({
  url: '/users/search',
  client,
  method: 'POST', // 使用 POST 方法
})

额外参数

添加固定的请求参数:

tsx
const queryFn = createRequestAdapter<Product>({
  url: '/products',
  client,
  extraParams: {
    category: 'electronics',
    inStock: true,
  },
})

无限滚动

配合无限滚动使用:

tsx
const queryFn = createRequestAdapter<User>({
  url: '/users',
  client,
})

<RequestList
  queryKey={['users']}
  queryFn={queryFn}
  mode="infinite" // 无限滚动模式
  renderItem={(user) => <div>{user.name}</div>}
/>

CRUD 操作

结合 useListMutations 实现增删改查:

tsx
import { RequestList, createRequestAdapter, useListMutations } from '@xcloud/ui-mobile'

function UserManagement() {
  const queryFn = createRequestAdapter<User>({
    url: '/users',
    client,
  })

  const mutations = useListMutations<User>({
    queryKey: ['users'],
    createFn: async (data) => await client.post('/users', data),
    updateFn: async (id, data) => await client.put(`/users/${id}`, data),
    deleteFn: async (id) => await client.delete(`/users/${id}`),
  })

  return (
    <RequestList
      queryKey={['users']}
      queryFn={queryFn}
      mode="pagination"
      renderItem={(user) => (
        <div>
          <span>{user.name}</span>
          <button onClick={() => mutations.update(user.id, { name: 'New Name' })}>
            编辑
          </button>
          <button onClick={() => mutations.delete(user.id)}>
            删除
          </button>
        </div>
      )}
      headerActions={
        <button onClick={() => mutations.create({ name: 'New User', email: 'new@example.com' })}>
          添加用户
        </button>
      }
    />
  )
}

完整示例

tsx
import { useState } from 'react'
import { RequestList, createSearchableRequestAdapter } from '@xcloud/ui-mobile'
import { createClient } from '@xcloud/request'

const client = createClient({
  baseURL: 'https://api.example.com',
})

interface Article {
  id: string
  title: string
  content: string
  author: string
  category: string
  createdAt: string
}

export function ArticleList() {
  const [search, setSearch] = useState('')
  const [category, setCategory] = useState('')

  const queryFn = createSearchableRequestAdapter<Article>({
    url: '/articles',
    client,
    searchFields: ['title', 'content', 'author'],
    filterFields: ['category'],
    extraParams: {
      published: true,
    },
  })

  return (
    <div className="article-list">
      {/* 搜索和过滤 */}
      <div className="filters">
        <input
          type="text"
          value={search}
          onChange={(e) => setSearch(e.target.value)}
          placeholder="搜索文章..."
        />

        <select value={category} onChange={(e) => setCategory(e.target.value)}>
          <option value="">全部分类</option>
          <option value="tech">技术</option>
          <option value="business">商业</option>
          <option value="lifestyle">生活</option>
        </select>
      </div>

      {/* 文章列表 */}
      <RequestList
        queryKey={['articles', { search, category }]}
        queryFn={queryFn}
        mode="infinite"
        renderItem={(article) => (
          <article className="article-card">
            <h2>{article.title}</h2>
            <p className="author">作者: {article.author}</p>
            <p className="content">{article.content.substring(0, 200)}...</p>
            <time>{new Date(article.createdAt).toLocaleDateString()}</time>
          </article>
        )}
        emptyMessage="暂无文章"
        errorMessage={(error) => `加载失败: ${error.message}`}
      />
    </div>
  )
}

适配器 API

createRequestAdapter

创建标准的请求适配器。

typescript
function createRequestAdapter<TData>(
  options: RequestAdapterOptions<TData>
): (context: QueryFunctionContext) => Promise<PagedResponse<TData>>

选项:

属性类型描述
urlstringAPI 端点 URL
clientRequestClient请求客户端实例(可选)
method'GET' | 'POST'HTTP 方法,默认 GET
transformParams(context) => Record<string, any>参数转换函数
transformResponse(response) => PagedResponse<TData>响应转换函数
extraParamsRecord<string, any>额外的请求参数

createSearchableRequestAdapter

创建支持搜索和过滤的适配器。

typescript
function createSearchableRequestAdapter<TData>(
  options: RequestAdapterOptions<TData> & {
    searchFields?: string[]
    filterFields?: string[]
  }
): (context: QueryFunctionContext) => Promise<PagedResponse<TData>>

额外选项:

属性类型描述
searchFieldsstring[]搜索字段列表
filterFieldsstring[]过滤字段列表

响应数据格式

@xcloud/request 的标准分页响应格式:

typescript
interface RequestPaginationResponse<T> {
  list: T[]           // 数据列表
  total: number       // 总数
  page: number        // 当前页
  pageSize: number    // 每页数量
  totalPages: number  // 总页数
  hasMore: boolean    // 是否有下一页
}

适配器会自动将其转换为 RequestList 需要的 PagedResponse 格式。

最佳实践

  1. 共享客户端实例: 在应用中创建一个全局的 RequestClient 实例,避免重复创建
  2. 类型安全: 始终为数据定义 TypeScript 类型
  3. 查询键管理: 使用一致的查询键结构,便于缓存管理
  4. 错误处理: 利用 @xcloud/request 的内置错误处理和重试机制
  5. 性能优化: 使用 staleTimecacheTime 优化缓存策略

API 文档

InfiniteList Props

属性类型默认值描述
queryKeyQueryKey-React Query 查询键
queryFn(context) => Promise<PagedResponse>-查询函数
renderItem(item, index) => ReactNode-渲染列表项
emptyTextstring'暂无数据'空状态文本
errorTitlestring'加载失败'错误标题
showLoadMoreButtonbooleanfalse显示加载更多按钮
skeletonCountnumber3骨架屏数量
renderSkeleton() => ReactNode-自定义骨架屏
renderEmpty() => ReactNode-自定义空状态
renderError(error, retry) => ReactNode-自定义错误状态

VirtualInfiniteList Props

继承 InfiniteList 所有属性,额外支持:

属性类型默认值描述
heightnumber | string'100vh'虚拟滚动容器高度
estimateSizenumber50预估每项高度(px)
overscannumber5预渲染项目数量

SelectableList Props

继承 InfiniteList 所有属性,额外支持:

属性类型默认值描述
selectionOptionsUseListSelectionOptions-选择配置
batchActionsBatchAction[][]批量操作配置
showSelectAllbooleantrue是否显示全选按钮
selectAllTextstring'全选'全选按钮文本
stickyBatchBarbooleantrue批量操作栏是否吸顶
renderBatchBar(params) => ReactNode-自定义批量操作栏
onSelectionChange(ids, items) => void-选中状态改变回调

GroupedList Props

继承 InfiniteList 所有属性,额外支持:

属性类型默认值描述
groupBy(item) => string-分组函数
renderGroupHeader(key, items) => ReactNode-分组标题渲染函数
stickyHeaderbooleantrue分组标题是否吸顶
collapsiblebooleanfalse分组是否可折叠
defaultExpandedGroupsstring[]-默认展开的分组
expandedGroupsstring[]-受控模式:展开的分组
onGroupToggle(key, expanded) => void-分组展开/折叠回调
sortGroups(a, b) => number-分组排序函数

SortableList Props

属性类型默认值描述
itemsTData[]-列表数据
getItemId(item) => string | number(item) => item.id获取项目 ID
onReorder(items) => void-排序改变回调
renderItem(item, index) => ReactNode-列表项渲染函数
showDragHandlebooleantrue是否显示拖拽手柄
dragHandleIconReactNode'☰'拖拽手柄图标
disabledbooleanfalse是否禁用拖拽

PullToRefresh Props

属性类型默认值描述
childrenReactNode-子组件
onRefresh() => Promise<void>-刷新回调
thresholdnumber80触发刷新的下拉距离(px)
maxPullDistancenumber150最大下拉距离(px)
indicatorHeightnumber60刷新指示器高度(px)
disabledbooleanfalse是否禁用下拉刷新
renderIndicator(params) => ReactNode-自定义刷新指示器

SwipeableListItem Props

属性类型默认值描述
childrenReactNode-列表项内容
leftActionsSwipeAction[][]左侧操作配置
rightActionsSwipeAction[][]右侧操作配置
thresholdnumber50触发操作的滑动距离(px)
disabledbooleanfalse是否禁用滑动

类型定义

PagedResponse

tsx
interface PagedResponse<TData> {
  data: TData[]
  total?: number
  page?: number
  pageSize?: number
  hasMore?: boolean
  nextPage?: number
}

BatchAction

tsx
interface BatchAction<TData> {
  key: string
  label: string
  icon?: ReactNode
  danger?: boolean
  disabled?: boolean
  onAction: (items: TData[]) => Promise<void> | void
}

SwipeAction

tsx
interface SwipeAction {
  key: string
  label: string
  icon?: ReactNode
  color?: string
  disabled?: boolean
  onAction: () => void | Promise<void>
}

组合使用示例

虚拟滚动 + 下拉刷新

tsx
import { PullToRefresh, VirtualInfiniteList } from '@xcloud/ui-mobile'

<PullToRefresh
  onRefresh={async () => {
    await queryClient.invalidateQueries({ queryKey: ['products'] })
  }}
>
  <VirtualInfiniteList
    queryKey={['products']}
    queryFn={fetchProducts}
    renderItem={(product) => <ProductCard product={product} />}
    height={600}
    estimateSize={100}
  />
</PullToRefresh>

分组 + 可选择

tsx
import { GroupedList, useListSelection } from '@xcloud/ui-mobile'

const selection = useListSelection({ multiple: true })

<GroupedList
  queryKey={['contacts']}
  queryFn={fetchContacts}
  groupBy={(contact) => contact.name[0].toUpperCase()}
  renderGroupHeader={(key) => <div>{key}</div>}
  renderItem={(contact) => (
    <div onClick={() => selection.toggleSelect(contact)}>
      <input type="checkbox" checked={selection.isSelected(contact)} />
      <span>{contact.name}</span>
    </div>
  )}
  stickyHeader
/>

主要特性

🚀 高性能

  • 虚拟滚动支持数千条数据流畅渲染
  • 基于 Intersection Observer 的自动触底加载
  • 优化的 DOM 操作和渲染性能

💾 智能缓存

  • 基于 TanStack React Query
  • 自动缓存和失效管理
  • 支持乐观更新

🎯 丰富功能

  • 无限滚动、分页、虚拟滚动
  • 单选/多选、批量操作
  • 分组显示、拖拽排序
  • 下拉刷新、滑动操作

🎨 灵活定制

  • 自定义加载、空状态、错误状态
  • 支持骨架屏
  • 完整的 TypeScript 类型支持
  • 可自定义样式

注意事项

  1. 必须提供 QueryClientProvider: 确保在应用根组件中设置了 QueryClientProvider
  2. 查询键唯一性: 每个列表的 queryKey 应该是唯一的
  3. 数据格式规范: queryFn 必须返回符合 PagedResponse 接口的数据
  4. 移动端支持: PullToRefresh 和 SwipeableListItem 需要在移动端或模拟器中测试

相关链接

基于 MIT 许可发布