import React, { useState, useEffect, useRef } from 'react' import { StyleSheet, StatusBar as RNStatusBar, View, Text, ActivityIndicator, } from 'react-native' import { StatusBar } from 'expo-status-bar' import { SafeAreaView } from 'react-native-safe-area-context' import { useRouter, useLocalSearchParams } from 'expo-router' import { useTranslation } from 'react-i18next' import SearchBar from '@/components/SearchBar' import WorksGallery, { type Category, type WorkItem } from '@/components/WorksGallery' import { useWorksSearch, type WorksSearchResult } from '@/hooks/use-works-search' /** * 将 API 返回的作品数据转换为 WorkItem 格式 */ function convertToWorkItem(apiWork: WorksSearchResult): WorkItem { return { id: parseInt(apiWork.id, 10), date: apiWork.createdAt, duration: `${String(Math.floor(apiWork.duration / 60)).padStart(2, '0')}:${String( apiWork.duration % 60 ).padStart(2, '0')}`, // TODO: 从 API 响应中获取分类信息 category: '写真' as Category, } } /** * 按日期分组作品 */ function groupWorksByDate(works: WorkItem[]): Record { return works.reduce((acc, work) => { const dateKey = work.date instanceof Date ? work.date.toISOString().split('T')[0] : new Date(work.date).toISOString().split('T')[0] if (!acc[dateKey]) { acc[dateKey] = [] } acc[dateKey].push(work) return acc }, {} as Record) } export default function SearchWorksResultsScreen() { console.log('========== SearchWorksResultsScreen 组件渲染 ==========') const { t } = useTranslation() const router = useRouter() const params = useLocalSearchParams() const [searchText, setSearchText] = useState((params.q as string) || '') const [page, setPage] = useState(1) const [allWorks, setAllWorks] = useState([]) const [isLoadingMore, setIsLoadingMore] = useState(false) console.log('========== 当前状态:', { searchText, page, allWorksLength: allWorks.length, isLoadingMore }) const categories: Category[] = [ t('worksList.all') as Category, t('worksList.pets') as Category, t('worksList.portrait') as Category, t('worksList.together') as Category, ] const [selectedCategory, setSelectedCategory] = useState(categories[0]) // 使用真实的搜索接口 const { data, works, isLoading, error } = useWorksSearch({ keyword: searchText, category: selectedCategory, page, limit: 20, }) // 当搜索关键词或分类变化时,重置页码和累积数据 useEffect(() => { console.log('[SearchResults] 重置: searchText=', searchText, 'category=', selectedCategory) setPage(1) setAllWorks([]) }, [searchText, selectedCategory]) // 累积新数据 useEffect(() => { console.log('[SearchResults] works变化:', { worksLength: works?.length, page, allWorksLength: allWorks.length, isLoading, data: data ? { total: data.total, totalPages: data.totalPages } : null }) if (works) { if (works.length > 0) { if (page === 1) { console.log('[SearchResults] 设置第1页数据') setAllWorks(works) } else { console.log('[SearchResults] 追加第', page, '页数据') setAllWorks((prev) => [...prev, ...works]) } } else if (page === 1) { console.log('[SearchResults] 第1页无结果,清空数据') setAllWorks([]) } setIsLoadingMore(false) } }, [works]) // 加载更多函数 const handleLoadMore = () => { console.log('[SearchResults] handleLoadMore调用:', { isLoading, isLoadingMore, hasData: !!data, page, totalPages: data?.totalPages }) if (isLoading || isLoadingMore || !data) { console.log('[SearchResults] 跳过加载: 正在加载或无数据') return } // 检查是否还有更多数据 if (page >= data.totalPages) { console.log('[SearchResults] 跳过加载: 已到最后一页') return } console.log('[SearchResults] 开始加载第', page + 1, '页') setIsLoadingMore(true) setPage((prev) => prev + 1) } // 根据分类过滤结果(如果 API 不支持分类筛选,则在前端过滤) const filteredWorks = selectedCategory === categories[0] ? allWorks : allWorks.filter((work) => { const convertedWork = convertToWorkItem(work) return convertedWork.category === selectedCategory }) // 转换为 WorkItem 格式 const workItems: WorkItem[] = filteredWorks.map(convertToWorkItem) // 按日期分组 const groupedWorks = groupWorksByDate(workItems) // 处理错误状态 if (error) { return ( { router.push({ pathname: '/searchWorksResults', params: { q: text }, }) }} onBack={() => router.back()} placeholder={t('search.searchWorks')} marginBottom={0} readOnly={true} onInputPress={() => { router.push({ pathname: '/searchWorks', params: { q: searchText }, }) }} onClearPress={() => { router.push({ pathname: '/searchWorks', params: { q: '' }, }) }} /> {error instanceof Error ? error.message : t('search.errorOccurred')} ) } return ( {/* Top Bar with Search */} { router.push({ pathname: '/searchWorksResults', params: { q: text }, }) }} onBack={() => router.back()} placeholder={t('search.searchWorks')} marginBottom={0} readOnly={true} onInputPress={() => { router.push({ pathname: '/searchWorks', params: { q: searchText }, }) }} onClearPress={() => { router.push({ pathname: '/searchWorks', params: { q: '' }, }) }} /> {/* Loading State */} {isLoading && ( {t('search.loading')} )} {/* Empty State */} {!isLoading && workItems.length === 0 && searchText.trim() && ( {t('search.noResults')} )} {/* Results */} {!isLoading && workItems.length > 0 && ( { router.push({ pathname: '/generationRecord' as any, params: { id: id.toString() }, }) }} onEndReached={handleLoadMore} ListFooterComponent={ isLoadingMore ? ( {t('search.loadingMore')} ) : undefined } /> )} ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#090A0B', }, loadingContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#090A0B', }, loadingText: { color: '#F5F5F5', fontSize: 14, marginTop: 12, }, errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#090A0B', paddingHorizontal: 20, }, errorText: { color: '#FF6666', fontSize: 14, textAlign: 'center', }, emptyContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', backgroundColor: '#090A0B', }, emptyText: { color: '#8A8A8A', fontSize: 14, }, loadingMoreContainer: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', paddingVertical: 20, gap: 8, }, loadingMoreText: { color: '#F5F5F5', fontSize: 12, }, })