Skip to content
uAdmin

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类型默认 / 用途
dataRow[]必填;当前要显示的行
columnsUDataColumn<Row>[]必填;用 v-model:columns 接住显隐更改
rowKeystring'id'
totalnumber0;整个结果集条数,非当前页条数
page / pageSizenumber1 / 10,受控分页
pageSizesnumber[]未传时使用分页子组件默认值
searchValue / searchPlaceholderstring搜索输入值/占位文案
loadingbooleanfalse;表格容器 loading
selectablebooleanfalse;显示选择列
showToolbar / showPagination / showHeaderboolean均为 true
border / stripeboolean均为 false
size'large' | 'default' | 'small'传给 el-table
height / maxHeightnumber | string传给 el-table
fitHeightbooleanfalse;true 使用表体高度 100%,需父级高度链
emptyTextstring未传时使用组件内置空数据语言键
actionsWidthnumber | string160;仅 actions 插槽存在时有操作列

列定义 ​

UDataColumn<Row> 从 @uadmin/ui 导出。以下字段对应当前实现:

字段类型 / 行为
label / prop必填标题 / 可选行字段名
width / minWidthstring | number
sortableboolean | 'custom';true 为表格本地排序,custom 为远程排序事件
fixed'left' | 'right'
align / headerAlign'left' | 'center' | 'right';align 默认 left
className / labelClassName单元格 / 表头类名
showOverflowTooltip超长内容提示
hidden静态隐藏,可被列工具栏更新
hideboolean | ((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 为函数的列不能通过工具栏切换。

插槽与事件 ​

SlotScope / 用途
toolbar-left / toolbar-right工具栏两侧扩展
toolbar-filters搜索旁的业务筛选器
cell-${prop} 或列指定 slot{ row, column, $index }
actions{ row, column, $index };生成固定在右侧的操作列
bulk-actions{ selected, clear };选中行时的批量按钮
bulk{ selected, clear };替换整个批量区,调用方自行判断是否显示
empty空数据展示
EventPayload
update:page / update:pageSizenumber
update:searchValuestring
update:columns新的列数组
selection-changeRow[]
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 · 图表

Vue 3 · TypeScript · Element Plus