省市区组件封装(无后端)

1.准备城市数据

这里是拿了vant的area的数据,此链接可获取:Vant Weapp中area.js完整数据_Codernmx-CSDN博客

2.准备组件

这个省市区联动用到el-input和el-tabs

省市区数据的处理是根据前面编码遍历循环出来

子组件

<template>

<!-- v-clickOutSide="handleClose"自定义指令用来点击组件外的区域关闭组件 -->
    <div class="select_all_region" :id="onlyOneId" v-clickOutSide="handleClose">
         <el-input prefix-icon="el-icon-location" :id="onlyOneIdInput" :readonly='readonly' class="input_s_freight" :clearable="clearable" :disabled = "disabled"  :placeholder="placeholder" @focus="handleShowText($event, 2)"
        v-model="showText" />
          <el-tabs v-model="activeName" class="lh20" type="border-card" @tab-click="handleClick"  v-show="dialogIsShow">
              <el-tab-pane v-for="(item, index) in [{data:commonCity, label: '常用城市', type: -1}, {data:provinceInfo, label: '省', type: 0}, {data:cityInfo, label: '市', type: 1}, {data:areaInfo, label: '区', type: 2}]" :key="index" :label="item.label" :name='item.type.toStrimg'>
                  <div class="click_item" v-for="(val, key) in item.data" :key="key" @click="handleContent(key, val, item.type)">{{val}}</div>
              </el-tab-pane>
          </el-tabs>
    </div>
</template>
<script>
// 导入城市数据
import area from './area'
// 导入自定义指令
import { clickOutSide } from '@/util/clickOutSide';
export default {
    directives: {clickOutSide},
    props: {
          value: {
        type: String,
        default: ''
      },
 
        disabled: {
            type: Boolean,
            default: function() {
                return false
            }
        },
        placeholder: {
            type: String
        },
         clearable: {
            type: Boolean,
            default: function() {
                return true
            }
        },
        // 是否可以不用三级
         changeOnSelect: { // 是否可以所以选择不用三级
            type: Boolean,
            default: false
        },
       // 是否只读
        readonly: {
            type: Boolean,
            default: false
        },
        // 省市区code
          selectedOptions: {
            type: Object,
            default: function() {
                return {
                    data: []
                }
            }
        },
    },

    data() {
        return {
      showText: '',
      area: area,
      dialogIsShow: false, // 是否展示弹框
      commonCity: [], // 热门城市
      provinceInfo: area.province_list, // 省
      cityInfo: {}, // 市
      areaInfo: {}, // 区
      activeName: '1', // 默认tab
      nameData: [], // 名字数组
      onlyOneId: 'select_all_region' + Math.random().toString().slice(2), // 每调用一次都会生成新的id
      onlyOneIdInput: 'select_all_region_input' + Math.random().toString().slice(2)
      }
    },
    methods: {
    // input焦点事件
    
   handleShowText(e, show) {
        console.log(e);
    this.dialogIsShow = !this.dialogIsShow
    },
     handleClick(tab, e) {
    console.log(e.target, 'target');
    },

     // tab选中事件

    handleContent(key, val, type) {
    if (type === 0) {
       
    this.selectedOptions.data[0] = key  // 将省的code和名字赋值对应的数组
    this.nameData[0] = val
   
    this.cityInfoEvent(key, type)  // 获取该省市的数据
    
    this.activeName = '2' // 切换tab栏

    } else if (type === 1) {
    this.selectedOptions.data[1] = key
    this.nameData[1] = val
    
    this.areaInfoEvent(key, type) // 获取该省区
    this.activeName = '3'

    } else if (type === 2) {
    this.selectedOptions.data[2] = key
    this.nameData[2] = val
    // 双向绑定做处理
      this.showText = this.nameData.join('/')
  
      this.dialogIsShow = !this.dialogIsShow
   
        this.$emit('myevent', this.showText)  //   触发父组件事件并传值
        
        this.$emit('update:data', this.selectedOptions.data) // 更新父组件的数据
    }
    },
    // 获取市
    cityInfoEvent () {
        this.cityInfo = {}
        let str = this.selectedOptions.data[0].substring(0, 2)
        console.log(str);
        Object.keys(area.city_list).forEach((e, i) => {
        
            if (e.substring(0, 2) === str) {
               this.cityInfo[e] = area.city_list[e]
            }
        })
    },
    // 获取区
    areaInfoEvent () {
        this.areaInfo = {}
        let str1 = this.selectedOptions.data[1].substring(0, 4)
        console.log(str1);
        Object.keys(area.county_list).forEach((e, i) => {
           
            if (e.substring(0, 4) === str1) {
               this.areaInfo[e] = area.county_list[e]
            }
        })
    },
        // 自定义指令触发的函数
    handleClose() {
        console.log(1);
        this.dialogIsShow = false;
    }
    }
}
</script>
<style lang="scss">
// 改动tab的样式
  .el-tabs {
    position: absolute;
    width: 450px;
    top:45px;
    z-index: 99;
    color: #606266;
    .el-tabs__content {
        .click_item {
            height: 20px !important;
            display: inline-block;
            margin: 0 5px 5px;
            cursor: pointer;
            &:hover {
                color: #429fd5;
            }
        }
        .current_blue {
            color: #429fd5;
        }
    }
  }
    .lh20 {
        line-height: 20px !important;
    }
}
</style>

父组件调用

  <c-Region  :selectedOptions="selectedOptionstest1" :data.sync="selectedOptionstest1.data" @myevent='getData'></c-Region>

在data里定义
export default {
		data() {
			return {
  selectedOptionstest1: {
	  			// 省市区使用方法
	  			data: [], // 省市区的code 
	  			callback(val) {}
	  	},
methods: {
子组件触发的父组件方法,此处其实可以不要,只是让父组件的模型可以拿到值
 getData(val) {
        console.log(val);
        this.mailingForm.input = val
    }}
}

在util里定义自定义组件

const clickOutSide = {
    // binding一个对象
    // vnode编译生成的虚拟节点
    bind(el, binding, vnode) {
    function clickHandler(e) {
        // 判断点击的元素是否是本身是返回
        if (el.contains(e.target)) {
           return false
        }
        // 判断指令中是否绑定了函数,如果绑定就调用那个方法,binding.value就是handleClose的方法
        if (binding.expression) {
            binding.value(e)
        }
    }
    // 给当前元素绑定个私有变量,方便在unbind中可以解除事件监听
    el._vueClickOutside_ = clickHandler
    document.addEventListener('click', clickHandler)
    },
    // unbind: 只调用一次, 指令与元素解绑时调用。
    unbind (el, binding) {
        document.removeEventListener('click', el._vueClickOutside_)
        delete el._vueClickOutside_
    }
}

export { clickOutSide }

如有不对地方请指出

以下是参考和用到的文档

自定义指令: vue 自定义指令clickOutside,点击弹窗之外区域关闭弹窗 - 简书

vue自定义指令VNode详解(转) - 为乐而来 - 博客园

Vue .sync修饰符与$emit(update:xxx)_CV斗士-CSDN博客

感恩所有博主提供的帮助与参考

JeecgBoot Vue3版提供了省市区组件。该组件可以实现省、市、区县的管理功能。在源码中的api.js文件中,有三个接口函数可以用来获取省、市、区县的数据,分别是provinceList、cityList和districtList。这些接口函数可以通过调用getAction方法来发送请求获取相应的数据。除此之外,还可以通过导入JAreaTreeTag组件来使用省市区组件。这个组件可以实现省市区的树形展示,并提供选择功能。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [JeecgBoot平台前端Vue3源码](https://download.csdn.net/download/dcs_pk/88099139)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [jeecgboot省市区联动](https://blog.csdn.net/iwu2495rff/article/details/105392743)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [jeecg-boot自定义省市区划控件](https://blog.csdn.net/qq_45645324/article/details/122898043)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值