<template>
|
<view>
|
<view class="area-select-display" @click="openPicker">
|
<text :class="{ placeholder: !displayText }">{{ displayText || '请选择省/市/区' }}</text>
|
<text class="icon-arrow">›</text>
|
</view>
|
|
<u-picker
|
ref="pickerRef"
|
:show="pickerShow"
|
:columns="columns"
|
keyName="name"
|
@confirm="onConfirm"
|
@change="onChange"
|
@cancel="pickerShow = false"
|
title="选择地区"
|
/>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, computed, onMounted, nextTick } from 'vue'
|
|
const props = defineProps({
|
modelValue: {
|
type: Object,
|
default: () => ({})
|
}
|
})
|
|
const emit = defineEmits(['update:modelValue', 'change'])
|
|
const pickerShow = ref(false)
|
const columns = ref([[], [], []])
|
const pickerRef = ref(null)
|
|
// 存储数据
|
const provinceList = ref([]) // [{ name, code, cities }]
|
const cityMap = ref(new Map()) // provinceCode -> cities
|
const districtMap = ref(new Map()) // cityCode -> districts
|
|
const displayText = computed(() => {
|
const { sheng, shi, qu } = props.modelValue
|
if (sheng && shi && qu) return `${sheng} ${shi} ${qu}`
|
if (sheng && shi) return `${sheng} ${shi}`
|
if (sheng) return sheng
|
return ''
|
})
|
|
// 加载地区数据
|
const loadRegionData = async () => {
|
try {
|
const res = await uni.$uapi.get('/project/dict/treeselect')
|
if (res.code === 200 && res.data) {
|
buildMappings(res.data)
|
} else {
|
console.error('获取地区数据失败', res)
|
}
|
} catch (error) {
|
console.error('地区数据接口异常', error)
|
}
|
}
|
|
// 构建映射
|
const buildMappings = (treeData) => {
|
provinceList.value = []
|
cityMap.value.clear()
|
districtMap.value.clear()
|
|
treeData.forEach(province => {
|
const provinceName = province.areaname
|
const provinceCode = province.areacode
|
const cities = []
|
|
if (province.subarea && province.subarea.length) {
|
province.subarea.forEach(city => {
|
const cityName = city.areaname
|
const cityCode = city.areacode
|
const districts = []
|
|
if (city.subarea && city.subarea.length) {
|
city.subarea.forEach(district => {
|
districts.push({
|
name: district.areaname,
|
code: district.areacode
|
})
|
})
|
}
|
districtMap.value.set(cityCode, districts)
|
cities.push({
|
name: cityName,
|
code: cityCode,
|
districts: districts
|
})
|
})
|
}
|
|
provinceList.value.push({
|
name: provinceName,
|
code: provinceCode,
|
cities: cities
|
})
|
cityMap.value.set(provinceCode, cities)
|
})
|
|
// 初始化第一列
|
const firstColumn = provinceList.value.map(p => p.name)
|
columns.value = [firstColumn, [], []]
|
}
|
|
// 选择器变化事件(使用 setColumnValues)
|
const onChange = (e) => {
|
const { columnIndex, value, index, picker = pickerRef.value } = e
|
if (columnIndex === 0) {
|
// 省变化:更新第二列(市)
|
const province = provinceList.value[index]
|
if (province && province.cities) {
|
const cityNames = province.cities.map(c => c.name)
|
picker.setColumnValues(1, cityNames)
|
// 清空第三列
|
picker.setColumnValues(2, [])
|
} else {
|
picker.setColumnValues(1, [])
|
picker.setColumnValues(2, [])
|
}
|
} else if (columnIndex === 1) {
|
// 市变化:更新第三列(区县)
|
const provinceIdx = e.values[0] ? provinceList.value.findIndex(p => p.name === e.values[0]) : -1
|
if (provinceIdx !== -1) {
|
const province = provinceList.value[provinceIdx]
|
const city = province.cities[index]
|
if (city && city.districts) {
|
const districtNames = city.districts.map(d => d.name)
|
picker.setColumnValues(2, districtNames)
|
} else {
|
picker.setColumnValues(2, [])
|
}
|
} else {
|
picker.setColumnValues(2, [])
|
}
|
}
|
}
|
|
// 打开选择器(支持回显)
|
const openPicker = async () => {
|
pickerShow.value = true
|
await nextTick()
|
const picker = pickerRef.value
|
if (!picker) return
|
|
const { sheng, shi, qu } = props.modelValue
|
if (sheng) {
|
// 查找省索引
|
const provinceIdx = provinceList.value.findIndex(p => p.name === sheng)
|
if (provinceIdx !== -1) {
|
const province = provinceList.value[provinceIdx]
|
// 设置第一列选中
|
picker.setIndexes([provinceIdx, 0, 0])
|
// 更新第二列数据
|
const cityNames = province.cities.map(c => c.name)
|
picker.setColumnValues(1, cityNames)
|
if (shi) {
|
const cityIdx = province.cities.findIndex(c => c.name === shi)
|
if (cityIdx !== -1) {
|
// 设置第二列选中
|
picker.setIndexes([provinceIdx, cityIdx, 0])
|
// 更新第三列数据
|
const city = province.cities[cityIdx]
|
const districtNames = city.districts.map(d => d.name)
|
picker.setColumnValues(2, districtNames)
|
if (qu) {
|
const districtIdx = city.districts.findIndex(d => d.name === qu)
|
if (districtIdx !== -1) {
|
picker.setIndexes([provinceIdx, cityIdx, districtIdx])
|
}
|
}
|
}
|
}
|
} else {
|
// 未找到,重置
|
picker.setColumnValues(1, [])
|
picker.setColumnValues(2, [])
|
picker.setIndexes([0, 0, 0])
|
}
|
} else {
|
// 无预选,重置列
|
picker.setColumnValues(1, [])
|
picker.setColumnValues(2, [])
|
picker.setIndexes([0, 0, 0])
|
}
|
}
|
|
// 确认选择
|
const onConfirm = (e) => {
|
const { value: values, indexes } = e
|
const provinceName = values[0]
|
const cityName = values[1]
|
const districtName = values[2]
|
const province = provinceList.value.find(p => p.name === provinceName)
|
if (province) {
|
const city = province.cities.find(c => c.name === cityName)
|
const district = city ? city.districts.find(d => d.name === districtName) : null
|
const newValue = {
|
sheng: province.name,
|
shi: city ? city.name : '',
|
qu: district ? district.name : '',
|
provinceCode: province.code,
|
cityCode: city ? city.code : '',
|
districtCode: district ? district.code : ''
|
}
|
emit('update:modelValue', newValue)
|
emit('change', newValue)
|
}
|
pickerShow.value = false
|
}
|
|
defineExpose({ refresh: loadRegionData })
|
|
onMounted(() => {
|
loadRegionData()
|
})
|
</script>
|
|
<style lang="scss" scoped>
|
.area-select-display {
|
background: #fafafa;
|
border-radius: 12rpx;
|
padding: 20rpx 24rpx;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
border: 2rpx solid #e5e5e7;
|
transition: border-color 0.25s;
|
&:active {
|
border-color: #0f95b0;
|
}
|
.placeholder {
|
color: #b0b0b0;
|
}
|
.icon-arrow {
|
font-size: 34rpx;
|
color: #86868b;
|
transform: rotate(90deg);
|
}
|
}
|
</style>
|