最近在使用vue+iview做一个后台管理系统,在做表格部分时发现iview的表格并不支持搜索,不过iview易于拓展,可以很方便的拓展出表格搜索功能,比如在iview-admin就存在可搜索表格,但是功能比较基础,而且没有封装为单独组件,接下来我们就一步步的实现一个支持下拉选择指定列进行搜索的表格搜索组件。完整代码:https://github.com/shawpo/vue-iview-tableSearch
首先我们可以来看下iview-admin表格搜索的实现,以示例中按姓名搜索为例,其实现实现了按姓名搜索。
// 关键代码
<template>
......
<Input
v-model="searchConName1"
icon="search"
@on-change="handleSearch1"
placeholder="请输入姓名搜索..."
style="width: 200px"
/>
......
</template>
<script>
......
export default {
......
methods: {
......
search (data, argumentObj) {
let res = data;
let dataClone = data;
for (let argu in argumentObj) {
if (argumentObj[argu].length > 0) {
res = dataClone.filter(d => {
return d[argu].indexOf(argumentObj[argu]) > -1;
});
dataClone = res;
}
}
return res;
},
handleSearch1 () {
this.data1 = this.initTable1;
this.data1 = this.search(this.data1, {name: this.searchConName1});
},
......
},
......
};
</script>
- 首先简单说明下这个示例的实现流程:将input输出框的on-change事件与组件的handleSearch1方法绑定,在输入框内容发生变化时运行handleSearch1 方法。而handleSearch1方法则将表格的初始数据数据(this.data1)与搜索条件对象(将搜索列与搜索内容拼装成对象形式后)传递给search方法执行搜索,并将表格数据设置search方法返回搜索结果。
- 接下来是代码的说明,从handleSearch1方法开始,其首先将原始的表格数据赋值给data1,以保障每次搜索都是从原始表格数据中搜索,将搜索列与搜索内容拼装成对象形式作为搜索条件,将然后将data1以及搜索条件对象传递给search方法,这里的'name'为表格数据中对应列内容的字段名。接下来就是重要的search方法了,可以看出这个方法是支持多条件(多列)搜索的,其首先对搜索条件对象进行了遍历,当对象的一个属性的值不没空时(则说明这个属性可以作为搜索的一个条件),通过函数filter对dataClone进行过滤,并将过滤结果赋值给dataClone以用于下一个循环,循环完毕后,所有的搜索条件都执行完毕,dataClone即为搜索结果,将其返回即可。
接下来我们就对这个简单的搜索进行拓展以支持下拉选择指定列进行搜索并封装为单独组件,想要支持这一功能,我们首先需要列名以及表格数据中对应列内容的字段名等信息,而创建iview表格的所需的列配置参数(columns)中就包含这些信息,因此可以很方便的开发出适用于iview表格的搜索组件。
- 首先是UI部分
这非常简单,我们可以使用iview提供的复合型输入框轻松实现
<Input
placeholder="请选择列名并输入内容..."
v-model="searchContent"
@on-change="handleSearch">
<Select slot="prepend" style="width: 80px" v-model="searchColumn">
<Option
v-for="column in searchColumns"
:value="column.key"
:key="column.key">
{{column.title}}
</Option>
</Select>
</Input>
- 完成了UI部分之后就需要为UI提供数据完成数据绑定,首先是为下拉选择器提供数据
同样非常简单,父组件使用时将iview表格的columns参数对应的对象数组(父组件可对数组进行处理,过滤不需要支持搜索的列)传递过来即可。
// 关键代码
......
<table-search
......
:searchColumns="searchColumns"
>
</table-search>
......
computed: {
searchColumns: function () {
// 过滤不需要支持搜索的列,这里过滤掉了“状态”列
return this.menusColumns.filter( (d) => {
return d['key'] && d['key'] != 'status'
})
}
},
......
- 表格数据
要实现搜索,当然需要表格数据,与上面iview-admin的示例不同的是,我们将表单搜索封装成了单独组件,因此我们需要从父组件获取表格数据,并在完成搜索之后,将父组件的表格数据更新为搜索结果的数据。我们可以很容易的想到:使用.sync修饰符从父组件中将表格数据传递过来,像上面一样克隆数据,执行搜索,然后通过触发事件将父组件的表格数据更新为搜索结果的数据即可。没错,确实是这样的,整个流程很简单,但是这里存在一个问题,父组件的表格数据通常是从服务器中获取的,存在异步操作。我们需要当服务器端完成异步操作之后再对其进行克隆,否则若克隆的表格数据为空,无论如何搜索得到值都为空。
// 父组件
<table-search
:tableData.sync="menusData"
:searchColumns="searchColumns"
>
</table-search>
watch: {
// 因父组件表格数据通常存在异步操作
// 需监听props以得到正确的数据
tableData: function (newTableData) {
if (newTableData.length > 0 && this.tableDataClone.length == 0) {
this.tableDataClone = newTableData
}
}
}
- 执行搜索
这一过程与iview-admin的示例非常相似,不同的是要执行搜索的列来自于用户在下拉选择中的输入,因此我们需要获取用户输入并将其处理为搜索条件对象传递给search方法执行搜索,而搜索完成后我们需要更新父组件表格数据。
methods: {
// 表格搜索函数,可支持多列搜索
search: function (data, argumentObj) {
let res = data;
let dataClone = data;
for (let argu in argumentObj) {
if (argumentObj[argu].length > 0) {
res = dataClone.filter(d => {
return d[argu].indexOf(argumentObj[argu]) > -1;
});
dataClone = res;
}
}
return res;
},
handleSearch: function () {
var argumentObjStr = '{"' + this.searchColumn + '": "' + this.searchContent + '"}' // 拼接json
var argumentObj = JSON.parse(argumentObjStr) // 转为对象
var res = this.search(this.tableDataClone, argumentObj) // 执行搜索,获取搜索结果
this.$emit('update:tableData', res) // 更新表格数据为搜索结果
}
},
结束