useList 通用列表管理hook

useList 通用列表管理hook

概述

useList.hook.ts 是一个用于统一管理列表数据的加载、分页、筛选和搜索功能的通用hook。

核心功能

  1. 响应式状态管理 :使用 reactive 管理筛选条件和分页配置
  2. 数据请求封装:统一处理 loading 状态、错误捕获和数据赋值
  3. 追加/替换模式 :通过 isPush 参数支持普通分页和滚动追加加载两种场景
  4. 筛选重置:保存初始筛选条件,支持一键重置

详细代码

typescript 复制代码
import { reactive, ref, toRaw } from 'vue'

export interface IPage {
  pageNum: number
  pageSize: number
  total?: number
}

export interface GetListFnArgsType<T> {
  pagination: IPage
  filter: T
}
export default function useList<T, U extends Record<string, any>>(
  filterOption: U,
  getListFn: (args: GetListFnArgsType<U>) => Promise<InResult<{ records: Array<T>, total: number }>>, // InResult 类型定义请阅读uni.request 二次封装这篇文章
  pageOption?: IPage,
) {
  const list = ref<Array<T>>([])
  const loading = ref(false)
  const filter = reactive<U>({ ...filterOption })
  const pagination = reactive<IPage>({
    ...pageOption,
    pageSize: pageOption?.pageSize || 10,
    pageNum: pageOption?.pageNum || 1,
  })

  // 请求列表数据
  const loadData = async (curPage = pagination.pageNum, isPush = true) => {
    pagination.pageNum = curPage
    loading.value = true

    const req = { pagination, filter: toRaw(filter) as U }
    try {
      const res = await getListFn(req as GetListFnArgsType<U>)
      loading.value = false
      if (!isPush) {
        list.value = res.data.records as Array<any>
      }
      else {
        list.value = [...list.value, ...res.data.records as Array<any>]
      }
      pagination.total = res.data.total
    }
    catch (error) {
      loading.value = false
      console.log(error)
    }
  }

  // 重置筛选
  const filterReset = () => {
    Object.assign(filter, filterOption)
    loadData(1, false)
  }

  // 搜索
  const filterSearch = () => {
    console.log(121212)
    return loadData(1, false)
  }

  // 分页请求
  const loadDataPage = (page: number, isPush = true) => {
    return loadData(page, isPush)
  }

  // 分页大小改变
  const loadDataPageSize = (pageSize: number) => {
    pagination.pageSize = pageSize
    loadData(1, false)
  }

  return {
    list,
    loading,
    filter,
    pagination,
    loadData,
    loadDataPage,
    loadDataPageSize,
    filterReset,
    filterSearch,
  }
}

类型定义

IPage

默认分页参数接口:

typescript 复制代码
interface IPage {
  pageNum: number // 当前页码
  pageSize: number // 每页条数
  total?: number // 总条数(响应数据填充)
}

GetListFnArgsType

列表请求参数类型:

typescript 复制代码
interface GetListFnArgsType<T> {
  pagination: IPage // 分页信息
  filter: T // 筛选条件
}

返回值

属性 类型 说明
list Ref<Array<T>> 列表数据
loading Ref<boolean> 加载状态
filter Reactive<U> 筛选条件(响应式)
pagination Reactive<IPage> 分页信息(响应式)
loadData (curPage?: number, isPush?: boolean) => Promise<void> 加载数据
loadDataPage (page: number, isPush?: boolean) => Promise<void> 分页加载
loadDataPageSize (pageSize: number) => void 改变每页条数
filterReset () => void 重置筛选条件
filterSearch () => Promise<void> 搜索(重置页码为1)

方法详解

loadData(curPage, isPush)

加载列表数据:

  • curPage : 当前页码,默认为 pagination.pageNum
  • isPush : 是否追加数据,true 表示追加(用于滚动加载),false 表示替换(默认)

loadDataPage(page, isPush)

分页加载,调用 loadData 的封装。

loadDataPageSize(pageSize)

改变每页条数并重新加载第一页数据。

filterReset()

重置筛选条件为初始值,并重新加载第一页数据。

filterSearch()

执行搜索,重置页码为1并重新加载数据。

使用示例

typescript 复制代码
import type { IEvent } from '@/api/task/type'
import { getEventList } from '@/api/task'
import useList, { type GetListFnArgsType } from '@/hooks/uesList.hook'

const filterOption = {
  keyword: '',
  status: '',
  startTime: '',
  endTime: '',
}

async function getListData(params: GetListFnArgsType<typeof filterOption>) {
  const { pagination, filter } = params
  const req = {
    params: {
      pageNo: pagination.pageNum,
      pageSize: pagination.pageSize,
    },
    data: {
      startTime: filter.startTime,
      endTime: filter.endTime,
    },
  }

  const res = await getEventList(req)
  return res

  /**
   * 这里假设接口返回的数据格式不是 records 和 total
   * 你可以在 getListData 中根据实际情况调整 records 和 total 的赋值,如:
   * const res = await getEventList(req);
   * return {data: { records: res.data.list, total: res.data.totalSize }}
   */
}

const { list, filter, pagination, filterSearch, loadDataPage } = useList<IEvent, typeof filterOption>(filterOption, getListData, { pageNum: 1, pageSize: 20 })
html 复制代码
<template>
  <cus-list v-model:keyword="filter.keyword" :refresh-func="filterSearch" :load-func="loadDataPage" :pagination="pagination" @search="filterSearch" @clear="filterSearch">
    <template #serach-left>
      <van-dropdown-menu style="--dropdown-menu-title-active-text-color: var(--uni-color-primary);--dropdown-menu-background-color: transparent;--dropdown-menu-title-text-color: var(--uni-text-color); --dropdown-menu-option-active-color: var(--uni-color-primary)" custom-class="min-w-[100rpx]">
        <van-dropdown-item v-model:value="filter.siteId" :options="siteIdOptions" @change="onSiteChange" />
      </van-dropdown-menu>
    </template>

    <template #search-filter="{ data }">
      <view class="box-border h-full w-full p-2">
        <view class="flex items-center gap-2">
          <view>开始时间:</view>
          <cus-date-picker v-model="filter.startTime" type="date" />
        </view>
        <view class="mt-2 flex items-center gap-2">
          <view>结束时间:</view>
          <cus-date-picker v-model="filter.endTime" type="date" />
        </view>
        <!-- <view>其它过滤条件开发中。。。</view> -->
        <view class="mt-5 flex gap-2">
          <van-button size="small" block class="flex-1" @click="onFilterReset(data)">
            重置
          </van-button>
          <van-button type="primary" block size="small" class="flex-1" @click="onFilter(data)">
            确定
          </van-button>
        </view>
      </view>
    </template>

    <template #default>
      <task-item v-for="item in list" :key="item.id" :record="item" />
    </template>
  </cus-list>
</template>
相关推荐
DarkLONGLOVE11 小时前
快速上手 Pinia!Vue3 极简状态管理使用教程
javascript·vue.js
宸翰12 小时前
解决 uni-app App 端 vue-i18n 占位符丢失:封装跨端可用的 tf 格式化方法
前端·vue.js·uni-app
用户21366100357217 小时前
VueRouter进阶-动态路由与嵌套路由
前端·vue.js
暴走的小呆1 天前
Vue 2 中 Object 的变化侦测:从 getter/setter 到 Dep、Watcher、Observer
vue.js
英勇无比的消炎药1 天前
TinyVue v-auto-tip: 文本超长自动提示的优雅方案
vue.js
时光足迹1 天前
腾讯云 TRTC UniApp SDK 从入门到上线
前端·vue.js·uni-app
时光足迹1 天前
uni-app 里把加密视频嵌入页面播放?我分析了 4 种方案,只有 1 种接近完美
前端·vue.js·uni-app
时光足迹1 天前
JPush UniApp UTS 插件完全参考手册:API、事件与厂商通道一网打尽
vue.js·ios·uni-app
时光足迹1 天前
极光推送全攻略(下):uni-app 代码实现与 iOS 排查实战
vue.js·ios·uni-app
疯狂的魔鬼2 天前
一个"懂分寸"的文本省略组件是怎样炼成的
前端·vue.js·设计