|
|
- <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>
|