生产管理系统 - 删除api-example
This commit is contained in:
@@ -1,420 +0,0 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import { Copy, Eye, EyeOff, Code, FileJson } from 'lucide-react';
|
||||
|
||||
interface OpenApiExample {
|
||||
path: string;
|
||||
method: string;
|
||||
summary?: string;
|
||||
description?: string;
|
||||
parameters?: any[];
|
||||
requestBody?: any;
|
||||
responses?: any;
|
||||
examples?: any;
|
||||
}
|
||||
|
||||
interface OpenApiExamplesProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
// 示例数据 - 这些通常从 OpenAPI 规范中提取
|
||||
const openApiExamples: OpenApiExample[] = [
|
||||
{
|
||||
path: '/api/v1/auth/login',
|
||||
method: 'POST',
|
||||
summary: '用户登录',
|
||||
description: '用户登录(需要验证码)',
|
||||
requestBody: {
|
||||
'application/json': {
|
||||
example: {
|
||||
identifier: 'admin',
|
||||
password: 'admin123',
|
||||
captcha_id: 'test-captcha-id',
|
||||
captcha_text: '1234'
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Successful Response',
|
||||
content: {
|
||||
'application/json': {
|
||||
example: {
|
||||
access_token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...',
|
||||
token_type: 'bearer',
|
||||
expires_in: 3600
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/api/v1/auth/register',
|
||||
method: 'POST',
|
||||
summary: '用户注册',
|
||||
description: '用户注册',
|
||||
requestBody: {
|
||||
'application/json': {
|
||||
example: {
|
||||
username: 'newuser',
|
||||
password: 'password123',
|
||||
email: 'user@example.com',
|
||||
full_name: '新用户',
|
||||
phone: '13800138000',
|
||||
tenant_id: 'tenant-uuid',
|
||||
scope: 'user',
|
||||
department_id: 'dept-uuid'
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Successful Response',
|
||||
content: {
|
||||
'application/json': {
|
||||
example: {
|
||||
id: 'uuid-12345',
|
||||
username: 'newuser',
|
||||
email: 'user@example.com',
|
||||
phone: '13800138000',
|
||||
is_active: true,
|
||||
created_at: '2024-01-01T00:00:00Z'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/api/v1/users',
|
||||
method: 'POST',
|
||||
summary: '创建用户',
|
||||
description: '创建用户(需要管理员权限)',
|
||||
requestBody: {
|
||||
'application/json': {
|
||||
example: {
|
||||
username: 'testuser',
|
||||
password: 'password123',
|
||||
email: 'test@example.com',
|
||||
full_name: '测试用户',
|
||||
phone: '13900139000',
|
||||
tenant_id: 'tenant-uuid',
|
||||
scope: 'user',
|
||||
department_id: 'dept-uuid'
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
200: {
|
||||
description: 'Successful Response',
|
||||
content: {
|
||||
'application/json': {
|
||||
example: {
|
||||
id: 'uuid-67890',
|
||||
username: 'testuser',
|
||||
email: 'test@example.com',
|
||||
phone: '13900139000',
|
||||
is_active: true,
|
||||
created_at: '2024-01-01T00:00:00Z'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: '/api/v1/tenants',
|
||||
method: 'POST',
|
||||
summary: '创建租户',
|
||||
description: '创建新租户',
|
||||
requestBody: {
|
||||
'application/json': {
|
||||
example: {
|
||||
company_name: '新企业有限责任公司',
|
||||
tenant_code: 'NEW001',
|
||||
company_type: '有限责任公司'
|
||||
}
|
||||
}
|
||||
},
|
||||
responses: {
|
||||
201: {
|
||||
description: 'Successful Response',
|
||||
content: {
|
||||
'application/json': {
|
||||
example: {
|
||||
id: 'tenant-uuid',
|
||||
company_name: '新企业有限责任公司',
|
||||
tenant_code: 'NEW001',
|
||||
company_type: '有限责任公司',
|
||||
audit_status: 'pending',
|
||||
created_at: '2024-01-01T00:00:00Z'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export default function OpenApiExamples({ className }: OpenApiExamplesProps) {
|
||||
const [copiedCode, setCopiedCode] = useState<string | null>(null);
|
||||
const [visibleExamples, setVisibleExamples] = useState<Set<string>>(new Set());
|
||||
|
||||
const copyToClipboard = async (text: string, id: string) => {
|
||||
try {
|
||||
await navigator.clipboard.writeText(text);
|
||||
setCopiedCode(id);
|
||||
setTimeout(() => setCopiedCode(null), 2000);
|
||||
} catch (err) {
|
||||
console.error('Failed to copy:', err);
|
||||
}
|
||||
};
|
||||
|
||||
const toggleExampleVisibility = (id: string) => {
|
||||
setVisibleExamples(prev => {
|
||||
const newSet = new Set(prev);
|
||||
if (newSet.has(id)) {
|
||||
newSet.delete(id);
|
||||
} else {
|
||||
newSet.add(id);
|
||||
}
|
||||
return newSet;
|
||||
});
|
||||
};
|
||||
|
||||
const getMethodColor = (method: string) => {
|
||||
switch (method.toUpperCase()) {
|
||||
case 'GET': return 'bg-green-100 text-green-800 border-green-200';
|
||||
case 'POST': return 'bg-blue-100 text-blue-800 border-blue-200';
|
||||
case 'PUT': return 'bg-yellow-100 text-yellow-800 border-yellow-200';
|
||||
case 'DELETE': return 'bg-red-100 text-red-800 border-red-200';
|
||||
default: return 'bg-gray-100 text-gray-800 border-gray-200';
|
||||
}
|
||||
};
|
||||
|
||||
const formatJson = (obj: any) => {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card className={className}>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<FileJson className="h-5 w-5" />
|
||||
OpenAPI 示例文档
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
基于 OpenAPI 规范的接口示例,展示了请求参数和响应格式
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Tabs defaultValue="all" className="w-full">
|
||||
<TabsList className="grid w-full grid-cols-4">
|
||||
<TabsTrigger value="all">全部示例</TabsTrigger>
|
||||
<TabsTrigger value="auth">认证</TabsTrigger>
|
||||
<TabsTrigger value="users">用户管理</TabsTrigger>
|
||||
<TabsTrigger value="tenants">租户管理</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="all" className="space-y-4">
|
||||
{openApiExamples.map((example) => (
|
||||
<OpenApiExampleCard
|
||||
key={`${example.method}-${example.path}`}
|
||||
example={example}
|
||||
getMethodColor={getMethodColor}
|
||||
formatJson={formatJson}
|
||||
copyToClipboard={copyToClipboard}
|
||||
copiedCode={copiedCode}
|
||||
visibleExamples={visibleExamples}
|
||||
toggleExampleVisibility={toggleExampleVisibility}
|
||||
/>
|
||||
))}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="auth" className="space-y-4">
|
||||
{openApiExamples
|
||||
.filter(e => e.path.includes('/auth/'))
|
||||
.map((example) => (
|
||||
<OpenApiExampleCard
|
||||
key={`${example.method}-${example.path}`}
|
||||
example={example}
|
||||
getMethodColor={getMethodColor}
|
||||
formatJson={formatJson}
|
||||
copyToClipboard={copyToClipboard}
|
||||
copiedCode={copiedCode}
|
||||
visibleExamples={visibleExamples}
|
||||
toggleExampleVisibility={toggleExampleVisibility}
|
||||
/>
|
||||
))}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="users" className="space-y-4">
|
||||
{openApiExamples
|
||||
.filter(e => e.path.includes('/users'))
|
||||
.map((example) => (
|
||||
<OpenApiExampleCard
|
||||
key={`${example.method}-${example.path}`}
|
||||
example={example}
|
||||
getMethodColor={getMethodColor}
|
||||
formatJson={formatJson}
|
||||
copyToClipboard={copyToClipboard}
|
||||
copiedCode={copiedCode}
|
||||
visibleExamples={visibleExamples}
|
||||
toggleExampleVisibility={toggleExampleVisibility}
|
||||
/>
|
||||
))}
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="tenants" className="space-y-4">
|
||||
{openApiExamples
|
||||
.filter(e => e.path.includes('/tenants'))
|
||||
.map((example) => (
|
||||
<OpenApiExampleCard
|
||||
key={`${example.method}-${example.path}`}
|
||||
example={example}
|
||||
getMethodColor={getMethodColor}
|
||||
formatJson={formatJson}
|
||||
copyToClipboard={copyToClipboard}
|
||||
copiedCode={copiedCode}
|
||||
visibleExamples={visibleExamples}
|
||||
toggleExampleVisibility={toggleExampleVisibility}
|
||||
/>
|
||||
))}
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
interface OpenApiExampleCardProps {
|
||||
example: OpenApiExample;
|
||||
getMethodColor: (method: string) => string;
|
||||
formatJson: (obj: any) => string;
|
||||
copyToClipboard: (text: string, id: string) => void;
|
||||
copiedCode: string | null;
|
||||
visibleExamples: Set<string>;
|
||||
toggleExampleVisibility: (id: string) => void;
|
||||
}
|
||||
|
||||
function OpenApiExampleCard({
|
||||
example,
|
||||
getMethodColor,
|
||||
formatJson,
|
||||
copyToClipboard,
|
||||
copiedCode,
|
||||
visibleExamples,
|
||||
toggleExampleVisibility
|
||||
}: OpenApiExampleCardProps) {
|
||||
const exampleId = `${example.method}-${example.path}`;
|
||||
const isVisible = visibleExamples.has(exampleId);
|
||||
|
||||
return (
|
||||
<div className="border rounded-lg p-4 space-y-3">
|
||||
<div className="flex items-start justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<Badge className={getMethodColor(example.method)}>
|
||||
{example.method}
|
||||
</Badge>
|
||||
<div>
|
||||
<h3 className="font-semibold text-lg">{example.summary}</h3>
|
||||
<p className="text-sm text-muted-foreground font-mono">
|
||||
{example.path}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => toggleExampleVisibility(exampleId)}
|
||||
>
|
||||
{isVisible ? <EyeOff className="h-4 w-4" /> : <Eye className="h-4 w-4" />}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{example.description && (
|
||||
<p className="text-sm text-muted-foreground">{example.description}</p>
|
||||
)}
|
||||
|
||||
{isVisible && (
|
||||
<div className="space-y-4">
|
||||
{/* Request Body Examples */}
|
||||
{example.requestBody && (
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-medium text-sm flex items-center gap-2">
|
||||
<Code className="h-4 w-4" />
|
||||
请求示例
|
||||
</h4>
|
||||
{Object.entries(example.requestBody).map(([contentType, content]: [string, any]) => (
|
||||
<div key={contentType} className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Badge variant="outline">{contentType}</Badge>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => copyToClipboard(formatJson(content.example), `request-${exampleId}`)}
|
||||
>
|
||||
{copiedCode === `request-${exampleId}` ? '已复制!' : <Copy className="h-4 w-4" />}
|
||||
</Button>
|
||||
</div>
|
||||
<pre className="bg-muted p-3 rounded-md overflow-x-auto text-xs">
|
||||
<code>{formatJson(content.example)}</code>
|
||||
</pre>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Response Examples */}
|
||||
{example.responses && (
|
||||
<div className="space-y-2">
|
||||
<h4 className="font-medium text-sm flex items-center gap-2">
|
||||
<Code className="h-4 w-4" />
|
||||
响应示例
|
||||
</h4>
|
||||
{Object.entries(example.responses).map(([statusCode, response]: [string, any]) => (
|
||||
<div key={statusCode} className="space-y-2">
|
||||
<div className="flex items-center gap-2">
|
||||
<Badge variant={statusCode.startsWith('2') ? 'default' : 'destructive'}>
|
||||
{statusCode}
|
||||
</Badge>
|
||||
<span className="text-sm text-muted-foreground">
|
||||
{response.description}
|
||||
</span>
|
||||
</div>
|
||||
{response.content && Object.entries(response.content).map(([contentType, content]: [string, any]) => (
|
||||
<div key={contentType} className="space-y-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<Badge variant="outline">{contentType}</Badge>
|
||||
{content.example && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => copyToClipboard(formatJson(content.example), `response-${exampleId}-${statusCode}`)}
|
||||
>
|
||||
{copiedCode === `response-${exampleId}-${statusCode}` ? '已复制!' : <Copy className="h-4 w-4" />}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{content.example && (
|
||||
<pre className="bg-muted p-3 rounded-md overflow-x-auto text-xs">
|
||||
<code>{formatJson(content.example)}</code>
|
||||
</pre>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -61,13 +61,7 @@ const navbarData = {
|
||||
url: "/central-config",
|
||||
description: "租户管理、用户管理、系统监控",
|
||||
icon: <Settings className="size-5 shrink-0" />,
|
||||
},
|
||||
{
|
||||
title: "API 测试示例",
|
||||
url: "/api-example",
|
||||
description: "测试和展示 OpenAPI 客户端调用",
|
||||
icon: <Brain className="size-5 shrink-0" />,
|
||||
},
|
||||
}
|
||||
],
|
||||
auth: {
|
||||
login: { title: "登录", url: "/login" },
|
||||
|
||||
Reference in New Issue
Block a user