uniapp vue3+ts H5 省市区选择器组件

uniapp vue3+ts H5 省市区选择器组件

因项目需求,需要一个特殊的选择器,项目使用的是 uniapp vue3+ts,插件库市场的省市区悬着器和需求有些差别,就自己动手写了一个,可实现的需求如下:

1、可通过点击按钮进去选择器组件,也可以通过输入框触发进入选择器组件

2、可以自己绑定数据

3、组件可返回最后一个选择结果或者一个多级的选择结果

组件效果图如下:
在这里插入图片描述
有视频展示

uniapp vue3+ts H5 省市区选择器组件(CSDN

废话不多说了,上代码

组件代码

在这个 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)
    })
  )
}


到此大功告成,如有疑惑或者问题可留言联系我,看到我会第一时间回复的,欢迎大家一起来讨论,如果对你有帮助请点个赞 @_@

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值