vue利用级联选择器实现全国省市区乡村五级菜单联动
Posted 穆雄雄
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue利用级联选择器实现全国省市区乡村五级菜单联动相关的知识,希望对你有一定的参考价值。
“大家好,我是雄雄,欢迎关注微信公众号:雄雄的小课堂。
”
现在是:2022年2月13日20:09:27
今天分享一个五级级联地址的组件的使用吧。
前言
接到这样的一个需求:需要根据地址查询列表信息,地址可以分别按照省、市、县、乡、村 五级作为条件查询。
如果但是这个需求还好说,以前使用的方式是放五个下拉列表,通过懒加载的方式,根据省得到该省下的市,根据市得到该市下的县……最后可以得到村的级别,然后作为条件查询,也就是下面的这种方式:
但是,客户说,太长了……因为他的另一个需求是这样的:
如果放5个的话,就会造成装不下的bug。
实现思路
原来采用的是select
,现在看来,select
显然不是最好的解决方式,去element-ui上面看了看,发现了另一个组件:Cascader
级联选择器,样式如下:
于是就把这个Cascader
组件改了改,改成了省市区乡村的组件,同时也做成了公共组件,项目中可以多次共用。
数据表结构
全国省市区乡村没有使用json
的方式,将所有的省市区乡村信息都放在了数据库中,用的时候根据父级编号查询,数据库表结构如下:
级联实现地址联动
实现思路:页面初次加载的时候,将全国所有省查询出来,封装放在级联组件Cascader
中,当用户选择某个省的时候,将当前省的编码作为父级编码,去查询所有的市。比如选的山东省,查询出来的就是山东所有市,济南,聊城,菏泽等。
为了解决系统性能问题,我将查询出来的信息都放在了redis
缓存中了,缓存里面没有的话去数据库里面找去,有则在缓存中取,效率要高点儿。
下面是公共组件中的代码:
<!--
作者:mxx
时间:2022年2月11日16:22:07
描述:el-Cascader实现省市区乡村菜单
-->
<template>
<div>
<div class="block">
<el-cascader
v-model="selectedOptions"
:show-all-levels="false"
ref="sysCascader"
:size="size"
:placeholder="placeholder"
:props="props"
@change="handleChange"
clearable>
</el-cascader>
</div>
<!-- 鼠标悬浮触发 -->
<!-- <div class="block">
<span class="demonstration">hover 触发子菜单</span>
<el-cascader
expand-trigger="hover"
:options="options"
v-model="selectedOptions2"
@change="handleChange">
</el-cascader>
</div>-->
</div>
</template>
<script>
import listArea from "../../../api/system/area";
export default
name: "addressCommonComponentsUtils",
props:
//组件大小
size:
type: String,
default: "mini"
,
//组件的提示信息
placeholder:
type: String,
default: "请选择地区"
,
data()
let self = this;
return
/*省市区的集合*/
options: [],
props:
lazy: true,
checkStrictly: true,
lazyLoad: this.getLazyLoad,
,
selectedOptions: [],
// 地址查询参数
qParams:
pid: null,
type: null,
,
//省市区的值
addressInfo:
//编码
//省
sonProvinceValue: null,
//市
sonCityValue: null,
//区
sonAreaValue: null,
//街道
sonStreetValue: null,
//乡村
sonCountryValue: null,
//名字
//省
sonProvinceName: null,
//市
sonCityName: null,
//区
sonAreaName: null,
//街道
sonStreetName: null,
//乡村
sonCountryName: null,
,
;
,
created()
,
methods:
//懒加载省份信息到组件中
getLazyLoad(node, resolve)
setTimeout(() =>
if (node.level == 0)
this.qParams.type = 1;
listArea(this.qParams).then(res =>
const cities = res.rows.map((value, i) => (
value: value.areaId,
label: value.name,
leaf: node.level >= 2,
));
// 通过调用resolve将子节点数据返回,通知组件数据加载完成
resolve(cities);
)
.catch(err =>
console.log(err);
);
if (node.level != 0 && node.level < 5)
this.qParams.type = node.level + 1;
this.qParams.pid = node.value;
listArea(this.qParams).then(res =>
const areas = res.rows.map((value, i) => (
value: value.areaId,
label: value.name,
leaf: node.level >= 4
));
//设置省市区街村的值
this.setNameAndValue(node);
// 通过调用resolve将子节点数据返回,通知组件数据加载完成
resolve(areas);
)
.catch(err =>
console.log(err);
);
else if (node.level == 5)
//最后一个节点,不去查询了,直接赋值
const areas =
value: node.value,
label: node.label,
leaf: node.level
;
//将值和名称赋值到变量里面
this.addressInfo.sonCountryValue = node.value;
this.addressInfo.sonCountryName = node.label;
resolve(areas);
, 10);
,
//通过当前选择的地区,设置到对应的变量中
setNameAndValue(node)
switch (node.level)
case 1:
this.addressInfo.sonProvinceValue = node.value;
this.addressInfo.sonProvinceName = node.label;
this.addressInfo.sonCityValue = null;
this.addressInfo.sonCityName = null;
this.addressInfo.sonAreaValue = null;
this.addressInfo.sonAreaName = null;
this.addressInfo.sonStreetValue = null;
this.addressInfo.sonStreetName = null;
this.addressInfo.sonCountryValue = null;
this.addressInfo.sonCountryName = null;
break;
case 2:
this.addressInfo.sonCityValue = node.value;
this.addressInfo.sonCityName = node.label;
this.addressInfo.sonAreaValue = null;
this.addressInfo.sonAreaName = null;
this.addressInfo.sonStreetValue = null;
this.addressInfo.sonStreetName = null;
this.addressInfo.sonCountryValue = null;
this.addressInfo.sonCountryName = null;
break;
case 3:
this.addressInfo.sonAreaValue = node.value;
this.addressInfo.sonAreaName = node.label;
this.addressInfo.sonStreetValue = null;
this.addressInfo.sonStreetName = null;
this.addressInfo.sonCountryValue = null;
this.addressInfo.sonCountryName = null;
break;
case 4:
this.addressInfo.sonStreetValue = node.value;
this.addressInfo.sonStreetName = node.label;
this.addressInfo.sonCountryValue = null;
this.addressInfo.sonCountryName = null;
break;
case 5:
this.addressInfo.sonCountryValue = node.value;
this.addressInfo.sonCountryName = node.label;
break;
,
/*改变事件*/
handleChange(event)
let node = this.$refs.sysCascader.getCheckedNodes()[0];
//解决不点字 只点击单选按钮的问题
this.setNameAndValue(node);
//将该组件中的值传递给父组件
//getCheckedAddressInfo: 是父组件指定的传数据绑定的函数,
this.$emit('getCheckedAddressInfo', this.addressInfo)
,
</script>
<style scoped>
</style>
关键代码都有相应的注释,应该看起来都没有问题。
或许有些地方的代码冗余,但是我没法再优化了……这个组件大致的功能就是将用户选择的地址信息,传递到父组件中,父组件通过getCheckedAddressInfo
函数拿到用户选择的地址信息,不管选择哪一级都可以,最后在根据地址信息做其他操作。
listArea
方法:根据条件查询地址信息,传递的参数是地址实体,在本文的最后我附上后台的实现代码。
级联地址组件的使用
使用方法也很简单,只需这么几步:
1.先导入组件:
import addressCommonComponentsUtils from "../area/addressCommonComponentsUtils";
2.注册组件:
在export default
里面写如下代码:
components:
"address-utils": addressCommonComponentsUtils,
,
3.在template
中引入级联地址组件:
<address-utils
:size="mini"
:placeholder="checkdiqu"
@getCheckedAddressInfo="getCheckedAddressInfo">
</address-utils>
4.在methods
中写getCheckedAddressInfo
方法回去选择的地址信息:
//获取用户选择的地址信息
getCheckedAddressInfo(value)
//给地址栏中赋值
this.queryParams.fieldProvince = value.sonProvinceName;
this.queryParams.fieldCity = value.sonCityName;
this.queryParams.fieldArea = value.sonAreaName;
this.queryParams.fieldStreet = value.sonStreetName;
this.queryParams.fieldCommunity = value.sonCountryName;
,
参数说明:
编码:比如北京是110000,山西是:140000等
//省 `sonProvinceValue,
//市 `sonCityValue,
//区 `sonAreaValue,
//街道 `sonStreetValue,
//乡村 `sonCountryValue,
名字:比如北京是**【北京】,山西是【山西省】**
//省
sonProvinceName
,//市 `sonCityName,
//区 `sonAreaName,
//街道 `sonStreetName,
//乡村
sonCountryName
,
最后实现的效果如下:
附:后台查询地址的代码
area.js:
// 查询地址列表
export function listArea(query)
return request(
url: '/system/area/list',
method: 'get',
params: query
)
SysAreaController中的代码:
@Autowired
private ISysAreaService sysAreaService;
/**
* 查询地址列表
*/
//@PreAuthorize("@ss.hasPermi('system:area:list')")
@GetMapping("/list")
public TableDataInfo list(SysArea sysArea)
// startPage();
List<SysArea> list = sysAreaService.selectSysAreaList(sysArea);
return getDataTable(list);
SysAreaServiceImpl中的代码:
@Autowired
private SysAreaMapper sysAreaMapper;
@Autowired
private RedisCache redisCache;
/**
* 查询地址
*
* @param id 地址主键
* @return 地址
*/
@Override
public SysArea selectSysAreaById(Long id)
return sysAreaMapper.selectSysAreaById(id);
/**
* 查询地址列表
*
* @param sysArea 地址
* @return 地址
*/
@Override
public List<SysArea> selectSysAreaList(SysArea sysArea)
//分批次判断地址
List<SysArea> areaList = new ArrayList<>();
//地址对象
SysArea area = new SysArea();
if(sysArea.getPid()==null)
sysArea.setPid("100000");
//市、区县、街道、村
if (redisCache.getCacheList(sysArea.getPid() + "ListCache").size()==0)
System.out.println("=========================走数据库了=========================");
area.setType(sysArea.getType());
//将父级编号放进来
area.setPid(sysArea.getPid());
areaList = sysAreaMapper.selectSysAreaList(area);
redisCache.setCacheList(sysArea.getPid() + "ListCache", areaList);
else
System.out.println("=========================走缓存了=========================");
//从缓存中取出来
areaList = redisCache.getCacheList(sysArea.getPid() + "ListCache");
return areaList;
Mapper
层和Service
的接口层就不放了,就正常写,返回集合就行:
/**
* 查询地址列表
*
* @param sysArea 地址
* @return 地址集合
*/
public List<SysArea> selectSysAreaList(SysArea sysArea);
最后是mapper.xml中的sql:
<sql id="selectSysAreaVo">
select id, name, area_id, pid, type, status, del_flag, create_time, update_time, pids, m_pid, m_pids, m_type, region, remark from sys_area
</sql>
<select id="selectSysAreaList" parameterType="SysArea" resultMap="SysAreaResult">
<include refid="selectSysAreaVo"/>
<where>
<if test="name != null and name != ''"> and name like concat('%', #name, '%')</if>
<if test="areaId != null and areaId != ''"> and area_id = #areaId</if>
<if test="pid != null and pid != ''"> and pid = #pid</if>
<if test="type != null "> and type = #type</if>
<if test="status != null "> and status = #status</if>
<if test="pids != null and pids != ''"> and pids = #pids</if>
<if test="mPid != null and mPid != ''"> and m_pid = #mPid</if>
<if test="mPids != null and mPids != ''"> and m_pids = #mPids</if>
<if test="mType != null "> and m_type = #mType</if>
<if test="region != null and region != ''"> and region = #region</if>
</where>
</select>
总结
好了,今天分享的就这些了,源码也都贴上来啦,记录记录,一来或许可以帮助大家,二来自己以后用的时候,也可以直接上来扒代码了。
下一篇:springboot
解析word
文档,包括复杂的图片,复选框等。
问:你知道CV工程师是啥吗?
以上是关于vue利用级联选择器实现全国省市区乡村五级菜单联动的主要内容,如果未能解决你的问题,请参考以下文章
Element-ui(Cascader 级联选择器)实现三级联动
vue中使用elementUI 的级联选择器el-cascader实现动态加载
在EXCEL中的一个表格里插入一个全国省市区的三级联动下拉列表,如何做?谢谢
Vue+ElementUI Cascader级联选择器 省市区编辑回显(通过CodeToText,TextToCode辅助)