本期简介
下拉框这个组件用的地方非常多,普通用法就是将数据列表一次性查询渲染,在列表里面直接本地搜索,优点是可缓存、速度快,但在某些场合并不适用,比如要在下拉框中选择一所中国的学校,幼儿园/小学/初中/高中/大学加起来总共31万条数据,一次性不可能在下拉框渲染,缺点有:会卡顿、查询返回全量数据中大部分是不必要的数据。
所以远程搜索这个功能就比较适用这个场景。
- 本期要点
- Iview Select组件的简单介绍
- 后端编写学校搜索接口
- 前端编写调用搜索接口的相关方法
- Select组件触发远程搜索
- 编辑时对已选择的数据设置默认值(1个和多个)
- 最终实现效果:输入即搜索,下拉框显示了学段和所在省份的辅助信息,本例是限制的单选,官方是多选的案例。
一、Iview Select组件的简单介绍
Iview Select组件官方文档:https://v4.iviewui.com/components/select
1、远程搜索示例效果
2、远程搜索示例源码
<template>
<Row>
<Col span="12">
<Select
v-model="model14"
multiple
filterable
:remote-method="remoteMethod2"
:loading="loading2">
<Option v-for="(option, index) in options2" :value="option.value" :key="index">{{option.label}}</Option>
</Select>
</Col>
</Row>
</template>
<script>
export default {
data () {
return {
model13: '',
loading1: false,
options1: [],
model14: [],
loading2: false,
options2: [],
list: ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New hampshire', 'New jersey', 'New mexico', 'New york', 'North carolina', 'North dakota', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode island', 'South carolina', 'South dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West virginia', 'Wisconsin', 'Wyoming']
}
},
methods: {
remoteMethod2 (query) {
if (query !== '') {
this.loading2 = true;
setTimeout(() => {
this.loading2 = false;
const list = this.list.map(item => {
return {
value: item,
label: item
};
});
this.options2 = list.filter(item => item.label.toLowerCase().indexOf(query.toLowerCase()) > -1);
}, 200);
} else {
this.options2 = [];
}
}
}
}
</script>
3、Select官方详解
二、后端编写学校搜索接口
1、后端搜索接口
接口说明:参数stage表示学段,取值的含义表示幼儿园、小学、初中、高中、大学,参数keyword表示要搜索的关键字
@GetMapping("/api/v1/school/search")
public Response<List<SchoolVo>> searchSchools(@RequestParam("stage") StageEnum stage, @RequestParam("keyword") String keyword) {
return this.success(schoolService.querySchools(stage, keyword));
}
2、业务搜索实现
学校信息字段非常多,有30多个,全部都返回是不好的实现,所以这里定义了SchoolVo只返回必要的字段给前端,至于SchoolPo属性虽然很多,但从SQL层实际上查询的也只有需要的字段,其他字段不必返回。
- SchoolVo
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class SchoolVo {
@JsonFormat(shape = JsonFormat.Shape.STRING)
private Long id;
private String name;
private String provinceName;
private String stageName;
}
- 业务搜索方法
@Override
public List<SchoolVo> querySchools(StageEnum stage, String keyword) {
List<SchoolPo> simplePos = schoolMapper.selectSimpleSchools(stage, keyword);
return JsonUtils.copy(simplePos, SchoolVo.class);
}
3、Mybatis Mapper层的实现
@Mapper
public interface SchoolMapper extends BaseMapper<SchoolPo> {
List<SchoolPo> selectSimpleSchools(@Param("stage") StageEnum stage, @Param("keyword") String keyword);
}
4、Mbatis Mapper SQL编写
这里只返回了学校id、学校名称、所在省份名称、学段,匹配使用的模糊搜索,name如果有索引,这里索引会失效,但为了考虑查询性能,这里对整个SQL做了如下优化:
(1)按需查询,网络传输数据量小
(2)使用limit 100取巧,因为该SQL是用来搜索的,如果将搜索到的全部返回,会造成查询慢、传输量大、浪费流量,即便限制500条,渲染到列表也是多余的,这种情况一般是用户搜索的时候,只输入了很少的信息,搜索出来的数据就很多,如果搜索更具体一点的学校名称,查询返回的数据量就会很少了,实际使用中,搜索接口的性能还是可以接受的,毕竟是模糊搜索
- SQL
<select id="selectSimpleSchools" resultMap="BaseResultMap">
select
id,
name,
province_name,
stage_name
from common_basic_school
<where>
<if test="stage!=null">
stage=#{stage}
</if>
<if test="keyword!=null and keyword!=''">
and name like concat('%',#{keyword},'%')
</if>
</where>
order by name asc
limit 100
</select>
三、前端编写调用搜索接口的相关方法
1、在src/api/apis.js中增加搜索的接口请求
school.searchSchool = (params) => {
return http.get(
'/api/v1/school/search',
params
);
};
export default {school};
2、开发搜索
<FormItem label="学校" prop="schoolId">
<div>
<Spin size="large" fix v-if="schoolLoading">
<Icon type="ios-loading" size=24 class="demo-spin-icon-load"></Icon>
</Spin>
<Select
ref="school"
v-model="formData.schoolId"
filterable
remote
size="large"
style="width: 600px"
prefix="ios-home"
multiple
:default-label="selectedSchool"
:filter-by-label="true"
:label-in-value="true"
:remote-method="searchSchool"
:loading="schoolLoading"
:clearable="true"
placeholder="输入关键字搜索学校"
@on-change="selectChange">
<Option v-for="item in schools" :value="item.id" :key="item.id" :label="item.name">
<Row>
<Col span="16">
{{ item.name }}
</Col>
<Col span="3">
<span style="float:right;color:#ccc">{{ item.stageName }}</span>
</Col>
<Col span="3">
<span style="float:right;color:#ccc">{{ item.provinceName }}</span>
</Col>
</Row>
</Option>
</Select>
</div>
</FormItem>
代码说明:
Spin组件
:用来在搜索的时候显示加载中的状态
v-model="formData.schoolId"
: 组件与formData.schoolId进行双向绑定
filterable
: 支持关键字过滤
remote
: 支持远程搜索
multiple
: 支持多选
selectedSchool
:当前选中项的中文名称
filter-by-label
:通过名称进行搜索过滤
remote-method
:远程搜索调用的方法,参数是我们输入的内容,而不是双向绑定的schoolId
selectChange
:选中某项时触发该方法,用于设置单选或多选
四、Select组件触发远程搜索
搜索时query是输入的内容,使用setTimeout让搜索间隔500毫秒发起
searchSchool (query) {
console.log('search', query);
if (query) {
this.schoolLoading = true;
setTimeout(() => {
apis.school.searchSchool({'stage': this.selectStage, 'keyword': query}).then(res => {
this.schools = res.body;
}).finally(() => {
this.schoolLoading = false;
});
}, 500);
} else {
this.schools = [];
}
},
五、编辑时对已选择的数据设置默认值(1个和多个)
一般的下拉框设置默认值比较简单,和v-model双向绑定即可默认选中某项,但搜索框默认进来时空的,需要输入内容才会有数据返回,列表才会有数据,所以编辑的时候设置默认值的原理就是对select设置搜索内容》select触发远程搜索》搜索结果默认选中匹配的一项
说明:对this.$refs.school.query设置内容即可触发远程搜索
apis.album.getAlbumModDetail({albumId: albumId}).then(res => {
this.formData = res.body;
// 设置修改的数据
this.formData.schoolId = this.formData.school ? this.formData.school.id : '';
this.$refs.school.query = this.formData.school ? this.formData.school.name : '';
console.log('query=', this.$refs.school.query);
console.log('name=', this.formData.school.name);
});
远程搜索select组件设置默认值过程效果图