Explorar el Código

✨ feat(home): 实现默认目的地地区数据加载与显示功能

- 引入react-query用于地区数据获取与缓存
- 添加默认目的地地区数据查询逻辑,支持并行获取省市区数据
- 优化getAreaDisplayText方法,优先使用默认目的地地区数据
- 增加地区加载状态显示,避免默认目的地显示"未知地区"
- 设置5分钟缓存时间,减少重复请求

✅ test(home): 添加默认目的地地区数据加载测试

- mock areaClient API客户端,模拟地区数据响应
- 验证默认目的地地区数据加载成功后正确显示名称
- 确保地区数据加载过程中不显示"请选择地区"文本
yourname hace 3 meses
padre
commit
d454d44986
Se han modificado 2 ficheros con 161 adiciones y 25 borrados
  1. 92 10
      mini/src/pages/home/index.tsx
  2. 69 15
      mini/tests/pages/HomePage.test.tsx

+ 92 - 10
mini/src/pages/home/index.tsx

@@ -3,10 +3,12 @@ import { View, Text, Swiper, SwiperItem, Image, Button, Picker } from '@tarojs/c
 import { navigateTo } from '@tarojs/taro'
 import { format } from 'date-fns'
 import { zhCN } from 'date-fns/locale'
+import { useQuery } from '@tanstack/react-query'
 import { TabBarLayout } from '@/layouts/tab-bar-layout'
 import { AreaPicker } from '../../components/AreaPicker'
 import { Navbar } from '@/components/ui/navbar'
 import { showToast } from '@/utils/toast'
+import { areaClient } from '@/api'
 
 
 interface SearchParams {
@@ -62,7 +64,7 @@ const HomePage: React.FC = () => {
       Number(process.env.TARO_APP_DEFAULT_END_PROVINCE_ID),
       Number(process.env.TARO_APP_DEFAULT_END_CITY_ID),
       Number(process.env.TARO_APP_DEFAULT_END_DISTRICT_ID)
-    ].filter(id => id && !isNaN(id))
+    ]
   }
 
   const [searchParams, setSearchParams] = useState<SearchParams>(() => {
@@ -88,6 +90,70 @@ const HomePage: React.FC = () => {
   const [currentPickerType, setCurrentPickerType] = useState<'start' | 'end'>('start')
   const [areaData, setAreaData] = useState<AreaInfo[]>([])
 
+  // 获取默认目的地的地区数据
+  const defaultEndIds = getValidDefaultEndIds()
+  const { data: defaultEndAreaData, isLoading: isLoadingDefaultEndAreas } = useQuery({
+    queryKey: ['default-end-areas', defaultEndIds],
+    queryFn: async () => {
+      if (!defaultEndIds || defaultEndIds.length !== 3) {
+        console.debug('没有有效的默认目的地配置,跳过地区数据加载')
+        return []
+      }
+
+      const [provinceId, cityId, districtId] = defaultEndIds
+      console.debug('开始加载默认目的地地区数据:', { provinceId, cityId, districtId })
+
+      try {
+        // 并行获取所有地区数据
+        const [provincesResponse, citiesResponse, districtsResponse] = await Promise.all([
+          areaClient.provinces.$get({ query: { page: 1, pageSize: 100 } }),
+          areaClient.cities.$get({ query: { provinceId, page: 1, pageSize: 100 } }),
+          areaClient.districts.$get({ query: { cityId, page: 1, pageSize: 100 } })
+        ])
+
+        if (provincesResponse.status !== 200 || citiesResponse.status !== 200 || districtsResponse.status !== 200) {
+          throw new Error('获取地区数据失败')
+        }
+
+        const provinces = await provincesResponse.json()
+        const cities = await citiesResponse.json()
+        const districts = await districtsResponse.json()
+
+        console.debug('地区数据获取成功:', {
+          provinces: provinces.data?.provinces?.length,
+          cities: cities.data?.cities?.length,
+          districts: districts.data?.districts?.length
+        })
+
+        // 查找对应的地区信息
+        const province = provinces.data?.provinces?.find((p: any) => p.id === provinceId)
+        const city = cities.data?.cities?.find((c: any) => c.id === cityId)
+        const district = districts.data?.districts?.find((d: any) => d.id === districtId)
+
+        const areaInfos: AreaInfo[] = []
+
+        if (province) {
+          areaInfos.push({ id: province.id, name: province.name, type: 'province' })
+        }
+        if (city) {
+          areaInfos.push({ id: city.id, name: city.name, type: 'city' })
+        }
+        if (district) {
+          areaInfos.push({ id: district.id, name: district.name, type: 'district' })
+        }
+
+        console.debug('构建的地区信息:', areaInfos)
+        return areaInfos
+      } catch (error) {
+        console.error('获取默认目的地地区数据失败:', error)
+        return []
+      }
+    },
+    enabled: !!defaultEndIds && defaultEndIds.length === 3,
+    staleTime: 5 * 60 * 1000, // 5分钟缓存
+    retry: 2
+  })
+
   // 出行方式选项 - 组合查询映射
   const travelOptions = [
     {
@@ -199,15 +265,27 @@ const HomePage: React.FC = () => {
       return defaultText || '请选择地区'
     }
 
-    // 根据areaIds获取地区名称
-    const areaNames = areaIds.map(id => {
-      const area = areaData.find(a => a.id === id)
-      return area ? area.name : '未知地区'
-    })
+    // 检查是否是默认目的地,如果是则优先使用默认目的地地区数据
+    const isDefaultEnd = defaultEndIds && areaIds.length === defaultEndIds.length &&
+                        areaIds.every((id, index) => id === defaultEndIds[index])
 
-    const result = areaNames.join(' ') || (defaultText || '请选择地区')
-    console.debug('getAreaDisplayText:', { areaIds, areaNames, result, areaData })
-    return result
+    if (isDefaultEnd && defaultEndAreaData && defaultEndAreaData.length > 0) {
+      // 使用默认目的地地区数据
+      const areaNames = defaultEndAreaData.map(area => area.name)
+      const result = areaNames.join(' ') || (defaultText || '请选择地区')
+      console.debug('getAreaDisplayText (使用默认目的地数据):', { areaIds, areaNames, result, defaultEndAreaData })
+      return result
+    } else {
+      // 使用本地存储的地区数据
+      const areaNames = areaIds.map(id => {
+        const area = areaData.find(a => a.id === id)
+        return area ? area.name : '未知地区'
+      })
+
+      const result = areaNames.join(' ') || (defaultText || '请选择地区')
+      console.debug('getAreaDisplayText (使用本地数据):', { areaIds, areaNames, result, areaData })
+      return result
+    }
   }
 
   // 获取出行方式样式
@@ -342,7 +420,11 @@ const HomePage: React.FC = () => {
             >
               <Text className="text-sm text-gray-600 block">目的地</Text>
               <Text className="text-sm font-medium text-gray-800 block mt-1">
-                {getAreaDisplayText(searchParams.endAreaIds, '默认目的地')}
+                {isLoadingDefaultEndAreas && defaultEndIds ? (
+                  '加载中...'
+                ) : (
+                  getAreaDisplayText(searchParams.endAreaIds, '默认目的地')
+                )}
               </Text>
             </Button>
           </View>

+ 69 - 15
mini/tests/pages/HomePage.test.tsx

@@ -6,6 +6,23 @@ import HomePage from '../../src/pages/home/index'
 // 导入 Taro mock 函数
 import taroMock from '../__mocks__/taroMock'
 
+// Mock API 客户端
+const mockAreaClient = {
+  provinces: {
+    $get: jest.fn()
+  },
+  cities: {
+    $get: jest.fn()
+  },
+  districts: {
+    $get: jest.fn()
+  }
+}
+
+jest.mock('../../src/api', () => ({
+  areaClient: mockAreaClient
+}))
+
 // Mock AreaPicker 组件
 jest.mock('../../src/components/AreaPicker', () => ({
   AreaPicker: jest.fn(({ visible, onClose, onConfirm, value, title }) => {
@@ -530,28 +547,65 @@ describe('首页集成测试', () => {
     process.env.TARO_APP_DEFAULT_END_CITY_ID = '22'
     process.env.TARO_APP_DEFAULT_END_DISTRICT_ID = '202'
 
+    // Mock API 响应
+    mockAreaClient.provinces.$get.mockResolvedValue({
+      status: 200,
+      json: async () => ({
+        success: true,
+        data: {
+          provinces: [
+            { id: 1, name: '北京市' },
+            { id: 2, name: '上海市' },
+            { id: 3, name: '广东省' }
+          ]
+        }
+      })
+    })
+
+    mockAreaClient.cities.$get.mockResolvedValue({
+      status: 200,
+      json: async () => ({
+        success: true,
+        data: {
+          cities: [
+            { id: 21, name: '北京市', provinceId: 1 },
+            { id: 22, name: '上海市', provinceId: 2 },
+            { id: 23, name: '广州市', provinceId: 3 }
+          ]
+        }
+      })
+    })
+
+    mockAreaClient.districts.$get.mockResolvedValue({
+      status: 200,
+      json: async () => ({
+        success: true,
+        data: {
+          districts: [
+            { id: 201, name: '朝阳区', cityId: 21 },
+            { id: 202, name: '浦东新区', cityId: 22 },
+            { id: 203, name: '天河区', cityId: 23 }
+          ]
+        }
+      })
+    })
+
     render(
       <Wrapper>
         <HomePage />
       </Wrapper>
     )
 
-    // 等待组件处理环境变量
+    // 等待组件处理环境变量和加载地区数据
     await waitFor(() => {
-      // 检查目的地不再显示"请选择地区",而是显示其他文本
-      const pleaseSelectText = screen.queryByText('请选择地区')
-      expect(pleaseSelectText).not.toBeInTheDocument()
-
-      // 目的地应该显示其他文本,表示已设置了默认目的地
-      const destinationButtons = screen.getAllByText(/出发地|目的地/)
-      const destinationButton = destinationButtons.find(btn => btn.textContent === '目的地')
-      expect(destinationButton).toBeInTheDocument()
-
-      // 检查目的地显示的不是"请选择地区",而是其他文本
-      // 由于地区数据未加载,会显示"未知地区 未知地区 未知地区"
-      const destinationText = destinationButton?.parentElement?.querySelector('.text-gray-800')
-      expect(destinationText).not.toHaveTextContent('请选择地区')
-      expect(destinationText).toHaveTextContent('未知地区 未知地区 未知地区')
+      // 检查目的地不再显示"请选择地区",而是显示实际地区名称
+      const destinationButton = screen.getByText('目的地').closest('button')
+      const pleaseSelectText = destinationButton?.querySelector('span.text-sm.font-medium.text-gray-800.block.mt-1')
+      expect(pleaseSelectText).not.toHaveTextContent('请选择地区')
+
+      // 目的地应该显示实际地区名称
+      const destinationText = screen.getByText('上海市 上海市 浦东新区')
+      expect(destinationText).toBeInTheDocument()
     })
   })