| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- import React, { useState, useEffect } from 'react'
- import { View, Text, Picker, Button } from '@tarojs/components'
- import { useQuery } from '@tanstack/react-query'
- import { areaClient } from '../api'
- interface AreaInfo {
- id: number
- name: string
- type: 'province' | 'city' | 'district'
- }
- interface AreaPickerProps {
- visible: boolean
- onClose: () => void
- onConfirm: (areaIds: number[], areaInfos: AreaInfo[]) => void
- value?: number[]
- title?: string
- }
- export const AreaPicker: React.FC<AreaPickerProps> = ({
- visible,
- onClose,
- onConfirm,
- value = [],
- title = '选择地区'
- }) => {
- const [selectedProvince, setSelectedProvince] = useState<number | undefined>(value[0])
- const [selectedCity, setSelectedCity] = useState<number | undefined>(value[1])
- const [selectedDistrict, setSelectedDistrict] = useState<number | undefined>(value[2])
- // 获取省份列表
- const { data: provincesResponse } = useQuery({
- queryKey: ['areas', 'provinces'],
- queryFn: async () => {
- const res = await areaClient.provinces.$get({
- query: { page: 1, pageSize: 50 }
- })
- if (res.status !== 200) throw new Error('获取省份列表失败')
- return await res.json()
- }
- })
- // 获取城市列表
- const { data: citiesResponse } = useQuery({
- queryKey: ['areas', 'cities', selectedProvince],
- queryFn: async () => {
- if (!selectedProvince) return { success: true, data: { cities: [] }, message: '' }
- const res = await areaClient.cities.$get({
- query: { provinceId: selectedProvince, page: 1, pageSize: 50 }
- })
- if (res.status !== 200) throw new Error('获取城市列表失败')
- return await res.json()
- },
- enabled: !!selectedProvince
- })
- // 获取区县列表
- const { data: districtsResponse } = useQuery({
- queryKey: ['areas', 'districts', selectedCity],
- queryFn: async () => {
- if (!selectedCity) return { success: true, data: { districts: [] }, message: '' }
- const res = await areaClient.districts.$get({
- query: { cityId: selectedCity, page: 1, pageSize: 50 }
- })
- if (res.status !== 200) throw new Error('获取区县列表失败')
- return await res.json()
- },
- enabled: !!selectedCity
- })
- // 提取数据
- const provinces = provincesResponse?.data?.provinces || []
- const cities = citiesResponse?.data?.cities || []
- const districts = districtsResponse?.data?.districts || []
- // 初始化选择值 - 只在组件首次显示时初始化
- const [hasInitialized, setHasInitialized] = useState(false)
- useEffect(() => {
- if (visible && !hasInitialized) {
- setSelectedProvince(value[0])
- setSelectedCity(value[1])
- setSelectedDistrict(value[2])
- setHasInitialized(true)
- } else if (!visible) {
- setHasInitialized(false)
- }
- }, [visible, value, hasInitialized])
- // 处理省份选择
- const handleProvinceChange = (e: any) => {
- const provinceIndex = Number(e.detail.value)
- const selectedProvinceObj = provinces[provinceIndex]
- if (selectedProvinceObj) {
- setSelectedProvince(selectedProvinceObj.id)
- setSelectedCity(undefined)
- setSelectedDistrict(undefined)
- }
- }
- // 处理城市选择
- const handleCityChange = (e: any) => {
- const cityIndex = Number(e.detail.value)
- const selectedCityObj = cities[cityIndex]
- if (selectedCityObj) {
- setSelectedCity(selectedCityObj.id)
- setSelectedDistrict(undefined)
- }
- }
- // 处理区县选择
- const handleDistrictChange = (e: any) => {
- const districtIndex = Number(e.detail.value)
- const selectedDistrictObj = districts[districtIndex]
- if (selectedDistrictObj) {
- setSelectedDistrict(selectedDistrictObj.id)
- }
- }
- // 确认选择
- const handleConfirm = () => {
- const areaIds: number[] = []
- const areaInfos: AreaInfo[] = []
- if (selectedProvince) {
- const province = provinces.find(p => p.id === selectedProvince)
- if (province) {
- areaIds.push(selectedProvince)
- areaInfos.push({ id: province.id, name: province.name, type: 'province' })
- }
- }
- if (selectedCity) {
- const city = cities.find(c => c.id === selectedCity)
- if (city) {
- areaIds.push(selectedCity)
- areaInfos.push({ id: city.id, name: city.name, type: 'city' })
- }
- }
- if (selectedDistrict) {
- const district = districts.find(d => d.id === selectedDistrict)
- if (district) {
- areaIds.push(selectedDistrict)
- areaInfos.push({ id: district.id, name: district.name, type: 'district' })
- }
- }
- onConfirm(areaIds, areaInfos)
- onClose()
- }
- // 取消选择
- const handleCancel = () => {
- onClose()
- }
- // 获取显示文本
- const getDisplayText = () => {
- if (!selectedProvince) return '请选择省市区'
- const province = provinces.find(p => p.id === selectedProvince)
- const city = cities.find(c => c.id === selectedCity)
- const district = districts.find(d => d.id === selectedDistrict)
- if (district && city && province) {
- return `${province.name} ${city.name} ${district.name}`
- } else if (city && province) {
- return `${province.name} ${city.name}`
- } else if (province) {
- return province.name
- }
- return '请选择省市区'
- }
- if (!visible) return null
- return (
- <View className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
- <View className="bg-white rounded-lg w-4/5 max-w-md">
- {/* 标题栏 */}
- <View className="flex justify-between items-center p-4 border-b border-gray-200">
- <Text className="text-lg font-bold text-gray-800">{title}</Text>
- </View>
- {/* 选择器区域 */}
- <View className="p-4">
- {/* 当前选择显示 */}
- <View className="mb-4 p-3 bg-gray-50 rounded-lg">
- <Text className="text-sm text-gray-600">已选择:</Text>
- <Text className="text-sm font-medium text-blue-600">{getDisplayText()}</Text>
- </View>
- {/* 三级选择器 */}
- <View className="flex space-x-2 mb-4">
- {/* 省份选择器 */}
- <View className="flex-1">
- <Text className="text-sm text-gray-600 mb-1 block">省份</Text>
- <Picker
- mode="selector"
- range={provinces}
- rangeKey="name"
- value={selectedProvince ? provinces.findIndex(p => p.id === selectedProvince) : -1}
- onChange={handleProvinceChange}
- >
- <View className="border border-gray-300 rounded px-3 py-2 text-sm bg-white">
- {selectedProvince ? provinces.find(p => p.id === selectedProvince)?.name : '请选择省份'}
- </View>
- </Picker>
- </View>
- {/* 城市选择器 */}
- <View className="flex-1">
- <Text className="text-sm text-gray-600 mb-1 block">城市</Text>
- <Picker
- mode="selector"
- range={cities}
- rangeKey="name"
- value={selectedCity ? cities.findIndex(c => c.id === selectedCity) : -1}
- onChange={handleCityChange}
- disabled={!selectedProvince}
- >
- <View className={`border border-gray-300 rounded px-3 py-2 text-sm ${
- !selectedProvince ? 'bg-gray-100 text-gray-400' : 'bg-white'
- }`}>
- {selectedCity ? cities.find(c => c.id === selectedCity)?.name : '请选择城市'}
- </View>
- </Picker>
- </View>
- {/* 区县选择器 */}
- <View className="flex-1">
- <Text className="text-sm text-gray-600 mb-1 block">区县</Text>
- <Picker
- mode="selector"
- range={districts}
- rangeKey="name"
- value={selectedDistrict ? districts.findIndex(d => d.id === selectedDistrict) : -1}
- onChange={handleDistrictChange}
- disabled={!selectedCity}
- >
- <View className={`border border-gray-300 rounded px-3 py-2 text-sm ${
- !selectedCity ? 'bg-gray-100 text-gray-400' : 'bg-white'
- }`}>
- {selectedDistrict ? districts.find(d => d.id === selectedDistrict)?.name : '请选择区县'}
- </View>
- </Picker>
- </View>
- </View>
- </View>
- {/* 按钮区域 */}
- <View className="flex border-t border-gray-200">
- <Button
- className="flex-1 py-3 text-gray-600 bg-white border-none rounded-none"
- onClick={handleCancel}
- >
- 取消
- </Button>
- <View className="w-px bg-gray-200"></View>
- <Button
- className="flex-1 py-3 text-blue-500 bg-white border-none rounded-none font-bold"
- onClick={handleConfirm}
- >
- 确定
- </Button>
- </View>
- </View>
- </View>
- )
- }
|