import { useTRPC } from '@fenghuo/client'; import { useQuery, keepPreviousData } from '@tanstack/react-query'; import { useState, useMemo, useEffect } from 'react'; import type { FilterState, PaginationState } from '../lib/articles/types.js'; import { DEFAULT_PAGE_SIZE } from '../lib/articles/constants.js'; import { Prisma } from '@fenghuo/db'; export function useArticles() { const trpc = useTRPC(); // 状态管理 const [filters, setFilters] = useState({ searchTerm: '', statusFilter: 'all', categoryFilter: 'all', sortBy: 'created-desc', }); const [pagination, setPagination] = useState({ currentPage: 1, pageSize: DEFAULT_PAGE_SIZE, totalPages: 1, totalCount: 0 }); const [selectedArticles, setSelectedArticles] = useState([]); const [quickEditId, setQuickEditId] = useState(null); const [batchAction, setBatchAction] = useState(''); // 将前端筛选状态映射到 Prisma 查询条件 const { where, orderBy } = useMemo(() => { const where: Prisma.PostWhereInput = {}; const orderBy: Prisma.PostOrderByWithRelationInput = {}; // 组合查询条件 const andConditions: Prisma.PostWhereInput[] = []; if (filters.statusFilter !== 'all') { andConditions.push({ status: filters.statusFilter }); } else { // 默认不包含草稿和垃圾箱 andConditions.push({ status: { notIn: ['DRAFT', 'TRASH'] } }); } if (filters.searchTerm) { andConditions.push({ OR: [ { title: { contains: filters.searchTerm, mode: 'insensitive' } }, { content: { contains: filters.searchTerm, mode: 'insensitive' } }, ], }); } if (filters.categoryFilter !== 'all') { andConditions.push({ terms: { some: { id: filters.categoryFilter } } }); } if (andConditions.length > 0) { where.AND = andConditions; } // 排序 switch (filters.sortBy) { case 'created-asc': orderBy.createdAt = 'asc'; break; case 'created-desc': orderBy.createdAt = 'desc'; break; case 'published-asc': orderBy.publishedAt = 'asc'; break; case 'published-desc': orderBy.publishedAt = 'desc'; break; case 'title-asc': orderBy.title = 'asc'; break; case 'title-desc': orderBy.title = 'desc'; break; } return { where, orderBy }; }, [filters]); const { data: articlesData, isLoading: isLoadingArticles, refetch: refetchArticles } = useQuery( trpc.post.findManyWithPagination.queryOptions( { page: pagination.currentPage, pageSize: pagination.pageSize, where, orderBy }, { placeholderData: keepPreviousData, } ) ); useEffect(() => { if (articlesData) { setPagination(prev => ({ ...prev, totalPages: articlesData.totalPages, totalCount: articlesData.totalCount })); } }, [articlesData]); const { data: statsData, isLoading: isLoadingStats } = useQuery( trpc.post.getStats.queryOptions({ where }) ); // 更新筛选器 const updateFilters = (updates: Partial) => { setFilters(prev => ({ ...prev, ...updates })); setPagination(prev => ({ ...prev, currentPage: 1 })); // 重置页码 }; const setCurrentPage = (page: number) => { setPagination(prev => ({ ...prev, currentPage: page })); }; // 选择操作 const handleSelectAll = (checked: boolean) => { if (checked) { setSelectedArticles(articlesData?.items.map(a => a.id) ?? []); } else { setSelectedArticles([]); } }; const handleSelectArticle = (articleId: string, checked: boolean) => { setSelectedArticles(prev => checked ? [...prev, articleId] : prev.filter(id => id !== articleId) ); }; const resetSelection = () => { setSelectedArticles([]); setBatchAction(''); }; return { // 数据 articles: articlesData?.items ?? [], paginatedArticles: articlesData?.items ?? [], // The api already paginates stats: statsData, // terms: termsData ?? [], // TODO: fetch terms separately if needed // 状态 filters, pagination, selectedArticles, quickEditId, batchAction, isLoading: isLoadingArticles || isLoadingStats, // 更新函数 updateFilters, setCurrentPage, setSelectedArticles, setQuickEditId, setBatchAction, // 操作函数 handleSelectAll, handleSelectArticle, resetSelection, refetchArticles }; }