317 lines
10 KiB
TypeScript
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,
|
|
}
|
|
}
|