外观
UDataTable:受控的业务列表
UDataTable 组合搜索框、列配置、Element Plus 表格、多选操作区和分页。它展示传入的 data,不会自动请求、筛选或分页;这些行为由页面负责。
可直接运行的本地分页示例
此例接收完整 rows 数组,在页面执行搜索与分页。实际远程列表应将查询交给 API,而不是对当前页数据再次 slice。
vue
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { UDataTable, type UDataColumn } from '@uadmin/ui'
type UserRow = { id: number; name: string; email: string }
const props = defineProps<{ rows: UserRow[] }>()
const { t } = useI18n({
useScope: 'local',
messages: {
'zh-CN': { name: '姓名', email: '邮箱', search: '搜索姓名或邮箱', clear: '取消选择' },
'en-US': {
name: 'Name',
email: 'Email',
search: 'Search name or email',
clear: 'Clear selection',
},
},
})
const page = ref(1)
const pageSize = ref(10)
const search = ref('')
const hidden = ref<string[]>([])
const columns = computed<UDataColumn<UserRow>[]>({
get: () => [
{ prop: 'name', label: t('name'), minWidth: 160, hidden: hidden.value.includes('name') },
{ prop: 'email', label: t('email'), minWidth: 220, hidden: hidden.value.includes('email') },
],
set: value => {
hidden.value = value.filter(column => column.hidden).map(column => column.prop!)
},
})
const filtered = computed(() => {
const keyword = search.value.trim().toLowerCase()
return props.rows.filter(row => `${row.name} ${row.email}`.toLowerCase().includes(keyword))
})
const data = computed(() =>
filtered.value.slice((page.value - 1) * pageSize.value, page.value * pageSize.value),
)
watch([search, pageSize], () => {
page.value = 1
})
watch(
() => filtered.value.length,
total => {
page.value = Math.min(page.value, Math.max(1, Math.ceil(total / pageSize.value)))
},
)
</script>
<template>
<UDataTable
v-model:page="page"
v-model:page-size="pageSize"
v-model:search-value="search"
v-model:columns="columns"
:data="data"
:total="filtered.length"
:search-placeholder="t('search')"
selectable
>
<template #cell-name="{ row }"
><strong>{{ row.name }}</strong></template
>
<template #bulk-actions="{ clear }">
<el-button size="small" @click="clear">{{ t('clear') }}</el-button>
</template>
</UDataTable>
</template>列名通过 computed 响应语言变化;列显隐独立保存,切换语言不会重置用户选择。示例的行 ID 必须唯一。
远程数据:确定一个请求入口
远程列表一般维护 page/pageSize/search/sort,监听这些查询状态并调用 API。sortable: 'custom' 仅产生排序事件,由后端实际排序。不要同时在 watch、change 和 sort-change 都请求同一份数据。
事件差异很重要:搜索只发送 update:searchValue,不发送 change。修改 pageSize 时,change 的 page 为 1,但组件不会额外发送 update:page(1),页面要自己重置 page。上例已通过 watch 处理。
Props
| Prop | 类型 | 默认 / 用途 |
|---|---|---|
data | Row[] | 必填;当前要显示的行 |
columns | UDataColumn<Row>[] | 必填;用 v-model:columns 接住显隐更改 |
rowKey | string | 'id' |
total | number | 0;整个结果集条数,非当前页条数 |
page / pageSize | number | 1 / 10,受控分页 |
pageSizes | number[] | 未传时使用分页子组件默认值 |
searchValue / searchPlaceholder | string | 搜索输入值/占位文案 |
loading | boolean | false;表格容器 loading |
selectable | boolean | false;显示选择列 |
showToolbar / showPagination / showHeader | boolean | 均为 true |
border / stripe | boolean | 均为 false |
size | 'large' | 'default' | 'small' | 传给 el-table |
height / maxHeight | number | string | 传给 el-table |
fitHeight | boolean | false;true 使用表体高度 100%,需父级高度链 |
emptyText | string | 未传时使用组件内置空数据语言键 |
actionsWidth | number | string | 160;仅 actions 插槽存在时有操作列 |
列定义
UDataColumn<Row> 从 @uadmin/ui 导出。以下字段对应当前实现:
| 字段 | 类型 / 行为 |
|---|---|
label / prop | 必填标题 / 可选行字段名 |
width / minWidth | string | number |
sortable | boolean | 'custom';true 为表格本地排序,custom 为远程排序事件 |
fixed | 'left' | 'right' |
align / headerAlign | 'left' | 'center' | 'right';align 默认 left |
className / labelClassName | 单元格 / 表头类名 |
showOverflowTooltip | 超长内容提示 |
hidden | 静态隐藏,可被列工具栏更新 |
hide | boolean | ((column) => boolean);与 hidden 任一为 true 即隐藏 |
children | 多级表头子列;父列不渲染单元格 |
slot | 自定义单元格插槽名;默认回退 cell-${prop} |
cellRenderer | (scope) => VNode | VNode[] | string | number | null | undefined |
headerRenderer | 表头函数式渲染,scope 含 column/$index |
formatter | 类型允许 (row, cell?) => string,当前包装器实际只传 row |
渲染优先级为 cellRenderer → 指定 slot → cell-${prop} → 默认单元格/formatter。需要值时从 row 读取,不要依赖 formatter 的第二参数。hide 为函数的列不能通过工具栏切换。
插槽与事件
| Slot | Scope / 用途 |
|---|---|
toolbar-left / toolbar-right | 工具栏两侧扩展 |
toolbar-filters | 搜索旁的业务筛选器 |
cell-${prop} 或列指定 slot | { row, column, $index } |
actions | { row, column, $index };生成固定在右侧的操作列 |
bulk-actions | { selected, clear };选中行时的批量按钮 |
bulk | { selected, clear };替换整个批量区,调用方自行判断是否显示 |
empty | 空数据展示 |
| Event | Payload |
|---|---|
update:page / update:pageSize | number |
update:searchValue | string |
update:columns | 新的列数组 |
selection-change | Row[] |
sort-change | { prop, order: 'ascending' | 'descending' | null } |
change | { page, pageSize, sort?, search? };分页/排序时发出 |
公开方法:clearSelection()、toggleRowSelection(row, selected?)、doLayout()、getTableRef()。后者返回底层 el-table 实例;挂载前不可用。
常见误用
- 不要以为
total会让组件自动切分 data;远程查询只传当前页结果,本地查询先筛选再切片。 - 跨页选择没有默认保留策略;批量提交前由业务层确认选中 ID,并在删除成功后调用 clear。
- 普通 HTML 属性传到组件外层 section,不代表所有 el-table props/events 都被透传。用上述正式 API,底层需求必要时通过实例访问。
fit-height适合固定高度布局;普通长页面保持默认滚动。组合方式见 UPage。
相关:简单表格 UTable · 图表