表格
QTable 组件可以以表格的形式展示数据,通常被称为数据表,包含以下主要功能:
- 筛选
- 排序
- 对单行/多行数据进行自定义操作
- 分页(包括服务端排序)
- 网格模式(例如您可以使用 QCard 以非表格的形式展示数据)
- 通过作用域槽对某行或某个单元格进行自定义
- 能够在数据行的顶部或底部添加额外的行
- 选择某列(通过 QTableColumns 组件)
- 自定义表格顶部或底部的控件
- 响应式设计
TIP
如果您不需要分页、排序、筛选等功能,可以考虑使用 QMarkupTable 组件代替。
QTable API
QTh API
QTr API
QTd API
定义数据
我们来看一个示例来学习配置 columns
属性。我们会告诉 QTable row-key
是 “name” 且必须唯一,如果数据来自于数据库,我们通常会使用 id 作为 row-key
。
columns: [ // 一个对象数组
// column 对象定义
{
// 唯一的 id
// 某列数据的标识
// 会在 pagination.sortBy,"body-cell-[name],slot 等地方使用。
name: 'desc',
// 头部的标签
label: 'Dessert (100g serving)',
// 决定此列数据使用对象中的哪个字段
field: 'name',
// 或者 field: row => row.some.nested.prop,
// (可选的) 如果我们使用 visible-columns 属性,那么这个列将始终可见
required: true,
// (可选的) 对齐方式
align: 'left',
// (可选的) 告诉 QTable,您想要这个列是可排序的。
sortable: true,
// (可选的) 自定义排序函数
sort: (a, b, rowA, rowB) => parseInt(a, 10) - parseInt(b, 10),
// 函数返回值:
// * 小于 0 表示 a 小于 b,排序时 a 在 b 前面。
// * 等于 0,则 a 和 b 的位置保持不变,对其他不同的元素进行排序。
// * 大于 0 表示 a 大于 b,排序时 a 在 b 后面。
// (可选的) 重写 'column-sort-order' 属性;
// 设置排序顺序:
// 'ad' (ascending-descending)表示升序降序
// 'da' (descending-ascending)表示降序排序
sortOrder: 'ad', // 或者 'da'
// (可选的) 您可以使用一个函数来格式化数据
format: (val, row) => `${val}%`,
// 另一个格式化示例:
// format: val => val
// ? /* val 值为 true 则使用勾选状态的 Unicode 字符:☑ */ "\u2611"
// : /* 否则使用未选中状态的 Unicode 字符:☐ */ "\u2610",
// body td:
style: 'width: 500px',
// 或者使用一个函数 --> style: row => ... (return String/Array/Object)
classes: 'my-special-class',
// 或者使用一个函数 --> classes: row => ... (return String)
// header th:
headerStyle: 'width: 500px',
headerClasses: 'my-special-class'
},
{ name: 'calories', label: 'Calories', field: 'calories', sortable: true },
{ name: 'fat', label: 'Fat (g)', field: 'fat', sortable: true },
{ name: 'carbs', label: 'Carbs (g)', field: 'carbs' },
{ name: 'protein', label: 'Protein (g)', field: 'protein' },
{ name: 'sodium', label: 'Sodium (mg)', field: 'sodium' },
{ name: 'calcium', label: 'Calcium (%)', field: 'calcium', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) },
{ name: 'iron', label: 'Iron (%)', field: 'iron', sortable: true, sort: (a, b) => parseInt(a, 10) - parseInt(b, 10) }
]
基础用法
TIP
您可以将 dense
属性与 $q.screen
搭配使用来实现响应式的效果。例如::dense="$q.screen.lt.md"
,更多信息请参考:Screen Plugin。
省略列定义
您可以省略定义列 columns
,QTable 会根据数据中的第一列进行推断。需要注意的是,标题会自动转为大写,并且排序会自动开启。
固定表头/列
WARNING
请注意,固定表头或行是通过 CSS 中的 position: sticky
属性实现的,并不是所有的浏览器都支持它。使用前,请检查 caniuse.com。
TIP
固定表头或列都是由 css 实现的,所以在下面的几个示例中要注意 style 部分的代码,尤其是 position: sticky
分割线
样式
虚拟滚动
当使用虚拟滚动时,您需要使用 table-style
属性声明一个最大高度。在下面的示例中,我们还强制一次性显示了所有的列(注意 pagination
和 rows-per-page-options
)。
您可以在滚动到底部时动态加载新数据:
您可以同时使用虚拟滚动和分页:
下面的示例展示了如何将虚拟滚动与固定表头同时使用,请注意其中 virtual-scroll-sticky-start
属性被设置为了表头的高度。
这里有两个工具 CSS 类名可以控制虚滚动的大小计算:
- 使用
q-virtual-scroll--with-prev
类可以使被虚拟滚动渲染的元素与上一个元素一起分组(主要用于从同一行数据生成的多个表行)。 - 使用
q-virtual-scroll--skip
类可以使被虚拟滚动渲染的元素在计算大小时忽略此元素的大小。
选择
警告
为了使选中功能生效,必须设置 row-key
属性。
隐藏列,自定义头部,全屏
请注意在列定义中被标记了 required
的列无法被隐藏,始终可见。
弹出编辑
TIP
下面的示例中,我们利用 QPopupEdit 组件实现了就地编辑数据的功能。请注意,我们使用的是 body
类型的插槽,如果使用单元格类型的插槽,QPopupEdit 无法生效。
网格样式
TIP
您可以将 grid
属性与 $q.screen
搭配使用以实现响应式的效果。例如::grid="$q.screen.lt.md"
. 更多信息请参考: 屏幕插件。
下面的示例中,我们让 QTable 以网格模式展示(不使用特殊的插槽):
然而,如果您想完全自定义内容,请看下面的示例,其中:
- 我们使用一个 Vue 作用域范围的插槽,叫做
item
来定义每个记录应该如何展示(相当于非网格模式下的行),这样您就可以自由发挥了。 - 我们开启多选
展开行
WARNING
如果您为一行数据生成了多个 QTr,请为每个 QTr 设置一个唯一的 key
。
还可以采用外部扩张模式:
如果您在 QTable 中使用虚拟滚动功能,您应该知道,有两个工具 CSS 类名可以控制虚滚动的大小计算:
- 使用
q-virtual-scroll--with-prev
类可以使被虚拟滚动渲染的元素与上一个元素一起分组(主要用于从同一行数据生成的多个表行)。 - 使用
q-virtual-scroll--skip
类可以使被虚拟滚动渲染的元素在计算大小时忽略此元素的大小。
前/后插槽
分页
TIP
如果 pagination
声明了一个 rowsNumber
属性,那么就代表您为 Table 配置了服务端分页(& 排序 & 筛选)。请参考 服务端分页,排序和筛选 部分。
下面是两个处理分页(以及每页的排序和行数)的示例。
第一个示例强调如何配置基础分页功能:
第二个示例使用 “v-model:pagination” 指令,因为我们希望随时访问它的当前值。以下技术的一个用例是从 QTable 外部控制分页
分页插槽
出于学习目的,我们将使用一些基础的控件来实现自定义分页功能,以帮助您开始实现自己的分页控件。
加载状态
自定义头部
Body 插槽
下面的示例显示了如何使用插槽自定义整个行:
下面我们使用一个会被应用到每个单元格的插槽:
我们也可以指定自定义某些特殊的列。这种插槽的写法是 body-cell-[name]
,其中 [name]
应该被替换成行中用作 row-key 的属性。
Header 插槽
下面的示例显示了如何使用槽自定义整个标题行
下面我们使用一个会被应用于每个表头单元格的插槽:
我们也可以指定自定义某些特殊的表头单元格。这种插槽的写法是 header-cell-[name]
,其中 [name]
应该被替换成行中用作 row-key 的属性。
空数据
当表格没有数据可以展示时,您也可以使用空数据插槽 (“no-data”) 来自定义要展示的消息。也可以在 “Search” 输入框中输入一些数据。
处理底层
有一些属性可以用于隐藏底部区域的一些部分,下面有一些示例:
自定义排序
响应式表格
为了实现响应式表格,我们有两个工具可以使用:dense
和 grid
属性。我们还可以将其与 $q.screen
搭配使用,更多信息请参考:屏幕插件。
下面的第一个示例使用了 $q.screen.lt.md
来开启紧凑模式,第二个示例使用了 $q.screen.xs
来开启网格模式,所以您需要调整浏览器的窗口大小来查看它们的变化。
服务端分页,排序和筛选
当您的数据库中拥有大量的数据时,出于内存,UI 渲染性能等原因,很明显不能一次性全部加载它们。您可以一次只加载表格的一页数据,当用户想要访问下一页数据,或者想要重新排序/筛选时,再去服务端重新请求对应的数据。
要开启这个行为的第一步是声明
pagination
属性,并且其中必须包括rowsNumber
字段。QTable 需要知道可用的行总数,以便正确渲染分页控件。如果筛选导致rowsNumber
更改,则必须动态修改它。第二步是监听 QTable 的
@request
事件。如果因为页码,筛选或者排序的改变需要重新去服务端重新请求数据时,这个事件会被触发。最好声明
loading
属性来告知用户,后台正在请求数据。
TIP
在下面的示例中,模拟了使用 ajax 对服务器进行请求的步骤。虽然概念相似,但如果您要使用此代码,还需要进行适当的更改以连接到您自己的数据源。
导出数据
下面是一个简单的 csv 编码示例,然后使用 Quasar 提供的 exportFile 工具函数导出表格数据,浏览器应该会触发一个文件下载。对于更专业的编码方法,我们建议使用 csv-parse 和 csv-stringify 包。
提示
如果要导出用户筛选 + 排序的数据,还可以使用 QTable 内部的 filteredSortedRows
计算属性。
键盘导航
下面是一个使用键盘在表格所选行中导航的示例。使用 ArrowUp
,ArrowDown
,PageUp
,PageDown
,Home
和 End
键进行导航。
类型定义
export type QTableColumn<
Row extends Record<string, any> = any,
Key = keyof Row extends string ? keyof Row : string,
Field = Key | ((row: Row) => any),
> = Omit<NonNullable<QTableProps["columns"]>[number], "field" | "format"> & {
field: Field;
format?: (val: any, row: Row) => string;
};