import "./ApiDirectoryDesktop.css"; import { Pagination, Tag, Empty, Spin, Button } from "antd"; import { Tabs } from "antd"; import type { TabsProps } from "antd"; import React, { useState, useEffect, useMemo } from "react"; import { Radio } from "antd"; import type { RadioChangeEvent } from "antd"; import { Image } from "antd"; import { EyeOutlined, StarOutlined, StarFilled, LoadingOutlined, ApiOutlined, LinkOutlined, ReloadOutlined } from "@ant-design/icons"; import { useNavigate } from "react-router-dom"; import { api_request } from "@/utils/request"; import { getApiImageUrl } from "@/utils/apiImageUtils"; import { useSelector } from "react-redux"; import { selectIsLogin } from "@/features/login"; import { useLoginModal } from "@/contexts/LoginModalContext"; import { message } from "@/utils/message"; interface ApiCategory { id: number; name: string; icon: string; sort_order: number; item_count: number; } interface ApiItem { id: number; name: string; description: string; icon: string; url_path: string; method: string; category: number | null; category_name: string; is_enabled: boolean; is_new: boolean; is_coming_soon: boolean; views_count: number; color: string; image_url: string; sort_order: number; favorites_count: number; is_favorited: boolean; [key: string]: any; } const methodColors: Record = { GET: "green", POST: "orange", PUT: "blue", DELETE: "red", }; const ApiDirectoryDesktop: React.FC = () => { const navigate = useNavigate(); const isLogin = useSelector(selectIsLogin); const { openLoginModal } = useLoginModal(); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [categories, setCategories] = useState([]); const [items, setItems] = useState([]); const [activeCategory, setActiveCategory] = useState("all"); const [sortOrder, setSortOrder] = useState(1); const [currentPage, setCurrentPage] = useState(1); const [pageSize, setPageSize] = useState(12); const [favoriteLoading, setFavoriteLoading] = useState>({}); const handleApiClick = async (api: ApiItem) => { if (api.is_coming_soon) return; if (isLogin) { try { await api_request.history.create({ type: 'api', title: api.name, description: api.description, image: getApiImageUrl(api), category: api.category_name || 'API接口', link: `/open-api-detail/${api.id}`, }); } catch (err) { console.error('记录浏览历史失败:', err); } } navigate(`/open-api-detail/${api.id}`); }; const handleFavoriteToggle = async (apiId: number, e: React.MouseEvent) => { e.stopPropagation(); if (!isLogin) { openLoginModal(); return; } if (favoriteLoading[apiId]) return; setFavoriteLoading(prev => ({ ...prev, [apiId]: true })); try { const res: any = await api_request.apidirectory.toggleFavorite(apiId); if (res?.data) { setItems(prev => prev.map(item => item.id === apiId ? { ...item, is_favorited: res.data.favorited, favorites_count: res.data.favorites_count ?? (item.is_favorited ? item.favorites_count - 1 : item.favorites_count + 1), } : item )); } } catch { message.error("收藏操作失败"); } finally { setFavoriteLoading(prev => ({ ...prev, [apiId]: false })); } }; const fetchCategories = async () => { try { const res: any = await api_request.apidirectory.getCategories(); if (res && res.data) { setCategories(res.data); } } catch (err: any) { console.error('获取分类失败:', err); } }; const getOrderingValue = (order: number): string => { const orderingMap: Record = { 1: 'sort_order', 2: '-views_count', 3: '-created_at', 4: '-favorites_count', }; return orderingMap[order] || 'sort_order'; }; const fetchItems = async (categoryId?: number, order?: number) => { setLoading(true); setError(null); setCurrentPage(1); try { const params: any = {}; if (categoryId) { params.category_id = categoryId; } const ordering = getOrderingValue(order ?? sortOrder); if (ordering) { params.ordering = ordering; } const res: any = await api_request.apidirectory.getItems(params); if (res && res.data) { const sorted = [...res.data].sort((a: ApiItem, b: ApiItem) => { if (a.is_coming_soon && !b.is_coming_soon) return 1; if (!a.is_coming_soon && b.is_coming_soon) return -1; return 0; }); setItems(sorted); } } catch (err: any) { setError('获取API列表失败,请稍后重试'); console.error('获取API列表失败:', err); } finally { setLoading(false); } }; useEffect(() => { fetchCategories(); fetchItems(); }, []); const handleCategoryChange = (key: string) => { setActiveCategory(key); setCurrentPage(1); if (key === 'all') { fetchItems(undefined, sortOrder); } else { const category = categories.find(c => c.name === key); if (category) { fetchItems(category.id, sortOrder); } } }; const handleSortChange = (e: RadioChangeEvent) => { const newSortOrder = e.target.value; setSortOrder(newSortOrder); setCurrentPage(1); if (activeCategory === 'all') { fetchItems(undefined, newSortOrder); } else { const category = categories.find(c => c.name === activeCategory); if (category) { fetchItems(category.id, newSortOrder); } } }; const handlePageChange = (page: number, size: number) => { setCurrentPage(page); setPageSize(size); window.scrollTo({ top: 0, behavior: 'smooth' }); }; const paginatedItems = useMemo(() => { const startIndex = (currentPage - 1) * pageSize; const endIndex = startIndex + pageSize; return items.slice(startIndex, endIndex); }, [items, currentPage, pageSize]); const getCategoryColor = (categoryName: string): string => { const colorMap: Record = { '天气API': 'blue', '数据API': 'cyan', 'AI接口': 'purple', '实用API': 'green', }; return colorMap[categoryName] || 'default'; }; const tabItems: TabsProps["items"] = [ { key: 'all', label: '全部分类' }, ...categories.map(cat => ({ key: cat.name, label: cat.name, })), ].map(cat => ({ key: cat.key, label: cat.label, children: (
), })); if (loading && items.length === 0) { return (

加载中...

); } return ( <>

API接口大全

海量免费API接口,助力快速开发

提供天气查询、数据服务、AI接口等多种API,即调即用,轻松集成到您的项目中

{error && (
)} {!error && ( <>
{paginatedItems.length === 0 ? (
) : ( paginatedItems.map((api, index) => (
handleApiClick(api)} >
{api.name}
{api.category_name || '其他'}
{api.method}
{api.is_coming_soon && (
敬请期待
)} {api.is_new && (
NEW
)}
{api.name} {api.views_count.toLocaleString()}
{api.description}
{api.url_path}
handleFavoriteToggle(api.id, e)} > {favoriteLoading[api.id] ? ( ) : api.is_favorited ? ( ) : ( )} {" "}{api.favorites_count}
{api.is_coming_soon ? ( ) : ( )}
)) )}
`共 ${total} 个接口`} onChange={handlePageChange} pageSizeOptions={['6', '12', '24']} />
)} ); }; export default ApiDirectoryDesktop;