import { StatusBadge } from '@/components/ui/status-badge'; import { Colors } from '@/constants/theme'; import type { ListResponse } from '@/utils/api'; import api from '@/utils/api'; import { getApiCache } from '@/utils/storage'; import { ActivityIndicator, Toast } from '@ant-design/react-native'; import { Ionicons } from '@expo/vector-icons'; import { useRoute } from '@react-navigation/native'; import clsx from 'clsx'; import { Stack, useNavigation } from 'expo-router'; import { useCallback, useEffect, useRef, useState } from 'react'; import { FlatList, Pressable, Text, TextInput, View } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import type { Customer, CustomerLoanStatus } from '../(tabs)/customer'; const PAGE_SIZE = 15; const CACHE_KEY = 'customer_first'; export const CustomerLoanStatusText: Record, string> = { idle: '待匹配', completed: '已完成', unmatch: '匹配失败', all: '所有', unknow: '未知' }; export default function CustomerSelectScreen() { const insets = useSafeAreaInsets(); const navigation = useNavigation(); const route = useRoute(); const [searchKey, setSearchKey] = useState(''); const [list, setList] = useState([]); const [loading, setLoading] = useState(true); const [activeStatus, setActiveStatus] = useState('idle'); const startRef = useRef(0); const loanStatusRef = useRef('idle'); const hasMoreRef = useRef(false); const loadingRef = useRef(false); const searchTimerRef = useRef | null>(null); const load = useCallback(async (start: number, loanStatus: CustomerLoanStatus, q?: string) => { if (loadingRef.current) return; loadingRef.current = true; loanStatusRef.current = loanStatus; startRef.current = start; setLoading(true); try { const res = await api.post>('customer/list', { start, size: PAGE_SIZE, loanStatus, q, }); if (loanStatusRef.current !== loanStatus) return; const next = res?.list ?? []; if (start === 0 && !loanStatus && next.length) { getApiCache().setObject(CACHE_KEY, res, 60); } hasMoreRef.current = next.length >= PAGE_SIZE; setList((prev) => (start === 0 ? next : prev.concat(next))); startRef.current = start + next.length; if (!next.length && start > 0) { Toast.offline('没有更多数据可加载'); } } catch { Toast.fail('加载列表失败!'); } finally { setLoading(false); loadingRef.current = false; } }, []); // 首次加载 useEffect(() => { load(0, 'idle'); }, [load]); const loadMore = useCallback(() => { if (!hasMoreRef.current || loadingRef.current) return; load(startRef.current, loanStatusRef.current, searchKey); }, [load, searchKey]); const handleSelectStatus = useCallback( (status: CustomerLoanStatus) => { const next = activeStatus === status ? undefined : status; setActiveStatus(next); load(0, next, searchKey); }, [activeStatus, load, searchKey] ); const handlePick = useCallback( async (item: Customer) => { // @ts-ignore const res = await route.params?.onSelect?.(item); // alert(res); if (false === res) { return; } navigation.goBack(); }, [navigation] ); const handleSearch = useCallback((k: string) => { setSearchKey(k); if (searchTimerRef.current) clearTimeout(searchTimerRef.current); searchTimerRef.current = setTimeout(() => { load(0, loanStatusRef.current, k); }, 600); }, [load, searchKey]); const handleReset = useCallback(()=> { handleSearch(''); }, [handleSearch]); useEffect(() => { return () => { if (searchTimerRef.current) clearTimeout(searchTimerRef.current); }; }, []); // @ts-ignore const currentId = route.params?.current; const renderItem = useCallback( ({ item }: { item: Customer }) => ( handlePick(item)} className="mb-2 flex-row items-center gap-3 rounded-2xl border border-outline-variant bg-surface-container-lowest px-4 py-3 active:opacity-90 active:scale-[0.99]" > {currentId === item.id && } {item.name?.[0] ?? '?'} {item.name} {item.mobile} {item.loan_status && } ), [handlePick] ); const ListHeader = ( <> {searchKey.length > 0 ? ( ) : null} {Object.entries(CustomerLoanStatusText).map(([key, label]) => { const active = activeStatus === (key as CustomerLoanStatus); return ( handleSelectStatus(key as CustomerLoanStatus)} className={clsx("rounded-md px-1 border border-transparent active:opacity-84", { 'border-primary-container': active, })} > {label} ); })} ); const ListEmpty = !loading ? ( 暂无匹配客户 ) : ( 加载中 ); const ListFooter = loading && list.length > 0 ? ( 加载中 ) : null; return ( item.id} renderItem={renderItem} keyboardShouldPersistTaps="handled" showsVerticalScrollIndicator={false} ListHeaderComponent={ListHeader} ListEmptyComponent={ListEmpty} ListFooterComponent={ListFooter} onEndReached={loadMore} onEndReachedThreshold={0.5} /> ); }