vue3 手动封装城市三级联动
要做的功能 示意图是这样的,因为后端给的数据结构 不足以使用ant-design组件 的联动查询组件
所以只能自己分装 组件
当然 这个数据后端给的不一样的情况下 可能组件内对应的 逻辑方式就不一样
毕竟是 三个 数组 省份 城市 区域
我直接粘贴组件代码了
<template><div class="cascader-compoents-container"><div class="city-select" @click.stop="toggleCascader"><a-inputv-model:value="cascaderName":style="{ width: width + 'px', 'text-align': 'left' }"placeholder="选择城市"/><DownOutlinedstyle="font-size: 12px;color: rgba(0, 0, 0, 0.25);position: absolute;right: 11px;top: 10px;"/></div><div class="city-select-content" v-if="cascaderShow" v-click-outside="handleClose"><div class="province" :style="{ width: width / 2 + 'px' }" v-if="province.length > 0"><div class="item" v-for="item in province" :key="item.value" @click="onSelected(item, 0)"><div>{{ item.label }}</div><RightOutlined style="font-size: 12px; color: rgba(0, 0, 0, 0.25)" /></div></div><div class="city" :style="{ width: width / 2 + 'px' }" v-if="city.length > 0"><div class="item" v-for="item in city" :key="item.value" @click="onSelected(item, 1)"><div>{{ item.label }}</div><RightOutlined style="font-size: 12px; color: rgba(0, 0, 0, 0.25)" /></div></div><div class="district" :style="{ width: width / 2 + 'px' }" v-if="district.length > 0"><div class="item" v-for="item in district" :key="item.value" @click="onSelected(item, 2)"><div>{{ item.label }}</div></div></div></div></div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, toRefs, watch } from 'vue'
import * as addressApi from '@/api/address'const props = defineProps({width: {type: [Number, String],default: 300,},placeholder: {type: String,default: '选择城市',},modelValue: {type: Array,default: () => [],},
})
const { width, modelValue, placeholder } = toRefs(props)
const cascaderName = ref('')
const values = ref([])
const selectedId = ref([...modelValue.value])
const province = ref([])
const city = ref([])
const district = ref([])
const cascaderShow = ref(false)
const emits = defineEmits(['update:modelValue', 'onSelected'])
const updateCascaderName = () => {cascaderName.value = selectedId.value.map(item => item.label).join(' / ') || ''
}
// 监听 modelValue 变化以实现回显
watch(modelValue,async newVal => {selectedId.value = [...newVal]updateCascaderName()},{ immediate: true },
)const vClickOutside = {mounted(el: HTMLElement, binding: any) {el.clickOutsideEvent = (e: Event) => {if (!(el === e.target || el.contains(e.target as Node))) {binding.value(e)}}document.body.addEventListener('click', el.clickOutsideEvent)},unmounted(el: HTMLElement) {document.body.removeEventListener('click', el.clickOutsideEvent)},
}const toggleCascader = () => {cascaderShow.value = !cascaderShow.value
}const handleClose = () => {cascaderShow.value = false
}
// 添加点击外部关闭逻辑
const handleClickOutside = (e: MouseEvent) => {// const container = document.querySelector(`.cascader-compoents-container`)if (!e.currentTarget?.contains(e.target as Node)) {cascaderShow.value = false}
}
const getCityData = async (parentId: any, index: any) => {try {let { state, data, message: msg } = await addressApi.getChannelAreaList({ parentId })if (state === 200) {if (index == 0) {province.value = data.map((item: any) => {return {value: item.id,label: item.name,// children: [],}})district.value = []} else if (index == 1) {city.value = data.map((item: any) => {return {value: item.id,label: item.name,}})} else if (index == 2) {district.value = data.map((item: any) => {return {value: item.id,label: item.name,}})}}} catch (error) {message.error('网络请求连接失败~')}
}
const onSelected = (item, index) => {console.log(item, index, 'item, index')if (index == 0) {city.value = []district.value = []selectedId.value[0] = item}if (index == 1) {selectedId.value[1] = item}if (index == 2) {selectedId.value[2] = itemcascaderShow.value = false}cascaderName.value = selectedId.value.map(item => item.label).join(' / ')emits('onSelected', selectedId.value)emits('update:modelValue', [...selectedId.value])getCityData(item.value, index + 1)
}
onMounted(() => {document.addEventListener('click', handleClickOutside)getCityData(0, 0)
})
onBeforeUnmount(() => {document.removeEventListener('click', handleClickOutside)
})
</script>
<style lang="less" scoped>
.cascader-compoents-container {position: relative;.city-select {position: relative;}.city-select-content {position: absolute;display: flex;justify-content: space-around;left: 5px;top: 40px;z-index: 99;.province,.city,.district {background-color: #fff;height: 200px;box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);overflow-y: auto;border-radius: 5px;padding: 5px 0;transition: all, 0.3s;.item {display: flex;font-size: 13px;justify-content: space-between;align-items: center;padding: 0 10px;height: 35px;line-height: 30px;cursor: pointer;transition: all, 0.3s;&:hover {background-color: #f0f0f0;}}}.city,.district {margin-left: 10px;}}
}
</style>
父组件中使用
<CascaderCom @onSelected="getPcd1" v-model="selectedPcd2"></CascaderCom>@getPcd1 获取选择的省份城市 区域的 结果 自定义函数selectedPcd2 v-model 绑定的值 [] 有三个对象 分别对应 省 市 县 [{value:,label:},{value:,label:},{value:,label:}]在vue中 v-model 本来就是 modelValue 的语法糖
这里组件内用到了 监听 modelValue 的 时间 修改 组件内的 选择的数组这样能够解决 回显问题 因为 如果没有这个v-model 的 使用 父组件传递的回显数组 将不能够很快捷的在子组件中处理
这是我对于 项目业务逻辑中的相关的使用来进行的封装组件 此项目多处使用了 类似样式 而且接口都一样 所以我把接口也放到了组件里 这样能够随机能用 当然 这个接口 原则上不太适合在组件中使用
所以我展现组件的目的
1.自己能够随时使用
2.其他技术人可以参考我的逻辑
3.可以给其他前端人 带来逻辑 学习
4.本人不才 望指教
相关文章:

vue3 手动封装城市三级联动
要做的功能 示意图是这样的,因为后端给的数据结构 不足以使用ant-design组件 的联动查询组件 所以只能自己分装 组件 当然 这个数据后端给的不一样的情况下 可能组件内对应的 逻辑方式就不一样 毕竟是 三个 数组 省份 城市 区域 我直接粘贴组件代码了 <temp…...
Angular中Webpack与ngx-build-plus 浅学
Webpack 在 Angular 中的概念 Webpack 是一个模块打包工具,用于将多个模块和资源打包成一个或多个文件。在 Angular 项目中,Webpack 负责将 TypeScript、HTML、CSS 等文件打包成浏览器可以理解的 JavaScript 文件。Angular CLI 默认使用 Webpack 进行项目…...
大模型智能体核心技术:CoT与ReAct深度解析
**导读:**在当今AI技术快速发展的背景下,大模型的推理能力和可解释性成为业界关注的焦点。本文深入解析了两项核心技术:CoT(思维链)和ReAct(推理与行动),这两种方法正在重新定义大模…...
信息系统分析与设计复习
2024试卷 单选题(20) 1、在一个聊天系统(类似ChatGPT)中,属于控制类的是()。 A. 话语者类 B.聊天文字输入界面类 C. 聊天主题辨别类 D. 聊天历史类 解析 B-C-E备选架构中分析类分为边界类、控制类和实体类。 边界…...

Linux【5】-----编译和烧写Linux系统镜像(RK3568)
参考:讯为 1、文件系统 不同的文件系统组成了:debian、ubuntu、buildroot、qt等系统 每个文件系统的uboot和kernel是一样的 2、源码目录介绍 目录 3、正式编译 编译脚本build.sh 帮助内容如下: Available options: uboot …...
记一次spark在docker本地启动报错
1,背景 在docker中部署spark服务和调用spark服务的微服务,微服务之间通过fegin调用 2,问题,docker容器中服务器来后,注册中心都有,调用服务也正常,但是调用spark启动任务后报错,报错…...
【向量库】Weaviate 搜索与索引技术:从基础概念到性能优化
文章目录 零、概述一、搜索技术分类1. 向量搜索:捕捉语义的智能检索2. 关键字搜索:精确匹配的传统方案3. 混合搜索:语义与精确的双重保障 二、向量检索技术分类1. HNSW索引:大规模数据的高效引擎2. Flat索引:小规模数据…...
ABB馈线保护 REJ601 BD446NN1XG
配电网基本量程数字继电器 REJ601是一种专用馈线保护继电器,用于保护一次和二次配电网络中的公用事业和工业电力系统。该继电器在一个单元中提供了保护和监控功能的优化组合,具有同类产品中最佳的性能和可用性。 REJ601是一种专用馈线保护继电器…...

Heygem50系显卡合成的视频声音杂音模糊解决方案
如果你在使用50系显卡有杂音的情况,可能还是官方适配问题,可以使用以下方案进行解决: 方案一:剪映替换音色(简单适合普通玩家) 使用剪映换音色即可,口型还是对上的,没有剪映vip的&…...

Gitlab + Jenkins 实现 CICD
CICD 是持续集成(Continuous Integration, CI)和持续交付/部署(Continuous Delivery/Deployment, CD)的缩写,是现代软件开发中的一种自动化流程实践。下面介绍 Web 项目如何在代码提交到 Gitlab 后,自动发布…...

无头浏览器技术:Python爬虫如何精准模拟搜索点击
1. 无头浏览器技术概述 1.1 什么是无头浏览器? 无头浏览器是一种没有图形用户界面(GUI)的浏览器,它通过程序控制浏览器内核(如Chromium、Firefox)执行页面加载、JavaScript渲染、表单提交等操作。由于不渲…...

SDU棋界精灵——硬件程序ESP32实现opus编码
一、 音频处理框架 该项目基于Espressif的音频处理框架构建,核心组件包括 ESP-ADF 和 ESP-SR,以下是完整的音频处理框架实现细节: 1.核心组件 (1) 音频前端处理 (AFE - Audio Front-End) main/components/audio_pipeline/afe_processor.c功能: 声学回声…...

Spring AI中使用ChatMemory实现会话记忆功能
文章目录 1、需求2、ChatMemory中消息的存储位置3、实现步骤1、引入依赖2、配置Spring AI3、配置chatmemory4、java层传递conversaionId 4、验证5、完整代码6、参考文档 1、需求 我们知道大型语言模型 (LLM) 是无状态的,这就意味着他们不会保…...

Qt 按钮类控件(Push Button 与 Radio Button)(1)
文章目录 Push Button前提概要API接口给按钮添加图标给按钮添加快捷键 Radio ButtonAPI接口性别选择 Push Button(鼠标点击不放连续移动快捷键) Radio Button Push Button 前提概要 1. 之前文章中所提到的各种跟QWidget有关的各种属性/函数/方法&#…...
生成对抗网络(GAN)损失函数解读
GAN损失函数的形式: 以下是对每个部分的解读: 1. , :这个部分表示生成器(Generator)G的目标是最小化损失函数。 :判别器(Discriminator)D的目标是最大化损失函数。 GAN的训…...

汇编语言学习(三)——DoxBox中debug的使用
目录 一、安装DoxBox,并下载汇编工具(MASM文件) 二、debug是什么 三、debug中的命令 一、安装DoxBox,并下载汇编工具(MASM文件) 链接: https://pan.baidu.com/s/1IbyJj-JIkl_oMOJmkKiaGQ?pw…...
【Java基础】向上转型(Upcasting)和向下转型(Downcasting)
在面向对象编程中,转型(Casting) 是指改变对象的引用类型,主要涉及 继承关系 和 多态。 向上转型(Upcasting) ⬆️ 定义 将 子类对象 赋值给 父类引用(自动完成,无需强制转换&…...
GitHub 常见高频问题与解决方案(实用手册)
1.Push 提示权限错误(Permission denied) 问题: Bash Permission denied (publickey) fatal: Could not read from remote repository. 原因: 没有配置 SSH key 或使用了 HTTPS 而没有权限…...

数据可视化交互
目录 【实验目的】 【实验原理】 【实验环境】 【实验步骤】 一、安装 pyecharts 二、下载数据 三、实验任务 实验 1:AQI 横向对比条形图 代码说明: 运行结果: 实验 2:AQI 等级分布饼图 实验 3:多城市 AQI…...

安宝特方案丨从依赖经验到数据驱动:AR套件重构特种装备装配与质检全流程
在高压电气装备、军工装备、石油测井仪器装备、计算存储服务器和机柜、核磁医疗装备、大型发动机组等特种装备生产型企业,其产品具有“小批量、多品种、人工装配、价值高”的特点。 生产管理中存在传统SOP文件内容缺失、SOP更新不及、装配严重依赖个人经验、产品装…...

【JavaEE】万字详解HTTP协议
HTTP是什么?-----互联网的“快递小哥” 想象我们正在网上购物:打开淘宝APP,搜索“蓝牙耳机”,点击商品图片,然后下单付款。这一系列操作背后,其实有一个看不见的“快递小哥”在帮我们传递信息,…...
Vue3学习(接口,泛型,自定义类型,v-for,props)
一,前言 继续学习 二,TS接口泛型自定义类型 1.接口 TypeScript 接口(Interface)是一种定义对象形状的强大工具,它可以描述对象必须包含的属性、方法和它们的类型。接口不会被编译成 JavaScript 代码,仅…...

华为云Flexus+DeepSeek征文 | MaaS平台避坑指南:DeepSeek商用服务开通与成本控制
作者简介 我是摘星,一名专注于云计算和AI技术的开发者。本次通过华为云MaaS平台体验DeepSeek系列模型,将实际使用经验分享给大家,希望能帮助开发者快速掌握华为云AI服务的核心能力。 目录 作者简介 前言 一、技术架构概览 1.1 整体架构设…...
WEB3全栈开发——面试专业技能点P8DevOps / 区块链部署
一、Hardhat / Foundry 进行合约部署 概念介绍 Hardhat 和 Foundry 都是以太坊智能合约开发的工具套件,支持合约的编译、测试和部署。 它们允许开发者在本地或测试网络快速开发智能合约,并部署到链上(测试网或主网)。 部署过程…...

【动态规划】B4336 [中山市赛 2023] 永别|普及+
B4336 [中山市赛 2023] 永别 题目描述 你做了一个梦,梦里有一个字符串,这个字符串无论正着读还是倒着读都是一样的,例如: a b c b a \tt abcba abcba 就符合这个条件。 但是你醒来时不记得梦中的字符串是什么,只记得…...

可下载旧版app屏蔽更新的app市场
软件介绍 手机用久了,app越来越臃肿,老手机卡顿成常态。这里给大家推荐个改善老手机使用体验的方法,还能帮我们卸载不需要的app。 手机现状 如今的app不断更新,看似在优化,实则内存占用越来越大,对手机性…...

claude3.7高阶玩法,生成系统架构图,国内直接使用
文章目录 零、前言一、操作指南操作指导 二、提示词模板三、实战图书管理系统通过4o模型生成系统描述通过claude3.7生成系统架构图svg代码转换成图片 在线考试系统通过4o模型生成系统描述通过claude3.7生成系统架构图svg代码转换成图片 四、感受 零、前言 现在很多AI大模型可以…...

河北对口计算机高考MySQL笔记(完结版)(2026高考)持续更新~~~~
MySQL 基础概念 数据(Data):文本,数字,图片,视频,音频等多种表现形式,能够被计算机存储和处理。 **数据库(Data Base—简称DB):**存储数据的仓库…...

2025-06-01-Hive 技术及应用介绍
Hive 技术及应用介绍 参考资料 Hive 技术原理Hive 架构及应用介绍Hive - 小海哥哥 de - 博客园https://cwiki.apache.org/confluence/display/Hive/Home(官方文档) Apache Hive 是基于 Hadoop 构建的数据仓库工具,它为海量结构化数据提供类 SQL 的查询能力…...
DriveGPT4: Interpretable End-to-end Autonomous Driving via Large Language Model
一、研究背景与创新点 (一)现有方法的局限性 当前智驾系统面临两大核心挑战:一是长尾问题,即系统在遇到新场景时可能失效,例如突发交通状况或非常规道路环境;二是可解释性问题,传统方法无法解释智驾系统的决策过程,用户难以理解车辆行为的依据。传统语言模型(如 BERT…...