描述:
在上篇文章当中,分页组件+Table表格+编程式导航基本能够实现所需。但是如果对表格的字段进行排序功能,需要自定义表头,且点击触发排序事件。
后来发现,如果每个页面都这样写,会显得很冗余,想着能否尽量的减少代码。经过实践,将q-table
进行了简单的自定义封装。
结果:
- 将过滤匹配操作、排序操作放到了
c-table
中 - 将分页组件嵌入
c-table
中 - 能够自定义表内容,body部分
- 能够自定义筛选项部分,top部分
- 能够支持多选multiple,默认为none
- 默认左对齐
实现代码
<!--
by nickctang 2018-8-24
自定义Table组件
必须传入参数: tableData、columns、pagination
可选参数:tableLoading、selected、selection
参考文档:
-->
<template>
<q-table
no-data-label="没有更多数据"
:data="tableData"
:columns="columns"
:pagination.sync="paginationControl"
:loading="tableLoading"
:selection="selection"
:selected.sync="selectedSecond"
name="id"
color="secondary"
class="ctable" >
<template slot="top" slot-scope="props">
<slot name="top"
:pagination="props.pagination"
:pagesNumber="props.pagesNumber"
:isFirstPage="props.isFirstPage"
:inFullscreen="props.inFullscreen" >
</slot>
</template>
<tr slot="header" slot-scope="props">
<q-th v-if="selection=='multiple'" auto-width>
<q-checkbox
color="secondary"
v-if="props.multipleSelect"
v-model="props.selected"
indeterminate-value="some"
/>
</q-th>
<!-- <q-th v-else-if="selection=='single'" auto-width>
<q-checkbox
color="secondary"
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" style="min-width: 130px">
<span v-if="!col.sortby" >{{ col.label }}</span>
<q-btn v-else size="xs" dense flat class="no-padding no-wrap"
@click="tableSortBy(col.name, $route.query.order_by)">
{{ col.label }}
<q-icon v-if="$route.query.order_by === col.name" class="q-ml-xs" name="arrow_upward" color="primary" />
<q-icon v-else-if="$route.query.order_by === '-' + col.name" class="q-ml-xs" name="arrow_downward" color="primary" />
<q-icon v-else-if="col.sortby" class="q-ml-xs" name="unfold_more" />
</q-btn>
</q-th>
</tr>
<q-tr slot="body" slot-scope="props">
<!-- 外部q-td的传入参数:props是props.props -->
<slot name="body" :props="props" :row="props.row" >
<!-- 如果外部父组件没有引入自定义内容部分,默认是这样的 -->
<q-td v-for="item in columns" :key="item.name" :props="props">
{{props.row[item.name]}}
</q-td>
</slot>
</q-tr>
<template slot="bottom" slot-scope="props">
<div class="col"></div>
<c-pagination class="no-margin q-pb-sm" :pagination="pagination" />
</template>
</q-table>
</template>
<style lang="stylus">
.ctable .q-table-top{
min-height 0
padding 0
}
</style>
<script>
import cPagination from 'src/components/cPagination'
import { filterToQueryFormat } from '@/libs/utils'
export default {
name: 'CTable',
components: {
cPagination
},
data () {
return {
paginationControl: { rowsPerPage: 0 } // 显示全部
}
},
computed: {
selectedSecond: {
get: function () {
return this.selected
},
set: function (val) {
this.$emit('update:selected', val)
}
}
},
created () {
// 默认左对齐
this.columns.forEach((element, index) => {
this.columns[index].align = this.columns[index].align ? this.columns[index].align : 'left'
})
},
methods: {
/**
* 排序
* @param {{排序key}} name
* @param {{sortname}} sortname 路由排序名称
*/
tableSortBy: function (name, sortname) {
let query = JSON.parse(JSON.stringify(this.$route.query)) // 深度复制
if (sortname && sortname === name) {
query.order_by = '-' + name
} else if (sortname && sortname === '-' + name) {
query.order_by = undefined
} else {
query.order_by = name
}
this.filterSearch(query)
},
/**
* 过滤筛选,传入过滤项
* @param {{筛选过滤项}} filterForm
*/
filterSearch: function (filterForm) {
this.$router.push({
path: this.$route.path,
query: filterToQueryFormat(filterForm) // 将过滤项匹配到路由
})
}
},
props: {
tableData: {
required: true,
default: []
},
columns: {
required: true,
default: []
},
pagination: {
required: true,
default: {
offset: 0,
limit: 12,
count: 0
}
},
tableLoading: {
default: false
},
selection: {
default: 'none'
},
selected: Array
}
}
</script>
使用代码
<c-table
ref="ctable"
:tableData="tableData"
:columns="columns"
:pagination="pagination"
:tableLoading="tableLoading"
selection="multiple"
:selected.sync="selectedSecond"
>
<div class="row col q-px-lg q-py-sm q-mb-md fs-14 gutter-sm" slot="top" slot-scope="props">
<div class="col-md-2 col-xs-4">
<q-input :clearable="filterForm.last_modifier!=null" v-model="filterForm.last_modifier" float-label="最近修改人" />
</div>
<div class="col-md-2 col-xs-4">
<q-select
multiple
clearable
v-model="filterForm.status__in"
:options="CODEMETRIC_CC_STATUS.STATE_OPTIONS"
float-label="状态"
/>
</div>
<div class="col-md-2 col-xs-4">
<q-input clearable v-model="filterForm.ccn__lte" type="number" float-label="圈复杂度>=" />
</div>
<div class="col-md-2 col-xs-4">
<q-input clearable v-model="filterForm.ccn__gte" type="number" float-label="圈复杂度<=" />
</div>
<div class="col-md-2 col-xs-4">
<q-input :clearable="filterForm.file_path__icontains!=null" v-model="filterForm.file_path__icontains" float-label="所属文件" />
</div>
<div class="col-md-2 col-xs-4">
<q-input :clearable="filterForm.author!=null" v-model="filterForm.author" float-label="责任人" />
</div>
<div class="col self-center text-right">
<q-btn color="secondary" :disabled="selectedSecond.length==0" size="sm" label="无须关注" class="q-mr-md" />
<q-btn color="secondary" size="sm" icon="search" label="筛选" @click="$refs.ctable.filterSearch(filterForm)"/>
</div>
</div>
<template slot="body" slot-scope="props">
<q-td auto-width>
<q-checkbox color="secondary" v-model="props.props.selected" />
</q-td>
<q-td key="file_path" :props="props.props">
<q-btn flat color="primary" size="sm" no-caps class="no-padding">
{{ props.row.file_path.slice(props.row.file_path.lastIndexOf('/')) }}
</q-btn>
<q-tooltip anchor="top middle" self="bottom middle" :offset="[10, 10]">
{{ props.row.file_path }}
</q-tooltip>
</q-td>
<q-td key="func_name" :props="props.props">
{{ props.row.func_name }}
</q-td>
<q-td key="ccn" :props="props.props">
<q-chip dense square color="red">
{{ props.row.ccn }}
</q-chip>
</q-td>
<q-td key="status" :props="props.props">
<span :class="'text-' + CODEMETRIC_CC_STATUS.STATUS_CHOICES[props.row.status].color">{{ CODEMETRIC_CC_STATUS.STATUS_CHOICES[props.row.status].label }}</span>
</q-td>
<q-td key="last_modifier" :props="props.props">
{{ props.row.last_modifier }}
</q-td>
<q-td key="author" :props="props.props">
{{ props.row.author }}
</q-td>
<q-td key="is_tapdbug" :props="props.props">
{{ props.row.is_tapdbug?'已提单':'未提单' }}
</q-td>
</template>
</c-table>
使用注意
1.
由于c-table
经历了一层q-table
,存在父组件到孙子组件通讯。
在能够让c-table
自定义body的过程中
<q-tr slot="body" slot-scope="props">
<!-- 外部q-td的传入参数:props是props.props -->
<slot name="body" :props="props" :row="props.row" >
<!-- 如果外部父组件没有引入自定义内容部分,默认是这样的 -->
<q-td v-for="item in columns" :key="item.name" :props="props">
{{props.row[item.name]}}
</q-td>
</slot>
</q-tr>
props包含了key=xxx,而在slot中添加key一直报错
<slot name="body" :row="props.row" :key="props.key" >// 出错
所以干脆把props整个当作props传入,然后把row也一同传入
而在自定义body中,
<template slot="body" slot-scope="props">
<q-td auto-width>
<q-checkbox color="secondary" v-model="props.props.selected" />
</q-td>
<q-td key="file_path" :props="props.props">
<q-btn flat color="primary" size="sm" no-caps class="no-padding">
{{ props.row.file_path.slice(props.row.file_path.lastIndexOf('/')) }}
</q-btn>
</template>
q-td
原来直接传入props
,而由于经历了一层,所以此处的:props
传入的是props.props
从而保证了q-td
找不到key的问题。
2.
在点击筛选,触发过滤项匹配事件时,这里的问题在于如何从父组件向子组件传递点击事件,而触发子组件的事件。利用了vue的ref
。
<q-btn color="secondary" size="sm" icon="search" label="筛选" @click="$refs.ctable.filterSearch(filterForm)"/>
同时传递了筛选内容。此外,在使用ref
时,需要在c-table
中添加ref='xxx'
最后
可能采用的方式和逻辑比较怪异,应该还有其他地方做的不够兼容。望指正。