20260929
This commit is contained in:
@@ -0,0 +1,250 @@
|
||||
<template>
|
||||
<u-popup :show="show" mode="bottom" round="24rpx" close-on-click-overlay @close="handleClose">
|
||||
<view class="area-picker">
|
||||
<view class="area-picker-head">
|
||||
<text class="area-picker-title">选择省市</text>
|
||||
<view class="area-picker-actions">
|
||||
<view class="area-picker-btn" @tap="handleClose">取消</view>
|
||||
<view class="area-picker-btn primary" @tap="confirmSelection">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="area-picker-body">
|
||||
<view v-if="loading" class="area-loading">加载中...</view>
|
||||
<view v-else class="area-columns">
|
||||
<view class="area-column">
|
||||
<scroll-view scroll-y class="area-scroll">
|
||||
<view
|
||||
v-for="(item, index) in provinces"
|
||||
:key="index"
|
||||
:class="['area-item', index === provinceIndex ? 'active' : '']"
|
||||
@tap="selectProvince(index)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="area-column">
|
||||
<scroll-view scroll-y class="area-scroll">
|
||||
<view v-if="cities.length === 0" class="area-empty">暂无城市</view>
|
||||
<view
|
||||
v-for="(item, index) in cities"
|
||||
:key="index"
|
||||
:class="['area-item', index === cityIndex ? 'active' : '']"
|
||||
@tap="selectCity(index)"
|
||||
>
|
||||
{{ item.name }}
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {areaListReq, areaListByFidReq} from "@/api/common";
|
||||
|
||||
export default {
|
||||
name: 'AreaPicker',
|
||||
props: {
|
||||
show: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
defaultProvince: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
defaultCity: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
provinces: [],
|
||||
cities: [],
|
||||
provinceIndex: 0,
|
||||
cityIndex: 0
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
show(val) {
|
||||
if (val) {
|
||||
this.initAreas()
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose() {
|
||||
this.$emit('close')
|
||||
},
|
||||
normalizeAreaItem(item) {
|
||||
return {
|
||||
id: item.areaId || item.code || item.value || item.id || null,
|
||||
name: item.name || item.title || item.areaName || item.label || ''
|
||||
}
|
||||
},
|
||||
async fetchProvinces() {
|
||||
const {data} = await areaListReq({level: 1})
|
||||
const list = Array.isArray(data) ? data : (data && data.list ? data.list : [])
|
||||
return list.map(this.normalizeAreaItem)
|
||||
},
|
||||
async fetchCities(fid) {
|
||||
const {data} = await areaListByFidReq({fid})
|
||||
const list = Array.isArray(data) ? data : (data && data.list ? data.list : [])
|
||||
return list.map(this.normalizeAreaItem)
|
||||
},
|
||||
findIndexByName(list, name) {
|
||||
if (!name) return -1
|
||||
return list.findIndex(item => item.name === name)
|
||||
},
|
||||
async initAreas() {
|
||||
this.loading = true
|
||||
try {
|
||||
if (!this.provinces.length) {
|
||||
this.provinces = await this.fetchProvinces()
|
||||
}
|
||||
let provinceIndex = this.findIndexByName(this.provinces, this.defaultProvince)
|
||||
if (provinceIndex < 0) provinceIndex = 0
|
||||
this.provinceIndex = provinceIndex
|
||||
const province = this.provinces[provinceIndex]
|
||||
this.cities = province ? await this.fetchCities(province.id) : []
|
||||
let cityIndex = this.findIndexByName(this.cities, this.defaultCity)
|
||||
if (cityIndex < 0) cityIndex = 0
|
||||
this.cityIndex = cityIndex
|
||||
} catch (err) {
|
||||
uni.showToast({
|
||||
title: '地区加载失败',
|
||||
icon: 'none'
|
||||
})
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
async selectProvince(index) {
|
||||
if (index === this.provinceIndex) return
|
||||
this.provinceIndex = index
|
||||
this.cityIndex = 0
|
||||
const province = this.provinces[index]
|
||||
if (!province) {
|
||||
this.cities = []
|
||||
return
|
||||
}
|
||||
this.loading = true
|
||||
try {
|
||||
this.cities = await this.fetchCities(province.id)
|
||||
this.cityIndex = 0
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
selectCity(index) {
|
||||
this.cityIndex = index
|
||||
},
|
||||
confirmSelection() {
|
||||
const province = this.provinces[this.provinceIndex]
|
||||
const city = this.cities[this.cityIndex]
|
||||
if (!province || !city) {
|
||||
uni.showToast({
|
||||
title: '请选择省市',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
this.$emit('confirm', {
|
||||
province,
|
||||
city,
|
||||
provinceId: province.id,
|
||||
cityId: city.id,
|
||||
provinceName: province.name,
|
||||
cityName: city.name
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.area-picker {
|
||||
background: #ffffff;
|
||||
padding: 20rpx 20rpx 30rpx;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
}
|
||||
|
||||
.area-picker-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.area-picker-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
}
|
||||
|
||||
.area-picker-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.area-picker-btn {
|
||||
font-size: 24rpx;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.area-picker-btn.primary {
|
||||
color: #2fc6a7;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.area-picker-body {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.area-loading {
|
||||
padding: 24rpx 0;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
}
|
||||
|
||||
.area-columns {
|
||||
display: flex;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.area-column {
|
||||
flex: 1;
|
||||
border-radius: 16rpx;
|
||||
background: #f8fafc;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.area-scroll {
|
||||
height: 60vh;
|
||||
}
|
||||
|
||||
.area-item {
|
||||
padding: 16rpx 18rpx;
|
||||
font-size: 26rpx;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.area-item.active {
|
||||
color: #2fc6a7;
|
||||
font-weight: 700;
|
||||
background: #e6fffa;
|
||||
}
|
||||
|
||||
.area-empty {
|
||||
padding: 18rpx;
|
||||
font-size: 24rpx;
|
||||
color: #9ca3af;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user