gongxiangp2/hooks/useHomeState.ts
2026-05-15 12:48:36 +08:00

317 lines
10 KiB
TypeScript

/**
* 首页状态管理 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<React.SetStateAction<Material[]>>
loading: boolean
setLoading: React.Dispatch<React.SetStateAction<boolean>>
isTransitionLoading: boolean
setIsTransitionLoading: React.Dispatch<React.SetStateAction<boolean>>
// 筛选状态
searchKeyword: string
setSearchKeyword: React.Dispatch<React.SetStateAction<string>>
selectedCategory: string
setSelectedCategory: React.Dispatch<React.SetStateAction<string>>
selectedScene: string
setSelectedScene: React.Dispatch<React.SetStateAction<string>>
selectedMaterialType: string
setSelectedMaterialType: React.Dispatch<React.SetStateAction<string>>
selectedPlatform: string
setSelectedPlatform: React.Dispatch<React.SetStateAction<string>>
selectedProductCategory: string
setSelectedProductCategory: React.Dispatch<React.SetStateAction<string>>
selectedMonth: string
setSelectedMonth: React.Dispatch<React.SetStateAction<string>>
// 排序状态
sortBy: string
setSortBy: React.Dispatch<React.SetStateAction<string>>
sortOrder: 'desc' | 'asc'
setSortOrder: React.Dispatch<React.SetStateAction<'desc' | 'asc'>>
// 分类数据
categories: CategoryItem[]
setCategories: React.Dispatch<React.SetStateAction<CategoryItem[]>>
scenes: CategoryItem[]
setScenes: React.Dispatch<React.SetStateAction<CategoryItem[]>>
platforms: CategoryItem[]
setPlatforms: React.Dispatch<React.SetStateAction<CategoryItem[]>>
productCategories: CategoryItem[]
setProductCategories: React.Dispatch<React.SetStateAction<CategoryItem[]>>
materialTypes: { value: string; label: string }[]
setMaterialTypes: React.Dispatch<React.SetStateAction<{ value: string; label: string }[]>>
months: MonthItem[]
setMonths: React.Dispatch<React.SetStateAction<MonthItem[]>>
// 分页状态
displayedCount: number
setDisplayedCount: React.Dispatch<React.SetStateAction<number>>
currentPage: number
setCurrentPage: React.Dispatch<React.SetStateAction<number>>
hasMore: boolean
setHasMore: React.Dispatch<React.SetStateAction<boolean>>
isLoadingMore: boolean
setIsLoadingMore: React.Dispatch<React.SetStateAction<boolean>>
totalMaterialsCount: number
setTotalMaterialsCount: React.Dispatch<React.SetStateAction<number>>
pageSize: number
// UI状态
isFilterCollapsed: boolean
setIsFilterCollapsed: React.Dispatch<React.SetStateAction<boolean>>
showUploadModal: boolean
setShowUploadModal: React.Dispatch<React.SetStateAction<boolean>>
themeColor: string
setThemeColor: React.Dispatch<React.SetStateAction<string>>
// 搜索防抖处理
handleSearchChange: (value: string) => void
searchDebounceRef: React.MutableRefObject<NodeJS.Timeout | null>
}
export function useHomeState(): UseHomeStateReturn {
// 素材数据
const [materials, setMaterials] = useState<Material[]>([])
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<CategoryItem[]>([])
const [scenes, setScenes] = useState<CategoryItem[]>([])
const [platforms, setPlatforms] = useState<CategoryItem[]>([])
const [productCategories, setProductCategories] = useState<CategoryItem[]>([])
const [materialTypes, setMaterialTypes] = useState<{ value: string; label: string }[]>([])
const [months, setMonths] = useState<MonthItem[]>([])
// 分页状态
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<NodeJS.Timeout | null>(null)
const fetchMaterialsRef = useRef<((page?: number, append?: boolean) => Promise<number>) | 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,
}
}