一.需求分析
前几天公司有一个需求,多选已配置好的区域(包括省、市、区/县),并返回区域的id。这听起来好像很正常,但我的数据是这样的:
1.这是所有区域的json文件格式:
city下的每一项:country_code表示当前城市属于的国家; province_code表示当前城市属于的省份;code为当前城市的code。
country下的每一项:code为当前国家的code。
district下的每一项:country_code表示当前 县/区 属于的国家; province_code表示当前 县/区 属于的省份;city_code为当前 县/区 属于的城市。
province下的每一项:country_code表示当前省份属于的国家。
2.这是后台返回的已配置的区域的数据格式:
包含当前地区的country_code、province_code、city_code、district_code,唯一标识feelec_area_id。
最后需要返回一个feelec_area_id的数组给后端。
二.实现效果
1.级联选择效果图:
2.选择项返回结果:一个feelec_area_id组成的数组
先别急着鼓掌,接下来看看我是怎么实现的。
三.代码实现
要使用看似没有关系的数据实现数据的关联最重要的就是数据结构的重组。经过我不久的思索,思路是这样的:
1.通过el-cascader级联组件所需要的数据可以显然的知道我们需要一个树型结构的数据,用树型结构的数据来供我们选择。
2.我们这里默认是中国下的地区,所以我们最多需要三层数据,也就是数的深度最大为3。第一层为省份的信息,第二层为市的信息,第三层为 区/县 的信息(也有可能没有 区/县,那么就没有第三层)。
3.根据已配置的区域信息来看,我们只要通过属性分组,先把相同省份的分出来作为父级,子级就是省份下的市和区/县,再把子级的市来分组作为父级,那么市的子级就是区/县。
4.但是数据只有当前的区域对应的code,我们还需要省,市,区的名字。只需通过code在地区json文件找到名字再添加进每一层的数据中就行了。
我们期望最后重组的数据长这样:
let data = {
province_code:'省份code',
name:'省份名字',
children:[
{
city_code:'城市code',
name:'城市名字',
children:[
{
district_code:'区/县code',
name:'区/县名字',
feelec_area_id:'此区域id'
}
]
}
]
}
这一部分代码(有点复杂 看不懂的问我):
<template>
<div >
<el-cascader
style="width: 100%"
:options="options"
:props="props"
collapse-tags
placeholder="请选择服务区域"
@change="areaChange"
></el-cascader>
</div>
</template>
<script>
import Vue from "vue";
import publicAxios from "@/assets/api/publicAxios";
export default {
components: { Avatar },
data() {
return {
default_country_code: 1,
props: {
label: "name",
value: "feelec_area_id",
children: "children",
emitPath: false,
multiple: true,
},
options: [],
province: [],
form: {
feelec_areas: [],
},
};
},
computed: {
//所有地区的json数据
regionData() {
return this.$store.state.regionData;
},
//获取省份名称
provinceName() {
return function (country_code, province_code) {
const data = this.regionData.province.find(
(item) =>
item.code == province_code && item.country_code == country_code
);
return data.name;
};
},
//获取城市名称
cityName() {
return function (country_code, province_code, city_code) {
const data = this.regionData.city.find(
(item) =>
item.code == city_code &&
item.country_code == country_code &&
item.province_code == province_code
);
return data.name;
};
},
//获取地区名称
districtName() {
return function (country_code, province_code, city_code, district) {
const data = this.regionData.district.find(
(item) =>
item.code == district &&
item.country_code == country_code &&
item.province_code == province_code &&
item.city_code == city_code
);
return data.name;
};
},
},
created() {
this.dataInit();
},
methods: {
async dataInit() {
await this.$store.dispatch("getRegionJson");
await this.getServiceArea();
this.changeCountry(this.default_country_code);
},
/**
* @description: 数组对象按某一属性分组
* @param {分组的数组} arr
* @param {按哪种属性分组} field
* @param {重组后分组属性的名称} filedName
* @return {分组后的数组}
*/
group_area(arr, field, filedName) {
var obj = {};
for (var i = 0; i < arr.length; i++) {
for (const item in arr[i]) {
if (item == field) {
obj[arr[i][item]] = {
children: obj[arr[i][field]] ? obj[arr[i][field]].children : [],
[filedName]: arr[i][field],
};
}
}
obj[arr[i][field]].children.push(arr[i]);
}
var att = [];
for (const item in obj) {
att.push({
children: obj[item].children,
[filedName]: obj[item][filedName],
});
}
return att;
},
/**
* @description: 请求配置后的区域数据
* @param {*}
* @return {请求结果是一个数组对象}
*/
async getServiceArea() {
return publicAxios("/feelec/user/dispatch/service_area").then((res) => {
if (res.data.code === 1001) {
const data = res.data.data;
//第一次按省份province_code重组这个数组对象
let arr = this.group_area(data, "province_code", "province_code");
for (let item of arr) {
//第二次按省份下面的城市city_code来重组
const arr1 = this.group_area(
item.children,
"city_code",
"city_code"
);
//第一级添加子级children 并添加省份名字
Vue.set(item, "children", arr1);
Vue.set(item, "name", this.provinceName(1, item.province_code));
for (let item1 of arr1) {
//第二级添加城市名字
Vue.set(
item1,
"name",
this.cityName(1, item.province_code, item1.city_code)
);
for (let item2 of item1.children) {
// 第三级 没有区县的地区就没有区县的名字所以还是添加为城市的名字
if (item2.district_code) {
Vue.set(
item2,
"name",
this.districtName(
1,
item2.province_code,
item2.city_code,
item2.district_code
)
);
} else {
Vue.set(
item2,
"name",
this.cityName(1, item2.province_code, item2.city_code)
);
}
}
}
}
this.options = arr;
} else {
this.$message.error(res.data.message);
}
});
},
/**
* @description: 找到对应国家下面的省份
* @param {国家code} code
* @return {*}
*/
changeCountry(code) {
this.province = this.regionData.province.filter((item) => {
return item.country_code == code;
});
for (let item of this.province) {
const children = this.changeProvince(item.code, code);
if (children.length) {
Vue.set(item, "children", children);
}
}
},
/**
* @description: 找到对应省份下面的城市 再找到城市下面的区/县
* @param {省份code} provCode
* @param {国家code} counCode
* @return {数组}
*/
changeProvince(provCode, counCode) {
let city = this.regionData.city.filter((item) => {
return item.province_code == provCode && item.country_code == counCode;
});
const district = this.regionData.district.filter((item) => {
return item.province_code == provCode && item.country_code == counCode;
});
for (let city_item of city) {
const children = district.filter(
(item) => item.city_code == city_item.code
);
if (children.length) {
Vue.set(city_item, "children", children);
}
}
return city;
},
//勾选级联的多选框 返回的就是feelec_area_id的数组
areaChange(val) {
this.form.feelec_areas = val;
},
},
};
</script>
代码就上面这一坨,逻辑也并不复杂。只要你找到了解决问题的思路,所以思路很重要。