TinyVue Grid 表格 fetchData 完全指南:从入门到精通
前言
TinyVue 的 Grid 组件是企业级后台管理系统中最常用的数据展示组件之一。在处理大量数据时,我们通常需要从后端分页获取数据,而不是一次性加载所有数据到前端。TinyVue Grid 提供了两种优雅的方式来处理远程数据加载:fetchData 和 proxyConfig。
本文将详细介绍 fetchData 的使用方式,涵盖基础配置、分页、排序、筛选、手动刷新等核心场景,并附带完整的代码示例。
基础概念
TinyVue Grid 组件基于 vxe-table 封装,在保留原生 vxe-table 全部能力的同时,提供了更简洁的 fetchData API。fetchData 本质上是对 vxe-table proxy-config.ajax.query 的高层封装,让你能以更符合 Vue 开发习惯的方式来配置远程数据获取。
一、最简示例:自动加载数据
最基本的用法是配置 fetchData 的 api 属性,指向你的异步数据获取方法。Grid 会在挂载时自动调用该方法加载数据。
vue
<template>
<tiny-grid :fetch-data="fetchData">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column field="name" title="名称"></tiny-grid-column>
<tiny-grid-column field="area" title="所属区域"></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介" show-overflow></tiny-grid-column>
</tiny-grid>
</template>
<script>
import { TinyGrid, TinyGridColumn } from '@opentiny/vue'
export default {
components: { TinyGrid, TinyGridColumn },
data() {
return {
fetchData: {
api: this.getData
}
}
},
methods: {
getData() {
return new Promise((resolve) => {
// 这里替换成你的真实接口调用
fetch('/api/company/list')
.then(res => res.json())
.then(data => {
resolve({
result: data.list,
page: { total: data.total }
})
})
})
}
}
}
</script>
返回数据结构说明:
getData 方法必须返回一个 Promise,resolve 的对象需遵循以下格式:
typescript
{
result: Array<RowVO>, // 当前页数据列表(必需)
page: {
total: number // 总记录数(启用分页时必需)
}
}
二、禁用自动加载:auto-load
如果不想在组件挂载时自动加载数据(比如需要条件筛选后才加载),可以设置 auto-load 为 false:
vue
<tiny-grid :auto-load="false" :fetch-data="fetchData">
<!-- columns... -->
</tiny-grid>
设置为 false 后,需要手动调用 Grid 的 handleFetch() 方法来触发数据加载:
javascript
this.$refs.grid.handleFetch()
三、分页加载
分页是远程数据加载最常见的场景。TinyVue Grid 通过 pagerConfig 配置分页器,fetchData 的回调会自动接收到页码和每页条数。
vue
<template>
<tiny-grid :fetch-data="fetchData" :pager="pagerConfig">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column field="name" title="名称"></tiny-grid-column>
<tiny-grid-column field="area" title="所属区域"></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介" show-overflow></tiny-grid-column>
</tiny-grid>
</template>
<script>
import { TinyGrid, TinyGridColumn, TinyPager } from '@opentiny/vue'
export default {
components: { TinyGrid, TinyGridColumn },
data() {
return {
pagerConfig: {
component: TinyPager,
attrs: {
currentPage: 1,
pageSize: 5,
pageSizes: [5, 10, 20],
total: 0,
layout: 'total, sizes, prev, pager, next, jumper'
}
},
fetchData: {
api: this.getData
}
}
},
methods: {
getData({ page }) {
// page 对象包含 currentPage 和 pageSize
let curPage = page.currentPage
let pageSize = page.pageSize
return new Promise((resolve) => {
fetch(`/api/company/list?page=${curPage}&pageSize=${pageSize}`)
.then(res => res.json())
.then(data => {
resolve({
result: data.list,
page: { total: data.total }
})
})
})
}
}
}
</script>
pagerConfig 配置详解
| 属性 | 类型 | 说明 |
|---|---|---|
component |
Component | 分页器组件,使用 TinyPager |
attrs.currentPage |
Number | 当前页码,默认 1 |
attrs.pageSize |
Number | 每页条数,默认 5 |
attrs.pageSizes |
Number[] | 可选的每页条数,如 [5, 10, 20] |
attrs.total |
Number | 总记录数,由服务端动态赋值 |
attrs.layout |
String | 分页器布局,支持 total, sizes, prev, pager, next, jumper |
当用户切换页码或每页条数时,Grid 会自动重新调用 getData 方法,并传入更新后的 page 参数。
四、排序与预排序
4.1 初始排序(prefetchArgs)
使用 prefetchArgs 可以在首次加载数据时指定排序规则:
vue
<tiny-grid :fetch-data="fetchData" :prefetch="prefetchArgs">
<!-- columns... -->
</tiny-grid>
javascript
data() {
return {
prefetchArgs: [
{ property: 'name', sort: 'desc' }
],
fetchData: {
api: this.getData
}
}
},
methods: {
getData({ page, sortBy }) {
// sortBy 包含当前的排序信息
let curPage = page.currentPage
let pageSize = page.pageSize
return new Promise((resolve) => {
fetch(`/api/company/list?page=${curPage}&pageSize=${pageSize}&sortBy=${sortBy}`)
.then(res => res.json())
.then(data => {
resolve({
result: data.list,
page: { total: data.total }
})
})
})
}
}
4.2 动态排序
用户点击列头排序时,Grid 会自动触发数据刷新,getData 的 sortBy 参数会自动更新。
五、数据筛选
筛选是最常见的交互需求之一。TinyVue Grid 支持通过动态修改 fetchData.args 来实现带筛选条件的数据加载。
vue
<template>
<div>
<!-- 筛选按钮 -->
<tiny-button @click="filterData('华南区')"> 筛选华南区 </tiny-button>
<tiny-button @click="filterData('华东区')"> 筛选华东区 </tiny-button>
<tiny-button @click="filterData('')"> 全部数据 </tiny-button>
<tiny-grid ref="grid" :fetch-data="fetchData" :pager="pagerConfig">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column field="name" title="名称"></tiny-grid-column>
<tiny-grid-column field="area" title="所属区域"></tiny-grid-column>
<tiny-grid-column field="address" title="地址"></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介" show-overflow></tiny-grid-column>
</tiny-grid>
</div>
</template>
<script>
import { TinyGrid, TinyGridColumn, TinyPager, TinyButton } from '@opentiny/vue'
export default {
components: { TinyGrid, TinyGridColumn, TinyButton },
data() {
return {
pagerConfig: {
component: TinyPager,
attrs: {
currentPage: 1,
pageSize: 5,
pageSizes: [5, 10],
total: 0,
layout: 'total, sizes, prev, pager, next, jumper'
}
},
fetchData: {
api: this.getData
}
}
},
methods: {
getData({ page, filterArgs }) {
let curPage = page.currentPage
let pageSize = page.pageSize
return new Promise((resolve) => {
const params = { page: curPage, pageSize }
if (filterArgs) {
params.area = filterArgs
}
fetch(`/api/company/list?${new URLSearchParams(params)}`)
.then(res => res.json())
.then(data => {
resolve({
result: data.list,
page: { total: data.total }
})
})
})
},
filterData(area) {
// 动态设置筛选参数
this.fetchData.args = { filterArgs: area }
// 手动触发数据刷新
this.$refs.grid.handleFetch()
}
}
}
</script>
关键步骤:
- 在筛选事件中,动态赋值
this.fetchData.args对象 - 调用
this.$refs.grid.handleFetch()手动触发数据重载 getData方法通过第二个参数filterArgs获取当前的筛选条件
六、getData 回调参数完整说明
fetchData.api 指向的方法接收一个参数对象,包含以下字段:
| 参数 | 类型 | 说明 |
|---|---|---|
page |
Object | 分页信息 |
page.currentPage |
Number | 当前页码 |
page.pageSize |
Number | 每页条数 |
filterArgs |
any | 通过 fetchData.args 传入的筛选参数 |
sortBy |
Object/Array | 当前排序信息 |
七、手动刷新与重新加载
除了筛选场景外,在以下场景中你也可能需要手动触发数据刷新:
- 新增 / 编辑 / 删除数据后
- 切换 Tab 后
- 收到 WebSocket 实时通知后
- 父组件状态变化后
javascript
// 方式一:重新加载(保持当前分页状态)
this.$refs.grid.handleFetch()
// 方式二:重置到第一页并重新加载
this.fetchData.args = { filterArgs: newValue }
this.$refs.grid.handleFetch()
八、进阶:使用 proxyConfig(vxe-table 原生方式)
TinyVue Grid 同时也完全兼容 vxe-table 的 proxyConfig 配置方式。如果你需要更细粒度的控制(如自定义 loading 状态、响应字段映射),可以使用这种方式:
vue
<template>
<tiny-grid v-bind="gridOptions">
<tiny-grid-column type="index" width="60"></tiny-grid-column>
<tiny-grid-column field="name" title="名称"></tiny-grid-column>
<tiny-grid-column field="area" title="所属区域"></tiny-grid-column>
<tiny-grid-column field="address" title="地址" show-overflow></tiny-grid-column>
</tiny-grid>
</template>
<script>
import { reactive } from 'vue'
export default {
setup() {
const gridOptions = reactive({
border: true,
height: 500,
pagerConfig: {}, // 启用分页
proxyConfig: {
// 关闭 loading(默认 true)
showLoading: false,
// 响应字段映射(当后端返回的字段名不同时使用)
response: {
result: 'data.list', // 后端返回数据的路径
total: 'data.totalCount' // 后端返回总数的路径
},
ajax: {
query: ({ page, sorts, filters }) => {
return fetch(`/api/list?page=${page.currentPage}&pageSize=${page.pageSize}`)
.then(res => res.json())
}
}
},
columns: [
{ type: 'seq', width: 70 },
{ field: 'name', title: 'Name' },
{ field: 'area', title: 'Area' },
{ field: 'address', title: 'Address', showOverflow: true }
]
})
return { gridOptions }
}
}
</script>
proxyConfig 配置项
| 配置项 | 类型 | 说明 |
|---|---|---|
showLoading |
Boolean | 是否显示加载中状态,默认 true |
response.result |
String | 响应结果列表字段的路径,支持点号嵌套(如 'data.list') |
response.total |
String | 响应总数字段的路径,支持点号嵌套(如 'data.totalCount') |
ajax.query |
Function | 查询接口函数,接收 { page, sorts, filters },返回 Promise |
九、fetchData vs proxyConfig 对比
| 特性 | fetchData | proxyConfig |
|---|---|---|
| API 风格 | TinyVue 封装风格 | vxe-table 原生风格 |
| 配置复杂度 | 简洁 | 灵活 |
| 响应字段映射 | 固定 result / page.total |
支持自定义路径映射 |
| Loading 状态 | 自动控制 | 可配置 showLoading |
| 分页参数 | 通过 pagerConfig 单独配置 |
一体化配置 |
| 筛选/排序 | 通过 args 手动传递 |
内置 sorts / filters 参数 |
| 适用场景 | 简单列表页,快速开发 | 复杂场景,需要精细控制的场景 |
十、实战:完整的 CRUD 列表页示例
以下是整合了查询、分页、新增、编辑、删除功能的完整示例:
vue
<template>
<div class="list-container">
<!-- 搜索区域 -->
<div class="search-area">
<tiny-input v-model="searchName" placeholder="请输入名称" style="width: 200px"></tiny-input>
<tiny-select v-model="searchArea" placeholder="请选择区域" style="width: 150px; margin-left: 10px">
<tiny-option label="全部" value=""></tiny-option>
<tiny-option label="华南区" value="华南区"></tiny-option>
<tiny-option label="华东区" value="华东区"></tiny-option>
<tiny-option label="华北区" value="华北区"></tiny-option>
</tiny-select>
<tiny-button type="primary" @click="handleSearch" style="margin-left: 10px">查询</tiny-button>
<tiny-button type="success" @click="handleAdd" style="margin-left: 10px">新增</tiny-button>
</div>
<!-- 表格区域 -->
<tiny-grid ref="grid" :fetch-data="fetchData" :pager="pagerConfig" style="margin-top: 16px">
<tiny-grid-column type="index" width="60" title="序号"></tiny-grid-column>
<tiny-grid-column field="name" title="名称" sortable></tiny-grid-column>
<tiny-grid-column field="area" title="所属区域"></tiny-grid-column>
<tiny-grid-column field="address" title="地址" show-overflow></tiny-grid-column>
<tiny-grid-column field="introduction" title="公司简介" show-overflow></tiny-grid-column>
<tiny-grid-column title="操作" width="180">
<template #default="{ row }">
<tiny-button type="text" size="small" @click="handleEdit(row)">编辑</tiny-button>
<tiny-button type="text" size="small" style="color: red" @click="handleDelete(row)">删除</tiny-button>
</template>
</tiny-grid-column>
</tiny-grid>
<!-- 新增/编辑弹窗 -->
<tiny-dialog :visible="dialogVisible" :title="dialogTitle" @close="dialogVisible = false">
<tiny-form :model="formData" label-width="80px">
<tiny-form-item label="名称">
<tiny-input v-model="formData.name"></tiny-input>
</tiny-form-item>
<tiny-form-item label="区域">
<tiny-select v-model="formData.area">
<tiny-option label="华南区" value="华南区"></tiny-option>
<tiny-option label="华东区" value="华东区"></tiny-option>
<tiny-option label="华北区" value="华北区"></tiny-option>
</tiny-select>
</tiny-form-item>
<tiny-form-item label="地址">
<tiny-input v-model="formData.address"></tiny-input>
</tiny-form-item>
<tiny-form-item label="简介">
<tiny-input v-model="formData.introduction" type="textarea"></tiny-input>
</tiny-form-item>
</tiny-form>
<template #footer>
<tiny-button @click="dialogVisible = false">取消</tiny-button>
<tiny-button type="primary" @click="handleSubmit">确定</tiny-button>
</template>
</tiny-dialog>
</div>
</template>
<script>
import {
TinyGrid, TinyGridColumn, TinyPager, TinyButton,
TinyInput, TinySelect, TinyOption, TinyDialog, TinyForm, TinyFormItem
} from '@opentiny/vue'
export default {
components: {
TinyGrid, TinyGridColumn, TinyButton,
TinyInput, TinySelect, TinyOption, TinyDialog, TinyForm, TinyFormItem
},
data() {
return {
searchName: '',
searchArea: '',
dialogVisible: false,
dialogTitle: '新增',
isEdit: false,
editId: null,
formData: {
name: '',
area: '',
address: '',
introduction: ''
},
pagerConfig: {
component: TinyPager,
attrs: {
currentPage: 1,
pageSize: 10,
pageSizes: [5, 10, 20, 50],
total: 0,
layout: 'total, sizes, prev, pager, next, jumper'
}
},
fetchData: {
api: this.getData
}
}
},
methods: {
// 获取列表数据
getData({ page }) {
const params = {
page: page.currentPage,
pageSize: page.pageSize,
name: this.searchName,
area: this.searchArea
}
return fetch(`/api/company/list?${new URLSearchParams(params)}`)
.then(res => res.json())
.then(data => ({
result: data.list,
page: { total: data.total }
}))
},
// 查询按钮
handleSearch() {
this.$refs.grid.handleFetch()
},
// 新增
handleAdd() {
this.dialogTitle = '新增'
this.isEdit = false
this.formData = { name: '', area: '', address: '', introduction: '' }
this.dialogVisible = true
},
// 编辑
handleEdit(row) {
this.dialogTitle = '编辑'
this.isEdit = true
this.editId = row.id
this.formData = { ...row }
this.dialogVisible = true
},
// 删除
handleDelete(row) {
this.$confirm('确定要删除该条记录吗?').then(() => {
fetch(`/api/company/delete/${row.id}`, { method: 'DELETE' })
.then(() => {
this.$message.success('删除成功')
this.$refs.grid.handleFetch() // 刷新表格
})
})
},
// 提交表单
handleSubmit() {
const url = this.isEdit ? `/api/company/update/${this.editId}` : '/api/company/add'
const method = this.isEdit ? 'PUT' : 'POST'
fetch(url, {
method,
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(this.formData)
})
.then(res => res.json())
.then(() => {
this.$message.success(this.isEdit ? '编辑成功' : '新增成功')
this.dialogVisible = false
this.$refs.grid.handleFetch() // 刷新表格
})
}
}
}
</script>
十一、常见问题与最佳实践
1. 数据不刷新?
确保:
getData方法返回的是 Promise 对象- 返回格式正确:
{ result: Array, page: { total: Number } } - 手动刷新时调用了
this.$refs.grid.handleFetch()
2. 分页器不显示?
确保:
pagerConfig正确传入,且设置了component: TinyPagergetData返回值中正确设置了page.total(total 为 0 时分页器不会渲染)
3. 如何使用 TypeScript?
typescript
interface CompanyVO {
id: number
name: string
area: string
address: string
introduction: string
}
interface FetchParams {
page: {
currentPage: number
pageSize: number
}
filterArgs?: string
sortBy?: { property: string; sort: 'asc' | 'desc' }
}
async getData({ page, filterArgs, sortBy }: FetchParams): Promise<{
result: CompanyVO[]
page: { total: number }
}> {
const response = await fetch(`/api/company/list?page=${page.currentPage}&pageSize=${page.pageSize}`)
const data = await response.json()
return {
result: data.list,
page: { total: data.total }
}
}
4. 首次加载不想要默认数据?
设置 :auto-load="false",然后在合适的时机手动调用 handleFetch()。
5. 如何保存和恢复查询状态?
javascript
// 保存查询参数
const savedQuery = {
searchName: this.searchName,
searchArea: this.searchArea,
page: this.pagerConfig.attrs.currentPage
}
// 恢复查询参数
this.searchName = savedQuery.searchName
this.searchArea = savedQuery.searchArea
this.pagerConfig.attrs.currentPage = savedQuery.page
this.$refs.grid.handleFetch()
总结
TinyVue Grid 的 fetchData 提供了一个简洁且强大的远程数据加载方案。核心要点:
- 通过
fetchData: { api: this.getData }配置数据获取方法 getData回调接收分页、排序、筛选参数,返回标准格式的 Promise- 结合
pagerConfig实现分页,结合fetchData.args实现筛选 - 通过
this.$refs.grid.handleFetch()手动触发刷新 - 复杂场景可退回到
proxyConfig原生模式
掌握这些用法后,你可以轻松应对各种企业级表格数据管理场景。Happy coding! 🚀