微信小程序自定义数据实现级联省市区组件

文章来源:CSDN 发布日期:2024-04-23 分类:小程序博客 阅读( )

前言

在微信小程序中,官方文档提供的省市区组件,可以让用户更加方便快捷地选择省市区,但是官方提供的组件有一个缺点,无法自定义数据,但如果项目中需要使用自己的数据,显然就得寻找其它的组件实现。


官方组件

  • 优点

    • 使用官方组件具有稳定性和兼容性,可以保证在不同的微信小程序版本中正常运行;
    • 官方组件的使用文档详细,易于上手,可以快速实现级联省市区组件;
    • 官方组件的样式和交互效果都比较简洁,符合微信小程序的设计风格。
  • 缺点

    • 官方组件的样式和交互效果比较单一,无法满足一些特殊需求;
    • 官方组件的自定义能力比较有限,无法进行个性化定制。

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                    }                ]            }        ]    }]

实现效果

在这里插入图片描述

最新文章:

二维码