flnxi-Wu/hooks/useHomeFilterFields.ts
WuYongJie 63ec6b7dfb fix: 修复排行榜按钮消失、tag排序和输入框浅色模式问题
1. 排行榜悬浮窗关闭后小按钮消失问题
   - 修复面板居中模式关闭时按钮位置错误
   - 保存并恢复按钮的居中状态

2. 首页tag按钮按素材数量排序
   - 添加按素材数量降序排序逻辑

3. 上传弹窗输入框浅色模式文字不可见
   - 添加 text-slate-900 确保浅色模式下文字可见
   - 添加 placeholder 样式确保占位符正确显示
2025-12-14 21:41:29 +08:00

163 lines
3.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* useHomeFilterFields Hook
*
* 首页筛选字段数据获取Hook,优化了以下功能:
* - 使用SWR进行数据缓存和自动重新验证
* - 100ms内UI更新(无闪烁)
* - 使用防抖技术避免重复请求
* - 支持手动刷新
* - 错误处理和重试
*
* @created 2024-12-13
*/
import { useCallback, useMemo } from 'react'
import useSWR from 'swr'
export interface FilterOption {
label: string
value: string
count?: number
}
export interface FilterField {
key: string
label: string
type?: string
options: FilterOption[]
}
interface HomeFilterFieldsResponse {
fields: FilterField[]
}
const CACHE_KEY = '/api/home-filter-fields'
// SWR fetcher
const fetcher = async (url: string): Promise<HomeFilterFieldsResponse> => {
const response = await fetch(url, { cache: 'no-store' })
if (!response.ok) {
throw new Error('Failed to fetch filter fields')
}
return response.json()
}
interface UseHomeFilterFieldsOptions {
/** 是否启用自动刷新 */
refreshInterval?: number
/** 是否在窗口聚焦时重新验证 */
revalidateOnFocus?: boolean
/** 是否在重新连接时重新验证 */
revalidateOnReconnect?: boolean
}
interface UseHomeFilterFieldsReturn {
/** 筛选字段列表 */
fields: FilterField[]
/** 字段映射(按key索引) */
fieldsMap: Record<string, FilterField>
/** 是否正在加载 */
isLoading: boolean
/** 是否正在验证(后台刷新) */
isValidating: boolean
/** 错误信息 */
error: Error | undefined
/** 手动刷新 */
refresh: () => Promise<void>
/** 数据是否已准备好 */
isReady: boolean
}
/**
* 首页筛选字段数据获取Hook
*/
export function useHomeFilterFields(
options: UseHomeFilterFieldsOptions = {}
): UseHomeFilterFieldsReturn {
const {
refreshInterval = 0,
revalidateOnFocus = false,
revalidateOnReconnect = true,
} = options
const { data, error, isLoading, isValidating, mutate } = useSWR<HomeFilterFieldsResponse>(
CACHE_KEY,
fetcher,
{
refreshInterval,
revalidateOnFocus,
revalidateOnReconnect,
// 保持之前的数据,避免闪烁
keepPreviousData: true,
// 错误重试
errorRetryCount: 3,
errorRetryInterval: 1000,
// 去重请求
dedupingInterval: 2000,
}
)
const fields = useMemo(() => {
if (!data?.fields) return []
// 规范化选项数据
return data.fields.map(field => ({
...field,
options: Array.isArray(field.options)
? field.options.map(opt => {
if (typeof opt === 'string') {
return { label: opt, value: opt }
}
return {
label: opt.label || opt.value,
value: opt.value,
count: typeof opt.count === 'number' ? opt.count : undefined,
}
}).filter(opt => opt.value)
: [],
}))
}, [data])
const fieldsMap = useMemo(() => {
const map: Record<string, FilterField> = {}
fields.forEach(field => {
map[field.key] = field
})
return map
}, [fields])
const refresh = useCallback(async () => {
await mutate()
}, [mutate])
return {
fields,
fieldsMap,
isLoading,
isValidating,
error,
refresh,
isReady: !isLoading && !error,
}
}
/**
* 获取特定字段的选项
*/
export function useFilterFieldOptions(fieldKey: string): {
options: FilterOption[]
label: string
isLoading: boolean
} {
const { fieldsMap, isLoading } = useHomeFilterFields()
const field = fieldsMap[fieldKey]
return {
options: field?.options ?? [],
label: field?.label ?? fieldKey,
isLoading,
}
}
export default useHomeFilterFields