AI编写自己修改
三级联动方式

image.png
搜索方式

image.png
<template>
<view class="area-picker">
<!-- 触发按钮 -->
<!-- <view class="trigger" @click="open">
<text>{{ selectedArea || '请选择地区' }}</text>
<text class="icon">▼</text>
</view> -->
<view class="" @click="showPicker = true">
{{ selectedArea ? selectedArea :'请选择省市区'}}
<i class="iconfont icon-Downarrow-R" @click="open"></i>
</view>
<!-- 地区选择弹窗 -->
<u-popup
v-model="showPicker"
mode="bottom"
round
@close="showPicker = false"
>
<view class="picker-header">
<text class="title">选择地区</text>
<text class="close" @click="showPicker = false">×</text>
</view>
<!-- 搜索框 -->
<view class="search-box">
<u-search
v-model="searchKeyword"
placeholder="搜索省/市/区"
@search="onSearch"
/>
</view>
<!-- 三级联动选择器 -->
<view v-if="!searchKeyword" class="picker-container">
<picker-view
:value="pickerValue"
@change="onPickerChange"
class="picker-view"
indicator-style="height: 44px;"
>
<!-- 省份列 -->
<picker-view-column>
<view
v-for="(province, index) in provinces"
:key="province.code"
class="picker-item"
>
{{ province.name }}
</view>
</picker-view-column>
<!-- 城市列 -->
<picker-view-column>
<view
v-for="(city, index) in currentCities"
:key="city.code"
class="picker-item"
>
{{ city.name }}
</view>
</picker-view-column>
<!-- 区县列 -->
<picker-view-column>
<view
v-for="(district, index) in currentDistricts"
:key="district.code"
class="picker-item"
>
{{ district.name }}
</view>
</picker-view-column>
</picker-view>
</view>
<!-- 搜索结果列表 -->
<view v-else class="search-results">
<scroll-view scroll-y class="results-scroll">
<view
v-for="(item, index) in filteredAreas"
:key="item.code"
class="result-item"
@click="selectSearchResult(item)"
>
<text class="result-text">{{ item.fullPath }}</text>
</view>
<view v-if="filteredAreas.length === 0" class="empty">
未找到相关地区
</view>
</scroll-view>
</view>
<!-- 确认按钮 -->
<view class="actions">
<u-button type="default" @click="showPicker = false">取消</u-button>
<u-button type="primary" @click="confirmSelection">确认</u-button>
</view>
</u-popup>
</view>
</template>
<script>
// 这里需要导入地区数据,实际项目中可以从接口获取或使用静态数据
import areaData from './area-data.js'
import {
getRegionsList,
getProvinceApi,
getProvinceList,
getMerchMcc,
getAreaApi,
getCityApi,
getKeywords
} from '_a/home';
export default {
name: 'AreaPicker',
props: {
value: {
type: String,
default: ''
},
sourceType: {
type: String,
default: ''
}
},
data() {
return {
showPicker: false,
searchKeyword: '',
pickerValue: [0, 0, 0],
lastPickerValue: [0, 0, 0],
provinces: [],
cities: [],
districts: [],
searchResults: [],
selectedArea: '',
allAreas: ''
}
},
computed: {
currentCities() {
// if (this.provinces.length === 0) return []
// const provinceIndex = this.pickerValue[0]
// return this.provinces[provinceIndex]?.children || []
// this.cities = await this.getCityApiData(this.provinces[this.pickerValue[0]].code, this.channelCode);
// console.log('触发城市列表...')
// console.log(this.cities)
return this.cities;
},
currentDistricts() {
// if (this.currentCities.length === 0) return []
// const cityIndex = this.pickerValue[1]
// return this.currentCities[cityIndex]?.children || []
return this.districts;
},
// 根据搜索关键词过滤地区
filteredAreas() {
if (!this.searchKeyword.trim()) {
return this.allAreas
}
const kw = this.searchKeyword.toLowerCase()
return this.allAreas.filter(item => {
return (
item.fullPath.toLowerCase().includes(kw) ||
item.name.toLowerCase().includes(kw)
)
})
}
},
mounted() {
this.initAreaData()
if (this.value) {
this.selectedArea = this.value
}
},
methods: {
async getProvinceApiData(channelCode) {
let res = await getProvinceApi({channelCode});
// this.provinceList = res.data;
return res.data
},
async getCityApiData(code,channelCode) {
let res = await getCityApi({
code,channelCode
});
// this.provinceList = res.data;
return res.data
},
async getAreaApiData(code,channelCode) {
let res = await getAreaApi({
code,channelCode
});
// this.provinceList = res.data;
return res.data;
},
async loadFlattenedRegions(channelCode) {
this.loading = true;
try {
// 替换为你的实际接口地址
// const res = await this.$http.get('/api/v1/regions/flattened');
const res = await getRegionsList({channelCode});
this.allAreas = res.data || [];
console.log('加载扁平话内容... code' + channelCode)
} catch (error) {
console.error('加载地区数据失败:', error);
uni.showToast({ title: '地区数据加载失败', icon: 'none' });
} finally {
this.loading = false;
}
},
open() {
this.showPicker = true
console.log('打开')
},
// 初始化地区数据
async initAreaData() {
console.log('初始化的省市区数据')
console.log(this.sourceType)
// 这里使用模拟数据,实际项目中可以替换为真实数据
// this.provinces = areaData
this.provinces = await this.getProvinceApiData(this.sourceType);
this.cities = await this.getCityApiData(this.provinces[0].code, this.sourceType);
this.districts = await this.getAreaApiData(this.cities[0].code, this.sourceType);
this.loadFlattenedRegions(this.sourceType)
},
// 选择器变化
async onPickerChange(e) {
// console.log('选择器变化对象')
// console.log(e)
this.pickerValue = e.detail.value
const newValue = e.detail.value
const changedColumn = this.getChangedColumn(this.lastPickerValue, newValue)
// 当省份变化时,重置城市和区县
if (changedColumn == 0) {
// this.pickerValue = [this.pickerValue[0], 0, 0]
this.cities = await this.getCityApiData(this.provinces[newValue[0]].code, this.sourceType);
this.districts = await this.getAreaApiData(this.cities[newValue[1]].code, this.sourceType);
// console.log('触发城市列表...')
console.log(this.cities)
console.log(this.districts)
}
// 当城市变化时,重置区县
else if (changedColumn == 1) {
// this.pickerValue = [this.pickerValue[0], this.pickerValue[1], 0]
this.districts = await this.getAreaApiData(this.cities[newValue[1]].code, this.sourceType);
// console.log('触发区域列表...')
console.log(this.districts)
}
this.lastPickerValue = [...newValue]
await this.$forceUpdate();
},
getChangedColumn(oldValue, newValue) {
for (let i = 0; i < newValue.length; i++) {
if (oldValue[i] !== newValue[i]) {
return i
}
}
return -1 // 没有变化
},
// 选择搜索结果
selectSearchResult(item) {
this.selectedArea = item.fullPath
this.searchKeyword = ''
this.showPicker = false
this.selectedCodes = item.codes
this.$emit('input', item.fullName)
let codeList = item.fullCode.split('-')
let pathList = item.fullPath.split('-')
this.$emit('change', {
fullCode: item.fullCode,
fullPath: item.fullPath,
mercPro: codeList[0],
mercCity: codeList[1],
mercArea: codeList[2],
mercProName: pathList[0],
mercCityName: pathList[1],
mercAreaName: pathList[2]
})
},
// 确认选择
confirmSelection() {
const province = this.provinces[this.pickerValue[0]]
const city = this.currentCities[this.pickerValue[1]]
const district = this.currentDistricts[this.pickerValue[2]]
if (province && city && district) {
this.selectedArea = `${province.name}-${city.name}-${district.name}`
this.selectedCodes = [province.code, city.code, district.code]
// console.log('省市区')
// console.log(province)
// console.log('城市')
// console.log(city)
// console.log('区域')
// console.log(district)
// this.$emit('input', this.selectedArea)
// this.$emit('change', {
// area: this.selectedArea,
// codes: this.selectedCodes,
// province: province.name,
// city: city.name,
// district: district.name
// })
this.$emit('change', {
fullCode: this.selectedCodes,
fullPath: this.selectedArea,
mercPro: province.code,
mercCity: city.code,
mercArea: district.code,
mercProName: province.name,
mercCityName: city.name,
mercAreaName: district.name
})
}
this.showPicker = false
}
}
}
</script>
<style scoped>
.area-picker {
}
.trigger {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx;
background: #f7f8fa;
border-radius: 8rpx;
border: 1px solid #ebedf0;
}
.trigger .icon {
color: #969799;
font-size: 24rpx;
}
.picker-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 32rpx;
border-bottom: 1px solid #ebedf0;
}
.picker-header .title {
font-size: 32rpx;
font-weight: 500;
}
.picker-header .close {
font-size: 40rpx;
color: #969799;
}
.search-box {
padding: 0 32rpx;
}
.picker-container {
height: 400rpx;
}
.picker-view {
height: 100%;
}
.picker-item {
display: flex;
align-items: center;
justify-content: center;
height: 88rpx;
font-size: 28rpx;
}
.search-results {
height: 400rpx;
}
.results-scroll {
height: 100%;
}
.result-item {
padding: 24rpx 32rpx;
border-bottom: 1px solid #f5f5f5;
}
.result-item:active {
background-color: #f2f3f5;
}
.result-text {
font-size: 28rpx;
}
.empty {
text-align: center;
padding: 80rpx;
color: #969799;
font-size: 28rpx;
}
.actions {
display: flex;
padding: 32rpx;
gap: 16rpx;
}
.actions van-button {
flex: 1;
}
</style>
后端接口说明:
三级联动方式:省市区,分为三个接口分别获取,根据上一个地区编码获取下一个区域。
搜索方式:将省市区通过一个接口获取,每个区域都同时显示省市区完整信息。