写在前面
未采用: 前端放置js 或者 json文件进行 省市区三级联动
采用: 前端组件 + 后端接口实现三级联动
原因:首先微信小程序有大小限制,能省则省,其次:方便后台维护省市区数据,完整省市区每年更新好像还是挺频繁的
方案
后端保存数据格式
例如
前端要展示 "福建省-厦门市-集美区" 直接根据 - 分割截取就行
12,1,3 :代表选择数据 对应项中的第几个
9261586716360729,9261586716360744:代表选择省跟市的id
这两个数组串 主要是回显数据用 ,如下,点击展开回显
前端实现代码 [有注释说明]
template
bash
<uni-popup ref="cityDialog" type="bottom">
<view class="uni-list">
<view class="uni-list-cell">
<view class="uni-list-cell-left">
当前选择
</view>
<view class="uni-list-cell-db">
<picker @columnchange="columnchange" @change="changeAddress" range-key="name" :value="value"
:range="addressList" mode="multiSelector">
<view class="uni-input">{{cityValue}}</view>
</picker>
</view>
</view>
</view>
</uni-popup>
data
bash
export default {
data() {
return {
//存放三级联动数据
addressList: [[],[],[]],
//选择的省回显id
province: "",
//选择的市回显id
city: "",
//回显值
value: [],
//前端展示数据
cityValue:'',
timer:null
}
}
}
create
bash
created() {
console.log("created...")
this.userInfo = uni.getStorageSync("userInfo");
const info = this.userInfo.city
if (info) {
//info 就是上面方案里说的数据库city字段值
// 福建省-厦门市-集美区-12,1,3-9261586716360729,9261586716360744
//前端展示数据
this.cityValue = info.split('-').slice(0, 3).join('-');
//数据库 省市表主键 id
const idPart = info.split('-')[4].split(',');
this.province = idPart[0]
this.city = idPart[1]
this.value = []
// 添加回显id
this.value.push(...info.split('-')[3].split(',').map(Number));
}
//初始化省数据
this.initProvince();
},
methods
bash
columnchange(e) {
clearTimeout(this.timer)
const _this = this
this.timer = setTimeout(() => {
const {
column,
value
} = e.detail
const item = {
..._this.addressList[column][value]
}
if (column === 0) {
this.province = item.id
//清楚选择二级数据,不然切换别的省,第三级永远是上一次选择
this.city = ''
_this.loadCity(item.id)
} else if (column === 1) {
this.city = item.id
_this.loadThird(item.id)
}
}, 500)
},
initProvince() {
//调用接口获取省数据
listProvince().then(res => {
if (res.code == 200) {
this.addressList.splice(0, 1, [...res.data])
//如果this.province 数据没有,表示数据库没有记录
if (!this.province) {
this.province = res.data[0].id
}
this.loadCity(this.province)
}
})
},
loadCity(parentId) {
listCity(parentId).then(res => {
if (res.code == 200) {
this.addressList.splice(1, 1, [...res.data])
if(!this.city) {
this.city = res.data[0].id
}
this.loadThird(this.city)
}
})
},
loadThird(parentId) {
listCity(parentId).then(res => {
if (res.code == 200) {
this.addressList.splice(2, 1, [...res.data])
}
})
},
changeAddress(e) {
console.log("e", e)
const _this = this
_this.value = []
_this.value.push(...e.detail.value)
const arr = e.detail.value
let str = ''
arr.forEach((el, index) => {
const {
name
} = _this.addressList[index][el]
str += `${index ? '-':''}${name}`
})
this.cityValue = str
// 最终数据 省-市-区-选择数据第几个(逗号隔开)- 选择数据id(逗号隔开)
//例:福建省-厦门市-集美区-12,1,3-9261586716360729,9261586716360744
str = str + "-" + e.detail.value + "-" + this.province + "," + this.city
//TODO 保存,或者修改 数据
this.editInfo.city = str
},
后端实现代码
后端就两个接口 简单接口
bash
/**
* 获取省份
* @return province
*/
@GetMapping("listProvince")
public AjaxResult listProvince () {
return AjaxResult.success(sysProvinceCityDistrictService.listProvince());
}
```bash
/**
* 获取市/区县
* @return province
*/
@GetMapping("listCity")
public AjaxResult listCity (@RequestParam("parentId") String parentId) {
return AjaxResult.success(sysProvinceCityDistrictService.listCity(parentId));
}
service接口文件这边就忽略了,直接impl逻辑
逻辑也很简单,查缓存 ,没有就数据库,有就缓存
Constants.NUMBER_ZERO 常量 0
bash
@Override
public List<CityVo> listProvince() {
if (Boolean.TRUE.equals(redisTemplate.hasKey(CacheConstants.CITY_CACHE_KEY))) {
//从缓存获取
return (List<CityVo>) redisTemplate.opsForValue().get(CacheConstants.CITY_CACHE_KEY);
}
List<CityVo> cityVos = sysProvinceCityDistrictMapper.listProvince();
if (CollectionUtils.isEmpty(cityVos)) {
return null;
}
redisTemplate.opsForValue().set(CacheConstants.CITY_CACHE_KEY, cityVos);
return cityVos;
}
@Override
public List<CityVo> listCity(String parentId) {
List<CityVo> list = new ArrayList<>();
List<SysProvinceCityDistrict> reultList;
if (Boolean.TRUE.equals(redisTemplate.hasKey(CacheConstants.CITY_CACHE_KEY_CITY))) {
//从缓存获取
reultList = (List<SysProvinceCityDistrict>) redisTemplate.opsForValue().get(CacheConstants.CITY_CACHE_KEY_CITY);
} else {
QueryWrapper<SysProvinceCityDistrict> query = new QueryWrapper<>();
query.ne("parent_id", Constants.NUMBER_ZERO);
reultList = sysProvinceCityDistrictMapper.selectList(query);
}
if (CollectionUtils.isEmpty(reultList)) {
return list;
}
reultList.stream().filter(s -> s.getParentId().equals(parentId))
.forEach(info -> {
CityVo cityVo = new CityVo();
cityVo.setId(info.getId());
cityVo.setName(info.getName());
list.add(cityVo);
});
redisTemplate.opsForValue().set(CacheConstants.CITY_CACHE_KEY_CITY, reultList);
return list;
}
CityVo
bash
/**
* @author rongpeng.xia
* @date 2024-08-13 10:33:59
*/
@Data
public class CityVo implements Serializable {
private static final long serialVersionUID = 1L;
private String id;
private String name;
}
(SysProvinceCityDistrict)表实体类
bash
/**
* (SysProvinceCityDistrict)表实体类
*
* @author xiarp
* @since 2024-08-12 15:58:13
*/
@SuppressWarnings("serial")
@Data
public class SysProvinceCityDistrict implements Serializable {
private static final long serialVersionUID = 286422671100375146L;
/**
* id
*/
private String id;
/**
* 名字
*/
private String name;
/**
* 父id
*/
private String parentId;
/**
* 编码
*/
private String code;
/**
* 父编码
*/
private String parentCode;
/**
* 创建者
*/
private Long createId;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改者
*/
private Long updateId;
/**
* 修改时间
*/
private Date updateTime;
}
前端参考的连接:https://blog.csdn.net/m0_73205643/article/details/140954162