省市区镇村五级联动

方法

bash 复制代码
<template>
  <uni-popup ref="popupRef" type="bottom" @change="popChange" :safe-area="false">
    <view class="zq-address-picker">
      <view class="zq-address-picker__close">
        <uni-icons class="absolute" :size="20" type="closeempty" @click="closePopup" />
      </view>
      <view class="zq-address-picker__title">请选择所在地区</view>

      <!-- 已选择区域 -->
      <uni-list v-for="(item, index) in activeItems" :key="item.code" :border="false">
        <uni-list-item show-arrow clickable @click="selectLevel(item)">
          <template #header>
            <view style="display: flex; align-items: center">
              <view
                style="
                  width: 11px;
                  height: 11px;
                  background-color: #fe4246;
                  border-radius: 50%;
                  margin-right: 7px;
                  position: relative;
                "
              >
                <view
                  :style="{
                    width: '1px',
                    height: '50px',
                    'background-color': '#fe4246',
                    position: 'absolute',
                    left: '5px',
                    top: index < 1 ? 0 : index < activeItems.length - 1 ? '-20px' : '-50px'
                  }"
                >
                </view>
              </view>
              <view :class="activeLevel === item.level - 1 || !item.name ? 'zq-address-picker__select--active' : ''">
                {{ item.name || selectTitle }}
              </view>
            </view>
          </template>
        </uni-list-item>
      </uni-list>

      <view class="zq-address-picker__list">
        <view class="zq-address-picker__list-title">{{ selectTitle }}</view>
        <view class="zq-address-picker__list-content" @click="reGetList">
          <uni-load-more
            v-if="loadingStatus !== 'more'"
            :status="loadingStatus"
            :content-text="{ contentnomore: '加载失败,请点击重试' }"
          />

          <!-- 省市区选择 -->
          <uni-list :border="false">
            <uni-list-item
              v-for="(item, index) in currentList"
              :key="item.code"
              :title="item.name"
              :border="false"
              clickable
              @click="selectHandle(item, index)"
            >
              <template #footer>
                <uni-icons
                  v-if="activeItems[item.level] ? activeItems[item.level].code === item.code : false"
                  type="checkmarkempty"
                  class="zq-address-picker__list-tab--active"
                />
              </template>
            </uni-list-item>
          </uni-list>
        </view>
      </view>
    </view>
  </uni-popup>
</template>
<script setup>
import { ref, reactive, computed, watch } from 'vue'
import UserApi from '@/api/userApi'
import { children } from '@/uni_modules/lime-painter/components/common/relation'
// Props 定义
const props = defineProps({
  value: Boolean,
  selected: {
    type: Array,
    default: () => []
  }
})

// Emits 定义
const emit = defineEmits(['input', 'confirm'])

// 数据定义
const loading = ref(true)
const loadingStatus = ref('loading')
const country = ref([])
const list = ref([])
const activeLevel = ref(-1)
const activeIndex = ref(-1)
const activeItems = ref([])

// 模板 ref
const popupRef = ref(null)
let isClosing = false
// 关闭弹窗
const closePopup = () => {
  if (isClosing) return
  isClosing = true
  popupRef.value?.close()
  emit('confirm', activeItems.value)
  nextTick(() => {
    isClosing = false
  })
}

// 弹窗状态变化
const popChange = (e) => {
  const status = e.show
  if (activeLevel.value !== 4 && !status) {
    activeLevel.value = -1
    activeIndex.value = -1
    activeItems.value = []
  }
  emit('input', status)
}

// 已选择项切换
const selectLevel = (item) => {
  if (item.code) {
    activeLevel.value = item.level - 1
  }
}

// 选择处理
const selectHandle = (item, index) => {
  // 判断点击的级别 0:省 1:市 2:区 3:镇
  // UserApi.getdistrictcity({ code: item.code })
  // 选择省市区
  activeLevel.value = item.level
  activeIndex.value = index
  // // 直接通过索引赋值(Vue3 响应式支持)
  activeItems.value[item.level] = item
  activeItems.value.splice(item.level + 1)

  if (item.level == 0) {
    // 获取市
    UserApi.getdistrictcity({ provinceCode: item.code }).then((res) => {
      eachList(res.data, 1, 'cityCode', 'cityName', { provinceCode: item.code })
      item.children = res.data
      activeItems.value[item.level + 1] = {}
    })
  } else if (item.level == 1) {
    // 获取区
    UserApi.getdistrictcountry({ cityCode: item.code, provinceCode: item.provinceCode }).then((res) => {
      eachList(res.data, 2, 'countryCode', 'countryName', { cityCode: item.code, provinceCode: item.provinceCode })
      item.children = res.data
      activeItems.value[item.level + 1] = {}
    })
  } else if (item.level == 2) {
    // 获取镇
    UserApi.getdistricttown({
      countryCode: item.code,
      cityCode: item.cityCode,
      provinceCode: item.provinceCode
    }).then((res) => {
      eachList(res.data, 3, 'townCode', 'townName', {
        countryCode: item.code,
        cityCode: item.cityCode,
        provinceCode: item.provinceCode
      })
      item.children = res.data
      activeItems.value[item.level + 1] = {}
    })
  } else if (item.level == 3) {
    // 获取村
    UserApi.getdistrictvillage({
      townCode: item.code,
      countryCode: item.countryCode,
      cityCode: item.cityCode,
      provinceCode: item.provinceCode
    }).then((res) => {
      eachList(res.data, 4, 'villageCode', 'villageName', {
        townCode: item.code,
        countryCode: item.countryCode,
        cityCode: item.cityCode,
        provinceCode: item.provinceCode
      })
      item.children = res.data
      activeItems.value[item.level + 1] = {}
    })
  } else if (item.level == 4) {
    emit('confirm', activeItems.value)
  }
}

const eachList = (arr, level, code, name, obj) => {
  arr.forEach((v) => {
    v.level = level
    v.code = v[code]
    v.name = v[name]
    Object.assign(v, obj)
    if (v.children) {
      eachList(v.children, v.level + 1)
    }
  })
}

// 获取中国省市区数据
const getCityData = () => {
  if (!list.value.length) {
    loadingStatus.value = 'loading'
    UserApi.getdistrictprovince().then((res) => {
      if (Array.isArray(res.data)) {
        eachList(res.data, 0, 'provinceCode', 'provinceName')
        list.value = res.data
        if (props.selected.length) {
          console.log(7777)

          const active = []
          if (props.selected.length !== 1) {
            active[0] = list.value.find((v) => v.name === props.selected[1])
            active[1] = active[0]?.children.find((v) => v.name === props.selected[2])
            active[2] = active[1]?.children.find((v) => v.name === props.selected[3])
            active[3] = active[2]?.children.find((v) => v.name === props.selected[4])
            activeItems.value = active
            activeLevel.value = 4
          } else {
            activeLevel.value = -1
          }
        }

        loadingStatus.value = 'more'
      } else {
        loadingStatus.value = 'noMore'
      }
    })
  } else {
    loadingStatus.value = 'more'
  }
}

// 重新获取列表
const reGetList = () => {
  if (loadingStatus.value === 'noMore') {
    getList()
  }
}

const getList = () => {
  getCityData()
}

// 计算属性
const currentList = computed(() => {
  if (activeLevel.value !== -1) {
    return activeItems.value[activeLevel.value]?.children || []
  }
  return list.value
})

const selectTitle = computed(() => {
  const titles = {
    '-1': '请选择省份/地区',
    0: '请选择城市',
    1: '请选择区/县',
    2: '请选择街道/镇',
    3: '请选择社区/村庄'
  }
  return titles[activeLevel.value]
})

const showPick = () => {
  activeItems.value = []
  popupRef.value?.open()
  activeItems.value = props.selected || []
  getList()
}
const closePick = () => {
  closePopup()
}
defineExpose({
  showPick,
  closePick
})
</script>
<style lang="scss" scoped>
/* 样式保持不变 */

.zq-address-picker {
  border-top-left-radius: 20px;
  border-top-right-radius: 20px;
  background-color: white;
  padding-left: 8px;
  padding-right: 8px;
  padding-top: 16px;
  font-size: 16px;
  &__close {
    text-align: right;
    margin-right: 42px;
    margin-top: 5px;
    position: absolute;
    left: 0;
    right: 0;
    color: red;
  }

  &__title {
    font-weight: 500;
    font-size: 20px;
    color: #313440;
    line-height: 28px;
    text-align: center;
    font-style: normal;
  }

  &__select {
    &--active {
      color: $uni-color-primary;
    }
  }

  &__list {
    max-height: 48vh;
    overflow-y: auto;
    margin-top: 12px;

    &-tab {
      display: flex;
      margin-bottom: 20px;
      justify-content: space-between;
      padding-left: 16px;
      padding-right: 16px;

      &--hide {
        display: none;
      }

      &--active {
        color: $uni-color-primary !important;
      }
    }

    &-title {
      font-weight: bold;
      margin-bottom: 16px;
    }

    .uni-load-more {
      padding: 20px 0;
    }
  }

  &__quick {
    display: flex;
    flex-wrap: wrap;
    font-size: 14px;
    gap: 12px;
    margin-bottom: 14px;

    &-label {
      padding: 5px 12px;
      display: block;
      border-radius: 99px;
      background-color: #f4f3f3;
    }

    &-radio {
      display: none;
    }

    &--active {
      background-color: transparentize($uni-color-primary, 0.94);
      color: $uni-color-primary;
    }
  }
}
</style>

使用

bash 复制代码
    <pick @confirm="confirm" :selected="selectCity" ref="pickRef"></pick>

预览

相关推荐
智海观潮2 小时前
只用一周时间通过AI工具重写Next.js,Cloudflare推出vinext重建前端开发边界
开发语言·javascript·人工智能·大模型·web
窝子面2 小时前
二十三、第三方登录
前端·javascript·html
好运yoo2 小时前
在package.json中scripts这个配置的命令是什么意思
前端·webpack·json·vite·wepack
C澒2 小时前
前端跨业务线代码复用标准化体系构建与实践
前端·架构
big_rabbit05022 小时前
[算法][力扣242]有效的字母异位词
java·前端·leetcode
晨之清风2 小时前
Edge/Chrome安装浏览器AI侧边栏插件DeepSider
人工智能·chrome·edge·浏览器插件
A923A2 小时前
【Vue3大事件 | 项目笔记】第一天
前端·vue.js·笔记·前端框架
IT_陈寒2 小时前
SpringBoot自动配置揭秘:90%开发者不知道的核心原理
前端·人工智能·后端
huangyiyi666662 小时前
webpack + Vite
前端·webpack·node.js