生产管理系统 - 删除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",
|
url: "/central-config",
|
||||||
description: "租户管理、用户管理、系统监控",
|
description: "租户管理、用户管理、系统监控",
|
||||||
icon: <Settings className="size-5 shrink-0" />,
|
icon: <Settings className="size-5 shrink-0" />,
|
||||||
},
|
}
|
||||||
{
|
|
||||||
title: "API 测试示例",
|
|
||||||
url: "/api-example",
|
|
||||||
description: "测试和展示 OpenAPI 客户端调用",
|
|
||||||
icon: <Brain className="size-5 shrink-0" />,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
auth: {
|
auth: {
|
||||||
login: { title: "登录", url: "/login" },
|
login: { title: "登录", url: "/login" },
|
||||||
|
|||||||
Reference in New Issue
Block a user