223 lines
8.3 KiB
TypeScript
223 lines
8.3 KiB
TypeScript
|
|
"use client"
|
||
|
|
|
||
|
|
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
|
||
|
|
import { Button } from "@/components/ui/button"
|
||
|
|
import { Badge } from "@/components/ui/badge"
|
||
|
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||
|
|
import { ScrollArea } from "@/components/ui/scroll-area"
|
||
|
|
import { Code, Copy, ExternalLink } from "lucide-react"
|
||
|
|
|
||
|
|
interface APIDocDialogProps {
|
||
|
|
open: boolean
|
||
|
|
onOpenChange: (open: boolean) => void
|
||
|
|
api?: {
|
||
|
|
name: string
|
||
|
|
endpoint: string
|
||
|
|
method: string
|
||
|
|
description: string
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
export function APIDocDialog({ open, onOpenChange, api }: APIDocDialogProps) {
|
||
|
|
const copyToClipboard = (text: string) => {
|
||
|
|
navigator.clipboard.writeText(text)
|
||
|
|
}
|
||
|
|
|
||
|
|
return (
|
||
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||
|
|
<DialogContent className="max-w-3xl max-h-[80vh]">
|
||
|
|
<DialogHeader>
|
||
|
|
<DialogTitle className="flex items-center gap-2">
|
||
|
|
<Code className="w-5 h-5" />
|
||
|
|
API文档 - {api?.name || "API服务"}
|
||
|
|
</DialogTitle>
|
||
|
|
<DialogDescription>{api?.description}</DialogDescription>
|
||
|
|
</DialogHeader>
|
||
|
|
|
||
|
|
<div className="flex items-center gap-2 p-3 bg-slate-50 rounded-lg">
|
||
|
|
<Badge className={api?.method === "GET" ? "bg-blue-100 text-blue-700" : "bg-green-100 text-green-700"}>
|
||
|
|
{api?.method || "GET"}
|
||
|
|
</Badge>
|
||
|
|
<code className="flex-1 text-sm">{api?.endpoint || "/api/v1/example"}</code>
|
||
|
|
<Button variant="ghost" size="sm" onClick={() => copyToClipboard(api?.endpoint || "")}>
|
||
|
|
<Copy className="w-4 h-4" />
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<Tabs defaultValue="request">
|
||
|
|
<TabsList className="grid w-full grid-cols-4">
|
||
|
|
<TabsTrigger value="request">请求参数</TabsTrigger>
|
||
|
|
<TabsTrigger value="response">响应格式</TabsTrigger>
|
||
|
|
<TabsTrigger value="example">示例代码</TabsTrigger>
|
||
|
|
<TabsTrigger value="errors">错误码</TabsTrigger>
|
||
|
|
</TabsList>
|
||
|
|
|
||
|
|
<TabsContent value="request" className="mt-4">
|
||
|
|
<ScrollArea className="h-[300px]">
|
||
|
|
<table className="w-full text-sm">
|
||
|
|
<thead>
|
||
|
|
<tr className="border-b">
|
||
|
|
<th className="text-left py-2 px-3">参数名</th>
|
||
|
|
<th className="text-left py-2 px-3">类型</th>
|
||
|
|
<th className="text-left py-2 px-3">必填</th>
|
||
|
|
<th className="text-left py-2 px-3">说明</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
<tr className="border-b">
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<code>user_id</code>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">string</td>
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<Badge className="bg-red-100 text-red-700">是</Badge>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">用户唯一标识</td>
|
||
|
|
</tr>
|
||
|
|
<tr className="border-b">
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<code>fields</code>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">array</td>
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<Badge variant="outline">否</Badge>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">需要返回的字段列表</td>
|
||
|
|
</tr>
|
||
|
|
<tr className="border-b">
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<code>include_tags</code>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">boolean</td>
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<Badge variant="outline">否</Badge>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">是否包含标签信息, 默认 true</td>
|
||
|
|
</tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</ScrollArea>
|
||
|
|
</TabsContent>
|
||
|
|
|
||
|
|
<TabsContent value="response" className="mt-4">
|
||
|
|
<ScrollArea className="h-[300px]">
|
||
|
|
<pre className="p-4 bg-slate-900 rounded-lg text-sm text-green-400 overflow-x-auto">
|
||
|
|
{`{
|
||
|
|
"code": 0,
|
||
|
|
"message": "success",
|
||
|
|
"data": {
|
||
|
|
"user_id": "u_123456",
|
||
|
|
"name": "张三",
|
||
|
|
"phone": "138****5678",
|
||
|
|
"email": "zhang***@example.com",
|
||
|
|
"tags": ["高价值", "活跃用户", "VIP"],
|
||
|
|
"portrait": {
|
||
|
|
"age_group": "25-35",
|
||
|
|
"gender": "male",
|
||
|
|
"city": "上海",
|
||
|
|
"consumption_level": "high"
|
||
|
|
},
|
||
|
|
"value_score": {
|
||
|
|
"rfm_score": 85,
|
||
|
|
"ltv": 12580,
|
||
|
|
"churn_risk": 0.12
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}`}
|
||
|
|
</pre>
|
||
|
|
</ScrollArea>
|
||
|
|
</TabsContent>
|
||
|
|
|
||
|
|
<TabsContent value="example" className="mt-4">
|
||
|
|
<ScrollArea className="h-[300px]">
|
||
|
|
<div className="space-y-4">
|
||
|
|
<div>
|
||
|
|
<h4 className="text-sm font-medium mb-2">cURL</h4>
|
||
|
|
<pre className="p-4 bg-slate-900 rounded-lg text-sm text-green-400 overflow-x-auto">
|
||
|
|
{`curl -X GET "https://api.shenshoushou.com/api/v1/user/portrait?user_id=u_123456" \\
|
||
|
|
-H "Authorization: Bearer YOUR_API_KEY" \\
|
||
|
|
-H "Content-Type: application/json"`}
|
||
|
|
</pre>
|
||
|
|
</div>
|
||
|
|
<div>
|
||
|
|
<h4 className="text-sm font-medium mb-2">JavaScript</h4>
|
||
|
|
<pre className="p-4 bg-slate-900 rounded-lg text-sm text-green-400 overflow-x-auto">
|
||
|
|
{`const response = await fetch(
|
||
|
|
"https://api.shenshoushou.com/api/v1/user/portrait?user_id=u_123456",
|
||
|
|
{
|
||
|
|
headers: {
|
||
|
|
"Authorization": "Bearer YOUR_API_KEY",
|
||
|
|
"Content-Type": "application/json"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
);
|
||
|
|
const data = await response.json();`}
|
||
|
|
</pre>
|
||
|
|
</div>
|
||
|
|
</div>
|
||
|
|
</ScrollArea>
|
||
|
|
</TabsContent>
|
||
|
|
|
||
|
|
<TabsContent value="errors" className="mt-4">
|
||
|
|
<ScrollArea className="h-[300px]">
|
||
|
|
<table className="w-full text-sm">
|
||
|
|
<thead>
|
||
|
|
<tr className="border-b">
|
||
|
|
<th className="text-left py-2 px-3">错误码</th>
|
||
|
|
<th className="text-left py-2 px-3">说明</th>
|
||
|
|
<th className="text-left py-2 px-3">解决方案</th>
|
||
|
|
</tr>
|
||
|
|
</thead>
|
||
|
|
<tbody>
|
||
|
|
<tr className="border-b">
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<code>400</code>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">参数错误</td>
|
||
|
|
<td className="py-2 px-3">检查请求参数格式</td>
|
||
|
|
</tr>
|
||
|
|
<tr className="border-b">
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<code>401</code>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">未授权</td>
|
||
|
|
<td className="py-2 px-3">检查API Key是否正确</td>
|
||
|
|
</tr>
|
||
|
|
<tr className="border-b">
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<code>404</code>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">用户不存在</td>
|
||
|
|
<td className="py-2 px-3">确认用户ID是否正确</td>
|
||
|
|
</tr>
|
||
|
|
<tr className="border-b">
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<code>429</code>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">请求频率超限</td>
|
||
|
|
<td className="py-2 px-3">降低请求频率或升级配额</td>
|
||
|
|
</tr>
|
||
|
|
<tr className="border-b">
|
||
|
|
<td className="py-2 px-3">
|
||
|
|
<code>500</code>
|
||
|
|
</td>
|
||
|
|
<td className="py-2 px-3">服务器内部错误</td>
|
||
|
|
<td className="py-2 px-3">联系技术支持</td>
|
||
|
|
</tr>
|
||
|
|
</tbody>
|
||
|
|
</table>
|
||
|
|
</ScrollArea>
|
||
|
|
</TabsContent>
|
||
|
|
</Tabs>
|
||
|
|
|
||
|
|
<div className="flex justify-end gap-2">
|
||
|
|
<Button variant="outline">
|
||
|
|
<ExternalLink className="w-4 h-4 mr-2" />
|
||
|
|
在线调试
|
||
|
|
</Button>
|
||
|
|
</div>
|
||
|
|
</DialogContent>
|
||
|
|
</Dialog>
|
||
|
|
)
|
||
|
|
}
|