|
@ -0,0 +1,280 @@ |
|
|
|
|
|
<template> |
|
|
|
|
|
<uv-popup ref="popup" mode="bottom" :round="30" |
|
|
|
|
|
:safeAreaInsetBottom="false" @close="handleClose"> |
|
|
|
|
|
<view class="address-picker"> |
|
|
|
|
|
<view class="header"> |
|
|
|
|
|
<view class="title">选择地址</view> |
|
|
|
|
|
<view class="close-btn" @click="close"> |
|
|
|
|
|
<uv-icon name="close" size="40rpx"></uv-icon> |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> |
|
|
|
|
|
|
|
|
|
|
|
<view class="content"> |
|
|
|
|
|
<!-- 左侧一级地址列表 --> |
|
|
|
|
|
<view class="left-panel"> |
|
|
|
|
|
<scroll-view scroll-y class="scroll-view"> |
|
|
|
|
|
<view |
|
|
|
|
|
class="address-item" |
|
|
|
|
|
:class="{ active: selectedProvince && selectedProvince.id === item.id }" |
|
|
|
|
|
v-for="item in addressList" |
|
|
|
|
|
:key="item.id" |
|
|
|
|
|
@click="selectProvince(item)"> |
|
|
|
|
|
{{ item.adress }} |
|
|
|
|
|
</view> |
|
|
|
|
|
</scroll-view> |
|
|
|
|
|
</view> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 右侧二三级地址列表 --> |
|
|
|
|
|
<view class="right-panel"> |
|
|
|
|
|
<scroll-view scroll-y class="scroll-view"> |
|
|
|
|
|
<!-- 二级地址 --> |
|
|
|
|
|
<template v-if="selectedProvince && !selectedCity"> |
|
|
|
|
|
<view |
|
|
|
|
|
class="address-item" |
|
|
|
|
|
v-for="item in cityList" |
|
|
|
|
|
:key="item.id" |
|
|
|
|
|
@click="selectCity(item)"> |
|
|
|
|
|
{{ item.adress }} |
|
|
|
|
|
</view> |
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
|
|
<!-- 三级地址 --> |
|
|
|
|
|
<template v-if="selectedCity"> |
|
|
|
|
|
<view |
|
|
|
|
|
class="address-item back-item" |
|
|
|
|
|
@click="backToCity"> |
|
|
|
|
|
<uv-icon name="arrow-left" size="30rpx"></uv-icon> |
|
|
|
|
|
返回{{ selectedProvince.adress }} |
|
|
|
|
|
</view> |
|
|
|
|
|
<view |
|
|
|
|
|
class="address-item" |
|
|
|
|
|
v-for="item in districtList" |
|
|
|
|
|
:key="item.id" |
|
|
|
|
|
@click="selectDistrict(item)"> |
|
|
|
|
|
{{ item.adress }} |
|
|
|
|
|
</view> |
|
|
|
|
|
</template> |
|
|
|
|
|
</scroll-view> |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> |
|
|
|
|
|
</view> |
|
|
|
|
|
</uv-popup> |
|
|
|
|
|
</template> |
|
|
|
|
|
|
|
|
|
|
|
<script> |
|
|
|
|
|
import { mapState } from 'vuex' |
|
|
|
|
|
|
|
|
|
|
|
export default { |
|
|
|
|
|
name: 'AddressPicker', |
|
|
|
|
|
props: { |
|
|
|
|
|
// 是否只选择到市级,不选择区县 |
|
|
|
|
|
onlyCity: { |
|
|
|
|
|
type: Boolean, |
|
|
|
|
|
default: false |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
data() { |
|
|
|
|
|
return { |
|
|
|
|
|
selectedProvince: null, // 选中的省份 |
|
|
|
|
|
selectedCity: null, // 选中的城市 |
|
|
|
|
|
selectedDistrict: null, // 选中的区县 |
|
|
|
|
|
cityList: [], // 城市列表 |
|
|
|
|
|
districtList: [], // 区县列表 |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
computed: { |
|
|
|
|
|
...mapState(['addressList']) |
|
|
|
|
|
}, |
|
|
|
|
|
methods: { |
|
|
|
|
|
// 打开弹窗 |
|
|
|
|
|
open() { |
|
|
|
|
|
this.$refs.popup.open() |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 关闭弹窗 |
|
|
|
|
|
close() { |
|
|
|
|
|
this.$refs.popup.close() |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 弹窗关闭时重置状态 |
|
|
|
|
|
handleClose() { |
|
|
|
|
|
this.selectedProvince = null |
|
|
|
|
|
this.selectedCity = null |
|
|
|
|
|
this.selectedDistrict = null |
|
|
|
|
|
this.cityList = [] |
|
|
|
|
|
this.districtList = [] |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 选择省份 |
|
|
|
|
|
async selectProvince(province) { |
|
|
|
|
|
this.selectedProvince = province |
|
|
|
|
|
this.selectedCity = null |
|
|
|
|
|
this.selectedDistrict = null |
|
|
|
|
|
this.districtList = [] |
|
|
|
|
|
|
|
|
|
|
|
// 获取城市列表 |
|
|
|
|
|
try { |
|
|
|
|
|
this.cityList = await this.$store.dispatch('getChildAddressList', province.id) |
|
|
|
|
|
// 如果没有下级城市,直接确认选择 |
|
|
|
|
|
if (this.cityList.length === 0) { |
|
|
|
|
|
this.confirm() |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('获取城市列表失败:', error) |
|
|
|
|
|
this.cityList = [] |
|
|
|
|
|
// 获取失败时也直接确认 |
|
|
|
|
|
this.confirm() |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 选择城市 |
|
|
|
|
|
async selectCity(city) { |
|
|
|
|
|
this.selectedCity = city |
|
|
|
|
|
this.selectedDistrict = null |
|
|
|
|
|
|
|
|
|
|
|
// 如果只选择到市级,直接确认 |
|
|
|
|
|
if (this.onlyCity) { |
|
|
|
|
|
this.confirm() |
|
|
|
|
|
return |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 获取区县列表 |
|
|
|
|
|
try { |
|
|
|
|
|
this.districtList = await this.$store.dispatch('getChildAddressList', city.id) |
|
|
|
|
|
// 如果没有下级地址,直接确认 |
|
|
|
|
|
if (this.districtList.length === 0) { |
|
|
|
|
|
this.confirm() |
|
|
|
|
|
} |
|
|
|
|
|
} catch (error) { |
|
|
|
|
|
console.error('获取区县列表失败:', error) |
|
|
|
|
|
this.districtList = [] |
|
|
|
|
|
// 获取失败时也直接确认 |
|
|
|
|
|
this.confirm() |
|
|
|
|
|
} |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 选择区县 |
|
|
|
|
|
selectDistrict(district) { |
|
|
|
|
|
this.selectedDistrict = district |
|
|
|
|
|
this.confirm() |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 返回城市选择 |
|
|
|
|
|
backToCity() { |
|
|
|
|
|
this.selectedCity = null |
|
|
|
|
|
this.selectedDistrict = null |
|
|
|
|
|
this.districtList = [] |
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
// 确认选择 |
|
|
|
|
|
confirm() { |
|
|
|
|
|
const result = { |
|
|
|
|
|
province: this.selectedProvince, |
|
|
|
|
|
city: this.selectedCity, |
|
|
|
|
|
district: this.selectedDistrict |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// 生成完整地址文本 |
|
|
|
|
|
let fullAddress = '' |
|
|
|
|
|
if (this.selectedProvince) { |
|
|
|
|
|
fullAddress += this.selectedProvince.adress |
|
|
|
|
|
} |
|
|
|
|
|
if (this.selectedCity) { |
|
|
|
|
|
fullAddress += this.selectedCity.adress |
|
|
|
|
|
} |
|
|
|
|
|
if (this.selectedDistrict) { |
|
|
|
|
|
fullAddress += this.selectedDistrict.adress |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
result.fullAddress = fullAddress |
|
|
|
|
|
result.selectedAddress = this.selectedDistrict || this.selectedCity || this.selectedProvince |
|
|
|
|
|
|
|
|
|
|
|
this.$emit('confirm', result) |
|
|
|
|
|
this.close() |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
</script> |
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss"> |
|
|
|
|
|
.address-picker { |
|
|
|
|
|
height: 70vh; |
|
|
|
|
|
background: #fff; |
|
|
|
|
|
|
|
|
|
|
|
.header { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
justify-content: space-between; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
padding: 30rpx; |
|
|
|
|
|
border-bottom: 1px solid #eee; |
|
|
|
|
|
|
|
|
|
|
|
.title { |
|
|
|
|
|
font-size: 32rpx; |
|
|
|
|
|
font-weight: bold; |
|
|
|
|
|
color: #333; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.close-btn { |
|
|
|
|
|
padding: 10rpx; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.content { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
height: calc(70vh - 160rpx); |
|
|
|
|
|
|
|
|
|
|
|
.left-panel { |
|
|
|
|
|
width: 240rpx; |
|
|
|
|
|
border-right: 1px solid #eee; |
|
|
|
|
|
background: #f8f8f8; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.right-panel { |
|
|
|
|
|
flex: 1; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.scroll-view { |
|
|
|
|
|
height: 100%; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
.address-item { |
|
|
|
|
|
padding: 30rpx 20rpx; |
|
|
|
|
|
font-size: 28rpx; |
|
|
|
|
|
color: #333; |
|
|
|
|
|
border-bottom: 1px solid #f0f0f0; |
|
|
|
|
|
|
|
|
|
|
|
&:last-child { |
|
|
|
|
|
border-bottom: none; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&.active { |
|
|
|
|
|
background: #fff; |
|
|
|
|
|
color: $uni-color; |
|
|
|
|
|
font-weight: bold; |
|
|
|
|
|
position: relative; |
|
|
|
|
|
|
|
|
|
|
|
&::after { |
|
|
|
|
|
content: ''; |
|
|
|
|
|
position: absolute; |
|
|
|
|
|
right: 0; |
|
|
|
|
|
top: 0; |
|
|
|
|
|
bottom: 0; |
|
|
|
|
|
width: 6rpx; |
|
|
|
|
|
background: $uni-color; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
&.back-item { |
|
|
|
|
|
display: flex; |
|
|
|
|
|
align-items: center; |
|
|
|
|
|
color: $uni-color; |
|
|
|
|
|
background: #f8f8f8; |
|
|
|
|
|
|
|
|
|
|
|
uv-icon { |
|
|
|
|
|
margin-right: 10rpx; |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
</style> |