Vue城市多选组件

本文介绍了如何在Vue项目中实现一个城市多选组件,组件基于ElementUI,需求是选择城市到市级,全国选项为level=0,省为level=1,市为level=2,仅显示level=2的市。文章包含组件的需求、实现代码及使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

效果DEMO:

在这里插入图片描述

组件需求

项目工具需求:给多个城市配置信息,城市选择具体到市,将全国这一选项设置为level=0,省level=1,市level=2。展示只展示level=2的市。
项目UI库:elementUI

组件实现

组件代码

// selectCitys.vue

<template>
  <div class="abk-select-city" @click.stop="showCitysHandler">
    <el-input
      @input="inputChangeHandler"
      v-model="searchText"
      class="abk-select-input"
      :placeholder="placeholderText">
      <i slot="suffix" v-if="!showCitys" class="el-input__icon el-icon-caret-bottom"></i>
      <i slot="suffix" v-else @click.stop="hideCitysHandler" class="el-input__icon el-icon-caret-top"></i>
    </el-input>

    <div class="dropdown-box" v-show="showCitys">
      <div class="city-tree-box" v-show="showTrees">
        <el-tree
          :data="cityInfos"
          ref="selectCityTree"
          show-checkbox
          @check-change="treeChangeHandler"
          node-key="value">
        </el-tree>
      </div>

      <div class="city-search-box" v-show="!showTrees">
        <el-checkbox-group v-model="checkCitiesList">
          <el-checkbox class="checkbox-item" v-for="city in searchResultsList"
            :label="city.regionId"
            :key="city.regionId"
            @change="value => searchItemHandler(city, value)">
              <span v-if="city.level === 2">{
  
  {getCityNameByRegionId(city.upRegionId)}}</span> <span>{
  
  {city.name}}</span>
            </el-checkbox>
        </el-checkbox-group>
        <!-- <ul>
          <li v-for="item in searchResultsList" @click="searchItemHandler(item)">{
  
  {item.name}}</li>
        </ul> -->
      </div>
    </div>
  </div>
</template>
<style lang="less" scoped>
.abk-select-city{
  position: relative;
  width:300px;
  .el-icon-caret-bottom,.el-icon-caret-top{
    margin-top:-4px;
    color: #528ED5;
  }

  .dropdown-box{
    position: absolute;
    left: 0;
    right:0;
    top:40px;
    padding: 12px 0;
    background-color: #fff;
    border: 1px solid #e4e7ed;
    border-radius: 4px;
    z-index: 10;
    .checkbox-item{
      display: block;
      padding: 0px 12px;
    }
    .el-checkbox+.el-checkbox{
      margin-left: 0;
    }
  }
  .city-tree-box{
    max-height: 350px;
    overflow-y:auto;
  }
  .city-search-box{
    max-height: 250px;
    overflow-y:auto;
    li{
      padding: 5px 12px;
    }
    li:hover{
      background: #f5f7fa;
    }
  }
}
</style>
<style lang="less">
  /* 取消框架下拉右侧箭头 */
  .abk-select-city .el-input__suffix-inner{
    display: inline-block;
  }
</style>

<script>
/**
 * <select-city ref="selectCity" placeholder="请输入省市啊"></select-city>
 *
 * 获取所有选中节点 this.$refs.selectCity.getCheckedList()
 */

import _ from 'lodash'
import {cityLists} from "@/api/json_regions";

let cityMap = {}

export default {
  props: ['placeholder', 'checkcitylist'],
  data(){
    return {
			infos:[],
      citys: [],
      showTrees: true,
      showCitys: false,
      searchText: '',
      checkCitiesList: [],
      searchResultsList: [],
      placeholderText: this.placeholder || '请添加地区',
			data1:null,
    }
  },
  model: {
    prop: 'checkcitylist',
    event: 'change'
  },
  mounted(){
    this.treeChangeHandler()
  },
	watch:{
		citys(n,o){
			this.placeholderText='请添加地区'
		},
	},
  computed: {
    cityInfos(){
      let infos = []
      this.citys = cityLists

      let province = this.citys.filter(o => {
        cityMap[o.regionId] = o.label
        return o.level === 1
      });

      province.map(p => {
        p.children = [];
        this.citys.map(o => {
          if (o.level === 2) {
            if (o.value.substr(0, 2) === p.value.substr(0, 2)) {
              p.children.push(o)
            }
          }
        })
      })

      infos = [
        {
          label: '全国',
          value: '000000',
          level: 0,
          children: province
        }
      ]
      return infos
    },
    checkedTreeNodes(){
      let list = this.$refs.selectCityTree.getCheckedNodes();
      return list
    }
  },
  methods: {
    setChecked () {
      this.$nextTick(() => {
        // 设置默认选中
        this.$refs.selectCityTree.setCheckedKeys(this.checkcitylist);
        this.treeChangeHandler();
      });
    },
    hideSelectCityHandler(){
      this.hideCitysHandler()
      this.showTrees = true
      this.searchText = ''
    },
    getCheckedList(){
      // console.log('this.checkedTreeNodes', this.checkedTreeNodes)
      return this.checkedTreeNodes;
    },
    getCityNameByRegionId(id){
      return cityMap[id]
    },
    showCitysHandler(){
      this.showCitys = true
      document.removeEventListener('click', this.hideSelectCityHandler)
      document.addEventListener('click', this.hideSelectCityHandler, false)
    },
    hideCitysHandler(){
      this.showCitys = false
      this.$emit('blur')
      document.removeEventListener('click', this.hideSelectCityHandler)
    },
    inputChangeHandler(){
      let searchKey = this.searchText.toUpperCase()
      let citys = this.citys;
      if(searchKey == ""){
        this.searchResultsList = []
      }else {
        this.searchResultsList = citys
        .filter( o => {
          return o.level === 1 || o.level === 2
        })
        .filter( o => {
          o.shortSpell = o.shortSpell || ''
          o.spell = o.spell || ''
          o.fullName = o.fullName || ''
          return o.shortSpell.toUpperCase().indexOf(searchKey) === 0
                  || o.spell.toUpperCase().indexOf(searchKey) === 0
                  || o.fullName.indexOf(searchKey) === 0
        })
      }

      this.showTrees = this.searchResultsList.length === 0
    },
    searchItemHandler(city, value){
      this.$refs.selectCityTree.setChecked(city.regionId, value, true);
    },
    treeChangeHandler(){
      if (!this.checkcitylist) {
        this.$emit('change', this.getCheckedList())
      }
      if(this.getCheckedList()[0] && this.getCheckedList()[0].value === '000000'){
        this.placeholderText = '全国地区'
      }else{
        this.placeholderText = this.getCheckedList().filter(o => o.level === 2 || o.level === 0).map(o => o.label).join(',') || this.placeholder || '请添加地区'
      }
    }
  }
}
</script>

组件的使用

// main.js

import Antd from 'ant-design-vue'
import 'ant-design-vue/dist/antd.css'
Vue.use(Antd)
/**
* 说明:setCheckedKeys接收的市城市code的数组:["1", "2"]
*/

// 引入

import SelectCity from '@/***/SelectCity';

// 组件名
components: {
    SelectCity
},

// html导入
<select-city ref="cityChild" placeholder="请选择省市" @change="getCityArr" v-model="selectedCityLists"></select-city>

// method
getCityArr(data) {
    console.log('data', data)
    this.selectedCityLists = data
},

补充

// 城市JSON

export const cityLists = [
  {
    "value": "110000",
    "label": "北京",
    "level": 1
  },
  {
    "value": "110100",
    "label": "北京",
    "pre": "京",
    "level": 2
  },
  {
    "value": "120000",
    "label": "天津",
    "level": 1
  },
  {
    "value": "120100",
    "label": "天津",
    "pre": "津",
    "level": 2
  },
  {
    "value": "130000",
    "label": "河北省",
    "level": 1
  },
  {
    "value": "130100",
    "label": "石家庄",
    "pre": "冀A",
    "level": 2
  },
  {
    "value": "130200",
    "label": "唐山",
    "pre":
### Vue 实现下拉框 为了实现在 Vue 中带有选择的下拉框,可以基于 `v-model` 和数组绑定来管理所项目。下面是一个简单的实现方法。 #### 创建 MultiSelect 组件 MultiSelect.vue 文件内容如下: ```vue <template> <div class="multi-select"> <select multiple v-model="selectedOptions" @change="onChange"> <option value="">请选择</option> <option v-for="(item, index) in options" :key="index" :value="item.value">{{ item.label }}</option> </select> </div> </template> <script> export default { props: ['modelValue', 'options'], emits: ['update:modelValue'], computed: { selectedOptions: { get() { return this.modelValue; }, set(value) { this.$emit('update:modelValue', Array.from(value)); } } }, methods: { onChange(event) { const selectedValues = [...event.target.selectedOptions].map(option => option.value); this.$emit('update:modelValue', selectedValues); } } }; </script> <style scoped> .multi-select select { width: 100%; height: 30px; } </style> ``` 此组件接收两个 prop 参数:一个是用于双向数据绑定的选择项集合 `modelValue`;另一个是可供选择的数据列表 `options`[^1]。 #### 使用 MultiSelect 组件 在 App.vue 或者任意父级组件中引入并注册该组件,同时提供初始数据源以及监听其变化事件。 ```vue <template> <div id="app"> <h2>下拉菜单示例:</h2> <MultiSelect v-model="selectedItems" :options="cityList"></MultiSelect> <!-- 显示已项 --> <p>您选择了:</p> <ul> <li v-for="(item, idx) of displayedSelectedItems" :key="idx">{{ item }}</li> </ul> </div> </template> <script> import MultiSelect from './components/MultiSelect'; export default { name: "App", components: { MultiSelect }, data() { return { cityList: [ { label: '北京', value: 'beijing' }, { label: '上海', value: 'shanghai' }, { label: '广州', value: 'guangzhou' }, { label: '深圳', value: 'shenzhen' } ], selectedItems: [] }; }, computed: { displayedSelectedItems() { let labels = []; this.cityList.forEach(city => { if (this.selectedItems.includes(city.value)) { labels.push(city.label); } }); return labels; } } }; </script> ``` 上述代码展示了如何通过 `v-model` 将子组件中的选择状态同步到父组件,并且能够动态更新视图以反映当前被中的城市名称。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值