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>
相关推荐
愚者Pro2 小时前
Flutter Widget组件学习(专为 Uniapp 转 Flutter 定制)
vue.js·学习·flutter·uni-app
前端毕业班4 小时前
uniapp web 灵活控制 style scoped
前端·javascript·vue.js
卤蛋fg65 小时前
vxe-table 数据分组 + 单元格图表:柱状图与饼图渲染实战
vue.js
用户841794814566 小时前
vxe-table 数据分组:三种展示方式详解
vue.js
LJA648448 小时前
用 MCP + 一句话生成了完整的 CRUD 页面
vue.js
梵得儿SHI9 小时前
Vue 项目实战与性能优化:工程化与协作全指南(规范 + 配置 + 协作 + 文档)
前端·vue.js·代码规范·eslint·团队协作·前端工程化·前端架构
xjf77119 小时前
AI 可读性与识别能力对比-TypeDom vs Vue
前端·vue.js·人工智能
蜡台10 小时前
Vue2 + TS,分路径参数、查询参数、装饰器组件 / Vue.extend 两种写法,同时补充类型约束、监听路由、动态路由取值。
前端·javascript·vue.js·router
陪小甜甜赏月12 小时前
微信小程序分享onShareAppMessage
前端·微信小程序·小程序