🎯导读:本文介绍了一种基于Java的技术方案,用于构建中国省市区的多级树形结构数据。首先定义了一个VO类
AreaItemVo以封装地区信息。服务层通过查询数据库并利用Stream API筛选和构建树形结构,实现了getAreaTree()方法。此外,还提供了一个辅助方法searchSon()用于递归地查找子节点。控制器层则负责响应前端请求并返回树形数据。前端采用Element UI的级联选择器展示数据,并进行了优化处理,确保无子节点时的显示效果更佳。 🏠️ 项目仓库:智能排班系统 📙 项目介绍:【智能排班系统】开源说明
省市区数据
数据形式为sql文件,数据表字段设计如下,请点击数据下载
后端
vo
由于数据需要在前端显示,为了方便使用前端组件,特意封装此vo类
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.List;
/**
* 存储地址项 如广东省 茂名市 电白区
*/
@Data
@AllArgsConstructor
public class AreaItemVo {
/**
* 存储id
*/
private Long value;
private String label;
private List<AreaItemVo> children;
public AreaItemVo(Long value, String label) {
this.value = value;
this.label = label;
}
}
service
这段Java代码是用于构建一个表示地区层级结构(例如省份、城市、区县)的树形结构。以下是代码的详细解析:
方法 getAreaTree()
-
初始化结果列表:
- 创建一个空的
ArrayList类型的areaItemVoList,用于存储最终构建好的树形结构。
- 创建一个空的
-
查询数据库:
- 调用
baseMapper.selectList(null)方法从数据库中获取所有的省份、城市、区县等地理区域数据,并将其存储在provinceCityRegionEntityList列表中。
- 调用
-
筛选省份:
- 使用 Java 8 的 Stream API 对
provinceCityRegionEntityList进行过滤,找出所有父ID为0的实体(即省份),并将它们收集到一个新的列表fatherList中。
- 使用 Java 8 的 Stream API 对
-
构建树形结构:
- 遍历
fatherList(省份列表)中的每个省份实体father:- 创建一个新的
AreaItemVo实例,其中包含省份的ID和名称。 - 调用
searchSon()方法递归地构建该省份下的子节点(即城市和区县)。 - 将构建好的省份节点添加到
areaItemVoList。
- 创建一个新的
- 遍历
-
返回结果:
- 返回填充完成的
areaItemVoList,即整个地区的树形结构。
- 返回填充完成的
方法 searchSon()
-
查找子节点:
- 使用 Stream API 对
provinceCityRegionEntityList进行过滤,找出所有父ID与传入的AreaItemVo的值(即省份ID/市ID)相匹配的实体。
- 使用 Stream API 对
-
构建子节点:
- 对于每个找到的子实体
item1:- 创建一个新的
AreaItemVo实例,其中包含子实体的ID和名称。 - 再次调用
searchSon()方法递归地构建子节点的子节点(即更下一层的城市或区县)。 - 收集所有子节点到
sonList。
- 创建一个新的
- 对于每个找到的子实体
-
设置子节点:
- 将收集到的所有子节点列表
sonList设置为传入的AreaItemVo的children属性。
- 将收集到的所有子节点列表
通过这种方式,代码构建了一个完整的地区树形结构,其中每个节点都包含了其子节点的信息,形成了一个多级嵌套的层次结构。
@Override
public List<AreaItemVo> getAreaTree() {
////声明变量
List<AreaItemVo> areaItemVoList = new ArrayList<>();
//查询出所有省市区数据
List<ProvinceCityRegionEntity> provinceCityRegionEntityList = baseMapper.selectList(null);
//过滤出所有省
List<ProvinceCityRegionEntity> fatherList = provinceCityRegionEntityList.stream().filter(item -> {
if (item.getParentId() == 0) {
return true;
} else {
return false;
}
}).collect(Collectors.toList());
for (ProvinceCityRegionEntity father : fatherList) {
AreaItemVo areaItemVo = new AreaItemVo(father.getId(), father.getName());
this.searchSon(areaItemVo, provinceCityRegionEntityList);
areaItemVoList.add(areaItemVo);
}
return areaItemVoList;
}
private void searchSon(AreaItemVo father, List<ProvinceCityRegionEntity> provinceCityRegionEntityList) {
List<AreaItemVo> sonList = provinceCityRegionEntityList.stream().filter(item -> {
if (item.getParentId() == father.getValue().intValue()) {
return true;
} else {
return false;
}
}).map(item1 -> {
AreaItemVo son = new AreaItemVo(item1.getId(), item1.getName());
//继续给儿子寻找孙子
this.searchSon(son, provinceCityRegionEntityList);
return son;
}).collect(Collectors.toList());
father.setChildren(sonList);
}
controller
/**
* 获取省市区的树形结构数据
* @return
*/
@GetMapping("/getAreaTree")
public R getAreaTree() {
List<AreaItemVo> areaItemVoList = provinceCityRegionService.getAreaTree();
return R.ok().addData("areaItemVoList", areaItemVoList);
}
前端
使用Element UI的级联选择器来显示结果非常之贴切
<el-form-item label="地区">
<el-cascader v-model="province_city_area" :options="areaItemVoOptions" clearable></el-cascader>
</el-form-item>
发请求向后端获取数据
//获取省市区的树形结构数据
getAreaTree() {
provinceCityRegionApi.getAreaTree().then(
res => {
this.areaItemVoOptions = res.areaItemVoList
}
)
},
当然,直接接收后端的数据还是不行的,会出现如下问题:虽然区的children没有内容,但是还是会显示No data,不太美观
修正版,如果children没有元素,那就直接设置undefined即可
//获取省市区的树形结构数据
getAreaTree() {
provinceCityRegionApi.getAreaTree().then(
res => {
this.areaItemVoOptions = res.areaItemVoList
for (let i = 0; i < this.areaItemVoOptions.length; i++) {
this.setNullToUndefined(this.areaItemVoOptions[i])
}
}
)
},
//将没有子元素的父类的chidren设置为 undefined
setNullToUndefined(areaItemVo) {
if (areaItemVo.children.length < 1) {
areaItemVo.children = undefined;
} else {
for (let i = 0; i < areaItemVo.children.length; i++) {
this.setNullToUndefined(areaItemVo.children[i])
}
}
}