Files
smart-crop-ui/crop-x/src/app/(app)/api-example/page.tsx

1706 lines
66 KiB
TypeScript
Raw 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 } from 'react';
import {
// 认证相关
loginApiV1AuthLoginPost,
registerApiV1AuthRegisterPost,
registerWithCompanyApiV1AuthRegisterWithCompanyPost,
getCaptchaApiV1AuthCaptchaGet,
refreshTokenApiV1AuthRefreshPost,
logoutApiV1AuthLogoutPost,
getCurrentUserInfoApiV1AuthMeGet,
updateCurrentUserApiV1AuthMePut,
changePasswordApiV1AuthChangePasswordPost,
verifyUserTokenApiV1AuthVerifyTokenPost,
// 用户管理
getUsersApiV1UsersGet,
createUserApiV1UsersPost,
getUserApiV1UsersUserIdGet,
updateUserApiV1UsersUserIdPut,
deleteUserApiV1UsersUserIdDelete,
activateUserApiV1UsersUserIdActivatePost,
deactivateUserApiV1UsersUserIdDeactivatePost,
getUserStatsApiV1UsersStatsSummaryGet,
getDepartmentOptionsApiV1UsersDepartmentsOptionsGet,
// 系统用户管理
listSystemUsersApiV1UsersSystemUsersGet,
createSystemUserApiV1UsersSystemUsersPost,
getSystemUserApiV1UsersSystemUsersUserIdGet,
updateSystemUserApiV1UsersSystemUsersUserIdPut,
deleteSystemUserApiV1UsersSystemUsersUserIdDelete,
activateSystemUserApiV1UsersSystemUsersUserIdActivatePost,
deactivateSystemUserApiV1UsersSystemUsersUserIdDeactivatePost,
getSystemUserStatsApiV1UsersSystemUsersStatsSummaryGet,
// 租户管理
listTenantsApiV1TenantsGet,
createTenantApiV1TenantsPost,
getTenantApiV1TenantsTenantIdGet,
deleteTenantApiV1TenantsTenantIdDelete,
auditTenantApiV1TenantsTenantIdAuditPost,
disableTenantApiV1TenantsTenantIdDisablePatch,
enableTenantApiV1TenantsTenantIdEnablePatch,
getCurrentTenantApiV1TenantsMeGet,
submitTenantAuditApiV1TenantsSubmitPost,
getTenantAuditLogsApiV1TenantsAuditLogsGet,
listAnonymousTenantsApiV1TenantsAnonymousListGet,
// 部门管理
getDepartmentsApiV1DepartmentsDepartmentsGet,
createDepartmentApiV1DepartmentsDepartmentsPost,
getDepartmentApiV1DepartmentsDepartmentsDepartmentIdGet,
updateDepartmentApiV1DepartmentsDepartmentsDepartmentIdPut,
deleteDepartmentApiV1DepartmentsDepartmentsDepartmentIdDelete,
updateDepartmentOrderApiV1DepartmentsDepartmentsDepartmentIdOrderPatch,
updateDepartmentStatusApiV1DepartmentsDepartmentsDepartmentIdStatusPatch,
getDepartmentTreeApiV1DepartmentsDepartmentsTreeGet,
// 系统相关
getSystemInfoApiV1SystemInfoGet,
getSystemConfigApiV1SystemConfigGet,
getSystemStatsApiV1SystemStatsGet,
getSystemMetricsApiV1SystemMetricsGet,
getPerformanceSummaryApiV1SystemMetricsSummaryGet,
// 健康检查
healthCheckApiV1HealthGet,
detailedHealthCheckApiV1HealthDetailedGet,
// 类型导入
type UserCreate,
type UserCreateWithCompany,
type UserLoginWithCaptcha,
type UserUpdate,
type UserUpdatePassword,
type TenantCreateRequest,
type TenantUpdateRequest,
type TenantAuditRequest,
type DepartmentCreate,
type DepartmentUpdate,
type CaptchaResponse,
type Token
} from '@/lib/api';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { Badge } from '@/components/ui/badge';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { ScrollArea } from '@/components/ui/scroll-area';
import { Separator } from '@/components/ui/separator';
import { Label } from '@/components/ui/label';
import OpenApiExamples from './openapi-examples';
export default function ApiExamplePage() {
const [loading, setLoading] = useState(false);
const [results, setResults] = useState<any[]>([]);
const [errors, setErrors] = useState<string[]>([]);
// 登录表单状态
const [loginData, setLoginData] = useState<UserLoginWithCaptcha>({
identifier: '',
password: '',
captcha_id: '',
captcha_text: ''
});
// 验证码状态
const [captchaData, setCaptchaData] = useState<CaptchaResponse | null>(null);
// 注册表单状态
const [registerData, setRegisterData] = useState<UserCreate>({
username: '',
password: '',
email: '',
full_name: '',
phone: '',
tenant_id: '',
scope: '',
department_id: ''
});
// 带企业注册表单状态
const [registerWithCompanyData, setRegisterWithCompanyData] = useState<UserCreateWithCompany>({
user: {
username: '',
password: '',
email: '',
full_name: ''
},
company: {
name: '',
code: '',
description: ''
}
});
// 用户创建表单状态
const [createUserData, setCreateUserData] = useState<UserCreate>({
username: '',
password: '',
email: '',
full_name: '',
phone: '',
tenant_id: '',
scope: '',
department_id: ''
});
// 租户创建表单状态
const [createTenantData, setCreateTenantData] = useState<TenantCreateRequest>({
company_name: '',
tenant_code: '',
company_type: '有限责任公司'
});
// 部门创建表单状态
const [createDepartmentData, setCreateDepartmentData] = useState<DepartmentCreate>({
name: '',
code: '',
description: '',
parent_id: ''
});
// 通用ID输入状态
const [userId, setUserId] = useState('');
const [tenantId, setTenantId] = useState('');
const [departmentId, setDepartmentId] = useState('');
// 添加结果到显示列表
const addResult = (type: string, input: any, output: any, error?: string) => {
const result = {
id: Date.now(),
type,
input,
output,
error,
timestamp: new Date().toLocaleTimeString()
};
setResults(prev => [result, ...prev]);
if (error) {
setErrors(prev => [error, ...prev]);
}
};
// 清空结果
const clearResults = () => {
setResults([]);
setErrors([]);
};
// 用户登录
const handleLogin = async () => {
setLoading(true);
try {
addResult('用户登录', '登录请求', '发送中...');
const response = await loginApiV1AuthLoginPost({
body: loginData
});
if (response.data) {
addResult('用户登录', loginData, response.data);
} else if (response.error) {
addResult('用户登录', loginData, null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('用户登录', loginData, null, error.message);
} finally {
setLoading(false);
}
};
// 用户注册
const handleRegister = async () => {
setLoading(true);
try {
addResult('用户注册', '注册请求', '发送中...');
const response = await registerApiV1AuthRegisterPost({
body: registerData
});
if (response.data) {
addResult('用户注册', registerData, response.data);
} else if (response.error) {
addResult('用户注册', registerData, null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('用户注册', registerData, null, error.message);
} finally {
setLoading(false);
}
};
// 带企业用户注册
const handleRegisterWithCompany = async () => {
setLoading(true);
try {
addResult('用户注册并创建企业', '注册请求', '发送中...');
const response = await registerWithCompanyApiV1AuthRegisterWithCompanyPost({
body: registerWithCompanyData
});
if (response.data) {
addResult('用户注册并创建企业', registerWithCompanyData, response.data);
} else if (response.error) {
addResult('用户注册并创建企业', registerWithCompanyData, null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('用户注册并创建企业', registerWithCompanyData, null, error.message);
} finally {
setLoading(false);
}
};
// 获取验证码
const handleGetCaptcha = async () => {
setLoading(true);
try {
addResult('获取验证码', 'GET /api/v1/auth/captcha', '发送中...');
const response = await getCaptchaApiV1AuthCaptchaGet({});
if (response.data) {
setCaptchaData(response.data);
setLoginData(prev => ({ ...prev, captcha_id: response.data.captcha_id }));
addResult('获取验证码', '无参数', response.data);
} else if (response.error) {
addResult('获取验证码', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取验证码', '无参数', null, error.message);
} finally {
setLoading(false);
}
};
// 刷新令牌
const handleRefreshToken = async () => {
setLoading(true);
try {
addResult('刷新令牌', 'POST /api/v1/auth/refresh', '发送中...');
const response = await refreshTokenApiV1AuthRefreshPost({});
if (response.data) {
addResult('刷新令牌', '无参数', response.data);
} else if (response.error) {
addResult('刷新令牌', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('刷新令牌', '无参数', null, error.message);
} finally {
setLoading(false);
}
};
// 获取当前用户信息
const handleGetCurrentUser = async () => {
setLoading(true);
try {
addResult('获取当前用户', 'GET /api/v1/auth/me', '发送中...');
const response = await getCurrentUserInfoApiV1AuthMeGet({});
if (response.data) {
addResult('获取当前用户', '无参数', response.data);
} else if (response.error) {
addResult('获取当前用户', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取当前用户', '无参数', null, error.message);
} finally {
setLoading(false);
}
};
// 获取用户列表
const handleGetUsers = async () => {
setLoading(true);
try {
addResult('获取用户列表', 'GET /api/v1/users', '发送中...');
const response = await getUsersApiV1UsersGet({});
if (response.data) {
addResult('获取用户列表', '无参数', response.data);
} else if (response.error) {
addResult('获取用户列表', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取用户列表', '无参数', null, error.message);
} finally {
setLoading(false);
}
};
// 创建用户
const handleCreateUser = async () => {
setLoading(true);
try {
addResult('创建用户', 'POST /api/v1/users', '发送中...');
const response = await createUserApiV1UsersPost({
body: createUserData
});
if (response.data) {
addResult('创建用户', createUserData, response.data);
} else if (response.error) {
addResult('创建用户', createUserData, null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('创建用户', createUserData, null, error.message);
} finally {
setLoading(false);
}
};
// 获取用户详情
const handleGetUser = async () => {
if (!userId) {
addResult('获取用户详情', '缺少用户ID', null, '请输入用户ID');
return;
}
setLoading(true);
try {
addResult('获取用户详情', `GET /api/v1/users/${userId}`, '发送中...');
const response = await getUserApiV1UsersUserIdGet({
path: { user_id: userId }
});
if (response.data) {
addResult('获取用户详情', { user_id: userId }, response.data);
} else if (response.error) {
addResult('获取用户详情', { user_id: userId }, null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取用户详情', { user_id: userId }, null, error.message);
} finally {
setLoading(false);
}
};
// 用户登出
const handleLogout = async () => {
setLoading(true);
try {
addResult('用户登出', 'POST /api/v1/auth/logout', '发送中...');
const response = await logoutApiV1AuthLogoutPost({});
if (response.data) {
addResult('用户登出', '无参数', response.data);
} else if (response.error) {
addResult('用户登出', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('用户登出', '无参数', null, error.message);
} finally {
setLoading(false);
}
};
// 健康检查
const handleHealthCheck = async () => {
setLoading(true);
try {
addResult('健康检查', 'GET /api/v1/health', '发送中...');
const response = await healthCheckApiV1HealthGet({});
if (response.data) {
addResult('健康检查', '无参数', response.data);
} else if (response.error) {
addResult('健康检查', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('健康检查', '无参数', null, error.message);
} finally {
setLoading(false);
}
};
// 系统信息
const handleGetSystemInfo = async () => {
setLoading(true);
try {
addResult('系统信息', 'GET /api/v1/system/info', '发送中...');
const response = await getSystemInfoApiV1SystemInfoGet({});
if (response.data) {
addResult('系统信息', '无参数', response.data);
} else if (response.error) {
addResult('系统信息', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('系统信息', '无参数', null, error.message);
} finally {
setLoading(false);
}
};
return (
<div className="min-h-screen bg-background p-6">
<div className="max-w-7xl mx-auto">
<div className="flex justify-between items-center mb-6">
<h1 className="text-3xl font-bold">OpenAPI </h1>
<Button
variant="outline"
onClick={clearResults}
disabled={results.length === 0}
>
({results.length})
</Button>
</div>
<Tabs defaultValue="interactive" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="interactive"></TabsTrigger>
<TabsTrigger value="examples"></TabsTrigger>
<TabsTrigger value="documentation">OpenAPI </TabsTrigger>
</TabsList>
<TabsContent value="interactive" className="space-y-6">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
{/* 左侧API 操作面板 */}
<Card>
<CardHeader>
<CardTitle>API </CardTitle>
<CardDescription>
使 @hey-api/openapi-ts
</CardDescription>
</CardHeader>
<CardContent>
<Tabs defaultValue="auth" className="w-full">
<TabsList className="grid w-full grid-cols-5">
<TabsTrigger value="auth"></TabsTrigger>
<TabsTrigger value="user"></TabsTrigger>
<TabsTrigger value="tenant"></TabsTrigger>
<TabsTrigger value="department"></TabsTrigger>
<TabsTrigger value="system"></TabsTrigger>
</TabsList>
<TabsContent value="auth" className="space-y-4">
{/* 登录表单 */}
<div className="space-y-3">
<h4 className="font-medium"></h4>
<div className="space-y-2">
<Label htmlFor="identifier">//</Label>
<Input
id="identifier"
placeholder="请输入用户名、邮箱或手机号"
value={loginData.identifier}
onChange={(e) => setLoginData(prev => ({ ...prev, identifier: e.target.value }))}
/>
</div>
<div className="space-y-2">
<Label htmlFor="password"></Label>
<Input
id="password"
type="password"
placeholder="请输入密码"
value={loginData.password}
onChange={(e) => setLoginData(prev => ({ ...prev, password: e.target.value }))}
/>
</div>
<div className="space-y-2">
<Label htmlFor="captcha_text"></Label>
<div className="flex gap-2">
<Input
id="captcha_text"
placeholder="请输入验证码"
value={loginData.captcha_text}
onChange={(e) => setLoginData(prev => ({ ...prev, captcha_text: e.target.value }))}
className="flex-1"
/>
<Button
onClick={handleGetCaptcha}
disabled={loading}
variant="outline"
className="whitespace-nowrap"
>
</Button>
</div>
{captchaData && (
<div className="mt-2 p-2 bg-muted rounded">
<img
src={`data:image/png;base64,${captchaData.image}`}
alt="验证码"
className="h-12 mx-auto"
/>
<p className="text-xs text-center text-muted-foreground mt-1">
ID: {captchaData.captcha_id}
</p>
</div>
)}
</div>
<Button
onClick={handleLogin}
disabled={loading || !loginData.identifier || !loginData.password || !loginData.captcha_text || !loginData.captcha_id}
className="w-full"
>
{loading ? '登录中...' : '登录'}
</Button>
</div>
{/* 注册表单 */}
<div className="space-y-2">
<h4 className="font-medium"></h4>
<Input
placeholder="用户名"
value={registerData.username}
onChange={(e) => setRegisterData(prev => ({ ...prev, username: e.target.value }))}
/>
<Input
type="password"
placeholder="密码"
value={registerData.password}
onChange={(e) => setRegisterData(prev => ({ ...prev, password: e.target.value }))}
/>
<Input
placeholder="邮箱"
value={registerData.email}
onChange={(e) => setRegisterData(prev => ({ ...prev, email: e.target.value }))}
/>
<Input
placeholder="姓名"
value={registerData.full_name}
onChange={(e) => setRegisterData(prev => ({ ...prev, full_name: e.target.value }))}
/>
<Input
placeholder="手机号"
value={registerData.phone}
onChange={(e) => setRegisterData(prev => ({ ...prev, phone: e.target.value }))}
/>
<Input
placeholder="租户ID (可选)"
value={registerData.tenant_id || ''}
onChange={(e) => setRegisterData(prev => ({ ...prev, tenant_id: e.target.value || undefined }))}
/>
<Input
placeholder="用户作用域 (可选)"
value={registerData.scope || ''}
onChange={(e) => setRegisterData(prev => ({ ...prev, scope: e.target.value || undefined }))}
/>
<Input
placeholder="部门ID (可选)"
value={registerData.department_id || ''}
onChange={(e) => setRegisterData(prev => ({ ...prev, department_id: e.target.value || undefined }))}
/>
<Button
onClick={handleRegister}
disabled={loading || !registerData.username || !registerData.password || !registerData.email || !registerData.phone}
variant="outline"
className="w-full"
>
{loading ? '注册中...' : '注册'}
</Button>
</div>
{/* 其他认证操作 */}
<div className="grid grid-cols-1 gap-2">
<Button
onClick={handleGetCaptcha}
disabled={loading}
variant="outline"
>
</Button>
<Button
onClick={handleRefreshToken}
disabled={loading}
variant="outline"
>
</Button>
<Button
onClick={handleGetCurrentUser}
disabled={loading}
variant="outline"
>
</Button>
<Button
onClick={handleLogout}
disabled={loading}
variant="outline"
>
</Button>
</div>
</TabsContent>
<TabsContent value="user" className="space-y-4">
{/* 创建用户表单 */}
<div className="space-y-2">
<h4 className="font-medium"></h4>
<Input
placeholder="用户名"
value={createUserData.username}
onChange={(e) => setCreateUserData(prev => ({ ...prev, username: e.target.value }))}
/>
<Input
type="password"
placeholder="密码"
value={createUserData.password}
onChange={(e) => setCreateUserData(prev => ({ ...prev, password: e.target.value }))}
/>
<Input
placeholder="邮箱"
value={createUserData.email}
onChange={(e) => setCreateUserData(prev => ({ ...prev, email: e.target.value }))}
/>
<Input
placeholder="姓名"
value={createUserData.full_name}
onChange={(e) => setCreateUserData(prev => ({ ...prev, full_name: e.target.value }))}
/>
<Input
placeholder="手机号"
value={createUserData.phone}
onChange={(e) => setCreateUserData(prev => ({ ...prev, phone: e.target.value }))}
/>
<Input
placeholder="租户ID (可选)"
value={createUserData.tenant_id || ''}
onChange={(e) => setCreateUserData(prev => ({ ...prev, tenant_id: e.target.value || undefined }))}
/>
<Input
placeholder="用户作用域 (可选)"
value={createUserData.scope || ''}
onChange={(e) => setCreateUserData(prev => ({ ...prev, scope: e.target.value || undefined }))}
/>
<Input
placeholder="部门ID (可选)"
value={createUserData.department_id || ''}
onChange={(e) => setCreateUserData(prev => ({ ...prev, department_id: e.target.value || undefined }))}
/>
<Button
onClick={handleCreateUser}
disabled={loading || !createUserData.username || !createUserData.password || !createUserData.email || !createUserData.phone}
className="w-full"
>
{loading ? '创建中...' : '创建用户'}
</Button>
</div>
{/* 获取用户详情 */}
<div className="space-y-2">
<h4 className="font-medium"></h4>
<Input
placeholder="用户ID"
value={userId}
onChange={(e) => setUserId(e.target.value)}
/>
<Button
onClick={handleGetUser}
disabled={loading || !userId}
variant="outline"
className="w-full"
>
</Button>
</div>
{/* 其他用户操作 */}
<div className="grid grid-cols-1 gap-2">
<Button
onClick={handleGetUsers}
disabled={loading}
variant="outline"
>
</Button>
</div>
</TabsContent>
<TabsContent value="tenant" className="space-y-4">
{/* 创建租户表单 */}
<div className="space-y-2">
<h4 className="font-medium"></h4>
<Input
placeholder="企业名称"
value={createTenantData.company_name}
onChange={(e) => setCreateTenantData(prev => ({ ...prev, company_name: e.target.value }))}
/>
<Input
placeholder="企业编码"
value={createTenantData.tenant_code}
onChange={(e) => setCreateTenantData(prev => ({ ...prev, tenant_code: e.target.value }))}
/>
<select
value={createTenantData.company_type}
onChange={(e) => setCreateTenantData(prev => ({ ...prev, company_type: e.target.value as any }))}
className="w-full p-2 border rounded"
>
<option value="个体工商户"></option>
<option value="有限责任公司"></option>
<option value="股份有限公司"></option>
<option value="合伙企业"></option>
<option value="其他"></option>
</select>
<Button
onClick={async () => {
setLoading(true);
try {
addResult('创建租户', 'POST /api/v1/tenants', '发送中...');
const response = await createTenantApiV1TenantsPost({
body: createTenantData
});
if (response.data) {
addResult('创建租户', createTenantData, response.data);
} else if (response.error) {
addResult('创建租户', createTenantData, null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('创建租户', createTenantData, null, error.message);
} finally {
setLoading(false);
}
}}
disabled={loading || !createTenantData.company_name || !createTenantData.tenant_code}
className="w-full"
>
{loading ? '创建中...' : '创建租户'}
</Button>
</div>
{/* 其他租户操作 */}
<div className="grid grid-cols-1 gap-2">
<Button
onClick={async () => {
setLoading(true);
try {
addResult('获取租户列表', 'GET /api/v1/tenants', '发送中...');
const response = await listTenantsApiV1TenantsGet({});
if (response.data) {
addResult('获取租户列表', '无参数', response.data);
} else if (response.error) {
addResult('获取租户列表', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取租户列表', '无参数', null, error.message);
} finally {
setLoading(false);
}
}}
disabled={loading}
variant="outline"
>
</Button>
<Button
onClick={async () => {
setLoading(true);
try {
addResult('获取当前租户', 'GET /api/v1/tenants/me', '发送中...');
const response = await getCurrentTenantApiV1TenantsMeGet({});
if (response.data) {
addResult('获取当前租户', '无参数', response.data);
} else if (response.error) {
addResult('获取当前租户', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取当前租户', '无参数', null, error.message);
} finally {
setLoading(false);
}
}}
disabled={loading}
variant="outline"
>
</Button>
<Button
onClick={async () => {
setLoading(true);
try {
addResult('获取租户审计日志', 'GET /api/v1/tenants/audit-logs', '发送中...');
const response = await getTenantAuditLogsApiV1TenantsAuditLogsGet({});
if (response.data) {
addResult('获取租户审计日志', '无参数', response.data);
} else if (response.error) {
addResult('获取租户审计日志', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取租户审计日志', '无参数', null, error.message);
} finally {
setLoading(false);
}
}}
disabled={loading}
variant="outline"
>
</Button>
</div>
</TabsContent>
<TabsContent value="department" className="space-y-4">
{/* 创建部门表单 */}
<div className="space-y-2">
<h4 className="font-medium"></h4>
<Input
placeholder="部门编码"
value={createDepartmentData.code}
onChange={(e) => setCreateDepartmentData(prev => ({ ...prev, code: e.target.value }))}
/>
<Input
placeholder="部门名称"
value={createDepartmentData.name}
onChange={(e) => setCreateDepartmentData(prev => ({ ...prev, name: e.target.value }))}
/>
<Input
placeholder="部门描述"
value={createDepartmentData.description}
onChange={(e) => setCreateDepartmentData(prev => ({ ...prev, description: e.target.value }))}
/>
<Button
onClick={async () => {
setLoading(true);
try {
addResult('创建部门', 'POST /api/v1/departments/departments', '发送中...');
const response = await createDepartmentApiV1DepartmentsDepartmentsPost({
body: createDepartmentData
});
if (response.data) {
addResult('创建部门', createDepartmentData, response.data);
} else if (response.error) {
addResult('创建部门', createDepartmentData, null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('创建部门', createDepartmentData, null, error.message);
} finally {
setLoading(false);
}
}}
disabled={loading || !createDepartmentData.code || !createDepartmentData.name}
className="w-full"
>
{loading ? '创建中...' : '创建部门'}
</Button>
</div>
{/* 其他部门操作 */}
<div className="grid grid-cols-1 gap-2">
<Button
onClick={async () => {
setLoading(true);
try {
addResult('获取部门列表', 'GET /api/v1/departments/departments', '发送中...');
const response = await getDepartmentsApiV1DepartmentsDepartmentsGet({});
if (response.data) {
addResult('获取部门列表', '无参数', response.data);
} else if (response.error) {
addResult('获取部门列表', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取部门列表', '无参数', null, error.message);
} finally {
setLoading(false);
}
}}
disabled={loading}
variant="outline"
>
</Button>
<Button
onClick={async () => {
setLoading(true);
try {
addResult('获取部门树', 'GET /api/v1/departments/departments/tree', '发送中...');
const response = await getDepartmentTreeApiV1DepartmentsDepartmentsTreeGet({});
if (response.data) {
addResult('获取部门树', '无参数', response.data);
} else if (response.error) {
addResult('获取部门树', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取部门树', '无参数', null, error.message);
} finally {
setLoading(false);
}
}}
disabled={loading}
variant="outline"
>
</Button>
<Button
onClick={async () => {
setLoading(true);
try {
addResult('获取部门选项', 'GET /api/v1/users/departments/options', '发送中...');
const response = await getDepartmentOptionsApiV1UsersDepartmentsOptionsGet({});
if (response.data) {
addResult('获取部门选项', '无参数', response.data);
} else if (response.error) {
addResult('获取部门选项', '无参数', null, JSON.stringify(response.error));
}
} catch (error: any) {
addResult('获取部门选项', '无参数', null, error.message);
} finally {
setLoading(false);
}
}}
disabled={loading}
variant="outline"
>
</Button>
</div>
</TabsContent>
<TabsContent value="system" className="space-y-4">
{/* 系统操作 */}
<div className="grid grid-cols-1 gap-2">
<Button
onClick={handleHealthCheck}
disabled={loading}
variant="outline"
>
</Button>
<Button
onClick={handleGetSystemInfo}
disabled={loading}
variant="outline"
>
</Button>
</div>
</TabsContent>
</Tabs>
</CardContent>
</Card>
{/* 右侧:结果显示面板 */}
<Card>
<CardHeader>
<CardTitle> & </CardTitle>
<CardDescription>
API
</CardDescription>
</CardHeader>
<CardContent>
<div className="space-y-4 max-h-[600px] overflow-y-auto">
{results.length === 0 ? (
<div className="text-center text-muted-foreground py-8">
<p> API </p>
<p className="text-sm"> API </p>
</div>
) : (
results.map((result) => (
<div key={result.id} className="border rounded-lg p-4 space-y-2">
<div className="flex items-center justify-between">
<h4 className="font-medium">{result.type}</h4>
<div className="flex items-center gap-2">
<Badge variant="outline">{result.timestamp}</Badge>
{result.error ? (
<Badge variant="destructive"></Badge>
) : (
<Badge variant="default"></Badge>
)}
</div>
</div>
{/* 输入数据 */}
<div className="space-y-1">
<p className="text-sm font-medium text-muted-foreground">:</p>
<pre className="text-xs bg-muted p-2 rounded overflow-x-auto">
{JSON.stringify(result.input, null, 2)}
</pre>
</div>
{/* 输出数据 */}
<div className="space-y-1">
<p className="text-sm font-medium text-muted-foreground">
{result.error ? '错误信息:' : '响应数据:'}
</p>
<pre className={`text-xs p-2 rounded overflow-x-auto ${
result.error ? 'bg-destructive/10 text-destructive' : 'bg-muted'
}`}>
{result.error || JSON.stringify(result.output, null, 2)}
</pre>
</div>
</div>
))
)}
</div>
</CardContent>
</Card>
</div>
{/* 错误提示 */}
{errors.length > 0 && (
<div className="mt-6">
<Alert>
<AlertDescription>
{errors.length}
</AlertDescription>
</Alert>
</div>
)}
</TabsContent>
<TabsContent value="examples" className="space-y-6">
<ApiExamplesPage />
</TabsContent>
<TabsContent value="documentation" className="space-y-6">
<OpenApiExamples />
</TabsContent>
</Tabs>
</div>
</div>
);
}
// API示例页面组件
function ApiExamplesPage() {
const [examplesLoading, setExamplesLoading] = useState<Record<string, boolean>>({});
const [examplesResults, setExamplesResults] = useState<Record<string, any>>({});
// API示例配置 - 基于新生成的SDK
const apiExamples = [
// 认证相关
{
id: 'getCaptcha',
method: 'GET',
path: '/api/v1/auth/captcha',
title: '获取验证码',
description: '获取登录验证码',
exampleParams: null,
category: '认证',
expectedOutput: { captcha_id: 'xyz789', image: 'base64_image_data' }
},
{
id: 'login',
method: 'POST',
path: '/api/v1/auth/login',
title: '用户登录',
description: '用户登录(需要验证码)',
exampleParams: {
identifier: 'admin',
password: 'admin123',
captcha_id: 'test_captcha_id',
captcha_text: 'test'
},
category: '认证',
expectedOutput: { access_token: 'jwt_token', token_type: 'bearer', expires_in: 3600 }
},
{
id: 'register',
method: 'POST',
path: '/api/v1/auth/register',
title: '用户注册',
description: '用户注册接口',
exampleParams: {
username: 'newuser',
password: 'password123',
email: 'user@example.com',
full_name: '新用户',
phone: '13800138000',
tenant_id: 'tenant-uuid',
scope: 'user',
department_id: 'dept-uuid'
},
category: '认证',
expectedOutput: { id: 'uuid', username: 'newuser', email: 'user@example.com', phone: '13800138000' }
},
{
id: 'getCurrentUser',
method: 'GET',
path: '/api/v1/auth/me',
title: '获取当前用户',
description: '获取当前登录用户信息',
exampleParams: null,
category: '认证',
expectedOutput: { id: 'uuid', username: 'admin', is_active: true }
},
{
id: 'refreshToken',
method: 'POST',
path: '/api/v1/auth/refresh',
title: '刷新令牌',
description: '刷新访问令牌',
exampleParams: null,
category: '认证',
expectedOutput: { access_token: 'new_jwt_token' }
},
{
id: 'logout',
method: 'POST',
path: '/api/v1/auth/logout',
title: '用户登出',
description: '用户登出接口',
exampleParams: null,
category: '认证',
expectedOutput: { message: '登出成功' }
},
// 用户管理
{
id: 'getUsers',
method: 'GET',
path: '/api/v1/users',
title: '获取用户列表',
description: '获取用户列表(需要管理员权限)',
exampleParams: { page: 1, size: 20 },
category: '用户管理',
expectedOutput: { data: [{ id: 'uuid', username: 'admin' }], total: 1 }
},
{
id: 'createUser',
method: 'POST',
path: '/api/v1/users',
title: '创建用户',
description: '创建用户(需要管理员权限)',
exampleParams: {
username: 'testuser',
password: 'password123',
email: 'test@example.com',
full_name: '测试用户',
phone: '13900139000',
tenant_id: 'tenant-uuid',
scope: 'user',
department_id: 'dept-uuid'
},
category: '用户管理',
expectedOutput: { id: 'uuid', username: 'testuser', email: 'test@example.com', phone: '13900139000' }
},
{
id: 'getUserStats',
method: 'GET',
path: '/api/v1/users/stats/summary',
title: '用户统计',
description: '获取用户统计信息',
exampleParams: null,
category: '用户管理',
expectedOutput: { total_users: 10, active_users: 8, inactive_users: 2 }
},
{
id: 'getDepartmentOptions',
method: 'GET',
path: '/api/v1/users/departments/options',
title: '获取部门选项',
description: '获取部门选择列表(用于用户管理中的部门选择)',
exampleParams: null,
category: '用户管理',
expectedOutput: [{ id: 'uuid', code: 'TECH001', name: '技术开发部' }]
},
{
id: 'listSystemUsers',
method: 'GET',
path: '/api/v1/users/system/users',
title: '获取系统用户列表',
description: '获取系统用户列表(需要系统权限)',
exampleParams: { page: 1, size: 20, search: '', is_active: true },
category: '用户管理',
expectedOutput: { data: [{ id: 'uuid', username: 'admin', is_active: true }], total: 1 }
},
{
id: 'createSystemUser',
method: 'POST',
path: '/api/v1/users/system/users',
title: '创建系统用户',
description: '创建系统级用户(需要系统权限)',
exampleParams: {
username: 'system_admin',
password: 'admin123',
email: 'admin@system.com',
full_name: '系统管理员',
phone: '13700137000',
tenant_id: null,
scope: 'system',
department_id: null
},
category: '用户管理',
expectedOutput: { id: 'uuid', username: 'system_admin', scope: 'system' }
},
// 租户管理
{
id: 'listTenants',
method: 'GET',
path: '/api/v1/tenants',
title: '获取租户列表',
description: '获取租户列表',
exampleParams: { page: 1, size: 20, search: '', audit_status: 'approved' },
category: '租户管理',
expectedOutput: { data: [{ id: 'uuid', name: '测试企业' }], total: 1, page: 1, size: 20 }
},
{
id: 'getCurrentTenant',
method: 'GET',
path: '/api/v1/tenants/me',
title: '获取当前租户',
description: '获取当前登录租户信息',
exampleParams: null,
category: '租户管理',
expectedOutput: { id: 'uuid', name: '测试企业', code: 'TEST001', audit_status: 'approved' }
},
{
id: 'createTenant',
method: 'POST',
path: '/api/v1/tenants',
title: '创建租户',
description: '创建新租户',
exampleParams: {
company_name: '新企业有限责任公司',
tenant_code: 'NEW001',
company_type: '有限责任公司'
},
category: '租户管理',
expectedOutput: { id: 'uuid', company_name: '新企业有限责任公司', tenant_code: 'NEW001', company_type: '有限责任公司' }
},
{
id: 'getTenantAuditLogs',
method: 'GET',
path: '/api/v1/tenants/audit-logs',
title: '获取租户审计日志',
description: '获取租户审计日志',
exampleParams: { page: 1, size: 20, tenant_id: 'uuid' },
category: '租户管理',
expectedOutput: { data: [{ id: 'uuid', action: 'create', timestamp: '2024-01-01T00:00:00Z' }], total: 1 }
},
// 部门管理
{
id: 'getDepartments',
method: 'GET',
path: '/api/v1/departments/departments',
title: '获取部门列表',
description: '获取部门列表',
exampleParams: { page: 1, size: 20 },
category: '部门管理',
expectedOutput: { data: [{ id: 'uuid', name: '技术部', code: 'TECH' }], total: 1, page: 1, size: 20 }
},
{
id: 'createDepartment',
method: 'POST',
path: '/api/v1/departments/departments',
title: '创建部门',
description: '创建新部门',
exampleParams: {
code: 'TECH001',
name: '技术开发部',
description: '负责技术研发工作'
},
category: '部门管理',
expectedOutput: { id: 'uuid', code: 'TECH001', name: '技术开发部', description: '负责技术研发工作' }
},
{
id: 'getDepartmentTree',
method: 'GET',
path: '/api/v1/departments/departments/tree',
title: '获取部门树',
description: '获取部门树形结构',
exampleParams: null,
category: '部门管理',
expectedOutput: [{ id: 'uuid', code: 'TECH001', name: '技术开发部', description: '负责技术研发工作', children: [] }]
},
// 系统管理
{
id: 'healthCheck',
method: 'GET',
path: '/api/v1/health',
title: '健康检查',
description: 'API健康检查',
exampleParams: null,
category: '系统管理',
expectedOutput: { status: 'healthy', timestamp: '2024-01-01T00:00:00Z' }
},
{
id: 'detailedHealthCheck',
method: 'GET',
path: '/api/v1/health/detailed',
title: '详细健康检查',
description: '详细的系统健康检查',
exampleParams: null,
category: '系统管理',
expectedOutput: { status: 'healthy', database: 'connected', redis: 'connected' }
},
{
id: 'getSystemInfo',
method: 'GET',
path: '/api/v1/system/info',
title: '系统信息',
description: '获取系统信息',
exampleParams: null,
category: '系统管理',
expectedOutput: { version: '1.0.0', environment: 'development' }
},
{
id: 'getSystemStats',
method: 'GET',
path: '/api/v1/system/stats',
title: '系统统计',
description: '获取系统统计信息',
exampleParams: null,
category: '系统管理',
expectedOutput: { uptime: 3600, memory_usage: '50MB' }
}
];
// 调用示例API
const callExampleApi = async (example: typeof apiExamples[0]) => {
setExamplesLoading(prev => ({ ...prev, [example.id]: true }));
try {
let response;
switch (example.id) {
// 认证相关
case 'getCaptcha':
response = await getCaptchaApiV1AuthCaptchaGet({});
break;
case 'login':
response = await loginApiV1AuthLoginPost({
body: example.exampleParams
});
break;
case 'register':
response = await registerApiV1AuthRegisterPost({
body: example.exampleParams
});
break;
case 'getCurrentUser':
response = await getCurrentUserInfoApiV1AuthMeGet({});
break;
case 'refreshToken':
response = await refreshTokenApiV1AuthRefreshPost({});
break;
case 'logout':
response = await logoutApiV1AuthLogoutPost({});
break;
// 用户管理
case 'getUsers':
response = await getUsersApiV1UsersGet({
query: example.exampleParams
});
break;
case 'createUser':
response = await createUserApiV1UsersPost({
body: example.exampleParams
});
break;
case 'getUserStats':
response = await getUserStatsApiV1UsersStatsSummaryGet({});
break;
case 'getDepartmentOptions':
response = await getDepartmentOptionsApiV1UsersDepartmentsOptionsGet({});
break;
case 'listSystemUsers':
response = await listSystemUsersApiV1UsersSystemUsersGet({
query: example.exampleParams
});
break;
case 'createSystemUser':
response = await createSystemUserApiV1UsersSystemUsersPost({
body: example.exampleParams
});
break;
// 租户管理
case 'listTenants':
response = await listTenantsApiV1TenantsGet({
query: example.exampleParams
});
break;
case 'getCurrentTenant':
response = await getCurrentTenantApiV1TenantsMeGet({});
break;
case 'createTenant':
response = await createTenantApiV1TenantsPost({
body: example.exampleParams
});
break;
case 'getTenantAuditLogs':
response = await getTenantAuditLogsApiV1TenantsAuditLogsGet({
query: example.exampleParams
});
break;
// 部门管理
case 'getDepartments':
response = await getDepartmentsApiV1DepartmentsDepartmentsGet({
query: example.exampleParams
});
break;
case 'createDepartment':
response = await createDepartmentApiV1DepartmentsDepartmentsPost({
body: example.exampleParams
});
break;
case 'getDepartmentTree':
response = await getDepartmentTreeApiV1DepartmentsDepartmentsTreeGet({});
break;
// 系统管理
case 'healthCheck':
response = await healthCheckApiV1HealthGet({});
break;
case 'detailedHealthCheck':
response = await detailedHealthCheckApiV1HealthDetailedGet({});
break;
case 'getSystemInfo':
response = await getSystemInfoApiV1SystemInfoGet({});
break;
case 'getSystemStats':
response = await getSystemStatsApiV1SystemStatsGet({});
break;
default:
throw new Error(`Unknown API example: ${example.id}`);
}
setExamplesResults(prev => ({
...prev,
[example.id]: {
success: !response.error,
data: response.data,
error: response.error,
input: example.exampleParams,
timestamp: new Date().toLocaleTimeString()
}
}));
} catch (error: any) {
setExamplesResults(prev => ({
...prev,
[example.id]: {
success: false,
error: error.message,
input: example.exampleParams,
timestamp: new Date().toLocaleTimeString()
}
}));
} finally {
setExamplesLoading(prev => ({ ...prev, [example.id]: false }));
}
};
// 批量调用所有示例
const callAllExamples = async () => {
// 按类别调用,先调用认证相关的
const authExamples = apiExamples.filter(ex => ex.category === '认证');
for (const example of authExamples) {
await callExampleApi(example);
await new Promise(resolve => setTimeout(resolve, 200)); // 间隔200ms
}
// 然后调用其他类别的接口
const otherExamples = apiExamples.filter(ex => ex.category !== '认证');
for (const example of otherExamples) {
await callExampleApi(example);
await new Promise(resolve => setTimeout(resolve, 100)); // 间隔100ms
}
};
// 按类别调用
const callCategoryExamples = async (category: string) => {
const categoryExamples = apiExamples.filter(ex => ex.category === category);
for (const example of categoryExamples) {
await callExampleApi(example);
await new Promise(resolve => setTimeout(resolve, 150)); // 间隔150ms
}
};
// 获取所有类别
const categories = Array.from(new Set(apiExamples.map(ex => ex.category)));
return (
<div className="space-y-6">
<div className="flex justify-between items-center">
<div>
<h2 className="text-2xl font-bold"></h2>
<p className="text-muted-foreground">
OpenAPI规范自动生成的所有接口示例
</p>
</div>
<div className="flex gap-2">
<Button onClick={callAllExamples} disabled={Object.values(examplesLoading).some(v => v)}>
{Object.values(examplesLoading).some(v => v) ? '测试中...' : '批量测试所有接口'}
</Button>
</div>
</div>
{/* 按类别展示 */}
{categories.map((category) => {
const categoryExamples = apiExamples.filter(ex => ex.category === category);
const categorySuccessCount = categoryExamples.filter(ex => examplesResults[ex.id]?.success).length;
const categoryFailureCount = categoryExamples.filter(ex => examplesResults[ex.id]?.success === false).length;
const categoryTotalCount = categoryExamples.length;
return (
<Card key={category}>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-xl">{category}</CardTitle>
<CardDescription>
{categoryTotalCount} {categorySuccessCount} {categoryFailureCount}
</CardDescription>
</div>
<div className="flex gap-2">
<Button
onClick={() => callCategoryExamples(category)}
disabled={Object.values(examplesLoading).some(v => v)}
variant="outline"
size="sm"
>
</Button>
<div className="flex items-center gap-2 text-sm">
<span className="text-green-600">{categorySuccessCount}</span>
<span>/</span>
<span className="text-red-600">{categoryFailureCount}</span>
<span>/</span>
<span className="text-blue-600">{categoryTotalCount}</span>
</div>
</div>
</div>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
{categoryExamples.map((example) => (
<Card key={example.id} className="h-fit">
<CardHeader className="pb-3">
<div className="flex items-center justify-between">
<div className="flex-1">
<CardTitle className="text-sm">{example.title}</CardTitle>
<CardDescription className="text-xs">
{example.method} {example.path}
</CardDescription>
</div>
<Badge variant="outline" className="text-xs">
{example.method}
</Badge>
</div>
</CardHeader>
<CardContent className="space-y-3">
<p className="text-xs text-muted-foreground">{example.description}</p>
{/* 示例参数 */}
{example.exampleParams && (
<div className="space-y-1">
<h4 className="text-xs font-medium">:</h4>
<pre className="text-xs bg-muted p-2 rounded overflow-x-auto max-h-20">
{JSON.stringify(example.exampleParams, null, 2)}
</pre>
</div>
)}
{/* 实际结果 */}
{examplesResults[example.id] && (
<div className="space-y-2">
<div className="flex items-center justify-between">
<h4 className="text-xs font-medium">
<span className="ml-1 text-xs text-muted-foreground">
({examplesResults[example.id].timestamp})
</span>
</h4>
<Badge
variant={examplesResults[example.id].success ? "default" : "destructive"}
className="text-xs"
>
{examplesResults[example.id].success ? '成功' : '失败'}
</Badge>
</div>
<pre className={`text-xs p-2 rounded overflow-x-auto max-h-32 ${
examplesResults[example.id].success
? 'bg-green-50 text-green-800 border border-green-200'
: 'bg-red-50 text-red-800 border border-red-200'
}`}>
{examplesResults[example.id].error
? JSON.stringify(examplesResults[example.id].error, null, 2)
: JSON.stringify(examplesResults[example.id].data, null, 2)
}
</pre>
</div>
)}
<Button
onClick={() => callExampleApi(example)}
disabled={examplesLoading[example.id]}
className="w-full"
variant={examplesResults[example.id]?.success ? "outline" : "default"}
size="sm"
>
{examplesLoading[example.id] ? '测试中...' : '测试'}
</Button>
</CardContent>
</Card>
))}
</div>
</CardContent>
</Card>
);
})}
{/* 总体统计信息 */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 text-center">
<div>
<p className="text-2xl font-bold text-green-600">
{Object.values(examplesResults).filter(r => r?.success).length}
</p>
<p className="text-sm text-muted-foreground"></p>
</div>
<div>
<p className="text-2xl font-bold text-red-600">
{Object.values(examplesResults).filter(r => r?.success === false).length}
</p>
<p className="text-sm text-muted-foreground"></p>
</div>
<div>
<p className="text-2xl font-bold text-blue-600">
{Object.values(examplesResults).filter(r => r !== undefined).length}
</p>
<p className="text-sm text-muted-foreground"></p>
</div>
<div>
<p className="text-2xl font-bold text-gray-600">
{apiExamples.length}
</p>
<p className="text-sm text-muted-foreground"></p>
</div>
</div>
{/* 进度条 */}
<div className="mt-4">
<div className="w-full bg-gray-200 rounded-full h-2">
<div
className="bg-green-600 h-2 rounded-full transition-all duration-300"
style={{
width: `${apiExamples.length > 0 ? (Object.values(examplesResults).filter(r => r?.success).length / apiExamples.length) * 100 : 0}%`
}}
></div>
</div>
<p className="text-xs text-muted-foreground mt-1 text-center">
: {apiExamples.length > 0 ? Math.round((Object.values(examplesResults).filter(r => r?.success).length / apiExamples.length) * 100) : 0}%
</p>
</div>
</CardContent>
</Card>
</div>
);
}