数据表(Data Table)
QTable是一个允许您以表格方式显示数据的组件。 特征:
过滤
排序
具有自定义选择操作的单行/多行选择
分页(如果需要,包括服务器端)
通过有限范围的插槽, 对行和单元格进行全面定制
能够在数据行的顶部或底部添加额外的行
列选取器(通过本页其中一节中描述的QTableColumns组件)
自定义顶部或底部表格控件
响应式设计(狭窄窗口的“密集”模式)
安装 编辑 /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: [ { name: 'desc' , label: 'Dessert (100g serving)' , field: 'name' , required: true , align: 'left' , sortable: true sort: (a, b ) => parseInt (a, 10 ) - parseInt (b, 10 ) }, { 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 , 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 }) { this .loading = true axios .get(`/data/${pagination.page} ?sortBy=${pagination.sortBy} &descending=${pagination.descending} &filter=${filter} ` ) .then(({ data } ) => { this .serverPagination = pagination this .serverPagination.rowsNumber = data.rowsNumber this .serverData = data.rows this .loading = false }) .catch(error => { 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: [ { 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" > <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 >