Skip to content

useExtendList

useExtendList hook 是 useList 的扩展版本,在基础列表功能基础上增加了选中管理、批量导出、批量导入等高级功能。

功能特点

  • 📋 完整列表功能 - 继承 useList 的所有功能
  • 选中管理 - 提供行选中、全选、反选功能
  • 📤 批量导出 - 支持导出全部数据或选中数据
  • 📥 批量导入 - 支持 CSV 文件导入功能
  • 📄 智能分页 - 动态计算分页大小选项
  • 🔄 状态管理 - 提供完整的加载、选中状态管理
  • 进度反馈 - 导入导出进度实时反馈

接口关系

该hook内部使用 useListuseExportCsvuseImportCsv hooks。

typescript
// 参数接口
interface UseExtendListProps {
  path: string // API 路径
  key?: string | number // 数据项唯一标识字段
  totalField?: string // 总数字段名
  filters?: Record<string, any> // 筛选条件
  sorters?: Record<string, 'asc' | 'desc'> // 排序条件
  expanded?: boolean // 是否展开
  pagination?: boolean | IListPagination // 分页配置
  exportFilename?: string // 导出文件名
  exportMaxPage?: number // 导出最大页数
  total?: (data?: IDataProviderResponse) => number // 总数计算函数
  onExportSuccess?: (data?: IDataProviderResponse) => void // 导出成功回调
  onExportProgress?: (data?: IDataProviderPagination) => void // 导出进度回调
  onExportError?: (error?: IDataProviderError) => void // 导出错误回调
  onImportSuccess?: (progress?: IImportProgress) => void // 导入成功回调
  onImportProgress?: (progress?: IImportProgress) => void // 导入进度回调
  onImportError?: (error?: IDataProviderError) => void // 导入错误回调
}

// 分页接口
interface IListPagination {
  page: number // 当前页码
  pageSize: number // 每页数量
}

使用方法

js
import { useExtendList } from '@duxweb/dvha-core'

const {
  list,
  isLoading,
  checkeds,
  onExport,
  onImport,
  toggleChecked
} = useExtendList({
  path: 'users'
})

常用参数

js
const {
  list,
  isLoading,
  checkeds,
  isAllChecked,
  onExport,
  onImport,
  onRefresh
} = useExtendList({
  // 基础配置
  path: 'users', // API 路径
  key: 'id', // 数据项唯一标识,默认为 'id'
  totalField: 'total', // 总数字段名,默认为 'total'

  // 查询配置
  filters: { // 筛选条件
    status: 'active',
    role: 'user'
  },
  sorters: { // 排序条件
    created_at: 'desc'
  },

  // 分页配置
  pagination: { // 分页设置
    page: 1,
    pageSize: 20
  },

  // 导出配置
  exportFilename: 'users.csv', // 导出文件名
  exportMaxPage: 10, // 导出最大页数

  // 回调函数
  onExportSuccess: (data) => { // 导出成功
    console.log('导出成功:', data)
  },
  onExportProgress: (progress) => { // 导出进度
    console.log('导出进度:', progress)
  },
  onImportSuccess: (progress) => { // 导入成功
    console.log('导入成功:', progress)
    onRefresh() // 刷新列表
  }
})

参数说明

参数类型必需说明
pathstringAPI 资源路径
keystring | number数据项唯一标识,默认'id'
totalFieldstring总数字段名,默认'total'
filtersRecord<string, any>筛选条件
sortersRecord<string, 'asc' | 'desc'>排序条件
paginationboolean | IListPagination分页配置
exportFilenamestring导出文件名
exportMaxPagenumber导出最大页数
onExportSuccessFunction导出成功回调
onExportProgressFunction导出进度回调
onExportErrorFunction导出错误回调
onImportSuccessFunction导入成功回调
onImportProgressFunction导入进度回调
onImportErrorFunction导入错误回调

返回值

字段类型说明
listComputedRef<any[]>列表数据
metaComputedRef<object>元数据信息
totalComputedRef<number>总数
isLoadingRef<boolean>是否加载中
checkedsRef<Array>已选中的项目ID列表
isAllCheckedComputedRef<boolean>是否全选
isIndeterminateComputedRef<boolean>是否部分选中
paginationRef<object>分页配置
pageCountComputedRef<number>总页数
toggleCheckedFunction切换项目选中状态
toggleSelectAllFunction切换全选状态
isCheckedFunction检查项目是否选中
onRefreshFunction刷新列表
onExportFunction导出全部数据
onExportRowsFunction导出选中数据
onImportFunction打开导入文件选择器
isExportingRef<boolean>是否正在导出全部数据
isExportingRowsRef<boolean>是否正在导出选中数据
isImportingRef<boolean>是否正在导入

基础使用示例

js
import { useExtendList } from '@duxweb/dvha-core'

const {
  list,
  isLoading,
  checkeds,
  isAllChecked,
  toggleChecked,
  toggleSelectAll,
  onRefresh
} = useExtendList({
  path: 'users',
  pagination: {
    page: 1,
    pageSize: 20
  }
})

// 处理行选中
function handleRowCheck(id) {
  toggleChecked(id)
}

// 处理全选
function handleSelectAll() {
  toggleSelectAll()
}

// 刷新列表
function handleRefresh() {
  onRefresh()
}

导出功能示例

js
import { useExtendList } from '@duxweb/dvha-core'
import { ElMessage } from 'element-plus'

const {
  list,
  checkeds,
  onExport,
  onExportRows,
  isExporting,
  isExportingRows
} = useExtendList({
  path: 'users',
  exportFilename: 'users.csv',
  exportMaxPage: 20,
  onExportSuccess: (data) => {
    ElMessage.success('导出成功')
  },
  onExportProgress: (progress) => {
    console.log(`导出进度: ${progress.page}/${progress.totalPage}`)
  },
  onExportError: (error) => {
    ElMessage.error(`导出失败: ${error.message}`)
  }
})

// 导出全部数据
function handleExportAll() {
  onExport()
}

// 导出选中数据
function handleExportSelected() {
  if (checkeds.value.length === 0) {
    ElMessage.warning('请先选择要导出的数据')
    return
  }
  onExportRows()
}

导入功能示例

js
import { useExtendList } from '@duxweb/dvha-core'
import { ElMessage } from 'element-plus'

const {
  onImport,
  onRefresh,
  isImporting
} = useExtendList({
  path: 'users',
  onImportSuccess: (progress) => {
    ElMessage.success(`导入成功,共处理 ${progress.processed} 条记录`)
    onRefresh() // 刷新列表
  },
  onImportProgress: (progress) => {
    console.log(`导入进度: ${progress.processed}/${progress.total}`)
  },
  onImportError: (error) => {
    ElMessage.error(`导入失败: ${error.message}`)
  }
})

// 打开文件选择器导入
function handleImport() {
  onImport()
}

搜索筛选示例

js
import { useExtendList } from '@duxweb/dvha-core'
import { ref, watch } from 'vue'

const searchForm = ref({
  name: '',
  status: '',
  role: ''
})

const {
  list,
  isLoading,
  onUpdateFilters,
  onRefresh
} = useExtendList({
  path: 'users',
  filters: searchForm.value
})

// 监听搜索条件变化
watch(searchForm, (newFilters) => {
  onUpdateFilters(newFilters)
}, { deep: true })

// 重置搜索
function handleReset() {
  searchForm.value = {
    name: '',
    status: '',
    role: ''
  }
}

分页管理示例

js
import { useExtendList } from '@duxweb/dvha-core'

const {
  list,
  pagination,
  pageCount,
  total,
  currentPageSizes,
  onUpdatePage,
  onUpdatePageSize
} = useExtendList({
  path: 'users',
  pagination: {
    page: 1,
    pageSize: 20
  }
})

// 切换页码
function handlePageChange(page) {
  onUpdatePage(page)
}

// 切换每页数量
function handlePageSizeChange(pageSize) {
  onUpdatePageSize(pageSize)
}

完整示例

vue
<script setup lang="ts">
import { useExtendList } from '@duxweb/dvha-core'
import { ref } from 'vue'

const searchForm = ref({
  name: '',
  status: 'all'
})

const {
  list,
  isLoading,
  checkeds,
  isAllChecked,
  isIndeterminate,
  pagination,
  total,
  pageCount,
  toggleChecked,
  toggleSelectAll,
  isChecked,
  onUpdateFilters,
  onUpdatePage,
  onUpdatePageSize,
  onRefresh,
  onExport,
  onExportRows,
  onImport,
  isExporting,
  isExportingRows,
  isImporting
} = useExtendList({
  path: 'users',
  pagination: {
    page: 1,
    pageSize: 20
  },
  exportFilename: 'users.csv',
  onExportSuccess: () => {
    console.log('导出成功')
  },
  onImportSuccess: () => {
    console.log('导入成功')
    onRefresh()
  }
})

function handleSearch() {
  onUpdateFilters(searchForm.value)
}

function handleExportSelected() {
  if (checkeds.value.length === 0) {
    alert('请选择要导出的数据')
    return
  }
  onExportRows()
}
</script>

<template>
  <div>
    <!-- 搜索表单 -->
    <div class="search-form">
      <input v-model="searchForm.name" placeholder="搜索姓名">
      <select v-model="searchForm.status">
        <option value="all">
          全部状态
        </option>
        <option value="active">
          激活
        </option>
        <option value="inactive">
          未激活
        </option>
      </select>
      <button @click="handleSearch">
        搜索
      </button>
      <button @click="onRefresh">
        刷新
      </button>
    </div>

    <!-- 操作栏 -->
    <div class="toolbar">
      <button
        :disabled="isExporting"
        @click="onExport"
      >
        {{ isExporting ? '导出中...' : '导出全部' }}
      </button>
      <button
        :disabled="isExportingRows || checkeds.length === 0"
        @click="handleExportSelected"
      >
        {{ isExportingRows ? '导出中...' : '导出选中' }}
      </button>
      <button
        :disabled="isImporting"
        @click="onImport"
      >
        {{ isImporting ? '导入中...' : '导入数据' }}
      </button>
      <span>已选择 {{ checkeds.length }} 项</span>
    </div>

    <!-- 数据表格 -->
    <table v-loading="isLoading">
      <thead>
        <tr>
          <th>
            <input
              type="checkbox"
              :checked="isAllChecked"
              :indeterminate="isIndeterminate"
              @change="toggleSelectAll"
            >
          </th>
          <th>ID</th>
          <th>姓名</th>
          <th>邮箱</th>
          <th>状态</th>
        </tr>
      </thead>
      <tbody>
        <tr v-for="item in list" :key="item.id">
          <td>
            <input
              type="checkbox"
              :checked="isChecked(item.id)"
              @change="() => toggleChecked(item.id)"
            >
          </td>
          <td>{{ item.id }}</td>
          <td>{{ item.name }}</td>
          <td>{{ item.email }}</td>
          <td>{{ item.status }}</td>
        </tr>
      </tbody>
    </table>

    <!-- 分页 -->
    <div class="pagination">
      <span>共 {{ total }} 条记录</span>
      <button
        v-for="page in pageCount"
        :key="page"
        :class="{ active: pagination.page === page }"
        @click="() => onUpdatePage(page)"
      >
        {{ page }}
      </button>
    </div>
  </div>
</template>