1706 lines
66 KiB
TypeScript
1706 lines
66 KiB
TypeScript
'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>
|
||
);
|
||
} |