数据表(Data Table)

QTable是一个允许您以表格方式显示数据的组件。 特征:

安装

编辑 /quasar.conf.js:

framework: {
components: [
'QTable',

// 只选择你正在使用的内容:
'QTh',
'QTr',
'QTd',
'QTableColumns'
]
}

基本用法

这是最基本的QTable:

<template>
<q-table
title="Table Title"
:data="tableData"
:columns="columns"
row-key="name"
/>
</template>

<script>
export default {
data: () => ({
columns: [
{
name: 'desc',
required: true,
label: 'Dessert (100g serving)',
align: 'left',
field: 'name',
sortable: true
},
...
],
tableData: [
{
name: 'Frozen Yogurt',
calories: 159,
fat: 6.0,
carbs: 24,
protein: 4.0,
sodium: 87,
calcium: '14%',
iron: '1%'
},
...
]
})
}
</script>

国际化

通过Quasar I18n默认处理不同QTable标签的默认值。如果您的语言包缺失,请为其提交一个PR。

QTable Vue属性

Vue属性 类型 说明
data 对象数组 需要显示的包含行数组的数据。
columns 对象数组 必填)定义每列的属性。
row-key 字符串 必填)每行的属性名称,用于定义各行的唯一数据键。
pagination 对象 使用.sync 。控制分页和排序。通过包含rowsNumber属性可以启用表格的“server-mode”。详情请参阅下一节。
rows-per-page-options 数组 数字数组, 表示用户选择每页应显示多少行的选项。例如:’[3,5,7,0]’。注意值0表示“全部”。
selection 字符串 设置选择模式。 ‘single’,’multiple’或(默认)’none’之一。
selected 数组 使用.sync 。选定行的唯一键的数组。
visible-columns 数组 包含可见列的列属性值’name’的字符串数组。
loading 布尔 显示后台进程正在进行中(如提取数据等)。
color 字符串 默认表格控件的颜色(分页,复选框,…)。
dark 布尔 在深色背景上使用表格时。
dense 布尔 密集表,当你想在窗口上相同区域显示更多的数据的时候。在狭窄窗口上默认激活。
title 字符串 表的标题。
hide-header 布尔 隐藏表头。
hide-bottom 布尔 隐藏表格底部(通常包含分页控件)。
separator 字符串 设置行/列/单元格的分隔符。 ‘horizontal’、 ‘vertical’、 ‘cell’、 ‘none’之一。
table-style 字符串/数组/对象 <table>标签本身的样式。
table-class 字符串/数组/对象 <table>标签本身的类。
filter 字符串 filter-method()使用的表的过滤字符串。
filter-method 函数 当你想要一个自定义的过滤方法。详情请参阅下一节。

标签属性默认在Quasar的i18n中定义,但您可以覆盖它们:

Vue属性 类型 说明
no-data-label 字符串 当没有行存在时显示的消息。
no-results-label 字符串 当没有行与过滤器匹配时显示的消息。
loading-label 字符串 当表格当前没有行但正在提取它们时显示的消息。
selected-rows-label(rowsNumber) 函数 返回一个消息(字符串)以显示选择多少行的函数。取一个Number参数,它是所选的实际行数。
rows-per-page-label 字符串 重写’Rows per page:’。
pagination-label(start,end,total) 函数 覆盖默认的’x-y of z’分页标签。

重要
初始的排序列,排序的方向和页面通过pagination属性进行配置。查看下面的分页部分。

列定义

我们来看一个配置columns属性的例子。假设我们告诉QTable行键是’name’。

columns: /* 对象数组 */ [
// 列对象定义
{
// 唯一的ID(由row-key、pagination.sortBy、...使用)
name: 'desc',

// 头部标签
label: 'Dessert (100g serving)',

// 行对象属性以确定此列的值
field: 'name',
// 或者field: row => row.some.nested.prop

// (可选)如果我们使用可见列,这个列将始终可见
required: true,

// (可选)对齐
align: 'left',

// (可选)告诉QTable你想要这个列可排序
sortable: true

// (可选)比较功能,如果你有
    // 一些自定义数据或想要一个特定的方式来比较两行
sort: (a, b) => parseInt(a, 10) - parseInt(b, 10)
// 函数返回值:
    // *小于0,然后将a排序为低于b的索引,即首先出现
    // *为0,则相对于彼此保持a和b不变,但相对于所有不同的元素进行排序
    // *大于0,则将b排序为低于a的索引,即b先到达
},
{ 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) }
]

分页

当你想控制Table的分页时,使用pagination属性,但不要忘记添加.sync修饰符:

<template>
<div>
<q-table :pagination.sync="pagination" ... />
<q-btn @click="pagination.page++" label="Next page" ... />
</div>
</template>

<script>
export default {
data: () => ({
pagination: {
sortBy: null, // 字符串,列“name”属性值
descending: false,
page: 1,
rowsPerPage: 5 // 当前每页显示的行数
}
})
}

当分页有一个名为rowsNumber的属性时,这意味着您将为服务器端分页(& 排序 & 过滤)配置表。

自定义过滤方法

<template>
<q-table
:filter="terms"
:filter-method="myFilter"
...
</template>

<script>
export default {
data: () => ({ filter: '' }),
methods: {
// 这实际上是默认的过滤方法:
myFilter (rows, terms, cols, cellValue) {
const lowerTerms = terms ? terms.toLowerCase() : ''
return rows.filter(
row => cols.some(col => (cellValue(col, row) + '').toLowerCase().indexOf(lowerTerms) !== -1)
)
}
}
}
</script>

QTable Vue事件

Vue事件 参数 说明
@request Object {pagination,filter,getCellValue} 使用服务器端分页时触发(pagination属性对象包含rowsNumber

服务器端分页,过滤,排序

当你的数据库一个表包含大量的行时,显然不可能全部加载它们,因为有多种原因(内存,UI渲染性能……)。相反,您只能加载一个表格页。每当用户想要导航到另一个表格页,或想要按列进行排序或过滤表格时,就会向服务器发送请求以获取部分数据。

1.启用此行为的第一步是指定pagination属性,它必须包含rowsNumber。 QTable需要知道可用的总行数,才能正确呈现分页链接。

2.第二步是在QTable上监听@request事件。当需要从服务器获取数据时触发此事件,因为页码或排序或过滤都发生了变化。

3.最好还指定loading属性以通知用户后台进程正在进行。

<template>
<q-table
ref="table"
:data="serverData"
:columns="columns"
:filter="filter"
row-key="name"
:pagination.sync="serverPagination"
:loading="loading"
@request="request"
>
<template slot="top-right" slot-scope="props">
<q-search hide-underline v-model="filter" />
</template>
</q-table>
</template>

<script>
import tableData from 'assets/table-data'

export default {
data () {
return {
filter: '',
loading: false,
serverPagination: {
page: 1,
rowsNumber: 10 // 指定这个属性就确定了分页是服务器端的
},

serverData: [],
columns: [
{
name: 'desc',
required: true,
label: 'Dessert (100g serving)',
align: 'left',
field: 'name',
sortable: true
},
{ 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) }
]
}
},
methods: {
request ({ pagination, filter }) {
// 我们将QTable设置为“加载”状态
this.loading = true

// 我们根据收到的分页和过滤器来执行服务器数据提取
      //(在这里使用Axios,但可以是任何东西;参数因后端实现而异)

axios
.get(`/data/${pagination.page}?sortBy=${pagination.sortBy}&descending=${pagination.descending}&filter=${filter}`)
.then(({ data }) => {
// 更新分页以反映在UI中
this.serverPagination = pagination

// 我们还设置(或更新)了rowsNumber
this.serverPagination.rowsNumber = data.rowsNumber

// 然后我们更新提取的行
this.serverData = data.rows

// 最后我们告诉QTable退出“加载”状态
this.loading = false
})
.catch(error => {
// 做了什么事导致一个错误...

        // 我们告诉QTable退出“加载”状态
this.loading = false
})
}
},
mounted () {
// 一旦挂载,我们需要触发初始服务器数据获取
this.request({
pagination: this.serverPagination,
filter: this.filter
})
}
}
</script>

示例 - 功能

过滤器,列选择,分隔符,切换全屏

<template>
<q-table
:data="tableData"
:columns="columns"
:filter="filter"
:visible-columns="visibleColumns"
:separator="separator"
row-key="name"
color="secondary"
>
<template slot="top-left" slot-scope="props">
<q-search
hide-underline
color="secondary"
v-model="filter"
class="col-6"
/>
</template>
<template slot="top-right" slot-scope="props">
<q-table-columns
color="secondary"
class="q-mr-sm"
v-model="visibleColumns"
:columns="columns"
/>
<q-select
color="secondary"
v-model="separator"
:options="[
{ label: 'Horizontal', value: 'horizontal' },
{ label: 'Vertical', value: 'vertical' },
{ label: 'Cell', value: 'cell' },
{ label: 'None', value: 'none' }
]"
hide-underline
/>
<q-btn
flat round dense
:icon="props.inFullscreen ? 'fullscreen_exit' : 'fullscreen'"
@click="props.toggleFullscreen"
/>
</template>
</q-table>
</template>

<script>
export default {
data: () => ({
tableData: [ ... ],
columns: [ ... ],
visibleColumns: ['desc', 'fat', 'carbs', 'protein', 'sodium', 'calcium', 'iron'],
separator: 'horizontal',
filter: ''
})
}
</script>

行选择,额外的顶部/底部行,加载状态

<template>
<q-table
:data="tableData"
:columns="columns"
:selection="selection"
:selected.sync="selected"
:loading="loading"
row-key="name"
color="secondary"
:class="tableClass"
>
<q-tr slot="top-row" slot-scope="props">
<q-td colspan="100%">
<strong>Extra top row</strong>
</q-td>
</q-tr>

<q-tr slot="bottom-row" slot-scope="props">
<q-td colspan="100%">
<strong>Extra bottom row</strong>
</q-td>
</q-tr>

<template slot="top-left" slot-scope="props">
<q-select
v-model="selection"
stack-label="Selection"
hide-underline
:options="[
{ label: 'Single', value: 'single' },
{ label: 'Multiple', value: 'multiple' },
{ label: 'None', value: 'none' }
]"
color="secondary"
style="min-width: 100px"
/>
</template>
<div slot="top-right" slot-scope="props" class="column">
<q-toggle
v-model="loading"
label="Loading state"
color="secondary"
class="q-mb-sm"
/>
<q-toggle
v-model="dark"
label="On dark background"
color="secondary"
/>
</div>
</q-table>
</template>

<script>
export default {
data: () => ({
tableData: [ ... ],
columns: [ ... ],
loading: false,
dark: true,
selection: 'multiple',
selected: [
// 初始选择; 注意我们指定了
      // 所选行的row-key属性
{ name: 'Ice cream sandwich' }
]
})
}
</script>

控制分页,页面导航自定义控制和监控

<template>
<q-table
:data="tableData"
:columns="columns"
:pagination.sync="paginationControl"
row-key="name"
color="primary"
>
<div slot="pagination" slot-scope="props" class="row flex-center q-py-sm">
<q-btn
round dense size="sm" icon="undo" color="secondary" class="q-mr-sm"
:disable="props.isFirstPage"
@click="props.prevPage"
/>
<div class="q-mr-sm" style="font-size: small">
Page {{ props.pagination.page }} / {{ props.pagesNumber }}
</div>
<q-btn
round dense size="sm" icon="redo" color="secondary"
:disable="props.isLastPage"
@click="props.nextPage"
/>
</div>
</q-table>
</template>

<script>
export default {
data: () => ({
tableData: [ ... ],
columns: [ ... ],
paginationControl: { rowsPerPage: 3, page: 1 },
}),
watch: {
'paginationControl.page' (page) {
this.$q.notify({
color: 'secondary',
message: `Navigated to page ${page}`,
actions: page < 4
? [{
label: 'Go to last page',
handler: () => {
this.paginationControl.page = 4
}
}]
: null
})
}
}
}
</script>

行选择操作

<q-table
:data="tableData"
:columns="columns"
selection="multiple"
:selected.sync="selectedSecond"
row-key="name"
color="secondary"
title="Select some rows"
>
<!-- 只有当至少有一行被选中时才会显示 -->
<template slot="top-selection" slot-scope="props">
<q-btn color="secondary" flat label="Action 1" class="q-mr-sm" />
<q-btn color="secondary" flat label="Action 2" />
<div class="col" />
<q-btn color="negative" flat round delete icon="delete" @click="deleteRow" />
</template>
</q-table>

隐藏头部和底部

<q-table
:data="tableData"
:columns="columns"
row-key="name"
color="primary"
hide-header
hide-bottom
/>

显示嵌套属性或格式化列

您可以显示嵌套属性的值。 例如:

columns: [
{
name: 'author',
label: 'Author',
field: row => row.author.name
}
]

然后,您可以更进一步, 在列定义中为特定列格式化值。 例:

columns: [
{
name: 'author',
label: 'Author',
field: row => row.author.name,
format: val => `${val}%`
}
]

field返回的值用于排序行,而format值专门用于向用户显示值。 这对于需要按数据的初始值进行排序的情况非常有用。 然而,你可以(如果你想)避免格式化,并使用自定义范围的槽(行、列单元格)来定义Quasar应该如何格式化单元格。

示例 - 自定义

自定义表格顶部和底部

<q-table
:data="tableData"
:columns="columns"
row-key="name"
color="primary"
>
<div slot="top" slot-scope="props" class="row flex-center fit">
<img src="~assets/quasar-logo-full.svg">
</div>
<div slot="bottom" slot-scope="props" class="row flex-center fit">
<q-btn
round dense icon="chevron_left" color="primary" class="q-mr-md"
:disable="props.isFirstPage"
@click="props.prevPage"
/>
<q-btn
round dense icon="chevron_right" color="primary"
:disable="props.isLastPage"
@click="props.nextPage"
/>
</div>
</q-table>

自定义列单元格

<q-table
:data="tableData"
:columns="columns"
row-key="name"
color="secondary"
>
<!-- 插槽名称语法: body-cell-<column_name> -->
<q-td slot="body-cell-desc" slot-scope="props" :props="props">
<q-chip small color="secondary">{{ props.value }}</q-chip>
</q-td>
</q-table>

自定义行

<q-table
:data="tableData"
:columns="columns"
row-key="name"
>
<q-tr slot="body" slot-scope="props" :props="props">
<q-td key="desc" :props="props">
<span class="text-italic">{{ props.row.name }}</span>
<q-tooltip>I'd like to eat "{{ props.row.name }}"</q-tooltip>
</q-td>
<q-td key="calories" :props="props">
<div class="row items-center justify-between no-wrap">
<q-btn size="sm" round dense color="secondary" icon="remove" @click="props.row.calories--" class="q-mr-xs" />
<q-btn size="sm" round dense color="tertiary" icon="add" @click="props.row.calories++" class="q-mr-sm" />
<div>{{ props.row.calories }}</div>
</div>
</q-td>
<q-td key="fat" :props="props">{{ props.row.fat }}</q-td>
<q-td key="carbs" :props="props">
<q-chip small square color="amber">{{ props.row.carbs }}</q-chip>
</q-td>
<q-td key="protein" :props="props">{{ props.row.protein }}</q-td>
<q-td key="sodium" :props="props">{{ props.row.sodium }}</q-td>
<q-td key="calcium" :props="props">{{ props.row.calcium }}</q-td>
<q-td key="iron" :props="props">
{{ props.row.iron }}
</q-td>
</q-tr>
</q-table>

替代自定义行

<q-table
:data="tableData"
:columns="columns"
title="Click on a row"
dark
class="bg-black"
color="amber"
row-key="name"
>
<q-tr slot="body" slot-scope="props" :props="props" @click.native="rowClick(props.row)" class="cursor-pointer">
<q-td v-for="col in props.cols" :key="col.name" :props="props">
# {{ col.value }} #
</q-td>
</q-tr>
</q-table>

自定义头部(有工具提示)

<q-table
:data="tableData"
:columns="columns"
row-key="name"
>
<tr slot="header" slot-scope="props">
<q-th key="desc" :props="props">
Dessert
<q-tooltip>Pick a desert</q-tooltip>
</q-th>
<q-th key="calories" :props="props">
Calories
<q-tooltip>These are the calories</q-tooltip>
</q-th>
<q-th key="fat" :props="props">
Fat
<q-tooltip>This is the fat</q-tooltip>
</q-th>
<q-th key="carbs" :props="props">
Carbs
<q-tooltip>These are the carbohydrates</q-tooltip>
</q-th>
<q-th key="protein" :props="props">
Protein
<q-tooltip>These are the proteins</q-tooltip>
</q-th>
<q-th key="sodium" :props="props">
Sodium
<q-tooltip>This is the sodium</q-tooltip>
</q-th>
<q-th key="calcium" :props="props">
Calcium
<q-tooltip>This is the calcium</q-tooltip>
</q-th>
<q-th key="iron" :props="props">
Iron
<q-tooltip>This is the iron</q-tooltip>
</q-th>
</tr>
</q-table>

替代自定义头部

<q-table
:data="tableData"
:columns="columns"
row-key="name"
>
<q-tr slot="header" slot-scope="props" :props="props">
<q-th v-for="col in props.cols" :key="col.name" :props="props">
# {{ col.label }} #
</q-th>
</q-tr>
</q-table>

自定义头部和行——使用可选和可展开的行

<q-table
:data="tableData"
:columns="columns"
selection="multiple"
:selected.sync="selected"
row-key="name"
>
<q-tr slot="header" slot-scope="props">
<q-th auto-width>
<q-checkbox
v-if="props.multipleSelect"
v-model="props.selected"
indeterminate-value="some"
/>
</q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label }}
</q-th>
</q-tr>

<template slot="body" slot-scope="props">
<q-tr :props="props">
<q-td auto-width>
<q-checkbox color="primary" v-model="props.selected" />
</q-td>
<q-td key="desc" :props="props">
<q-checkbox color="primary" v-model="props.expand" checked-icon="remove" unchecked-icon="add" class="q-mr-md" />
{{ props.row.name }}
</q-td>
<q-td key="calories" :props="props">{{ props.row.calories }}</q-td>
<q-td key="fat" :props="props">{{ props.row.fat }}</q-td>
<q-td key="carbs" :props="props">{{ props.row.carbs }}</q-td>
<q-td key="protein" :props="props">{{ props.row.protein }}</q-td>
<q-td key="sodium" :props="props">{{ props.row.sodium }}</q-td>
<q-td key="calcium" :props="props">{{ props.row.calcium }}</q-td>
<q-td key="iron" :props="props">
<q-chip small square color="amber">{{ props.row.iron }}</q-chip>
</q-td>
</q-tr>
<q-tr v-show="props.expand" :props="props">
<q-td colspan="100%">
<div class="text-left">This is expand slot for row above: {{ props.row.name }}.</div>
</q-td>
</q-tr>
</template>
</q-table>