微信小程序自定义数据实现级联省市区组件功能
前言
在微信小程序中,官方文档提供的省市区组件,可以让用户更加方便快捷地选择省市区,但是官方提供的组件有一个缺点,无法自定义数据,但如果项目中需要使用自己的数据,显然就得寻找其它的组件实现。
官方组件
优点
- 使用官方组件具有稳定性和兼容性,可以保证在不同的微信小程序版本中正常运行;
- 官方组件的使用文档详细,易于上手,可以快速实现级联省市区组件;
- 官方组件的样式和交互效果都比较简洁,符合微信小程序的设计风格。
缺点
- 官方组件的样式和交互效果比较单一,无法满足一些特殊需求;
- 官方组件的自定义能力比较有限,无法进行个性化定制。
wxml
文件
<picker mode="region" bindchange="bindRegionChange" value="{{region}}" custom-item="{{customItem}}"> <view class="picker"> 当前选择:{{region[0]}},{{region[1]}},{{region[2]}} </view> </picker>
js
文件
Page({ data: { region: [], }, bindRegionChange: function (e) { console.log('picker发送选择改变,携带值为', e.detail.value) this.setData({ region: e.detail.value }) } })
实现效果
vant-weapp 实现
vant-weapp 中为我们提供了级联选择器,并且组件的样式和交互效果比较丰富,可以满足各种特殊需求,使用这个组件也可以实现,但是 vant-weapp
也有一个问题,当数据量比较大时,组件就会变得异常卡顿。
wxml
文件
<van-field value="{{ fieldValue }}" is-link readonly label="地区" placeholder="请选择所在地区" bind:tap="onClick" /> <van-popup show="{{ show }}" round position="bottom"> <van-cascader field-names="{{ fieldNames }}" wx:if="{{ show }}" value="{{ cascaderValue }}" title="请选择所在地区" options="{{ options }}" bind:close="onClose" bind:finish="onFinish" /> </van-popup>
js
文件
Page({ data: { show: false, fieldValue: '', cascaderValue: '', fieldNames: { text: 'label', value: 'value', children: 'children', }, options: [], }, onLoad() { this.setData({ options: JSON.parse(wx.getStorageSync('addressInfo')) }) }, onClick() { this.setData({ show: true, }); }, onClose() { this.setData({ show: false, }); }, onFinish(e) { const { selectedOptions, value } = e.detail; const fieldValue = selectedOptions .map((item) => item.label || item.value) .join('/'); this.setData({ fieldValue, cascaderValue: value, }) console.log(fieldValue); this.onClose() }, });
实现效果
肉眼可见非常卡顿,每点击一次都要等好几秒才能反应过来。
自定义组件
既然上面两种实现方法都不符合我们的需求,那么自己自定义组件就可以完全按照自己的需求进行设计和开发。
封装 wxml
文件
<picker mode="multiSelector" model:value="{{pickerValue}}" range-key="label" range="{{range}}" bindchange="onChange" bindcolumnchange="columnChange"> <view> <!-- 如果已经选择了选项,则显示选项的label属性,否则显示placeholder属性。 --> <text wx:if="{{label}}"> {{ label }} </text> <text style="color: #999" wx:else> {{ placeholder }}</text> </view> </picker>
封装 js
文件
Component({ properties: { // placeholder为选择器的默认提示文字 placeholder: { type: String, value: '请选择', }, // value为选择器的默认值,类型为数组 value: { type: Array, value: [], // observer监听value的变化,如果有值则调用setLabel方法设置选择器的label observer(selectedValues) { if (selectedValues && selectedValues.length) { this.setLabel(); } } } }, data: { // label为选择器的显示值 label: '', // range为选择器的可选项,类型为数组,包含三个数组,分别为省、市、区/县 range: [], // pickerValue为选择器的选中值,类型为数组,包含三个数字,分别为省、市、区/县的下标 pickerValue: [], // addressList为选择器的数据源,类型为数组,包含省、市、区/县的信息 addressList: [], }, // attached生命周期函数,在组件实例进入页面节点树时执行 attached() { // 获取地址列表,如果value为空则初始化range this.getAddressList().then(() => { if (!this.data.value.length) { this.initRange(); } }); }, methods: { // getAddressItem方法用于将地址信息转换为选择器可用的格式 getAddressItem(data) { return { label: data.label, value: data.value }; }, // setAddressList方法用于将地址列表转换为选择器可用的格式 setAddressList(list) { return list.map((v) => this.getAddressItem(v)); }, // getAddressByCode方法用于根据value值获取地址信息及其在数组中的下标 getAddressByCode(list = [], value) { let index = list.findIndex(item => item.value === value); return [index, list[index] || {}]; }, // openChildren方法用于根据value值打开下一级选择器 openChildren(list, keys) { let result = []; const handle = (arr, keys) => { let newarr = arr.map((v, index) => { if (keys && keys.length) { let [currentKey, ...nextKey] = keys; if (currentKey === index && Array.isArray(v.children)) { handle(v.children, nextKey); } } return this.getAddressItem(v); }); result.push(newarr); } handle(list, keys); return result.reverse(); }, // onChange方法为选择器的change事件处理函数,用于设置label和触发change事件 onChange(e) { let [r1, r2, r3] = this.data.range; const [v1, v2, v3] = e.detail.value; const selected = [r1[v1], r2[v2], r3[v3]]; const values = selected.map(v => v.value); const label = selected.map(v => v.label).join('-'); this.setData({ label }); this.triggerEvent("change", { value: values, label }); }, // columnChange方法为选择器的columnchange事件处理函数,用于设置range和pickerValue columnChange(e) { const { column, value } = e.detail; this.setColumn(column, value); }, // setColumn方法用于设置range和pickerValue setColumn(column, value) { let addressList = this.data.addressList; if (!addressList || addressList.length === 0) return; let [r1, r2, r3] = this.data.range; if (column === 0) { r2 = this.setAddressList(addressList[value].children); r3 = this.setAddressList(addressList[value].children[0].children); this.setData({ pickerValue: [value, 0, 0] }); } else if (column === 1) { const [v1] = this.data.pickerValue; r3 = this.setAddressList(addressList[v1].children[value].children); this.setData({ pickerValue: [v1, value, 0] }); } this.setData({ range: [r1, r2, r3] }); }, // setLabel方法用于设置label setLabel() { let addressList = this.data.addressList; if (addressList && addressList.length) { const [v1, v2, v3] = this.data.value; const [s1, { label: t1, children: l1 }] = this.getAddressByCode(addressList, v1); const [s2, { label: t2, children: l2 }] = this.getAddressByCode(l1, v2); const [s3, { label: t3 }] = this.getAddressByCode(l2, v3); const label = [t1, t2, t3].filter(v => v).join('-'); const pickerValue = [s1, s2, s3]; const range = this.openChildren(addressList, [s1, s2, s3]); if (label.length) { this.setData({ label, range, pickerValue }); } } else { this.getAddressList().then(() => { this.setLabel(); }); } }, // initRange方法用于初始化range initRange() { if (!this.data.value.length) { const range = this.openChildren(this.data.addressList, [0, 0, 0]); this.setData({ range }); } }, // getAddressList方法用于获取地址列表 getAddressList() { return new Promise((resolve, reject) => { wx.getStorage({ key: 'addressInfo', success: (res) => { this.setData({ addressList: JSON.parse(res.data) }); resolve(); }, fail: (err) => { reject(err); } }); }); }, }, });
使用 wxml
文件
<picker-region bindchange="regionChange" placeholder="请选择所在区域" value="{{value}}" />
使用 js
文件
Page({ data: { value: "" }, regionChange(e) { console.log(e) }, })
模拟 json
数据格式
[ { "value": "110000", "label": "北京市", "regionLevel": "1", "parentRegionCode": "0", "children": [ { "value": "110100", "label": "市辖区", "regionLevel": "2", "parentRegionCode": "110000", "children": [ { "value": "110101", "label": "东城区", "regionLevel": "3", "parentRegionCode": "110100", "children": null }, { "value": "110118", "label": "密云区", "regionLevel": "3", "parentRegionCode": "110100", "children": null } ] } ] }, { "value": "120000", "label": "天津市", "regionLevel": "1", "parentRegionCode": "0", "children": [ { "value": "120100", "label": "市辖区", "regionLevel": "2", "parentRegionCode": "120000", "children": [ { "value": "120101", "label": "和平区", "regionLevel": "3", "parentRegionCode": "120100", "children": null }, { "value": "120102", "label": "河东区", "regionLevel": "3", "parentRegionCode": "120100", "children": null } ] } ] } ]
实现效果
到此这篇关于微信小程序自定义数据实现级联省市区组件的文章就介绍到这了,更多相关小程序级联省市区组件内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
相关文章
关于JavaScript的URL.createObjectURL()的使用方法
这篇文章主要介绍了关于URL.createObjectURL()的使用方法,使用createObjectURL可以节省性能并更快速,只不过需要在不使用的情况下手动释放内存,还不清楚的朋友一起来看看吧2023-04-04
最新评论