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博客
感恩所有博主提供的帮助与参考