uniapp vue3+ts H5 省市区选择器组件
因项目需求,需要一个特殊的选择器,项目使用的是 uniapp vue3+ts,插件库市场的省市区悬着器和需求有些差别,就自己动手写了一个,可实现的需求如下:
1、可通过点击按钮进去选择器组件,也可以通过输入框触发进入选择器组件
2、可以自己绑定数据
3、组件可返回最后一个选择结果或者一个多级的选择结果
组件效果图如下:
有视频展示
uniapp vue3+ts H5 省市区选择器组件(优快云
废话不多说了,上代码
组件代码
在这个 src/components 路径下新建文件 myRegion,然后新建 myRegion.vue
<template>
<view class="myRegion">
<view class="myRegion_c">
<view class="myRegion_ct">
<view class="myRegion_ctl"></view>
<view class="myRegion_ctc">请选择所在地区</view>
<view class="myRegion_ctr" @click="myClose"><text class="iconfont icon-fork"></text></view>
</view>
<view class="myRegion_cc">
<view v-if="arr.length > 0" class="box">
<view class="myRegion_cci" v-for="(p, o) in arr" :key="o" @click="againClick(p)">{{ p.areaName }}</view>
</view>
<view v-else class="myRegion_cci active">请选择</view>
</view>
<view class="myRegion_cb">
<view class="myRegion_cbi" v-for="(v, i) in regionList2" :key="i" @click="clickChange(v)">{{ v.areaName }}</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, onMounted, defineEmits } from 'vue'
import { getRegion } from '../../api/api' //此处填写你获取省市区的请求接口
let regionList = ref<any>([])
let regionList2 = ref<any>([])
let arr = ref<any>([])
let emit = defineEmits(['clickChange'])
onMounted(() => {
getRegionList()
})
// 获取地区数据 -------- (此处为获取省市区数据的请求方法,请根据你们自己的接口获取省市区数据)
let getRegionList = async () => {
let obj = {}
let res: any = await getRegion(obj)
if (res.code == 200) {
regionList.value = JSON.parse(JSON.stringify(res.data.varList)) //深拷贝
regionList2.value = JSON.parse(JSON.stringify(res.data.varList)) //深拷贝
} else {
uni.showToast({
title: res.message,
icon: 'error',
})
}
}
let clickChange = (v: any) => {
let obj: any = {
areaId: v.areaId,
areaName: v.areaName,
children: '',
}
let objs = {
areaId: 1,
areaName: '请选择',
}
if (arr.value.length > 0) {
arr.value.pop()
}
arr.value.push(obj)
if (v.children) {
obj.children = regionList2.value
regionList2.value = v.children
arr.value.push(objs)
} else {
// 没有子集了,返回数组的最后一个
emit('clickChange', { data1: JSON.stringify(obj), data2: false })
// 没有子集了,返回选择的地区数组
// emit('clickChange', { data1: JSON.stringify(arr.value), data2: false })
}
}
let againClick = (v: any) => {
let index = arr.value.findIndex((item: any) => item.areaName == v.areaName)
if (index == 0) {
regionList2.value = v.children
arr.value.splice(index, arr.value.length - index)
return false
}
if (index == arr.value.length - 1) {
return false
}
arr.value.splice(index, arr.value.length - index)
regionList2.value = v.children
let objs = {
areaId: 1,
areaName: '请选择',
}
arr.value.push(objs)
}
let myClose = () => {
emit('clickChange', { data1: '', data2: false })
}
</script>
<style lang="scss" scoped>
.myRegion {
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
background-color: rgba($color: #000000, $alpha: 0.1);
z-index: 10;
.myRegion_c {
width: 100%;
height: 60vh;
background-color: #fff;
position: absolute;
bottom: 0;
left: 0;
border-radius: 20rpx 20rpx 0 0;
overflow-y: auto;
.myRegion_ct {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100rpx;
display: flex;
align-items: center;
border-bottom: 1rpx solid rgba(0, 0, 0, 0.1);
.myRegion_ctl,
.myRegion_ctr {
width: 20%;
// font-size: 36rpx;
// font-weight: 500;
text-align: center;
padding-top: 10rpx;
box-sizing: border-box;
.icon-fork {
font-size: 40rpx;
}
}
.myRegion_ctc {
flex: 1;
text-align: center;
font-size: 36rpx;
// line-height: 200rpx;
}
}
.myRegion_cc {
position: absolute;
top: 100rpx;
left: 0;
width: 100%;
height: 100rpx;
// border-bottom: 4rpx solid rgba(13, 151, 214);
padding: 10rpx 20rpx;
box-sizing: border-box;
.box {
display: flex;
align-items: center;
}
.myRegion_cci {
// width: 150rpx;
height: 60rpx;
border-bottom: 4rpx solid rgba(13, 151, 214);
margin-right: 10rpx;
text-align: center;
line-height: 60rpx;
padding: 0 20rpx;
}
.active {
width: 150rpx;
}
}
.myRegion_cb {
width: 100%;
height: 100%;
margin-top: 200rpx;
overflow: auto;
padding: 0 20rpx;
box-sizing: border-box;
.myRegion_cbi {
width: 100%;
height: 80rpx;
line-height: 80rpx;
}
}
}
}
</style>
数据格式
//省市区的数据格式如下
[
{
areaId: 1,
areaName: "北京市",
children: [
{
areaId: 2,
areaName: "北京城区",
children: [
{
areaId: 3,
areaName: "东城区",
},
{
areaId: 4,
areaName: "西城区",
}
]
}
]
},
{
areaId: 19,
areaName: "天津市",
children: [
{
areaId: 20,
areaName: "天津城区",
children: [
{
areaId: 21,
areaName: "和平区",
},
{
areaId: 22,
areaName: "河东区",
}
]
},
{
areaId: 30,
areaName: "西区",
children: [
{
areaId: 31,
areaName: "XXX",
},
{
areaId: 32,
areaName: "XXX",
}
]
}
]
},
]
页面引入
<template>
//template引入
//触发组件事件 @click="choiceRegion"
<view class="search_rc" @click="choiceRegion" v-if="MyRegionData.areaName == null ? true : false"><text class="iconfont icon-ditu"></text></view>
<view class="search_rc" @click="choiceRegion" v-else>{{ MyRegionData.areaName }}</view>
//组件使用
<MyRegion v-if="MyRegion_show" ref="my_region" @clickChange="receiveChange"></MyRegion>
</template>
<script setup lang="ts">
import { ref} from 'vue'
import MyRegion from '../../components/myRegion/myRegion.vue'
let my_region = ref()
let MyRegion_show = ref<any>(false)
let searchInfo = ref<any>({
areaId: '', //区域
})
//组件数据接收
let receiveChange = (v: any) => {
console.log('接收的值', v)
if (v.data1) {
MyRegionData.value = JSON.parse(v.data1)
}
MyRegion_show.value = v.data2
searchInfo.value.areaId = MyRegionData.value.areaId
}
//触发组件显示
let choiceRegion = () => {
MyRegion_show.value = true
}
</script>
<style lang="scss" scoped>
//样式自己写,哈哈哈
</style>
提供两个处理数据不一样的递归方法
处理数组对象的键值对的键名不一样
//处理数据对象中键值对的键名不一样的,可处理多级嵌套,通过是否有 children 判断是否继续循环,使用 let arr = action(dataList)---- dataList 为你需要处理的数据
function action(data: any) {
// 使用递归函数
// if(!(data?.length <= 0)){
// 等价于
if (!data || data.length <= 0) {
// 递归的出口
return null
}
return data.map((x: any) => {
// 循环数据
const model: any = {
// 把后端返回过来的数据里面的键给替换成我想要的键
source: x,
text: x.areaName,
value: x.areaId,
}
const children = action(x.children) // 子级数据
if (children) {
// 一直往下循环查找有没有children这个键,如果有就直接添加一个子级字段名,这个字段名就是存子级数据
model.children = children
}
return model // 返回这个数据
})
}
多维数组转一维数组
//多维数组转一维数组
function flatten(arr: any) {
return [].concat(
...arr.map((item: any) => {
if (item.children) {
let arr = [].concat(item, ...flatten(item.children))
delete item.children
return arr
}
return [].concat(item)
})
)
}
到此大功告成,如有疑惑或者问题可留言联系我,看到我会第一时间回复的,欢迎大家一起来讨论,如果对你有帮助请点个赞 @_@