import { useState, useEffect, useCallback, useRef } from 'react'; import { supabase } from '../supabase/client'; import { statusMap } from '../utils/constants'; import { usePlanRealtime } from './useRealtime'; import type { ProductionPlan, PlanWithSteps, PlanProcessStep, Company, PlanFactory } from '../types'; interface UsePlanDataOptions { companyId: string | null; pageSize?: number; } interface PlanDataState { plans: PlanWithSteps[]; factories: Company[]; planFactories: PlanFactory[]; operatorNames: Record; loading: boolean; loadingMore: boolean; hasMore: boolean; currentPage: number; error: Error | null; } /** * 优化的计划数据获取 Hook * 支持分页加载和缓存 */ export function usePlanData({ companyId, pageSize = 20 }: UsePlanDataOptions) { const [state, setState] = useState({ plans: [], factories: [], planFactories: [], operatorNames: {}, loading: true, loadingMore: false, hasMore: true, currentPage: 1, error: null }); // 使用 ref 存储缓存,避免不必要的重渲染 const cacheRef = useRef<{ data: PlanDataState | null; timestamp: number; }>({ data: null, timestamp: 0 }); const CACHE_DURATION = 10000; // 10秒缓存 // 获取操作人名称 const fetchOperatorNames = useCallback(async (operatorIds: string[]) => { if (operatorIds.length === 0) return {}; const { data: profilesData } = await supabase .from('profiles') .select('id, username') .in('id', operatorIds); const names: Record = {}; profilesData?.forEach(p => { names[p.id] = p.username; }); return names; }, []); // 处理计划数据 const processPlansData = useCallback(( planData: ProductionPlan[], stepsData: PlanProcessStep[] | null, inventoryData: any[] | null, priceHistoryData: any[] | null ): PlanWithSteps[] => { return planData.map(plan => { const inventoryRecords = (inventoryData || []) .filter(r => r.plan_id === plan.id) .map(r => ({ time: r.created_at, quantity: r.quantity, rolls: r.rolls })); const priceHistory = (priceHistoryData || []) .filter(h => h.plan_id === plan.id) .map(h => ({ id: h.id, old_price: h.old_price, new_price: h.new_price, change_amount: h.change_amount, operator_name: h.operator_name, change_reason: h.change_reason, created_at: h.created_at })); return { ...plan, processSteps: (stepsData || []).filter(s => s.plan_id === plan.id), inventoryRecords, priceHistory }; }); }, []); // 获取数据 const fetchData = useCallback(async (page: number = 1, append: boolean = false) => { if (!companyId) { setState(prev => ({ ...prev, loading: false })); return; } const isFirstPage = page === 1; if (isFirstPage) { // 检查缓存 const now = Date.now(); if (cacheRef.current.data && now - cacheRef.current.timestamp < CACHE_DURATION) { setState(cacheRef.current.data); return; } setState(prev => ({ ...prev, loading: true, error: null })); } else { setState(prev => ({ ...prev, loadingMore: true })); } try { const offset = (page - 1) * pageSize; // 获取计划 const { data: planData, error: planError } = await supabase .from('production_plans') .select('*') .eq('purchaser_id', companyId) .order('created_at', { ascending: false }) .range(offset, offset + pageSize - 1); if (planError) throw planError; if (!planData || planData.length === 0) { setState(prev => ({ ...prev, loading: false, loadingMore: false, hasMore: false, plans: isFirstPage ? [] : prev.plans })); return; } const planIds = planData.map(p => p.id); // 并行获取关联数据 const [ stepsResult, inventoryResult, priceHistoryResult, pfResult ] = await Promise.all([ supabase.from('plan_process_steps').select('*').in('plan_id', planIds), supabase .from('inventory_records') .select('plan_id, quantity, rolls, created_at') .in('plan_id', planIds) .order('created_at', { ascending: false }) .limit(100), supabase .from('production_price_history') .select('*') .in('plan_id', planIds) .order('created_at', { ascending: false }) .limit(50), supabase.from('plan_factories').select('*').in('plan_id', planIds) ]); // 获取操作人信息 const operatorIds = [...new Set( (stepsResult.data || []) .filter(s => s.operator_id) .map(s => s.operator_id) .filter(Boolean) )] as string[]; const operatorNames = await fetchOperatorNames(operatorIds); // 获取工厂信息 let factories: Company[] = []; if (pfResult.data && pfResult.data.length > 0) { const factoryIds = pfResult.data.map(pf => pf.factory_id); const { data: factoryData } = await supabase .from('companies') .select('*') .in('id', factoryIds); factories = factoryData || []; } // 处理计划数据 const plansWithSteps = processPlansData( planData, stepsResult.data, inventoryResult.data, priceHistoryResult.data ); const newState: PlanDataState = { plans: append ? [...state.plans, ...plansWithSteps] : plansWithSteps, factories: isFirstPage ? factories : state.factories, planFactories: isFirstPage ? (pfResult.data || []) : state.planFactories, operatorNames: isFirstPage ? operatorNames : { ...state.operatorNames, ...operatorNames }, loading: false, loadingMore: false, hasMore: planData.length === pageSize, currentPage: page, error: null }; // 更新缓存 if (isFirstPage) { cacheRef.current = { data: newState, timestamp: Date.now() }; } setState(newState); } catch (err) { setState(prev => ({ ...prev, loading: false, loadingMore: false, error: err instanceof Error ? err : new Error('Unknown error') })); } }, [companyId, pageSize, state.plans, fetchOperatorNames, processPlansData]); // 加载更多 const loadMore = useCallback(() => { if (state.loadingMore || !state.hasMore) return; fetchData(state.currentPage + 1, true); }, [fetchData, state.currentPage, state.hasMore, state.loadingMore]); // 刷新数据 const refetch = useCallback(() => { cacheRef.current.timestamp = 0; fetchData(1, false); }, [fetchData]); // 初始加载 useEffect(() => { fetchData(1, false); }, [fetchData]); // 使用统一的实时订阅管理器 usePlanRealtime(companyId, () => { cacheRef.current.timestamp = 0; fetchData(1, false); }); return { ...state, loadMore, refetch }; }