import { Button } from '@heroui/button'; import { Card, CardBody, CardHeader } from '@heroui/card'; import { Input } from '@heroui/input'; import { Snippet } from '@heroui/snippet'; import { Modal, ModalBody, ModalContent, ModalHeader } from '@heroui/modal'; import { Popover, PopoverContent, PopoverTrigger } from '@heroui/popover'; import { useLocalStorage } from '@uidotdev/usehooks'; import { useEffect, useRef, useState } from 'react'; import toast from 'react-hot-toast'; import { IoLink, IoSend, IoSettingsSharp } from 'react-icons/io5'; import { PiCatDuotone } from 'react-icons/pi'; import key from '@/const/key'; import { OneBotHttpApiContent, OneBotHttpApiPath } from '@/const/ob_api'; import ChatInputModal from '@/components/chat_input/modal'; import CodeEditor from '@/components/code_editor'; import PageLoading from '@/components/page_loading'; import { request } from '@/utils/request'; import { parseAxiosResponse } from '@/utils/url'; import { generateDefaultJson, parse } from '@/utils/zod'; import DisplayStruct from './display_struct'; export interface OneBotApiDebugProps { path: OneBotHttpApiPath; data: OneBotHttpApiContent; } const OneBotApiDebug: React.FC = (props) => { const { path, data } = props; const currentURL = new URL(window.location.origin); currentURL.port = '3000'; const defaultHttpUrl = currentURL.href; const [httpConfig, setHttpConfig] = useLocalStorage(key.httpDebugConfig, { url: defaultHttpUrl, token: '', }); const [requestBody, setRequestBody] = useState('{}'); const [responseContent, setResponseContent] = useState(''); const [isFetching, setIsFetching] = useState(false); const [isStructOpen, setIsStructOpen] = useState(false); const responseRef = useRef(null); const parsedRequest = parse(data.request); const parsedResponse = parse(data.response); const sendRequest = async () => { if (isFetching) return; setIsFetching(true); const r = toast.loading('正在发送请求...'); try { const parsedRequestBody = JSON.parse(requestBody); const requestURL = new URL(httpConfig.url); requestURL.pathname = path; request .post(requestURL.href, parsedRequestBody, { headers: { Authorization: `Bearer ${httpConfig.token}`, }, responseType: 'text', }) .then((res) => { setResponseContent(parseAxiosResponse(res)); toast.success('请求发送完成,请查看响应'); }) .catch((err) => { toast.error('请求发送失败:' + err.message); setResponseContent(parseAxiosResponse(err.response)); }) .finally(() => { setIsFetching(false); responseRef.current?.scrollIntoView({ behavior: 'smooth', block: 'start', }); toast.dismiss(r); }); } catch (_error) { toast.error('请求体 JSON 格式错误'); setIsFetching(false); toast.dismiss(r); } }; useEffect(() => { setRequestBody(generateDefaultJson(data.request)); setResponseContent(''); }, [path]); return (

{data.description}

} tooltipProps={{ content: '点击复制地址' }} > {path}

请求配置

setHttpConfig({ ...httpConfig, url: e.target.value })} variant='bordered' labelPlacement='outside' classNames={{ inputWrapper: 'bg-default-100/50 backdrop-blur-sm border-default-200/50', }} /> setHttpConfig({ ...httpConfig, token: e.target.value })} variant='bordered' labelPlacement='outside' classNames={{ inputWrapper: 'bg-default-100/50 backdrop-blur-sm border-default-200/50', }} />
{/* Request Column */}
请求体 (Request)
setRequestBody(value ?? '')} language='json' options={{ minimap: { enabled: false }, fontSize: 13, padding: { top: 10, bottom: 10 }, scrollBeyondLastLine: false, }} />
{/* Response Column */}
响应 (Response)
                {responseContent || 等待请求响应...}
              
{/* Struct Display - maybe put in a modal or separate tab? For now, putting it in a collapsed/compact area at bottom is tricky with "h-[calc(100vh)]". User wants "Thorough optimization". I will make Struct Display a Drawer or Modal, OR put it below if we want scrolling. But I set height to fixed full screen. Let's put Struct Display in a Tab or Toggle at Top? Or just let the main container scroll and remove fixed height? Layout choice: Fixed height editors are good for workflow. Structure is reference. I will leave Struct Display OUT of the fixed view, or add a toggle to show it. Let's add a "View Structure" button in header that opens a Modal. Yes, that's cleaner. */} {() => ( <> API 数据结构定义

请求体结构 (Request)

响应体结构 (Response)

)}
); }; export default OneBotApiDebug;