“”
现在是:2022年2月13日20:09:27
今天分享一个五级级联地址的组件的使用吧。
前言
接到这样的一个需求:需要根据地址查询列表信息,地址可以分别按照省、市、县、乡、村 五级作为条件查询。
如果但是这个需求还好说,以前使用的方式是放五个下拉列表,通过懒加载的方式,根据省得到该省下的市,根据市得到该市下的县……最后可以得到村的级别,然后作为条件查询,也就是下面的这种方式:
image-20220213201630629 image-20220213201641535
但是,客户说,太长了……因为他的另一个需求是这样的:
image-20220213203322417
如果放5个的话,就会造成装不下的bug。
image-20220213203822894
实现思路
原来采用的是select
,现在看来,select
显然不是最好的解决方式,去element-ui上面看了看,发现了另一个组件:Cascader
级联选择器,样式如下:
image-20220213204120997
于是就把这个Cascader
组件改了改,改成了省市区乡村的组件,同时也做成了公共组件,项目中可以多次共用。
数据表结构
全国省市区乡村没有使用json
的方式,将所有的省市区乡村信息都放在了数据库中,用的时候根据父级编号查询,数据库表结构如下:
image-20220213204531326
级联实现地址联动
实现思路:页面初次加载的时候,将全国所有省查询出来,封装放在级联组件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, },
image-20220213212322706
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
,
最后实现的效果如下:
image-20220213213301547
附:后台查询地址的代码
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工程师是啥吗?