描述:
之前实现了自定义分页组件,但是由于现实业务需求(需要将过滤项表单、分页的offset和limit传递到路由中,即a?xxx=xxx&xxx=xxx
)。因此对分页组件进行了重新的设计。
分页组件的重新设计
上一篇:Quasar的自定义分页组件
之前的分页组件主要是监听每页显示数量limit和偏移量offset的变化来触发事件,进行回调,在回调处发起网络请求。
由于上述需求,在新的分页组件中主要监听了页面改变的监听事件,并通过页面改变监听,触发编程式导航,从而改变页面路由,通过对页面路由的更新来发起网络请求,从而实现目标。
共{{paginationModel.count}}条,第{{ page }} /{{max}}页
在父组件使用该分页组件时,只需要传入pagination
即可,pagination
是一个JSON对象,包含offset、limit、count。
在页面进行网络请求得到结果后,只需要改变pagination
的内容,即可动态的更新分页组件。
分页组件 + table + 编程式导航的结合使用
描述:
在父组件使用Table表格和分页组件的过程中,一般还会存在过滤项表单,来对数据进行过滤请求,这些过滤项和分页的offset、limit都需要当作路由的query参数。
实际上,该query参数模样,相当于axios进行get请求时的params。因此将表单对应组件的v-model和请求所需的param定义成相同的名称。
期间遇到的问题:
基本上还是多次请求的问题,在编程式导航过程中,如果没有对应的更新分页组件,由于count的变化,容易让分页组件响应编程式导航,而且由于没有更新offset、limit,从而导致编程式导航成功响应,进行了二次网络请求。
最终的解决:
对原来的分页组件进行的更新。在每次获取网络请求结束后,动态的改变pagination
中的count、limit、offset,从而基本触发了编程式导航,但是由于路由的query没有改变,而不会响应beforeRouteUpdate
,也就不会再次响应网络请求。
实现过程
- 首先,定义table。
// html
...
//vue
tableLoading: false, // 表格加载状态
paginationControl: { rowsPerPage: 0 }, // 显示全部
tableData: [],
columns: [{
name: 'severity',
required: true,
align: 'left',
label: '问题级别',
field: 'severity'
},...
],
- 其次,覆盖Table默认的分页,采用新的分页
// html
// vue
// table+分页组件所需
pagination: {
offset: 0,
limit: 12,
count: 0
}
- 此外,如果对过滤项进行点击过滤,是进行的编程式路由导航操作,将过滤项格式化成为规定的路由query格式(为了保证和get请求params一致)。
// vue methods中
filterSearch: function () {
this.$router.push({
path: this.$route.path,
query: filterToQueryFormat(this.filterForm) // 将过滤项格式化匹配到路由
})
}
// filterToQueryFormat,引用的utils。包含了
/**
* 筛选项格式化到url
* @param {{筛选项表单}} filterForm
* @return {{返回url的query}} query
*/
export function filterToQueryFormat (filterForm) {
console.log('filterToQuery')
let query = {}
for (var key in filterForm) {
if (key.endsWith('__in')) {
query[key] = filterForm[key].toString()
} else {
query[key] = filterForm[key] || filterForm[key] === '' ? filterForm[key] : null
}
}
query.offset = 0
console.log(filterForm)
console.log(query)
console.log('filterToQuery end')
return query
}
/**
* url格式化匹配筛选项
* @param {{url的query}} query
* @param {{筛选项表单}}} filterFrom 由于是对象的引用,所以在此对filterForm进行改变即改变了外层的filterForm
* @return {{返回url的query}} query
*/
export function queryTofilterFormat (query, filterForm) {
console.log('queryToFilter')
let filter = {}
for (var key in filterForm) {
if (key.endsWith('__in')) {
filter[key] = query[key] ? query[key].split(',') : []
} else {
filter[key] = query[key] ? query[key] : null
}
}
filter.limit = query.limit
filter.offset = query.offset
console.log(query)
console.log(filter)
console.log('queryToFilter end')
return filter
}
- 接下来,将过滤项表单定义为filterFrom,表单项所对应的具体组件v-model根据网络请求param一一对应。
// html
q-btn color="secondary" size="sm" icon="search" label="筛选" @click="filterSearch" />
// vue
filterForm: { // 过滤项表单
state__in: [],
resolution__in: [],
severity__in: [],
is_tapdbug: null,
file__icontains: null,
author: null,
ci_time__lte: null,
ci_time__gte: null
}
- 然后,每次进入页面,在mounted中进行第一次请求。对页面路由更新进行监听,从而发起请求。
beforeRouteUpdate
是只有页面路由的params或query进行改变才会触发,原页面复用,不进行页面刷新。
// vue
mounted () {
// 加载数据
this.tableRequest(this.$route.query)
},
beforeRouteUpdate (to, from, next) {
// 路由query更新触发
this.tableRequest(to.query)
next()
},
- 最后,对数据请求的操作。
///
/**
* 表格数据请求
* @param {{路由query参数}} query
*/
tableRequest: function (query) {
this.tableLoading = true // 表格加载动画
// 判断路由是否有limit和offset,如果没有,默认12、0
query.limit = query.limit ? parseInt(query.limit) : 12
query.offset = query.offset ? parseInt(query.offset) : 0
issues(this.$route.params.project_id, query).then(response => {
// 请求成功后,改变分页组件显示
this.pagination = {
limit: query.limit,
offset: query.offset,
count: response.count
}
// 将路由的query匹配到过滤项表单中
this.filterForm = queryTofilterFormat(query, this.filterForm)
// 返回数据
this.tableData = response.results
// 关闭table加载状态
this.tableLoading = false
}).catch(error => {
this.tableLoading = false
console.log(error)
})
}
整个代码
// 分页组件
共{{paginationModel.count}}条,第{{ page }} /{{max}}页
// issue_list
问题列表
{{ CODELINT_STATUS.SEVERITY_CHOICES[props.row.severity].label }}
{{ props.row.file.slice(props.row.file.lastIndexOf('/')) }}
{{ props.row.file }}
{{ props.row.revision.substring(0, 8) }}
{{ props.row.revision }}
{{ formatDatetime(props.row.ci_time) }}
{{ props.row.checkrule__display_name }}
{{ props.row.msg }}
{{ CODELINT_STATUS.STATE_CHOICES[props.row.state].label }}
{{ props.row.author }}
{{ props.row.is_tapdbug?'已提单':'未提单' }}
// utils
/**
* 筛选项格式化到url
* @param {{筛选项表单}} filterForm
* @return {{返回url的query}} query
*/
export function filterToQueryFormat (filterForm) {
console.log('filterToQuery')
let query = {}
for (var key in filterForm) {
if (key.endsWith('__in')) {
query[key] = filterForm[key].toString()
} else {
query[key] = filterForm[key] || filterForm[key] === '' ? filterForm[key] : null
}
}
query.offset = 0
console.log(filterForm)
console.log(query)
console.log('filterToQuery end')
return query
}
/**
* url格式化匹配筛选项
* @param {{url的query}} query
* @param {{筛选项表单}}} filterFrom 由于是对象的引用,所以在此对filterForm进行改变即改变了外层的filterForm
* @return {{返回url的query}} query
*/
export function queryTofilterFormat (query, filterForm) {
console.log('queryToFilter')
let filter = {}
for (var key in filterForm) {
if (key.endsWith('__in')) {
filter[key] = query[key] ? query[key].split(',') : []
} else {
filter[key] = query[key] ? query[key] : null
}
}
filter.limit = query.limit
filter.offset = query.offset
console.log(query)
console.log(filter)
console.log('queryToFilter end')
return filter
}
最后
代码感觉还是不够精简,虽然实现了一定的流程化,但是感觉还有其它改进过程,望指正。。