0

0

如何使用 Vue 实现城市选择器?

WBOY

WBOY

发布时间:2023-06-25 13:43:26

|

2136人浏览过

|

来源于php中文网

原创

近年来,前端技术不断地更新,前端框架的出现也让我们日常开发中效率得到很大的提升。在 vue.js 的框架下,我们可以很容易地实现许多常用的功能组件,比如城市选择器。

那么,如何在 Vue 中实现城市选择器呢?本篇文章将给大家分享一个简单的实现方法。

一、数据准备

在实现城市选择器之前,我们需要准备好城市数据。由于城市数据比较多,我们需要使用一个 JSON 数据格式来存储。在这里,我提供了一个 JSON 数据文件,大家可以在网上或其他资源中寻找。

城市数据文件样例:

立即学习前端免费学习笔记(深入)”;

[
  {
    "label": "北京市",
    "value": "110000",
    "children": [
      {
        "label": "北京市",
        "value": "110100",
        "children": [
          {
            "label": "东城区",
            "value": "110101"
          },
          {
            "label": "西城区",
            "value": "110102"
          },
          {
            "label": "崇文区",
            "value": "110103"
          },
          ...
        ]
      }
    ]
  },
  {
    "label": "上海市",
    "value": "310000",
    "children": [
      {
        "label": "上海市",
        "value": "310100",
        "children": [
          {
            "label": "黄浦区",
            "value": "310101"
          },
          {
            "label": "徐汇区",
            "value": "310104"
          },
          {
            "label": "长宁区",
            "value": "310105"
          },
          ...
        ]
      }
    ]
  },
  ...
]

二、选择器组件实现

2.1 引入城市数据

我们需要在组件的 script 部分中引入城市数据:

<script>
  import cityData from './city-data.json';

  export default {
    // 组件属性和方法
  }
</script>

2.2 定义选择器组件

由于城市选择器可以用于多个地方,我们可以将它定义为一个组件。在这个组件中,我们需要定义一些属性和方法。

PHP168  行业B2B
PHP168 行业B2B

解决问题如下:只列举最近用户提交问题,其余问题前面几次补丁已经解决,不在复述。1、解决搜索问题。以前搜索一定要确定到省下面的某个市,这个不符合用户体验。 现在改为,省--所有城市(默认为所有城市,也可以自己选择某个市)。2、解决首页推荐产品部显示问题。(以前没有考虑多个其他浏览器)3、解决供应、求购 今日产品显示问题。(理由同上)4、解决收藏商家、供应、求购问题。 (链接错误)5、解决后台分类过

下载

属性:

  • modelValue:当前选中的城市值;
  • placeholder:选择器输入框中的提示语;
  • width:选择器输入框的宽度;
  • disabled:选择器是否禁用;
  • readonly:选择器是否只读。

方法:

  • handleChangeCity:选中城市后的回调方法。
<template>
  <div class="city-picker">
    <input type="text" :readonly="readonly" :disabled="disabled" :placeholder="placeholder" :style="{width: width}" v-model="selectedCity">
    <!-- 其他相关 DOM 结构 -->
  </div>
</template>

<script>
  import cityData from './city-data.json';

  export default {
    props: {
      modelValue: {
        type: String,
        default: ''
      },
      placeholder: {
        type: String,
        default: '请选择城市'
      },
      width: {
        type: String,
        default: '200px'
      },
      disabled: {
        type: Boolean,
        default: false
      },
      readonly: {
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        selectedCity: this.modelValue,
        // 城市数据
        cityData: []
      }
    },
    methods: {
      handleChangeCity(value) {
        this.selectedCity = value;
        // 触发父组件的 onChange 事件
        this.$emit('onChange', value);
      }
    },
    mounted() {
      this.cityData = cityData;
    }
  }
</script>

2.3 渲染城市数据

在选择器中显示城市数据需要进行递归渲染。在渲染时,我们需要定义一个函数,递归遍历每一层的城市数据。由于城市数据可能有多级,我们需要使用递归的方式进行遍历。在代码实现中,我们使用了 Vue 组件中定义的 template 的方式进行渲染。

<template>
  <div>
    <!-- 递归渲染省份数据 -->
    <template v-for="province in cityData">
      <div :key="province.value" class="province">
        <div @click="handleShowCity(province)">{{ province.label }}</div>
        <template v-if="province.children && province.children.length > 0">
          <div v-show="province.showCity">
            <!-- 递归渲染城市和区县数据 -->
            <template v-for="city in province.children">
              <div :key="city.value" class="city">
                <div @click="handleShowDistrict(city)">{{ city.label }}</div>
                <template v-if="city.children && city.children.length > 0">
                  <div v-show="city.showDistrict">
                    <div v-for="district in city.children" :key="district.value">{{ district.label }}</div>
                  </div>
                </template>
              </div>
            </template>
          </div>
        </template>
      </div>
    </template>
  </div>
</template>

<script>
  import cityData from './city-data.json';

  export default {
    props: {
      modelValue: {
        type: String,
        default: ''
      },
      placeholder: {
        type: String,
        default: '请选择城市'
      },
      width: {
        type: String,
        default: '200px'
      },
      disabled: {
        type: Boolean,
        default: false
      },
      readonly: {
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        selectedCity: this.modelValue,
        // 城市数据
        cityData: []
      }
    },
    methods: {
      handleShowCity(province) {
        // 点击省份时,展开或关闭城市数据
        province.showCity = !province.showCity;
      },
      handleShowDistrict(city) {
        // 点击城市时,展开或关闭区县数据
        city.showDistrict = !city.showDistrict;
        // 选中城市后,调用 handleChangeCity 方法
        this.handleChangeCity(city.label);
      },
      handleChangeCity(value) {
        this.selectedCity = value;
        // 触发父组件的 onChange 事件
        this.$emit('onChange', value);
      },
      // 递归遍历城市数据,渲染出每一个层级的城市数据
      renderCity(cityData) {
        cityData.forEach(city => {
          city.showDistrict = false;
          if (city.children && city.children.length > 0) {
            this.renderCity(city.children);
            city.showCity = false;
          }
        })
      }
    },
    mounted() {
      this.cityData = cityData;
      // 渲染城市数据
      this.renderCity(this.cityData);
    }
  }
</script>

2.4 完整选择器组件代码

最终的城市选择器组件代码如下所示:

<template>
  <div class="city-picker">
    <input type="text" :readonly="readonly" :disabled="disabled" :placeholder="placeholder" :style="{width: width}" v-model="selectedCity">
    <!-- 城市选择器弹出框 -->
    <div class="city-picker-modal" v-show="showModal">
      <div class="city-picker-header">
        <span>请选择城市</span>
        <span class="close-icon" @click="handleCloseModal">&times;</span>
      </div>
      <div class="city-picker-body">
        <!-- 渲染城市选择器树形结构 -->
        <div class="city-picker-tree">
          <div class="top-tab">
            <div
              :class="{ active: (activeTab === 'province') }"
              @click="handleToggleTab('province')"
            >省份</div>
            <div
              :class="{ active: (activeTab === 'city') }"
              @click="handleToggleTab('city')"
            >城市</div>
            <div
              :class="{ active: (activeTab === 'district') }"
              @click="handleToggleTab('district')"
            >区县</div>
          </div>
          <div class="tab-content">
            <template v-if="activeTab === 'province'">
              <!-- 渲染省份数据 -->
              <template v-for="province in cityData">
                <div :key="province.value" class="province">
                  <div @click="handleShowCity(province)">{{ province.label }}</div>
                  <template v-if="province.children && province.children.length > 0">
                    <div v-show="province.showCity">
                      <!-- 渲染城市数据 -->
                      <template v-for="city in province.children">
                        <div :key="city.value" class="city">
                          <div @click="handleShowDistrict(city)">{{ city.label }}</div>
                          <template v-if="city.children && city.children.length > 0">
                            <div v-show="city.showDistrict">
                              <!-- 渲染区县数据 -->
                              <div v-for="district in city.children" :key="district.value">{{ district.label }}</div>
                            </div>
                          </template>
                        </div>
                      </template>
                    </div>
                  </template>
                </div>
              </template>
            </template>
            <template v-else-if="activeTab === 'city'">
              <!-- 渲染城市数据 -->
              <template v-for="province in cityData">
                <template v-if="province.children && province.children.length > 0">
                  <template v-for="city in province.children">
                    <div :key="city.value" class="city">{{ city.label }}</div>
                  </template>
                </template>
              </template>
            </template>
            <template v-else-if="activeTab === 'district'">
              <!-- 渲染区县数据 -->
              <template v-for="province in cityData">
                <template v-if="province.children && province.children.length > 0">
                  <template v-for="city in province.children">
                    <template v-if="city.children && city.children.length > 0">
                      <template v-for="district in city.children">
                        <div :key="district.value">{{ district.label }}</div>
                      </template>
                    </template>
                  </template>
                </template>
              </template>
            </template>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script>
  import cityData from './city-data.json';

  export default {
    props: {
      modelValue: {
        type: String,
        default: ''
      },
      placeholder: {
        type: String,
        default: '请选择城市'
      },
      width: {
        type: String,
        default: '200px'
      },
      disabled: {
        type: Boolean,
        default: false
      },
      readonly: {
        type: Boolean,
        default: false
      }
    },
    data() {
      return {
        // 当前选中的城市
        selectedCity: this.modelValue,
        // 城市数据
        cityData: [],
        // 显示弹出框标志位
        showModal: false,
        // 当前显示的 tab 标签页
        activeTab: 'province'
      }
    },
    methods: {
      // 选中省份时,展开或关闭城市数据
      handleShowCity(province) {
        province.showCity = !province.showCity;
        this.activeTab = (province.showCity ? 'city' : 'province');
      },
      // 选中城市时,展开或关闭区县数据,并选中城市
      handleShowDistrict(city) {
        city.showDistrict = !city.showDistrict;
        this.activeTab = (city.showDistrict ? 'district' : 'city');
        this.selectedCity = city.label;
        // 触发父组件 onChange 事件
        this.$emit('onChange', city.label);
        // 关闭弹出层
        this.showModal = false;
      },
      // 切换 tab 标签页
      handleToggleTab(tab) {
        this.activeTab = tab;
      },
      // 关闭城市选择器弹窗
      handleCloseModal() {
        this.showModal = false;
      }
    },
    mounted() {
      this.cityData = cityData;
      // 递归渲染城市数据,设置状态
      this.cityData.forEach((province) => {
        province.showCity = false;
        if (province.children && province.children.length > 0) {
          province.children.forEach((city) => {
            city.showDistrict = false;
          })
        }
      })
    }
  }
</script>

三、使用城市选择器

在 Vue 项目中的使用城市选择器组件很简单,只需要在需要使用的页面中引入城市选择器组件,然后在使用时传入相应的参数即可。下面是使用代码示例:

<template>
  <div>
    <CityPicker
      v-model="city"
      :width="200"
    ></CityPicker>
  </div>
</template>

<script>
  import CityPicker from './components/CityPicker';

  export default {
    components: {
      CityPicker
    },
    data() {
      return {
        city: ''
      }
    },
    methods: {
      handleChangeCity(value) {
        console.log('选中的城市为:', value);
      }
    }
  }
</script>

至此,我们已经可以在 Vue 应用中使用城市选择器组件了。这个城市选择器组件代码非常简单,但是实现了基本的城市选择功能,可以依照自己的需求进行扩展和优化。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

28

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

23

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

27

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

16

2026.02.27

Golang 高级特性与最佳实践:提升代码艺术
Golang 高级特性与最佳实践:提升代码艺术

本专题深入剖析 Golang 的高级特性与工程级最佳实践,涵盖并发模型、内存管理、接口设计与错误处理策略。通过真实场景与代码对比,引导从“可运行”走向“高质量”,帮助构建高性能、可扩展、易维护的优雅 Go 代码体系。

18

2026.02.27

Golang 测试与调试专题:确保代码可靠性
Golang 测试与调试专题:确保代码可靠性

本专题聚焦 Golang 的测试与调试体系,系统讲解单元测试、表驱动测试、基准测试与覆盖率分析方法,并深入剖析调试工具与常见问题定位思路。通过实践示例,引导建立可验证、可回归的工程习惯,从而持续提升代码可靠性与可维护性。

2

2026.02.27

漫蛙app官网链接入口
漫蛙app官网链接入口

漫蛙App官网提供多条稳定入口,包括 https://manwa.me、https

164

2026.02.27

deepseek在线提问
deepseek在线提问

本合集汇总了DeepSeek在线提问技巧与免登录使用入口,助你快速上手AI对话、写作、分析等功能。阅读专题下面的文章了解更多详细内容。

8

2026.02.27

AO3官网直接进入
AO3官网直接进入

AO3官网最新入口合集,汇总2026年可用官方及镜像链接,助你快速稳定访问Archive of Our Own平台。阅读专题下面的文章了解更多详细内容。

309

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

相关下载

更多

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Vue 教程
Vue 教程

共42课时 | 9万人学习

Vue3.x 工具篇--十天技能课堂
Vue3.x 工具篇--十天技能课堂

共26课时 | 1.6万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号