mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-13 00:10:27 +00:00
refactor: 优化eslint配置,提升代码质量 (#1341)
* feat: 统一并标准化eslint * lint: napcat.webui * lint: napcat.webui * lint: napcat.core * build: fix * lint: napcat.webui * refactor: 重构eslint * Update README.md
This commit is contained in:
@@ -1,133 +1,131 @@
|
||||
import { Button } from '@heroui/button'
|
||||
import { Card, CardBody, CardHeader } from '@heroui/card'
|
||||
import { Input } from '@heroui/input'
|
||||
import { Snippet } from '@heroui/snippet'
|
||||
import { useLocalStorage } from '@uidotdev/usehooks'
|
||||
import { motion } from 'motion/react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import toast from 'react-hot-toast'
|
||||
import { IoLink, IoSend } from 'react-icons/io5'
|
||||
import { PiCatDuotone } from 'react-icons/pi'
|
||||
import { Button } from '@heroui/button';
|
||||
import { Card, CardBody, CardHeader } from '@heroui/card';
|
||||
import { Input } from '@heroui/input';
|
||||
import { Snippet } from '@heroui/snippet';
|
||||
import { useLocalStorage } from '@uidotdev/usehooks';
|
||||
import { motion } from 'motion/react';
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import toast from 'react-hot-toast';
|
||||
import { IoLink, IoSend } from 'react-icons/io5';
|
||||
import { PiCatDuotone } from 'react-icons/pi';
|
||||
|
||||
import key from '@/const/key'
|
||||
import { OneBotHttpApiContent, OneBotHttpApiPath } from '@/const/ob_api'
|
||||
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 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 { request } from '@/utils/request';
|
||||
import { parseAxiosResponse } from '@/utils/url';
|
||||
import { generateDefaultJson, parse } from '@/utils/zod';
|
||||
|
||||
import DisplayStruct from './display_struct'
|
||||
import DisplayStruct from './display_struct';
|
||||
|
||||
export interface OneBotApiDebugProps {
|
||||
path: OneBotHttpApiPath
|
||||
data: OneBotHttpApiContent
|
||||
path: OneBotHttpApiPath;
|
||||
data: OneBotHttpApiContent;
|
||||
}
|
||||
|
||||
const OneBotApiDebug: React.FC<OneBotApiDebugProps> = (props) => {
|
||||
const { path, data } = props
|
||||
const currentURL = new URL(window.location.origin)
|
||||
currentURL.port = '3000'
|
||||
const defaultHttpUrl = currentURL.href
|
||||
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 [isCodeEditorOpen, setIsCodeEditorOpen] = useState(false)
|
||||
const [isResponseOpen, setIsResponseOpen] = useState(false)
|
||||
const [isFetching, setIsFetching] = useState(false)
|
||||
const responseRef = useRef<HTMLDivElement>(null)
|
||||
const parsedRequest = parse(data.request)
|
||||
const parsedResponse = parse(data.response)
|
||||
token: '',
|
||||
});
|
||||
const [requestBody, setRequestBody] = useState('{}');
|
||||
const [responseContent, setResponseContent] = useState('');
|
||||
const [isCodeEditorOpen, setIsCodeEditorOpen] = useState(false);
|
||||
const [isResponseOpen, setIsResponseOpen] = useState(false);
|
||||
const [isFetching, setIsFetching] = useState(false);
|
||||
const responseRef = useRef<HTMLDivElement>(null);
|
||||
const parsedRequest = parse(data.request);
|
||||
const parsedResponse = parse(data.response);
|
||||
|
||||
const sendRequest = async () => {
|
||||
if (isFetching) return
|
||||
setIsFetching(true)
|
||||
const r = toast.loading('正在发送请求...')
|
||||
if (isFetching) return;
|
||||
setIsFetching(true);
|
||||
const r = toast.loading('正在发送请求...');
|
||||
try {
|
||||
const parsedRequestBody = JSON.parse(requestBody)
|
||||
const requestURL = new URL(httpConfig.url)
|
||||
requestURL.pathname = path
|
||||
const parsedRequestBody = JSON.parse(requestBody);
|
||||
const requestURL = new URL(httpConfig.url);
|
||||
requestURL.pathname = path;
|
||||
request
|
||||
.post(requestURL.href, parsedRequestBody, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${httpConfig.token}`
|
||||
Authorization: `Bearer ${httpConfig.token}`,
|
||||
},
|
||||
responseType: 'text'
|
||||
responseType: 'text',
|
||||
})
|
||||
.then((res) => {
|
||||
setResponseContent(parseAxiosResponse(res))
|
||||
toast.success('请求发送完成,请查看响应')
|
||||
setResponseContent(parseAxiosResponse(res));
|
||||
toast.success('请求发送完成,请查看响应');
|
||||
})
|
||||
.catch((err) => {
|
||||
toast.error('请求发送失败:' + err.message)
|
||||
setResponseContent(parseAxiosResponse(err.response))
|
||||
toast.error('请求发送失败:' + err.message);
|
||||
setResponseContent(parseAxiosResponse(err.response));
|
||||
})
|
||||
.finally(() => {
|
||||
setIsFetching(false)
|
||||
setIsResponseOpen(true)
|
||||
setIsFetching(false);
|
||||
setIsResponseOpen(true);
|
||||
responseRef.current?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start'
|
||||
})
|
||||
toast.dismiss(r)
|
||||
})
|
||||
} catch (error) {
|
||||
toast.error('请求体 JSON 格式错误')
|
||||
setIsFetching(false)
|
||||
toast.dismiss(r)
|
||||
block: 'start',
|
||||
});
|
||||
toast.dismiss(r);
|
||||
});
|
||||
} catch (_error) {
|
||||
toast.error('请求体 JSON 格式错误');
|
||||
setIsFetching(false);
|
||||
toast.dismiss(r);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
setRequestBody(generateDefaultJson(data.request))
|
||||
setResponseContent('')
|
||||
}, [path])
|
||||
setRequestBody(generateDefaultJson(data.request));
|
||||
setResponseContent('');
|
||||
}, [path]);
|
||||
|
||||
return (
|
||||
<section className="p-4 pt-14 rounded-lg shadow-md">
|
||||
<h1 className="text-2xl font-bold mb-4 flex items-center gap-1 text-primary-400">
|
||||
<section className='p-4 pt-14 rounded-lg shadow-md'>
|
||||
<h1 className='text-2xl font-bold mb-4 flex items-center gap-1 text-primary-400'>
|
||||
<PiCatDuotone />
|
||||
{data.description}
|
||||
</h1>
|
||||
<h1 className="text-lg font-bold mb-4">
|
||||
<h1 className='text-lg font-bold mb-4'>
|
||||
<Snippet
|
||||
className="bg-default-50 bg-opacity-50 backdrop-blur-md"
|
||||
symbol={<IoLink size={18} className="inline-block mr-1" />}
|
||||
className='bg-default-50 bg-opacity-50 backdrop-blur-md'
|
||||
symbol={<IoLink size={18} className='inline-block mr-1' />}
|
||||
tooltipProps={{
|
||||
content: '点击复制地址'
|
||||
content: '点击复制地址',
|
||||
}}
|
||||
>
|
||||
{path}
|
||||
</Snippet>
|
||||
</h1>
|
||||
<div className="flex gap-2 items-center">
|
||||
<div className='flex gap-2 items-center'>
|
||||
<Input
|
||||
label="HTTP URL"
|
||||
placeholder="输入 HTTP URL"
|
||||
label='HTTP URL'
|
||||
placeholder='输入 HTTP URL'
|
||||
value={httpConfig.url}
|
||||
onChange={(e) =>
|
||||
setHttpConfig({ ...httpConfig, url: e.target.value })
|
||||
}
|
||||
setHttpConfig({ ...httpConfig, url: e.target.value })}
|
||||
/>
|
||||
<Input
|
||||
label="Token"
|
||||
placeholder="输入 Token"
|
||||
label='Token'
|
||||
placeholder='输入 Token'
|
||||
value={httpConfig.token}
|
||||
onChange={(e) =>
|
||||
setHttpConfig({ ...httpConfig, token: e.target.value })
|
||||
}
|
||||
setHttpConfig({ ...httpConfig, token: e.target.value })}
|
||||
/>
|
||||
<Button
|
||||
onPress={sendRequest}
|
||||
color="primary"
|
||||
size="lg"
|
||||
radius="full"
|
||||
color='primary'
|
||||
size='lg'
|
||||
radius='full'
|
||||
isIconOnly
|
||||
isDisabled={isFetching}
|
||||
>
|
||||
@@ -135,17 +133,17 @@ const OneBotApiDebug: React.FC<OneBotApiDebugProps> = (props) => {
|
||||
</Button>
|
||||
</div>
|
||||
<Card
|
||||
shadow="sm"
|
||||
className="my-4 bg-opacity-50 backdrop-blur-md overflow-visible"
|
||||
shadow='sm'
|
||||
className='my-4 bg-opacity-50 backdrop-blur-md overflow-visible'
|
||||
>
|
||||
<CardHeader className="font-bold text-lg gap-1 pb-0">
|
||||
<span className="mr-2">请求体</span>
|
||||
<CardHeader className='font-bold text-lg gap-1 pb-0'>
|
||||
<span className='mr-2'>请求体</span>
|
||||
<Button
|
||||
color="warning"
|
||||
variant="flat"
|
||||
color='warning'
|
||||
variant='flat'
|
||||
onPress={() => setIsCodeEditorOpen(!isCodeEditorOpen)}
|
||||
size="sm"
|
||||
radius="full"
|
||||
size='sm'
|
||||
radius='full'
|
||||
>
|
||||
{isCodeEditorOpen ? '收起' : '展开'}
|
||||
</Button>
|
||||
@@ -156,24 +154,23 @@ const OneBotApiDebug: React.FC<OneBotApiDebugProps> = (props) => {
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{
|
||||
opacity: isCodeEditorOpen ? 1 : 0,
|
||||
height: isCodeEditorOpen ? 'auto' : 0
|
||||
height: isCodeEditorOpen ? 'auto' : 0,
|
||||
}}
|
||||
>
|
||||
<CodeEditor
|
||||
value={requestBody}
|
||||
onChange={(value) => setRequestBody(value ?? '')}
|
||||
language="json"
|
||||
height="400px"
|
||||
language='json'
|
||||
height='400px'
|
||||
/>
|
||||
|
||||
<div className="flex justify-end gap-1">
|
||||
<div className='flex justify-end gap-1'>
|
||||
<ChatInputModal />
|
||||
<Button
|
||||
color="primary"
|
||||
variant="flat"
|
||||
color='primary'
|
||||
variant='flat'
|
||||
onPress={() =>
|
||||
setRequestBody(generateDefaultJson(data.request))
|
||||
}
|
||||
setRequestBody(generateDefaultJson(data.request))}
|
||||
>
|
||||
填充示例请求体
|
||||
</Button>
|
||||
@@ -182,61 +179,61 @@ const OneBotApiDebug: React.FC<OneBotApiDebugProps> = (props) => {
|
||||
</CardBody>
|
||||
</Card>
|
||||
<Card
|
||||
shadow="sm"
|
||||
className="my-4 relative bg-opacity-50 backdrop-blur-md"
|
||||
shadow='sm'
|
||||
className='my-4 relative bg-opacity-50 backdrop-blur-md'
|
||||
>
|
||||
<PageLoading loading={isFetching} />
|
||||
<CardHeader className="font-bold text-lg gap-1 pb-0">
|
||||
<span className="mr-2">响应</span>
|
||||
<CardHeader className='font-bold text-lg gap-1 pb-0'>
|
||||
<span className='mr-2'>响应</span>
|
||||
<Button
|
||||
color="warning"
|
||||
variant="flat"
|
||||
color='warning'
|
||||
variant='flat'
|
||||
onPress={() => setIsResponseOpen(!isResponseOpen)}
|
||||
size="sm"
|
||||
radius="full"
|
||||
size='sm'
|
||||
radius='full'
|
||||
>
|
||||
{isResponseOpen ? '收起' : '展开'}
|
||||
</Button>
|
||||
<Button
|
||||
color="success"
|
||||
variant="flat"
|
||||
color='success'
|
||||
variant='flat'
|
||||
onPress={() => {
|
||||
navigator.clipboard.writeText(responseContent)
|
||||
toast.success('响应内容已复制到剪贴板')
|
||||
navigator.clipboard.writeText(responseContent);
|
||||
toast.success('响应内容已复制到剪贴板');
|
||||
}}
|
||||
size="sm"
|
||||
radius="full"
|
||||
size='sm'
|
||||
radius='full'
|
||||
>
|
||||
复制
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<CardBody>
|
||||
<motion.div
|
||||
className="overflow-y-auto text-sm"
|
||||
className='overflow-y-auto text-sm'
|
||||
initial={{ opacity: 0, height: 0 }}
|
||||
animate={{
|
||||
opacity: isResponseOpen ? 1 : 0,
|
||||
height: isResponseOpen ? 300 : 0
|
||||
height: isResponseOpen ? 300 : 0,
|
||||
}}
|
||||
>
|
||||
<pre>
|
||||
<code>
|
||||
{responseContent || (
|
||||
<div className="text-gray-400">暂无响应</div>
|
||||
<div className='text-gray-400'>暂无响应</div>
|
||||
)}
|
||||
</code>
|
||||
</pre>
|
||||
</motion.div>
|
||||
</CardBody>
|
||||
</Card>
|
||||
<div className="p-2 md:p-4 border border-default-50 dark:border-default-200 rounded-lg backdrop-blur-sm">
|
||||
<h2 className="text-xl font-semibold mb-2">请求体结构</h2>
|
||||
<div className='p-2 md:p-4 border border-default-50 dark:border-default-200 rounded-lg backdrop-blur-sm'>
|
||||
<h2 className='text-xl font-semibold mb-2'>请求体结构</h2>
|
||||
<DisplayStruct schema={parsedRequest} />
|
||||
<h2 className="text-xl font-semibold mt-4 mb-2">响应体结构</h2>
|
||||
<h2 className='text-xl font-semibold mt-4 mb-2'>响应体结构</h2>
|
||||
<DisplayStruct schema={parsedResponse} />
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export default OneBotApiDebug
|
||||
export default OneBotApiDebug;
|
||||
|
||||
Reference in New Issue
Block a user