Vue3.0 手写省市区三级联动组件
全国地区数据会很大,我们可以直接用这个地址https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json实现的效果如下:考虑到城市组件也会在项目其它地方使用,所以把城市组件定义成全局组件。来复习一下vue3.0中,将组件封装成全局的:1.src/components 下新建.vue文件,用来放城市组件,2.3.知道了实现
全国地区数据会很大,我们可以直接用这个地址
https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json
实现的效果如下:
考虑到城市组件也会在项目其它地方使用,所以把城市组件定义成全局组件。
来复习一下vue3.0中,将组件封装成全局的:
1.src/components 下新建.vue文件,用来放城市组件,
2.
3.
知道了实现的效果是: 当我点击这个城市组件的时候,弹层显示,再次点击弹层隐藏,点击组件外面区域也可关闭弹层。
vueuse/core中onClickOutside 可以监听在元素之外点击:
https://vueuse.org/core/onclickoutside/#onclickoutside
使用这个vueuse里面的Api记得先装包!!!!
<template>
<div class="xtx-city" ref="target">
<div class="select" @click="toggle" :class="{active: show}">
<span class="placeholder">请选择配送地址</span>
<span class="value"></span>
<i class="iconfont icon-angle-down"></i>
</div>
<div class="option" v-show="show">
<span class="ellipsis" v-for="i in 24" :key="i">北京市</span>
</div>
</div>
</template>
<script>
import { ref } from 'vue'
+ import axios from 'axios'
import { onClickOutside } from '@vueuse/core'
export default {
name: 'XtxCity',
setup () {
+ const url = 'https://yjy-oss-files.oss-cn-zhangjiakou.aliyuncs.com/tuxian/area.json'
const show = ref(false)
+ const getCityData = () => {
+ return axios({ url })
}
const close = () => {
show.value = false
}
const open = () => {
show.value = true
}
const toggle = () => {
show.value === true ? close() : open()
}
const target = ref(null)
onClickOutside(target, () => {
close()
})
return { show, toggle, open, close, target,+ getCityData }
}
}
</script>
<style scoped lang="less">
.xtx-city {
display: inline-block;
position: relative;
z-index: 400;
.select {
border: 1px solid #e4e4e4;
height: 30px;
padding: 0 5px;
line-height: 28px;
cursor: pointer;
&.active {
background: #fff;
}
.placeholder {
color: #999;
}
.value {
color: #666;
font-size: 12px;
}
i {
font-size: 12px;
margin-left: 5px;
}
}
.option {
width: 542px;
border: 1px solid #e4e4e4;
position: absolute;
left: 0;
top: 29px;
background: #fff;
min-height: 30px;
line-height: 30px;
display: flex;
flex-wrap: wrap;
padding: 10px;
> span {
width: 130px;
text-align: center;
cursor: pointer;
border-radius: 4px;
padding: 0 3px;
&:hover {
background: #f5f5f5;
}
}
}
}
</style>
开头说到,渲染数据时,使用的一个地址,那么在代码中如何运用? 使用时,我添加到了上面代码段中,带 + 号的
接下来,考虑一个问题,什么时候发请求,拿全国数据呢? 组件开始创建的时候吗?
答案是:当这个弹出层打开时去发请求,因为用户可能不点击这个城市组件,如果你在组件创建的时候就去发请求,用户不点击,全国数据又很大,浪费资源
const citydata = ref([ ]) // 接收城市数据
如果用户没有登录:当前商品数据中,后端会传递 userAddresses: null, 此时,我们应该用默认地址:北京市 市辖区 东城区
如果用户已经登录:当前商品数据中,后端会传递 userAddresses: 地址数组:
因为我把城市组件单独放在一个文件中了,而上面这幅图 里面的fullLocation是我请求下图左侧数据时返回来的,对于我这个组件来说,属于父传子操作
这段代码是写在父组件中的
setup(){
// 默认情况
const provinceCode = ref('110000')
const cityCode = ref('119900')
const countyCode = ref('110101')
const fullLocation = ref('北京市 市辖区 东城区')
// 有默认地址
if (props.data.userAddresses) { // 如果userAddresses有值
//找userAddresses数组中哪一项的isDefault ===1
const defaultAddr = props.data.userAddresses.find(addr => addr.isDefault === 1)
if (defaultAddr) { // 如果有,取到它的数据项
provinceCode.value = defaultAddr.provinceCode
cityCode.value = defaultAddr.cityCode
countyCode.value = defaultAddr.countyCode
fullLocation.value = defaultAddr.fullLocation
}
}
}
把数据传给子组件
<XxxCity :fullLocation="fullLocation" />
子组件接收数据渲染
props: {
fullLocation: { type: String, default: '' }
},
接下来是实现子组件里面选省市区之后,父组件上的数据同步更新:
setup(props, { emit }){
const changeResult = reactive({
provinceCode: '',
provinceName: '',
cityCode: '',
cityName: '',
countyCode: '',
countyName: '',
fullLocation: ''
})
const changeItem = (item) => {
// 省份
if (item.level === 0) {
changeResult.provinceCode = item.code
changeResult.provinceName = item.name
}
// 市
if (item.level === 1) {
changeResult.cityCode = item.code
changeResult.cityName = item.name
}
// 区
if (item.level === 2) {
changeResult.countyCode = item.code
changeResult.countyName = item.name
changeResult.fullLocation = `${changeResult.provinceName} ${changeResult.cityName} ${changeResult.countyName}`
close() // close也是写在了子组件中,所以直接调用即可
emit('change', changeResult)
}
}
//计算属性,根据我点击的省,自动去找市和区
const currList = computed(() => {
let currList = citydata.value
if (changeResult.provinceCode) {
currList = currList.find(it => it.code === changeResult.provinceCode).areaList
}
if (changeResult.cityCode) {
currList = currList.find(c => c.code === changeResult.cityCode).areaList
}
return currList
})
// return { currList ...........} 外面使用记得rerurn
}
父组件接收事件:
<dd>至 <XtxCity :fullLocation="fullLocation" @change="changeCity"/></dd>
const changeCity = (result) => {
provinceCode.value = result.provinceCode
cityCode.value = result.cityCode
countyCode.value = result.countyCode
fullLocation.value = result.fullLocation
}
return { ....., changeCity }
每次打开的时候,清空选择结果:
更多推荐
所有评论(0)