vue3 手动封装城市三级联动

news2025/6/10 7:29:03

要做的功能 示意图是这样的,因为后端给的数据结构 不足以使用ant-design组件 的联动查询组件

所以只能自己分装 组件

当然 这个数据后端给的不一样的情况下 可能组件内对应的 逻辑方式就不一样

毕竟是 三个 数组 省份 城市 区域

我直接粘贴组件代码了

<template>
  <div class="cascader-compoents-container">
    <div class="city-select" @click.stop="toggleCascader">
      <a-input
        v-model:value="cascaderName"
        :style="{ width: width + 'px', 'text-align': 'left' }"
        placeholder="选择城市"
      />
      <DownOutlined
        style="
          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] = item
    cascaderShow.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.本人不才 望指教

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

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

相关文章

Linux【5】-----编译和烧写Linux系统镜像(RK3568)

参考&#xff1a;讯为 1、文件系统 不同的文件系统组成了&#xff1a;debian、ubuntu、buildroot、qt等系统 每个文件系统的uboot和kernel是一样的 2、源码目录介绍 目录 3、正式编译 编译脚本build.sh 帮助内容如下&#xff1a; Available options: uboot …

Heygem50系显卡合成的视频声音杂音模糊解决方案

如果你在使用50系显卡有杂音的情况&#xff0c;可能还是官方适配问题&#xff0c;可以使用以下方案进行解决&#xff1a; 方案一&#xff1a;剪映替换音色&#xff08;简单适合普通玩家&#xff09; 使用剪映换音色即可&#xff0c;口型还是对上的&#xff0c;没有剪映vip的&…

Gitlab + Jenkins 实现 CICD

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

无头浏览器技术:Python爬虫如何精准模拟搜索点击

1. 无头浏览器技术概述 1.1 什么是无头浏览器&#xff1f; 无头浏览器是一种没有图形用户界面&#xff08;GUI&#xff09;的浏览器&#xff0c;它通过程序控制浏览器内核&#xff08;如Chromium、Firefox&#xff09;执行页面加载、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、需求 我们知道大型语言模型 &#xff08;LLM&#xff09; 是无状态的&#xff0c;这就意味着他们不会保…

Qt 按钮类控件(Push Button 与 Radio Button)(1)

文章目录 Push Button前提概要API接口给按钮添加图标给按钮添加快捷键 Radio ButtonAPI接口性别选择 Push Button&#xff08;鼠标点击不放连续移动快捷键&#xff09; Radio Button Push Button 前提概要 1. 之前文章中所提到的各种跟QWidget有关的各种属性/函数/方法&#…

生成对抗网络(GAN)损失函数解读

GAN损失函数的形式&#xff1a; 以下是对每个部分的解读&#xff1a; 1. ⁡, ​ &#xff1a;这个部分表示生成器&#xff08;Generator&#xff09;G的目标是最小化损失函数。 &#xff1a;判别器&#xff08;Discriminator&#xff09;D的目标是最大化损失函数。 GAN的训…

汇编语言学习(三)——DoxBox中debug的使用

目录 一、安装DoxBox&#xff0c;并下载汇编工具&#xff08;MASM文件&#xff09; 二、debug是什么 三、debug中的命令 一、安装DoxBox&#xff0c;并下载汇编工具&#xff08;MASM文件&#xff09; 链接&#xff1a; https://pan.baidu.com/s/1IbyJj-JIkl_oMOJmkKiaGQ?pw…

数据可视化交互

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

安宝特方案丨从依赖经验到数据驱动:AR套件重构特种装备装配与质检全流程

在高压电气装备、军工装备、石油测井仪器装备、计算存储服务器和机柜、核磁医疗装备、大型发动机组等特种装备生产型企业&#xff0c;其产品具有“小批量、多品种、人工装配、价值高”的特点。 生产管理中存在传统SOP文件内容缺失、SOP更新不及、装配严重依赖个人经验、产品装…

【JavaEE】万字详解HTTP协议

HTTP是什么&#xff1f;-----互联网的“快递小哥” 想象我们正在网上购物&#xff1a;打开淘宝APP&#xff0c;搜索“蓝牙耳机”&#xff0c;点击商品图片&#xff0c;然后下单付款。这一系列操作背后&#xff0c;其实有一个看不见的“快递小哥”在帮我们传递信息&#xff0c;…

华为云Flexus+DeepSeek征文 | MaaS平台避坑指南:DeepSeek商用服务开通与成本控制

作者简介 我是摘星&#xff0c;一名专注于云计算和AI技术的开发者。本次通过华为云MaaS平台体验DeepSeek系列模型&#xff0c;将实际使用经验分享给大家&#xff0c;希望能帮助开发者快速掌握华为云AI服务的核心能力。 目录 作者简介 前言 一、技术架构概览 1.1 整体架构设…

【动态规划】B4336 [中山市赛 2023] 永别|普及+

B4336 [中山市赛 2023] 永别 题目描述 你做了一个梦&#xff0c;梦里有一个字符串&#xff0c;这个字符串无论正着读还是倒着读都是一样的&#xff0c;例如&#xff1a; a b c b a \tt abcba abcba 就符合这个条件。 但是你醒来时不记得梦中的字符串是什么&#xff0c;只记得…

可下载旧版app屏蔽更新的app市场

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

claude3.7高阶玩法,生成系统架构图,国内直接使用

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

河北对口计算机高考MySQL笔记(完结版)(2026高考)持续更新~~~~

MySQL 基础概念 数据&#xff08;Data&#xff09;&#xff1a;文本&#xff0c;数字&#xff0c;图片&#xff0c;视频&#xff0c;音频等多种表现形式&#xff0c;能够被计算机存储和处理。 **数据库&#xff08;Data Base—简称DB&#xff09;&#xff1a;**存储数据的仓库…

2025-06-01-Hive 技术及应用介绍

Hive 技术及应用介绍 参考资料 Hive 技术原理Hive 架构及应用介绍Hive - 小海哥哥 de - 博客园https://cwiki.apache.org/confluence/display/Hive/Home(官方文档) Apache Hive 是基于 Hadoop 构建的数据仓库工具&#xff0c;它为海量结构化数据提供类 SQL 的查询能力&#xf…

AI书签管理工具开发全记录(十八):书签导入导出

文章目录 AI书签管理工具开发全记录&#xff08;十八&#xff09;&#xff1a;书签导入导出1.前言 &#x1f4dd;2.书签结构分析 &#x1f4d6;3.书签示例 &#x1f4d1;4.书签文件结构定义描述 &#x1f523;4.1. ​整体文档结构​​4.2. ​核心元素类型​​4.3. ​层级关系4.…

docker容器互联

1.docker可以通过网路访问 2.docker允许映射容器内应用的服务端口到本地宿主主机 3.互联机制实现多个容器间通过容器名来快速访问 一 、端口映射实现容器访问 1.从外部访问容器应用 我们先把之前的删掉吧&#xff08;如果不删的话&#xff0c;容器就提不起来&#xff0c;因…