<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
|
|
v-if="showSelectWholeCity"
|
|
class="address-item whole-city-item"
|
|
@click="selectWholeProvince">
|
|
<uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
|
|
选择整个{{ selectedProvince.adress }}
|
|
</view>
|
|
|
|
<view
|
|
class="address-item"
|
|
:class="{
|
|
'selected': multiple && isCitySelected(item),
|
|
'active': !multiple && selectedCity && selectedCity.id === item.id
|
|
}"
|
|
v-for="item in cityList"
|
|
:key="item.id"
|
|
@click="selectCity(item)">
|
|
{{ item.adress }}
|
|
<uv-icon v-if="multiple && isCitySelected(item)"
|
|
name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
|
|
</view>
|
|
|
|
<!-- 多选时的确认按钮 -->
|
|
<view v-if="multiple && selectedCities.length > 0"
|
|
class="confirm-btn" @click="confirmMultipleCitySelection">
|
|
<button class="confirm-button">确认选择城市 ({{ selectedCities.length }})</button>
|
|
</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
|
|
v-if="showSelectWholeCity"
|
|
class="address-item whole-city-item"
|
|
@click="selectWholeCity">
|
|
<uv-icon name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
|
|
选择整个{{ selectedCity.adress }}
|
|
</view>
|
|
|
|
<view
|
|
class="address-item"
|
|
:class="{
|
|
'selected': multiple && isDistrictSelected(item),
|
|
'active': !multiple && selectedDistrict && selectedDistrict.id === item.id
|
|
}"
|
|
v-for="item in districtList"
|
|
:key="item.id"
|
|
@click="selectDistrict(item)">
|
|
{{ item.adress }}
|
|
<uv-icon v-if="multiple && isDistrictSelected(item)"
|
|
name="checkmark-circle" size="30rpx" color="#3796F8"></uv-icon>
|
|
</view>
|
|
|
|
<!-- 多选时的确认按钮 -->
|
|
<view v-if="multiple && selectedDistricts.length > 0"
|
|
class="confirm-btn" @click="confirmMultipleSelection">
|
|
<button class="confirm-button">确认选择 ({{ selectedDistricts.length }})</button>
|
|
</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
|
|
},
|
|
// 是否支持多选区县
|
|
multiple: {
|
|
type: Boolean,
|
|
default: false
|
|
},
|
|
// 是否显示"选择整个城市"选项
|
|
showSelectWholeCity: {
|
|
type: Boolean,
|
|
default: false
|
|
}
|
|
},
|
|
data() {
|
|
return {
|
|
selectedProvince: null, // 选中的省份
|
|
selectedCity: null, // 选中的城市
|
|
selectedDistrict: null, // 选中的区县
|
|
selectedCities: [], // 多选时选中的城市列表
|
|
selectedDistricts: [], // 多选时选中的区县列表
|
|
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.selectedCities = []
|
|
this.selectedDistricts = []
|
|
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) {
|
|
if (this.multiple) {
|
|
// 多选模式
|
|
const index = this.selectedCities.findIndex(item => item.id === city.id)
|
|
if (index > -1) {
|
|
// 取消选择
|
|
this.selectedCities.splice(index, 1)
|
|
} else {
|
|
// 添加选择
|
|
this.selectedCities.push(city)
|
|
}
|
|
} else {
|
|
// 单选模式
|
|
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) {
|
|
if (this.multiple) {
|
|
// 多选模式
|
|
const index = this.selectedDistricts.findIndex(item => item.id === district.id)
|
|
if (index > -1) {
|
|
// 取消选择
|
|
this.selectedDistricts.splice(index, 1)
|
|
} else {
|
|
// 添加选择
|
|
this.selectedDistricts.push(district)
|
|
}
|
|
} else {
|
|
// 单选模式
|
|
this.selectedDistrict = district
|
|
this.confirm()
|
|
}
|
|
},
|
|
|
|
// 检查城市是否被选中(多选模式)
|
|
isCitySelected(city) {
|
|
return this.selectedCities.some(item => item.id === city.id)
|
|
},
|
|
|
|
// 检查区县是否被选中(多选模式)
|
|
isDistrictSelected(district) {
|
|
return this.selectedDistricts.some(item => item.id === district.id)
|
|
},
|
|
|
|
// 选择整个省份
|
|
selectWholeProvince() {
|
|
this.selectedCity = null
|
|
this.selectedCities = []
|
|
this.selectedDistrict = null
|
|
this.selectedDistricts = []
|
|
this.confirm()
|
|
},
|
|
|
|
// 选择整个城市
|
|
selectWholeCity() {
|
|
this.selectedDistrict = null
|
|
this.selectedDistricts = []
|
|
this.confirm()
|
|
},
|
|
|
|
// 确认多选城市选择
|
|
confirmMultipleCitySelection() {
|
|
// 直接返回多选城市的数据
|
|
this.confirm()
|
|
},
|
|
|
|
// 确认多选选择
|
|
confirmMultipleSelection() {
|
|
this.confirm()
|
|
},
|
|
|
|
// 返回城市选择
|
|
backToCity() {
|
|
this.selectedCity = null
|
|
this.selectedDistrict = null
|
|
this.districtList = []
|
|
},
|
|
|
|
// 确认选择
|
|
confirm() {
|
|
const result = {
|
|
province: this.selectedProvince,
|
|
city: this.selectedCity,
|
|
district: this.selectedDistrict,
|
|
cities: this.selectedCities,
|
|
districts: this.selectedDistricts
|
|
}
|
|
|
|
// 生成完整地址文本
|
|
let fullAddress = ''
|
|
if (this.selectedProvince) {
|
|
fullAddress += this.selectedProvince.adress
|
|
}
|
|
|
|
// 多选城市模式
|
|
if (this.multiple && this.selectedCities.length > 0) {
|
|
const cityNames = this.selectedCities.map(item => item.adress).join(',')
|
|
fullAddress += cityNames
|
|
result.selectedAddress = this.selectedProvince // 多选城市时返回省份作为选中地址
|
|
result.selectedCities = this.selectedCities
|
|
} else if (this.selectedCity) {
|
|
fullAddress += this.selectedCity.adress
|
|
|
|
// 多选区县模式
|
|
if (this.multiple && this.selectedDistricts.length > 0) {
|
|
const districtNames = this.selectedDistricts.map(item => item.adress).join(',')
|
|
fullAddress += districtNames
|
|
result.selectedAddress = this.selectedCity // 多选时返回城市作为选中地址
|
|
result.selectedDistricts = this.selectedDistricts
|
|
} else if (this.selectedDistrict) {
|
|
// 单选区县模式
|
|
fullAddress += this.selectedDistrict.adress
|
|
result.selectedAddress = this.selectedDistrict
|
|
} else {
|
|
// 选择整个城市
|
|
result.selectedAddress = this.selectedCity
|
|
}
|
|
} else {
|
|
// 选择整个省份
|
|
result.selectedAddress = this.selectedProvince
|
|
}
|
|
|
|
result.fullAddress = fullAddress
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
&.whole-city-item {
|
|
display: flex;
|
|
align-items: center;
|
|
color: #3796F8;
|
|
background: rgba(#3796F8, 0.1);
|
|
font-weight: bold;
|
|
|
|
uv-icon {
|
|
margin-right: 10rpx;
|
|
}
|
|
}
|
|
|
|
&.selected {
|
|
background: rgba($uni-color, 0.1);
|
|
color: $uni-color;
|
|
font-weight: bold;
|
|
position: relative;
|
|
|
|
&::after {
|
|
content: '';
|
|
position: absolute;
|
|
right: 0;
|
|
top: 0;
|
|
bottom: 0;
|
|
width: 6rpx;
|
|
background: $uni-color;
|
|
}
|
|
}
|
|
}
|
|
|
|
.confirm-btn {
|
|
position: sticky;
|
|
bottom: 0;
|
|
background: #fff;
|
|
padding: 20rpx;
|
|
border-top: 1px solid #eee;
|
|
|
|
.confirm-button {
|
|
width: 100%;
|
|
background: $uni-color;
|
|
color: #fff;
|
|
border: none;
|
|
border-radius: 10rpx;
|
|
padding: 20rpx;
|
|
font-size: 28rpx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
</style>
|