import Taro from '@tarojs/taro';
|
import { useEffect, useState } from 'react';
|
import { View, Image, Input, ScrollView } from '@tarojs/components';
|
import './index.less';
|
import { getOpenCity, getCityList } from '../../api/select-city';
|
|
|
|
export default function SelectCityPage() {
|
const [scrollIntoView, setScrollIntoView] = useState(''); // 滚动到的城市字母
|
const [searchValue, setSearchValue] = useState(''); // 搜索值
|
const [selectedCity, setSelectedCity] = useState<any>({}); // 选中的城市
|
const [filteredPendingList, setFilteredPendingList] = useState<any[]>([]); // 过滤后的开放城市列表
|
const [openCityList, setOpenCityList] = useState<any[]>([]); // 开放城市列表
|
const [searchResultList, setSearchResultList] = useState<any[]>([]); // 搜索结果列表
|
const [isLoading, setIsLoading] = useState(true);
|
|
useEffect(() => {
|
const city = Taro.getStorageSync('selectedCity');
|
if (city) {
|
setSelectedCity(city);
|
}
|
|
// 并行请求:获取开通城市列表和所有城市列表
|
setIsLoading(true);
|
Promise.all([
|
getOpenCity(),
|
getCityList({ isOpen: 0 }) // 0代表待开通
|
]).then(([openRes, allRes]) => {
|
const openCities = openRes.cities || [];
|
setOpenCityList(openCities);
|
|
const allCities = allRes || [];
|
|
// 分组
|
const groups: Record<string, string[]> = {};
|
|
allCities.forEach((city: any) => {
|
const name = city.name;
|
// 使用接口返回的 initial 字段进行分组
|
const letter = (city.initial || '#').toUpperCase();
|
|
if (!groups[letter]) groups[letter] = [];
|
groups[letter].push(name);
|
});
|
|
// 转换为数组格式并排序
|
const list = Object.keys(groups).sort().map(key => ({
|
title: key,
|
items: groups[key]
|
}));
|
|
setFilteredPendingList(list);
|
|
}).catch(err => {
|
console.error('获取城市数据失败', err);
|
}).finally(() => {
|
setIsLoading(false);
|
});
|
}, [])
|
|
useEffect(() => {
|
if (!searchValue) {
|
setSearchResultList([]);
|
return;
|
}
|
searchCity()
|
}, [searchValue])
|
|
const handleScrollTo = (letter) => {
|
setScrollIntoView(`city-${letter}`);
|
};
|
|
const handleSelectCity = (city, isOpen) => {
|
if (!isOpen) {
|
Taro.showToast({
|
title: '该城市暂未开通服务',
|
icon: 'none'
|
});
|
return;
|
}
|
Taro.setStorageSync('selectedCity', {
|
name: city.name,
|
code: city.adcode || city.code,
|
id: city.id,
|
});
|
Taro.navigateBack();
|
};
|
|
const searchCity = () => {
|
if (!searchValue) return;
|
getCityList({ cityName: searchValue }).then((res: any) => {
|
const openCity = openCityList.find(city => city.name.includes(searchValue));
|
if (openCity) {
|
const filteredRes = (res || []).filter(city => city.name !== openCity.name);
|
filteredRes.unshift(openCity);
|
setSearchResultList(filteredRes);
|
} else {
|
setSearchResultList(res || []);
|
}
|
});
|
}
|
|
const changeSearchValue = (e) => {
|
setSearchValue(e.detail.value)
|
}
|
|
return (
|
<View className='select-city-page'>
|
<View className='search-box'>
|
<Input
|
onInput={(e) => changeSearchValue(e)}
|
onBlur={() => searchCity()}
|
onConfirm={() => searchCity()}
|
value={searchValue}
|
placeholder='请输入城市名'
|
className='search-input'
|
placeholderClass='placeholder-class'
|
/>
|
</View>
|
{
|
isLoading &&
|
<View className='city-content skeleton' aria-hidden='true'>
|
<View className='skeleton-service'>
|
<View className='skeleton-title'></View>
|
<View className='skeleton-chip-list'>
|
{Array.from({ length: 6 }).map((_, index) => (
|
<View className='skeleton-chip' key={index}></View>
|
))}
|
</View>
|
</View>
|
<View className='skeleton-non-operational'>
|
<View className='skeleton-title'></View>
|
<View className='skeleton-line-group'>
|
{Array.from({ length: 8 }).map((_, index) => (
|
<View className='skeleton-line' key={index}></View>
|
))}
|
</View>
|
</View>
|
<View className='skeleton-shortcut'>
|
{Array.from({ length: 6 }).map((_, index) => (
|
<View className='skeleton-shortcut-item' key={index}></View>
|
))}
|
</View>
|
</View>
|
}
|
{!isLoading && (
|
<>
|
{/* 搜索前 */}
|
{
|
!searchValue &&
|
<View className='city-content'>
|
<View className='service-city-box'>
|
<View className='service-city-title'>运营城市</View>
|
<View className='service-city-list'>
|
{
|
openCityList.map((item, index) => (
|
<View
|
key={item.id}
|
className={`service-city-item ${selectedCity.name === item.name && 'active-city'}`}
|
onClick={() => handleSelectCity(item, true)}
|
>
|
{item.name}
|
</View>
|
))
|
}
|
</View>
|
</View>
|
<View className='non-operational-city-box'>
|
<View className='non-operational-city-title'>待开放城市</View>
|
<ScrollView
|
className='non-operational-city-list'
|
scrollY
|
scrollWithAnimation
|
scrollIntoView={scrollIntoView}
|
>
|
{
|
filteredPendingList.map((group) => (
|
<View key={group.title} id={`city-${group.title}`} className='city-group'>
|
<View className='city-group-title'>{group.title}</View>
|
{
|
group.items.map((city, index) => {
|
const isOpen = openCityList.some(open => open.name === city);
|
return (
|
<View
|
onClick={() => handleSelectCity({}, isOpen)}
|
key={index}
|
className='non-operational-city-item'
|
|
>
|
{city}
|
</View>
|
)
|
}
|
)
|
}
|
</View>
|
))
|
}
|
</ScrollView>
|
</View>
|
<View className='shortcut'>
|
{
|
filteredPendingList.map((group) => (
|
<View
|
key={group.title}
|
className='shortcut-item'
|
onClick={(e) => {
|
e.stopPropagation();
|
handleScrollTo(group.title);
|
}}
|
>
|
{group.title}
|
</View>
|
))
|
}
|
</View>
|
</View>
|
}
|
{/* 搜索后 */}
|
{
|
searchValue &&
|
<View>
|
{
|
searchResultList.length > 0 ?
|
<View className='search-result-box'>
|
{
|
searchResultList.map((item, index) => {
|
const isOpen = openCityList.some(open => open.name === item.name);
|
return (
|
<View key={index} className='search-result-item' onClick={() => handleSelectCity(item, isOpen)}>
|
<View className='search-result-city'>{item.name}</View>
|
<View className={`search-result-zone ${!isOpen ? 'search-result-zone-disabled' : ''}`}>{isOpen ? '运营区' : '待开放'}</View>
|
</View>
|
)
|
})
|
}
|
</View>
|
:
|
<View className='no-data-box'>
|
<Image className='no-data-image' src={require('../../assets/no-data-city.png')} />
|
<View>暂无城市</View>
|
</View>
|
}
|
</View>
|
}
|
</>
|
)}
|
</View>
|
)
|
}
|