省市区街道/乡镇四级联动vue3

最近优化了一个省.市.区/县、乡镇/街道的四级联动组件,技术栈是element + vue3记录一下。

本来是这样的三级联动:

这个三级联动很简单,直接利用el-select组件把地区值带进去就行了,现在要优化成省.市.区/县、乡镇/街道的四级联动,变成这样:

 

下面进入正文: (说一下主要流程,最后附上全部代码)

首先要准备省市区和对应编码的JSON文件:

GitHub - modood/Administrative-divisions-of-China: 中华人民共和国行政区划:省级(省份)、 地级(城市)、 县级(区县)、 乡级(乡镇街道)、 村级(村委会居委会) ,中国省市区镇村二级三级四级五级联动地址数据。

可以参考这个地址,直接在浏览器下载也行,git 克隆到本地也行,这个json文件很大,大概两三兆,可以让后端返回。

省份分组时用到了一个三方包,需要把省份转成拼音获取首字母,直接下载就行
yarn add chinese-to-pinyin  或者  npm i chinese-to-pinyinimport pinyin from "chinese-to-pinyin"

然后调整数据结构,

//省份分组
const groupedProvinces = ref({"A-G": [],"H": [],"J-Q": [],"S-T": [],"X-Z": [],"其它": []
})//分解省市区数据
function extractLocations(data, level = 0, results = { provinces: [], cities: [], districts: [], streets: [] }) {for ( const item of data ) {// 根据层级确定当前是省/直辖市、市/区或街道,并存储数据if ( level === 0 ) {results.provinces.push(item)} else if ( level === 1 ) {results.cities.push(item)} else if ( level === 2 ) {results.districts.push(item)} else if ( level === 3 ) {results.streets.push(item)}// 如果存在子级,递归调用自身if ( item.children && item.children.length ) {extractLocations(item.children, level + 1, results)}}return results
}//省市区数组集合
const pcasList = ref(extractLocations(pcasCodeList))//按首字母分类的省份
function groupProvinces(provinces) {pcasList.value.provinces.forEach(province => {let firstLetter = pinyin(province.name, { removeTone: true }).charAt(0).toUpperCase()if ( province.name === "澳门特别行政区" || province.name === "台湾省" || province.name === "香港特别行政区" ) {// 澳门、台湾、香港特殊处理switch ( province.name ) {case "澳门特别行政区":case "台湾省":case "香港特别行政区":groupedProvinces.value["其它"].push(province)break}} else {if ( "ABCDEFG".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["A-G"].push(province)} else if ( "H".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["H"].push(province)} else if ( "JKLMNOPQ".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["J-Q"].push(province)} else if ( "ST".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["S-T"].push(province)} else if ( "XYZ".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["X-Z"].push(province)} else {// 其他不识别省份的处理console.warn("未识别的省份:", province.name)}}})
}groupProvinces(pcasList.value.provinces)

这样就实现了这个页面了

交互逻辑太多

为了避免文章太长

直接上全部代码
<template><el-popover v-model:visible="popoverVisible" :width="460" placement="bottom" trigger="click"><template #reference><el-input v-model="dataForm.PCASName" placeholder="请选择省市区街道/乡镇" /></template><div><el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick"><el-tab-pane :label="dataForm.provinceName" name="first"><div><div v-for="(item, itemName) in groupedProvinces" :key="itemName" class="addressItem"><div class="left">{{itemName}}</div><div class="right"><div v-for="(item,index) in item" :key="index":class="{'active': dataForm.provinceName === item.name }"class="provinceItem"@click="provinceItemFn(item)">{{ item.name }}</div></div></div></div></el-tab-pane><el-tab-pane v-if="dataForm.province" :label="dataForm.cityName" name="second"><div class="cityContent"><div v-for="(item, index) in dataForm.citesList" :key="index":class="{'active': dataForm.cityName === item.name }"class=" cityItem" @click="cityItemFn(item)">{{ item.name }}</div></div></el-tab-pane><el-tab-pane v-if="dataForm.city" :label="dataForm.areaName" name="three"><div class="cityContent"><div v-for="(item, index) in dataForm.areaList" :key="index":class="{'active': dataForm.areaName === item.name }"class=" cityItem" @click="areaItemFn(item)">{{ item.name }}</div></div></el-tab-pane><el-tab-pane v-if="dataForm.area" :label="dataForm.streetName" name="four"><div class="cityContent"><div v-for="(item, index) in dataForm.streetsList" :key="index":class="{'active': dataForm.streetName === item.name }"class=" cityItem" @click="streesItemFn(item)">{{ item.name }}</div></div></el-tab-pane></el-tabs></div></el-popover>
</template><script setup>
import { reactive, ref, watchEffect } from "vue"
import { cloneDeep } from "lodash-es"
import pcasCode from "@/views/owner_center/usualAddress/pcas-code.json"
import pinyin from "chinese-to-pinyin"//弹出框是否显示
let popoverVisible = ref(null)//省市区tab
const activeName = ref("first")//省市区code数据
let pcasCodeList = cloneDeep(pcasCode)//省份分组
const groupedProvinces = ref({"A-G": [],"H": [],"J-Q": [],"S-T": [],"X-Z": [],"其它": []
})//分解省市区数据
function extractLocations(data, level = 0, results = { provinces: [], cities: [], districts: [], streets: [] }) {for ( const item of data ) {// 根据层级确定当前是省/直辖市、市/区或街道,并存储数据if ( level === 0 ) {results.provinces.push(item)} else if ( level === 1 ) {results.cities.push(item)} else if ( level === 2 ) {results.districts.push(item)} else if ( level === 3 ) {results.streets.push(item)}// 如果存在子级,递归调用自身if ( item.children && item.children.length ) {extractLocations(item.children, level + 1, results)}}return results
}//省市区数组集合
const pcasList = ref(extractLocations(pcasCodeList))//按首字母分类的省份
function groupProvinces(provinces) {pcasList.value.provinces.forEach(province => {let firstLetter = pinyin(province.name, { removeTone: true }).charAt(0).toUpperCase()if ( province.name === "澳门特别行政区" || province.name === "台湾省" || province.name === "香港特别行政区" ) {// 澳门、台湾、香港特殊处理switch ( province.name ) {case "澳门特别行政区":case "台湾省":case "香港特别行政区":groupedProvinces.value["其它"].push(province)break}} else {if ( "ABCDEFG".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["A-G"].push(province)} else if ( "H".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["H"].push(province)} else if ( "JKLMNOPQ".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["J-Q"].push(province)} else if ( "ST".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["S-T"].push(province)} else if ( "XYZ".indexOf(firstLetter) !== -1 ) {groupedProvinces.value["X-Z"].push(province)} else {// 其他不识别省份的处理console.warn("未识别的省份:", province.name)}}})
}groupProvinces(pcasList.value.provinces)//tab栏点击事件
const handleClick = (tab) => {if ( tab.props.name === "second" ) {dataForm.value.citesList = pcasCodeList.find(item => item.code === dataForm.value.province)?.children || []} else if ( tab.props.name === "three" ) {const childrenArray = findChildrenByCode(pcasCodeList, dataForm.value.city)dataForm.value.areaList = childrenArray || []} else if ( tab.props.name === "four" ) {const childrenArray = findChildrenByCode(pcasCodeList, dataForm.value.area)dataForm.value.streetsList = childrenArray || []}
}let dataForm = ref({citesList: [],	//城市分组areaList: [],	//区县分组streetsList: [],	//街道乡镇分组province: "",	//省codecity: "", 	//城市codearea: "", 	//区县codestreet: "",	//街道乡镇codeprovinceName: "请选择", //省名称cityName: "请选择",// 城市名称areaName: "请选择", // 区县名称streetName: "请选择", //街道名称PCASName: "" //省市区街道名称
})//点击省
const provinceItemFn = (val) => {dataForm.value.provinceName = val.namedataForm.value.PCASName = updatePCASName(val.name)dataForm.value.province = val.codedataForm.value.citesList = val.children || []activeName.value = "second"resetSelections([ "city", "area", "street" ])console.log(val)
}//点击城市
const cityItemFn = (val) => {dataForm.value.cityName = val.namedataForm.value.PCASName = updatePCASName(dataForm.value.provinceName, val.name)dataForm.value.city = val.codedataForm.value.areaList = val.children || []activeName.value = "three"resetSelections([ "area", "street" ])console.log(val)
}//点击区县
const areaItemFn = (val) => {dataForm.value.areaName = val.namedataForm.value.PCASName = updatePCASName(dataForm.value.provinceName, dataForm.value.cityName, val.name)dataForm.value.area = val.codedataForm.value.streetsList = val.childrenresetSelections([ "street" ])activeName.value = "four"console.log(val)
}//点击街道/乡镇
const streesItemFn = (val) => {dataForm.value.streetName = val.namedataForm.value.PCASName = updatePCASName(dataForm.value.provinceName, dataForm.value.cityName, dataForm.value.areaName, val.name)dataForm.value.street = val.codepopoverVisible.value = falseconsole.log(val)
}watchEffect(() => {//判断某个地区为空时清空输入框内容if ( !popoverVisible.value && ( !dataForm.value.province || !dataForm.value.city || !dataForm.value.area || !dataForm.value.street ) ) {dataForm.value.PCASName = ""}//判断如果手动输入地区如“安徽省/芜湖市/弋江区/瀂港街道”匹配到对应code值等逻辑,否则清空const parts = dataForm.value.PCASName.split("/")if(parts.length === 4) {const matchedCodes = findCodesByNames(pcasCodeList, parts)if ( matchedCodes ) {dataForm.value.province = matchedCodes[0]dataForm.value.city = matchedCodes[1]dataForm.value.area = matchedCodes[2]dataForm.value.street = matchedCodes[3]dataForm.value.provinceName = parts[0]dataForm.value.cityName = parts[1]dataForm.value.areaName = parts[2]dataForm.value.streetName = parts[3]dataForm.value.citesList = findChildrenByCode(pcasCodeList, matchedCodes[0])dataForm.value.areaList = findChildrenByCode(pcasCodeList, matchedCodes[1])dataForm.value.streetsList = findChildrenByCode(pcasCodeList, matchedCodes[2])console.log(matchedCodes) // 输出找到的 code 数组} else {resetSelections([ "province", "city", "area", "street" ])activeName.value = "first"}}
})//重置选择
const resetSelections = (clearLevels) => {// 根据传入的层级清除选项if ( clearLevels.includes("province") ) {dataForm.value.province = ""dataForm.value.provinceName = "请选择"}if ( clearLevels.includes("city") ) {dataForm.value.city = ""dataForm.value.cityName = "请选择"dataForm.value.areaList = []}if ( clearLevels.includes("area") ) {dataForm.value.areaName = "请选择"dataForm.value.area = ""dataForm.value.streetsList = []}if ( clearLevels.includes("street") ) {dataForm.value.streetNameName = "请选择"dataForm.value.street = ""}
}// 更新省市区名称
const updatePCASName = (provinceName = "", cityName = "", areaName = "", streetName = "") => {const names = [ provinceName, cityName, areaName, streetName ].filter(name => name.trim() !== "")// 使用“/”连接数组中的名称return names.join("/")
}//根据输入框内容匹配对应的code值
function findCodesByNames(data, names, index = 0, codes = []) {if ( index < names.length ) {// 根据当前索引的名称查找数据const found = data.find(item => item.name === names[index])if ( found ) {// 如果找到了匹配项,加入 code,并继续递归搜索下一级codes[index] = found.code// 如果还有更深级别的名称,则继续递归,否则直接返回 codesreturn found.children && index + 1 < names.length ?findCodesByNames(found.children, names, index + 1, codes) : codes} else {// 如果未找到匹配项,证明省市区乡镇匹配错误,返回 falsereturn false}}// 如果所有省市区乡镇都已成功匹配对应的code,返回 codesreturn codes
}//根据某个code值寻找对应的子集地区数组
function findChildrenByCode(data, targetCode) {for ( const item of data ) {if ( item.code === targetCode ) {return item.children || []}if ( item.children ) {const result = findChildrenByCode(item.children, targetCode)if ( result ) return result}}return null
}</script><style lang="scss" scoped>
.addressItem{display: flex;font-size: 14px;margin-bottom: 4px;.left{min-width: 40px;color: #ee675b;margin-right: 16px;}.right{display: flex;flex-wrap: wrap;.provinceItem{margin-right: 18px;margin-bottom: 10px;&:hover{cursor: pointer;}}}
}.cityContent{display: flex;flex-wrap: wrap;font-size: 14px;.cityItem{margin-right: 18px;margin-bottom: 10px;cursor: pointer;}
}.active{color: #1166fe !important;
}</style>

这里我觉得有点冗余的是输入框输入地址和选择省市区乡镇的的联动效果,毕竟大部分人能选的话不会手输,如果不用的话直接禁用输入框就行,省下很多逻辑处理。

现在这这个组件刚写完

肯定涉及到父组件值的传入和子组件的值传出

以后再更新...

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/706916.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

提高笔记本电脑使用寿命,这几个技巧很重要!

“我新买了一台笔记本电脑&#xff0c;想问问在平常使用时我应该注意什么才可以更好地提高笔记本电脑的使用寿命呢&#xff1f;” 随着科技的不断发展&#xff0c;笔记本电脑已经成为我们日常生活中不可或缺的一部分。然而&#xff0c;许多人在使用笔记本电脑时&#xff0c;往往…

docker 容器修改端口

一般在运行容器时&#xff0c;我们都会通过参数 -p&#xff08;使用大写的-P参数则会随机选择宿主机的一个端口进行映射&#xff09;来指定宿主机和容器端口的映射&#xff0c;例如 docker run -it -d --name [container-name] -p 8088:80 [image-name]这里是将容器内的80端口…

平板江湖:华为“行”、苹果“停”、小米“等一等”

配图来自Canva可画 近年来&#xff0c;随着人们对日常生活和办公中的智能电子产品提出了越来越高的要求&#xff0c;智能手机和平板电脑的产品类型便开始变得更加丰富、价位覆盖更为全面、场景应用也更显多样起来。只不过&#xff0c;在进入新的一年后&#xff0c;与智能手机市…

【PHP】Workerman开源应用容器的GatewayWorker 与 iOS-OC对接

Workerman 开源高性能PHP应用容器 workerman是一款开源高性能PHP应用容器,它大大突破了传统PHP应用范围,被广泛的用于互联网、即时通讯、APP开发、硬件通讯、智能家居、物联网等领域的开发。 PHPSocket.io PHP版本的socket.io,具有良好的客户端兼容性,常用于即时通讯领域…

时间序列分析实战(八):时序的格兰杰因果检验

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

C语言题目练习

目录 前言 1、网购 1.1题目 描述 输入描述&#xff1a; 输出描述&#xff1a; ​编辑 1.1 解题 2、带空格直角三角形图案 2.1题目 描述 输入描述&#xff1a; 输出描述&#xff1a; ​编辑 2.2 解题 3、小乐乐改数字 3.1 题目 描述 输入描述&#xff1a; 输…

【自然语言处理三-self attention自注意是什么】

自然语言处理三-自注意力 self attention 自注意力是什么&#xff1f;自注意力模型出现的原因是什么&#xff1f;词性标注问题解决方法1-扩展window&#xff0c;引用上下文解决方法2-运用seq2seq架构新问题来了&#xff1a;参数量增加、无法并行的顽疾 自注意力self attention模…

备战蓝桥杯————k个一组反转单链表

k个反转单链表&#xff0c;顾名思义就是k个节点为一组进行反转&#xff0c;这是一道困难的题目&#xff0c;如何解答&#xff0c;可以在我们前面的反转链表中得到思路。 如何 K 个一组反转单链表 题目描述 给你链表的头节点 head &#xff0c;每 k 个节点一组进行翻转&#xf…

MySQL表分区技术介绍

目录 1. 分区概述 1.1 表分区 1.2 表分区与分表的区别 1.3 表分区的好处 1.4 分区表的限制因素 2. 如何判断当前MySQL是否支持分区&#xff1f; 3. 分区类型详解 3.1 MySQL支持分区类型 3.2 RANGE分区 3.2.1 根据数值范围分区 3.2.2 根据TIMESTAMP范围分区 3.2.3 根…

视频mov如何转换成mp4?五分钟学会4种方法

在今天的数字时代&#xff0c;我们不可避免地需要处理各种视频文件格式。MOV格式是由苹果公司开发的一种常见视频格式&#xff0c;而MP4格式则是更为通用和广泛支持的视频格式。 因此&#xff0c;当我们需要在不同设备或平台上播放或分享视频时&#xff0c;可能需要将MOV格式的…

Eclipse是如何创建web project项目的?

前面几篇描述先后描述了tomcat的目录结构和访问机制&#xff0c;以及Eclipse的项目类型和怎么调用jar包&#xff0c;还有java的main函数等&#xff0c;这些是一些基础问题&#xff0c;基础高清出来才更容易搞清楚后面要说的东西&#xff0c;也就是需求带动学习&#xff0c;后面…

Unity开发一个FPS游戏

在之前的文章Unity 3D Input System的使用-CSDN博客中,我介绍了如何用Input System来实现一个FPS游戏的移动控制,这里将进一步完善这个游戏。 以下是游戏的演示效果: fps_demo 添加武器模型 首先是增加主角玩家的武器,我们可以在网上搜索到很多免费的3D资源,例如在以下网…

12年高级工程师的“飞升之路”,redis和rabbitmq面试题

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

STM32 +合宙1.54“ 电子墨水屏(e-paper)驱动显示示例

STM32 合宙1.54“ 电子墨水屏&#xff08;e-paper&#xff09;驱动显示示例 &#x1f4cd;相关篇《Arduino框架下ESP32/ESP8266合宙1.54“ 电子墨水屏&#xff08;e-paper&#xff09;驱动显示示例》&#x1f516;程序是从GooDisplay品牌和微雪电子下同型号规格墨水屏的示例程序…

【C#】忽略大小的替换字符串

结果 代码 string a"aAAAAAAaBBaaCC"; string b Strings.Replace(a, "a", "D", 1, -1, CompareMethod.Text);

php基础学习之错误处理(其一)

一&#xff0c;错误处理的概念 错误处理指的是系统(或者用户)在执行某些代码的时候&#xff0c;发现有错误&#xff0c;就会通过错误处理的形式告知程序员&#xff0c;俗称报错 二&#xff0c;错误分类 语法错误&#xff1a;书写的代码不符合 PHP 的语法规范&#xff0c;语法错…

力扣刷题:383.赎金信

题目&#xff1a; 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。 如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。 magazine 中的每个字符只能在 ransomNote 中使用一次。 示例 1&…

mac电脑监控软件哪个好

在Mac电脑使用日益普及的今天&#xff0c;企业对于Mac终端的安全管理需求也日益增长。Mac电脑监控软件作为一种有效的管理工具&#xff0c;能够帮助企业提高数据安全性和员工工作效率。 在众多Mac电脑监控软件中&#xff0c;域智盾软件以其卓越的功能和性能脱颖而出&#xff0c…

3D工业相机及品牌集合

3D相机可以获取物理世界的空间信息&#xff0c;即立体三维的物理信息&#xff0c;不仅可以拍摄到场景的二维图像&#xff0c;而且能获取物体之间的位置关系&#xff0c;再经过进一步深化处理&#xff0c;还能完成三维建模等应用。 3D相机三种方案 1、结构光 通常采用特定波长…

R语言数学建模(一)—— 基础知识

R语言数学建模&#xff08;一&#xff09;—— 基础知识 文章目录 R语言数学建模&#xff08;一&#xff09;—— 基础知识前言一、建模软件1.1 软件建模的基础1.2 模型的分类1.3 不同类型模型间的联系1.4 一些术语1.5 建模如何适应数据分析过程 二、Tidyverse基础2.1 tidyvers…