先上效果图:
这个功能实现起来其实不是很难,主要有几处细节需要注意
1,vue中js模块的导出
2,linq的使用
3,一级分组和二级分组的业务逻辑处理
主要实现步骤如下:
一:准备linq
1,安装linq。
npm install linq
2,在main.js中引用
import linq from 'linq';
Vue.prototype.Enumerable=linq;
3,对linq的理解。个人觉得跟Java8中的stream操作较像,具体使用可以百度到
二:template页面
<el-form-item v-if="editDailogFormType=='region'" label="需求省份" prop="channelName">
<el-select v-model="regionConfigForm.citys" placeholder="请选择区域"
multiple
filterable
remote
reserve-keyword
:remote-method="(queryString)=>{remoteMethod(queryString);}"
style="width: 100%;">
<el-option-group
v-for="group in multipleSelectOption"
:key="group.province"
:label="group.province"
@click.native="checkProvince(group.province)">
<el-option
v-for="item in group.city"
:key="item.value"
:label="item.city_name"
:value="item.city_name">
</el-option>
</el-option-group>
</el-select>
</el-form-item>
画蛇添足地解释一下el-select下拉框的这些个属性
multipe:开启多选
filterable:开启搜索
remote:开启远程搜索
reserve-keyword:关键词
:remote-method:远程搜索触发的方法。
el-select下的el-option-group是分组名(如省份),el-option是分组下面的具体选项(如城市)。
@click.native是点击分组名时触发的方法。
三:
1,json数据的准备,我的json格式是[{province:xxx,city:[{city_name:xxx}]}]这种格式的。
由于省市是静态资源,我可以做成json文件,如果是动态资源,可以请求后台接口,从后台中获取数据。
2,拼音相关的js。
常用的js方法都写在下面了,细心的话可能已经发现,下方代码中的pinyin这个全局变量只定义了一个拼音。这个pinyin变量是个json格式的字符串,实在是太长了,太占篇幅,影响阅读。
包含了汉字转拼音,提取首拼,汉字、全拼、首拼的比较和包含方法。
具体操作:创建一个名为pinYinForVue的js文件。把以下内容copy进去
export const pinyin = {
'a': '\u554a\u963f\u9515',
}
export default {
arraySearch: function (l1, l2) {
for (let name in pinyin) {
if (pinyin[name].indexOf(l1) !== -1) {
return this.ucfirst(name)
}
}
return false
},
ucfirst: function (l1) {
if (l1.length > 0) {
let first = l1.substr(0, 1).toUpperCase();
let spare = l1.substr(1, l1.length);
return first + spare
}
},
/*获取首字母*/
getInitials: function (text) {
let pinyin="";
for (let i = 0; i < text.length; i++) {
pinyin=pinyin+this.convertToPinYin(text[i])[0];
}
return pinyin; //若取拼音,则返回 pinyin
},
/*判断text中是否包含key,通过汉字、拼音、首拼判断*/
wordInclude:function (text,key) {
if(this.convertToPinYin(text).toLowerCase().indexOf(key) != -1||//全拼是否包含
this.convertToPinYin(text).indexOf(key) != -1 ||//全拼转小写是否包含
this.getInitials(text).toLowerCase().indexOf(key) != -1||//首拼是否包含
this.getInitials(text).indexOf(key) != -1 ||//首拼转小写是否包含
text.indexOf(key) != -1 //汉字是否包含
){
return true;
}else{
return false;
}
},
/*这个转拼音的方法可以单独拿出去用(更灵活),也可以在下方的wordInclude中被调用*/
convertToPinYin: function (l1) {
let l2 = l1.length;
let I1 = '';
let reg = new RegExp('[a-zA-Z0-9]');
for (let i = 0; i < l2; i++) {
let val = l1.substr(i, 1);
let name = this.arraySearch(val, pinyin);
if (reg.test(val)) {
I1 += val
} else if (name !== false) {
I1 += name
}
}
I1 = I1.replace(/ /g, '-');
while (I1.indexOf('--') > 0) {
I1 = I1.replace('--', '-')
}
return I1
},
}
3,调用
这个注释是我写的最详细的一次
import pinyin from '../../utils/pinYinForVue'
import cityjson from '../../assets/city'
export default {
data(){
return {
regionConfigForm:{
citys:[]
}
multipleSelectOption:cityjson,
}
},
methods: {
remoteMethod(queryString) {
/*json中的一级分组是省份,二级分组是城市*/
this.multipleSelectOption=cityjson;
/*去掉重复key,linq中的distinct,where,select 类似Java8的stream流操作*/
this.multipleSelectOption=this.Enumerable.from(this.multipleSelectOption).distinct("o=>o.province").toArray();
/*遍历所有省份数据,根据输入框中的值对所有的一级分组过滤,保留符合条件的数据再重新组装成list*/
let parentSearchList=this.Enumerable.from(this.multipleSelectOption).where((data)=>{
let jsonHasQueryString=false;
/*如果省份的汉字、全拼、首拼是否包含输入的值*/
if(pinyin.wordInclude(data.province,queryString)){
jsonHasQueryString=true;
}
return jsonHasQueryString;
}).toArray();
let childSearchList=[];
/*遍历所有城市数据,根据json数据结构先遍历省份,再遍历省份下城市。*/
for(let i=0;i<this.multipleSelectOption.length;i++){
let parentItem=this.multipleSelectOption[i];
for(let j=0;j<parentItem.city.length;j++){
let childName=parentItem.city[j].city_name;
/*如果城市的汉字、全拼、首拼是否包含输入的值*/
if(pinyin.wordInclude(childName,queryString)){
childSearchList.push({province:parentItem.province,city:[{city_name:childName}]});
}
}
}
/*合并一级分组和二级分组的值,比如 输入 “hb”,
会出现一级分组“河北”及下属的多个二级城市,也会出现 单独的二级分组“淮北”以及所属的一级省份“安徽”*/
let nowmultipleSelectOption =parentSearchList.concat(childSearchList);
/*去掉key相同的重复数据,key相同虽然不影响使用,但控制台会报红*/
this.multipleSelectOption = this.Enumerable.from(nowmultipleSelectOption).distinct("o=>o.province").toArray();
},
/*一级分组名点击全选*/
checkProvince(province){
/*定义一个临时数组*/
let addProvinceCitys= [];
/*这个for循环是为了实现点击一级分组全部勾选的功能*/
for(let i=0;i<cityjson.length;i++){
/*遍历所有省份*/
let parentItem=cityjson[i];
if(parentItem.province==province){
/*若省份能跟此次点击的省份匹配,遍历城市*/
for(let j=0;j<parentItem.city.length;j++){
/*获取已选中元素中是否包含 此次省份下的城市,如果不包含则添加到临时数组中*/
let childName=parentItem.city[j].city_name;
let index=this.regionConfigForm.citys.indexOf(childName);
if(index==-1){
addProvinceCitys.push(childName);
}
}
}
}
/*其实还想做一个反选功能的,但思路暂时还没捋顺,以后再补充*/
/*将临时数组中合并到下拉框的已选择数组中,即可完成赋值选中*/
this.regionConfigForm.citys = this.regionConfigForm.citys.concat(addProvinceCitys);
},
}
}
over。