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

2023-12-05

前言

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


官方组件

  • 优点

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

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

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

实现效果

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

微信小程序自定义数据实现级联省市区组件 的相关文章

随机推荐

  • 视频转文字的app哪个好用?亲测好用的软件分享

    想象一下 当你在观看视频讲座或教学资料时 你希望把老师讲解的重点内容记录下来 以便后续的回顾和整理 但是手动输入文字真的是太麻烦了 这时 视频转文字软件就派上了用场 你只需简单地将视频上传到软件中 它会立即将视频中的对话或讲解转化为文字形式
  • C#8.0本质论第十五章--支持标准查询操作的集合接口

    C 8 0本质论第十五章 支持标准查询操作的集合接口 集合在C 3 0中通过称为语言集成查询 Language Integrated Query LINQ 的一套编程API进行了大刀阔斧的改革 通过一系列扩展方法和Lambda表达式 LIN
  • 大数相乘,限制:不可用 BigInt

    大数相乘 限制 不可用 BigInt 大数相乘 限制 不可用 BigInt 例如 输入 a 11111111111111111111 b 22222222222222222222 返回 246913580246913580241975308
  • 140-基于stm32单片机智能晾衣杆控制系统Proteus仿真+源程序

    资料编号 140 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 ds1302时钟 光敏传感器 蜂鸣器 LED灯 制作一个基于stm32单片机智能晾衣杆控制系统Proteus仿真 2 通过光敏传感器
  • 通过mount命令挂载另一台linux服务器的目录到本机

    说明 本次操作的目的是将另一台机器 192 168 1 100 的 backup目录挂载到本机的新建目录 back 1 在被挂载目录的机器上开启NFS服务并编辑 etc exports文件 1 1 查看是否已安装nfs 查看知道已安装 rp
  • 使用docker切换任意版本cuda使用GPU

    1 现存问题 在主机上运行很多下载来的机器学习代码时 这些大都运行在不同版本的tensorflow gpu或者pytorch等的包下的 但是 运行代码的过程中 存在匹配的问题 1 不同的版本的tensorflow gpu或pytorch对c
  • 138-基于stm32单片机汽车多功能仪表盘显示系统Proteus仿真+源程序

    资料编号 138 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DHT11传感器 ds1302时钟 LED灯 蜂鸣器 电位器 制作一个基于stm32单片机汽车多功能仪表盘显示系统Proteus仿真 2 通过DHT1
  • 【经典蓝牙 SSP协议介绍】

    协议定义 SSP Secure Simple Pairing 安全简单配对 协议目的 1 为用户简化配对过程 2 维护或提高蓝牙无线技术的安全性 安全性指两点目标 1 被动窃听保护 2 中间人 MITM 攻击保护 协议流程 下面说下我认为的
  • 133-基于stm32单片机停车场车位管理系统Proteus仿真+源程序

    资料编号 133 一 功能介绍 1 采用stm32单片机 4位数码管 独立按键 制作一个基于stm32单片机停车场车位管理系统Proteus仿真 2 通过按键进行模拟车辆进出 并且通过程序计算出当前的剩余车位数量 3 将剩余的车位数量显示到
  • 【网络安全】网络安全的标准和规范

    网络安全的标准和规范是网络安全领域的重要组成部分 它们为网络安全提供了技术依据 规定了网络安全的技术要求和操作方式 帮助我们构建安全的网络环境 下面 我们将详细介绍一些主要的网络安全标准和规范 以及它们在实际操作中的应用 一 ISO IEC
  • Elasticsearch-Kibana使用教程

    1 索引操作 1 1创建索引 PUT employee settings index refresh interval 1s number of shards 1 max result window 10000 number of repl
  • el-table 删除某行数据时 删除语句包含行号/序号

    el table可展示每行数据的序号列 在点击删除按钮的时候 会获取到该行所有的数据值 但是要想删除时提示到具体的序号 如 是否确认删除序号为1的数据项 我是这样写的 删除按钮操作 handleDelete row index用来存储该项数
  • Android中的Banner轮播图的使用

    目录 效果图 介绍 3 XML中banner常用的属性 使用 导入依赖 xml文件 活动代码 本文在碎片中使用 效果图 Banner效果图 介绍 Banner轮播图是一种常见的移动应用界面设计元素 用于展示多张图片或广告 并支持自动切换 它
  • “我,大三,寒假靠Python兼职赚了7567.6元。”

    目前很多同学已经结束期末考试 进入寒假 有很多同学问我 有什么兼职可以线上做的吗 最好还能提高自己的一些技能 以前周末或假期经常去当服务员 导购 发传单之类 最后发现 只是在无畏地消磨自己的时间 对本身技能并不能得到任何提高 所以 不想再重
  • 【王道】计算机组成原理笔记 第四章 指令系统

    前三章讲的分别是概述 运算器和存储器 接下来的第四章和第五章内容都是关于控制器的 我们知道 控制器通过指令来控制计算机 所以这一章介绍指令 下一章介绍控制器如何通过指令来控制计算机 4 1 1 指令格式 1 指令 操作码和地址码 1 1 指
  • linux内核机制之设备树

    设备树 Device Tree 基本概念及作用 在内核源码中 存在大量对板级细节信息描述的代码 这些代码充斥在 arch arm plat xxx和 arch arm mach xxx目录 对内核而言这些platform设备 resourc
  • 视频压缩怎么操作?看完这篇你就知道了

    亲们 你们是否经常为了视频文件过大而烦恼呢 别担心 现在有了视频压缩软件 我们可以轻松解决这个问题 视频压缩软件不仅在日常生活中大放异彩 也在工作和娱乐中发挥着重要的作用 无论是想要分享视频给朋友 还是上传到社交平台或视频网站 视频压缩软件
  • 企业如何为自己的未来做准备?

    如果企业不为未来做准备 就要为出局做准备工作 德鲁克 随着市场需求的不断变化 企业面对着激烈的市场竞争 其该如何为自己的未来做准备 具体企业面临着 建立竞争优势 管理多元化员工队伍 应用新的信息系统与技术 首先 企业在市场竞争中想要建立自己
  • 强化元学习算法在机器人控制中的应用研究

    随着人工智能和机器学习的快速发展 强化学习作为一种重要的机器学习方法 被广泛应用于机器人控制领域 然而 传统的强化学习算法在面对复杂任务和多样化环境时往往需要大量的训练时间和样本 为了解决这个问题 强化元学习算法应运而生 本文将探讨强化元学
  • 微信小程序自定义数据实现级联省市区组件

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