QTable 是一个允许您以表格方式显示数据的组件。它通常被称为数据表。它包含以下主要功能:
- 筛选
- 排序
- 单行/多行选择,支持自定义选择操作
- 分页(如果需要,包括服务器端分页)
- 网格模式(您可以使用例如 QCards 以非表格方式显示数据)
- 通过作用域插槽完全自定义行和单元格
- 能够在数据行的顶部或底部添加额外的行
- 列选择器(通过其中一节描述的 QTableColumns 组件)
- 自定义表格顶部和/或底部控件
- 响应式设计
TIP
如果您不需要分页、排序、筛选和 QTable 的所有其他功能,那么您可能想要查看 QMarkupTable 组件。
定义列
让我们以配置 columns 属性为例。我们将告诉 QTable row-key 是 ‘name’,它必须是唯一的。如果这是从数据库获取的数据,我们可能会使用行的 id。
columns: [
// 对象数组
// 列对象定义
{
// 唯一 id
// 标识列
// (用于 pagination.sortBy, "body-cell-[name]" 插槽, ...)
name: "desc",
// 头部标签
label: "Dessert (100g serving)",
// 行对象属性,用于确定此列的值
field: "name",
// 或 field: row => row.some.nested.prop,
// (可选) 如果我们使用 visible-columns,此列将始终可见
required: true,
// (可选) 对齐方式
align: "left",
// (可选) 告诉 QTable 您希望此列可排序
sortable: true,
// (可选) 如果您有自定义数据或想要特定方式比较两行,可以使用比较函数
// --> 注意,值为 null/undefined 的行将自动排序
// 而不调用此方法(如果您也想处理这些行,请使用 "rawSort")
sort: (a, b, rowA, rowB) => parseInt(a, 10) - parseInt(b, 10),
// 函数返回值:
// * 小于 0 则将 a 排序到比 b 更低的索引,即 a 在前
// * 等于 0 则保持 a 和 b 相对于彼此不变,但相对于所有不同元素排序
// * 大于 0 则将 b 排序到比 a 更低的索引,即 b 在前
// (可选) 需要 Quasar v2.13+
// 如果您有自定义数据或想要特定方式比较两行,可以使用比较函数
// --> 注意,如果您不想(自己)处理值为 null/undefined 的行,
// 有一个替代的 "sort" 方法(上面)
rawSort: (a, b, rowA, rowB) => parseInt(a, 10) - parseInt(b, 10),
// 与上面的替代 "sort" 方法具有相同的返回值
// (可选) 覆盖 'column-sort-order' 属性;
// 设置列排序顺序:'ad'(升序-降序)或 'da'(降序-升序)
sortOrder: "ad", // 或 'da'
// (可选) 您可以使用函数格式化数据
format: (val, row) => `${val}%`,
// 另一个格式示例:
// format: val => val
// ? /* Unicode 勾选标记已选中 */ "\u2611"
// : /* Unicode 勾选标记未选中 */ "\u2610",
// body td:
style: "width: 500px",
// 或作为函数 --> style: row => ... (返回 String/Array/Object)
classes: "my-special-class",
// 或作为函数 --> classes: row => ... (返回 String)
// header th:
headerStyle: "width: 500px",
// 或作为函数 --> headerStyle: () => ... (返回 String/Array/Object)
headerClasses: "my-special-class",
// 或作为函数 --> headerClasses: () => ... (返回 String)
},
{ 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
QTable 的第一个示例是使用 Vue 的 <template> 标签,但您也可以使用渲染函数(h)或 JSX。
基础
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 键进行导航。