/** * 首页状态管理 Hook * 提取自 app/page.tsx 的状态管理逻辑 */ import { useState, useCallback, useRef, useEffect } from 'react' import { MATERIAL_TYPE_OPTIONS } from '@/lib/material-type-options' // 素材类型定义 export interface Material { id: string userId: string previewUrl: string productModel: string categoryName: string sceneName: string platformName?: string productCategoryName?: string materialType: string renderer: string authorName: string zipFileName: string zipFileSize: string | number downloadCount: number likeCount: number isLiked: boolean isFavorited: boolean createdAt: string displayImages?: string[] videoSourceUrl?: string | null videoPreview540Url?: string | null videoPreviewClipUrl?: string | null videoCoverUrl?: string | null videoProcessingStatus?: string | null videoDuration?: number | null transcodeLastError?: string | null transcodeAttempts?: number | null transcodeProgress?: number | null transcodeStage?: string | null transcodeQueueWaitingIndex?: number | null transcodeQueueWaitingTotal?: number | null transcodeQueueProcessingIds?: string[] transcodeQueueMaxConcurrent?: number | null user: { username: string avatar?: string | null } } // 分类项类型 export interface CategoryItem { id: string name: string count: number } // 月份项类型 export interface MonthItem { value: string label: string count: number } // 默认素材类型列表(已分类排序) export const DEFAULT_MATERIAL_TYPES = [ ...MATERIAL_TYPE_OPTIONS, ] as string[] export interface UseHomeStateReturn { // 素材数据 materials: Material[] setMaterials: React.Dispatch> loading: boolean setLoading: React.Dispatch> isTransitionLoading: boolean setIsTransitionLoading: React.Dispatch> // 筛选状态 searchKeyword: string setSearchKeyword: React.Dispatch> selectedCategory: string setSelectedCategory: React.Dispatch> selectedScene: string setSelectedScene: React.Dispatch> selectedMaterialType: string setSelectedMaterialType: React.Dispatch> selectedPlatform: string setSelectedPlatform: React.Dispatch> selectedProductCategory: string setSelectedProductCategory: React.Dispatch> selectedMonth: string setSelectedMonth: React.Dispatch> // 排序状态 sortBy: string setSortBy: React.Dispatch> sortOrder: 'desc' | 'asc' setSortOrder: React.Dispatch> // 分类数据 categories: CategoryItem[] setCategories: React.Dispatch> scenes: CategoryItem[] setScenes: React.Dispatch> platforms: CategoryItem[] setPlatforms: React.Dispatch> productCategories: CategoryItem[] setProductCategories: React.Dispatch> materialTypes: { value: string; label: string }[] setMaterialTypes: React.Dispatch> months: MonthItem[] setMonths: React.Dispatch> // 分页状态 displayedCount: number setDisplayedCount: React.Dispatch> currentPage: number setCurrentPage: React.Dispatch> hasMore: boolean setHasMore: React.Dispatch> isLoadingMore: boolean setIsLoadingMore: React.Dispatch> totalMaterialsCount: number setTotalMaterialsCount: React.Dispatch> pageSize: number // UI状态 isFilterCollapsed: boolean setIsFilterCollapsed: React.Dispatch> showUploadModal: boolean setShowUploadModal: React.Dispatch> themeColor: string setThemeColor: React.Dispatch> // 搜索防抖处理 handleSearchChange: (value: string) => void searchDebounceRef: React.MutableRefObject } export function useHomeState(): UseHomeStateReturn { // 素材数据 const [materials, setMaterials] = useState([]) const [loading, setLoading] = useState(true) const [isTransitionLoading, setIsTransitionLoading] = useState(false) // 筛选状态 const [searchKeyword, setSearchKeyword] = useState('') const [selectedCategory, setSelectedCategory] = useState('') const [selectedScene, setSelectedScene] = useState('') const [selectedMaterialType, setSelectedMaterialType] = useState('') const [selectedPlatform, setSelectedPlatform] = useState('') const [selectedProductCategory, setSelectedProductCategory] = useState('') const [selectedMonth, setSelectedMonth] = useState('') // 排序状态 const [sortBy, setSortBy] = useState('createdAt') const [sortOrder, setSortOrder] = useState<'desc' | 'asc'>('desc') // 分类数据 const [categories, setCategories] = useState([]) const [scenes, setScenes] = useState([]) const [platforms, setPlatforms] = useState([]) const [productCategories, setProductCategories] = useState([]) const [materialTypes, setMaterialTypes] = useState<{ value: string; label: string }[]>([]) const [months, setMonths] = useState([]) // 分页状态 const [displayedCount, setDisplayedCount] = useState(24) const [currentPage, setCurrentPage] = useState(1) const [hasMore, setHasMore] = useState(true) const [isLoadingMore, setIsLoadingMore] = useState(false) const [totalMaterialsCount, setTotalMaterialsCount] = useState(0) const pageSize = 20 // UI状态 const [isFilterCollapsed, setIsFilterCollapsed] = useState(true) const [showUploadModal, setShowUploadModal] = useState(false) const [themeColor, setThemeColor] = useState('#1f2937') // 搜索防抖 const searchDebounceRef = useRef(null) const fetchMaterialsRef = useRef<((page?: number, append?: boolean) => Promise) | null>(null) // 搜索防抖处理函数 const handleSearchChange = useCallback((value: string) => { setSearchKeyword(value) if (searchDebounceRef.current) { clearTimeout(searchDebounceRef.current) } searchDebounceRef.current = setTimeout(() => { console.log('[搜索防抖] 触发搜索:', value) setCurrentPage(1) setDisplayedCount(24) setHasMore(true) // 触发重新获取数据(通过依赖变化) }, 500) }, []) // 从URL参数恢复筛选状态 useEffect(() => { if (typeof window === 'undefined') return const params = new URLSearchParams(window.location.search) const materialType = params.get('materialType') const category = params.get('category') const scene = params.get('scene') const platform = params.get('platform') const productCategory = params.get('productCategory') const month = params.get('month') const search = params.get('search') const sort = params.get('sortBy') const sortOrderParam = params.get('sortOrder') if (materialType) setSelectedMaterialType(materialType) if (category) setSelectedCategory(category) if (scene) setSelectedScene(scene) if (platform) setSelectedPlatform(platform) if (productCategory) setSelectedProductCategory(productCategory) if (month) setSelectedMonth(month) if (search) setSearchKeyword(search) if (sort) setSortBy(sort) if (sortOrderParam === 'asc' || sortOrderParam === 'desc') setSortOrder(sortOrderParam) }, []) // 同步筛选状态到URL参数 useEffect(() => { if (typeof window === 'undefined') return const params = new URLSearchParams() if (searchKeyword) params.set('search', searchKeyword) if (selectedMaterialType) params.set('materialType', selectedMaterialType) if (selectedCategory) params.set('category', selectedCategory) if (selectedScene) params.set('scene', selectedScene) if (selectedPlatform) params.set('platform', selectedPlatform) if (selectedProductCategory) params.set('productCategory', selectedProductCategory) if (selectedMonth) params.set('month', selectedMonth) if (sortBy && sortBy !== 'createdAt') params.set('sortBy', sortBy) if (sortOrder && sortOrder !== 'desc') params.set('sortOrder', sortOrder) const newUrl = params.toString() ? `${window.location.pathname}?${params.toString()}` : window.location.pathname window.history.replaceState({}, '', newUrl) }, [searchKeyword, selectedMaterialType, selectedCategory, selectedScene, selectedPlatform, selectedProductCategory, selectedMonth, sortBy, sortOrder]) return { // 素材数据 materials, setMaterials, loading, setLoading, isTransitionLoading, setIsTransitionLoading, // 筛选状态 searchKeyword, setSearchKeyword, selectedCategory, setSelectedCategory, selectedScene, setSelectedScene, selectedMaterialType, setSelectedMaterialType, selectedPlatform, setSelectedPlatform, selectedProductCategory, setSelectedProductCategory, selectedMonth, setSelectedMonth, // 排序状态 sortBy, setSortBy, sortOrder, setSortOrder, // 分类数据 categories, setCategories, scenes, setScenes, platforms, setPlatforms, productCategories, setProductCategories, materialTypes, setMaterialTypes, months, setMonths, // 分页状态 displayedCount, setDisplayedCount, currentPage, setCurrentPage, hasMore, setHasMore, isLoadingMore, setIsLoadingMore, totalMaterialsCount, setTotalMaterialsCount, pageSize, // UI状态 isFilterCollapsed, setIsFilterCollapsed, showUploadModal, setShowUploadModal, themeColor, setThemeColor, // 搜索防抖处理 handleSearchChange, searchDebounceRef, } }