catapi-web/components/api-docs.tsx
mei 36ef5db441 refactor(api-docs): 重构 API 文档组件并优化构建 ID 生成逻辑
- 移除了公告功能,替换为固定公告内容
- 更新了站点信息,包括友情链接和邮箱地址
- 优化了图片总数和接口文档的展示方式
- 重新实现了构建 ID 生成逻辑,使用项目文件哈希值代替时间戳
2025-02-07 11:10:18 +08:00

363 lines
12 KiB
TypeScript
Raw Permalink 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.

"use client";
import { useState, useEffect } from "react";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Badge } from "@/components/ui/badge";
import { Skeleton } from "@/components/ui/skeleton";
import { motion } from "framer-motion";
import { ExternalLink, GitCommit } from "lucide-react";
export default function ApiDocsComponent() {
const [activePage, setActivePage] = useState("home");
const [imageCount, setImageCount] = useState<number | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetchData = async () => {
setLoading(true);
try {
const [imageCountResponse] = await Promise.all([
fetch("/?api=total-pic")
]);
const imageCountData = await imageCountResponse.json();
setImageCount(imageCountData.count);
} catch (error) {
console.error("Failed to fetch data:", error);
setImageCount(null);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
const apiEndpoints = [
{
id: 1,
device: "手机",
method: "GET",
url: "/phone",
description: "竖屏随机图片API",
},
{
id: 2,
device: "电脑",
method: "GET",
url: "/pc",
description: "横屏随机图片API",
},
{
id: 3,
device: "All",
method: "GET",
url: "/favicon",
description: "获取本站favicon",
},
{
id: 4,
device: "All",
method: "GET",
url: "/bj",
description: "获取静态页面装饰图",
},
{
id: 5,
device: "All",
method: "GET",
url: "/fox",
description: "获取狐狸图",
},
];
const parameters = [
{
id: 1,
param: "time",
value: "Boolean",
description: "是否区分时段默认为false",
},
{
id: 2,
param: "mode",
value: "String",
description:
"填json则返回一串数组填redirect则会重定向至图片URL不填则返回一张图片",
},
{
id: 3,
param: "source",
value: "Boolean",
description: "是否使用原图(加载较慢)默认为true",
},
];
const returnData = [
{ id: 1, data: "code", description: "状态码" },
{ id: 2, data: "image_url", description: "图片地址" },
{ id: 3, data: "time", description: "是否分时段" },
{ id: 4, data: "endpoint_type", description: "适用设备" },
{ id: 5, data: "is_source", description: "是否为原图" },
{ id: 6, data: "mode", description: "输出模式" },
];
const friendlyLinks = [
{ name: "mei的网络日志", url: "https://mei.lv" }
];
const HomePage = () => (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="space-y-6"
>
<Card className="bg-white shadow-lg rounded-lg overflow-hidden">
<CardHeader className="bg-gradient-to-r from-purple-500 to-pink-500 text-white">
<CardTitle className="text-2xl font-bold"></CardTitle>
</CardHeader>
<CardContent className="p-6">
<p className="text-gray-700">
使{" "}
<code className="bg-gray-100 p-1 rounded text-pink-600 font-mono">
Cat API
</code>
, mei
</p>
<p className="mt-4 text-gray-600">
</p>
</CardContent>
</Card>
<Card className="bg-white shadow-lg rounded-lg overflow-hidden">
<CardHeader className="bg-gradient-to-r from-green-500 to-teal-500 text-white">
<CardTitle className="text-2xl font-bold"></CardTitle>
</CardHeader>
<CardContent className="p-6">
<div
className="mb-4 last:mb-0 bg-gray-50 p-4 rounded-lg"
>
<Badge
variant="outline"
className="bg-teal-100 text-teal-800 mb-2"
>
2024-10-03
</Badge>
<p className="text-gray-700">API已由无数据库的屎山代码重构为使用 Mysql </p>
</div>
</CardContent>
</Card>
<Card className="bg-white shadow-lg rounded-lg overflow-hidden">
<CardHeader className="bg-gradient-to-r from-blue-500 to-indigo-500 text-white">
<CardTitle className="text-2xl font-bold"></CardTitle>
</CardHeader>
<CardContent className="p-6">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-4">
{friendlyLinks.map((link, index) => (
<a
key={index}
href={link.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center p-4 bg-gray-50 rounded-lg shadow-sm hover:shadow-md transition-shadow duration-200 hover:bg-gray-100"
>
<ExternalLink className="w-5 h-5 mr-2 text-indigo-500" />
<span className="text-indigo-600 hover:underline">
{link.name}
</span>
</a>
))}
</div>
</CardContent>
</Card>
</motion.div>
);
const RandomImagePage = () => (
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.5 }}
className="space-y-8"
>
<Card className="bg-white shadow-lg rounded-lg overflow-hidden">
<CardHeader className="bg-gradient-to-r from-yellow-400 to-orange-500 text-white">
<CardTitle className="text-2xl font-bold"> API</CardTitle>
</CardHeader>
<CardContent className="p-6">
<p className="text-gray-700">
API !
</p>
<p className="mt-4 text-gray-600">
:{" "}
{loading ? (
<Skeleton className="h-4 w-[100px] inline-block ml-2" />
) : (
<Badge
variant="secondary"
className="ml-2 bg-orange-100 text-orange-800"
>
{imageCount !== null ? imageCount : "获取失败"}
</Badge>
)}
</p>
</CardContent>
</Card>
<Tabs
defaultValue="endpoints"
className="bg-white shadow-lg rounded-lg overflow-hidden"
>
<TabsList className="grid w-full grid-cols-3 bg-gradient-to-r from-purple-500 to-pink-500 p-1 gap-1">
<TabsTrigger
value="endpoints"
className="text-white data-[state=active]:bg-white data-[state=active]:text-purple-500"
>
API
</TabsTrigger>
<TabsTrigger
value="parameters"
className="text-white data-[state=active]:bg-white data-[state=active]:text-purple-500"
>
</TabsTrigger>
<TabsTrigger
value="returnData"
className="text-white data-[state=active]:bg-white data-[state=active]:text-purple-500"
>
</TabsTrigger>
</TabsList>
<TabsContent value="endpoints" className="p-6">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[50px]">#</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{apiEndpoints.map((endpoint) => (
<TableRow key={endpoint.id}>
<TableCell>{endpoint.id}</TableCell>
<TableCell>{endpoint.device}</TableCell>
<TableCell>{endpoint.method}</TableCell>
<TableCell><a href = {endpoint.url}>{endpoint.url}</a></TableCell>
<TableCell>{endpoint.description}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TabsContent>
<TabsContent value="parameters" className="p-6">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[50px]">#</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{parameters.map((param) => (
<TableRow key={param.id}>
<TableCell>{param.id}</TableCell>
<TableCell>{param.param}</TableCell>
<TableCell>{param.value}</TableCell>
<TableCell>{param.description}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TabsContent>
<TabsContent value="returnData" className="p-6">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[50px]">#</TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{returnData.map((data) => (
<TableRow key={data.id}>
<TableCell>{data.id}</TableCell>
<TableCell>{data.data}</TableCell>
<TableCell>{data.description}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TabsContent>
</Tabs>
</motion.div>
);
return (
<div className="flex flex-col min-h-screen bg-gray-100">
<div className="flex flex-1">
<nav className="w-64 bg-white shadow-md">
<div className="p-6">
<h1 className="text-2xl font-bold mb-6 text-gray-800">
Cat API
</h1>
<ul className="space-y-2">
<li>
<Button
variant={activePage === "home" ? "default" : "ghost"}
className="w-full justify-start text-left"
onClick={() => setActivePage("home")}
>
</Button>
</li>
<li>
<Button
variant={activePage === "random-image" ? "default" : "ghost"}
className="w-full justify-start text-left"
onClick={() => setActivePage("random-image")}
>
</Button>
</li>
</ul>
</div>
</nav>
<main className="flex-1 p-8 overflow-auto">
{activePage === "home" ? <HomePage /> : <RandomImagePage />}
</main>
</div>
<footer className="bg-white shadow-md mt-8">
<div className="container mx-auto px-6 py-4">
<div className="flex justify-between items-center">
<p className="text-gray-600">
&copy; 2024 mei. All rights reserved.
</p>
<div className="flex items-center text-gray-500">
<GitCommit className="w-5 h-5 mr-2" />
<span className="text-sm">{process.env.NEXT_PUBLIC_BUILD_ID || 'dev'}</span>
</div>
</div>
</div>
</footer>
</div>
);
}