|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import React, { useState, useEffect } from 'react'; |
|
|
import { |
|
|
Card, |
|
|
Typography, |
|
|
Tabs, |
|
|
TabPane, |
|
|
Button, |
|
|
Dropdown, |
|
|
} from '@douyinfe/semi-ui'; |
|
|
import { Code, Zap, Clock, X, Eye, Send } from 'lucide-react'; |
|
|
import { useTranslation } from 'react-i18next'; |
|
|
import CodeViewer from './CodeViewer'; |
|
|
|
|
|
const DebugPanel = ({ |
|
|
debugData, |
|
|
activeDebugTab, |
|
|
onActiveDebugTabChange, |
|
|
styleState, |
|
|
onCloseDebugPanel, |
|
|
customRequestMode, |
|
|
}) => { |
|
|
const { t } = useTranslation(); |
|
|
|
|
|
const [activeKey, setActiveKey] = useState(activeDebugTab); |
|
|
|
|
|
useEffect(() => { |
|
|
setActiveKey(activeDebugTab); |
|
|
}, [activeDebugTab]); |
|
|
|
|
|
const handleTabChange = (key) => { |
|
|
setActiveKey(key); |
|
|
onActiveDebugTabChange(key); |
|
|
}; |
|
|
|
|
|
const renderArrow = (items, pos, handleArrowClick, defaultNode) => { |
|
|
const style = { |
|
|
width: 32, |
|
|
height: 32, |
|
|
margin: '0 12px', |
|
|
display: 'flex', |
|
|
justifyContent: 'center', |
|
|
alignItems: 'center', |
|
|
borderRadius: '100%', |
|
|
background: 'rgba(var(--semi-grey-1), 1)', |
|
|
color: 'var(--semi-color-text)', |
|
|
cursor: 'pointer', |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<Dropdown |
|
|
render={ |
|
|
<Dropdown.Menu> |
|
|
{items.map((item) => { |
|
|
return ( |
|
|
<Dropdown.Item |
|
|
key={item.itemKey} |
|
|
onClick={() => handleTabChange(item.itemKey)} |
|
|
> |
|
|
{item.tab} |
|
|
</Dropdown.Item> |
|
|
); |
|
|
})} |
|
|
</Dropdown.Menu> |
|
|
} |
|
|
> |
|
|
{pos === 'start' ? ( |
|
|
<div style={style} onClick={handleArrowClick}> |
|
|
← |
|
|
</div> |
|
|
) : ( |
|
|
<div style={style} onClick={handleArrowClick}> |
|
|
→ |
|
|
</div> |
|
|
)} |
|
|
</Dropdown> |
|
|
); |
|
|
}; |
|
|
|
|
|
return ( |
|
|
<Card |
|
|
className='h-full flex flex-col' |
|
|
bordered={false} |
|
|
bodyStyle={{ |
|
|
padding: styleState.isMobile ? '16px' : '24px', |
|
|
height: '100%', |
|
|
display: 'flex', |
|
|
flexDirection: 'column', |
|
|
}} |
|
|
> |
|
|
<div className='flex items-center justify-between mb-6 flex-shrink-0'> |
|
|
<div className='flex items-center'> |
|
|
<div className='w-10 h-10 rounded-full bg-gradient-to-r from-green-500 to-blue-500 flex items-center justify-center mr-3'> |
|
|
<Code size={20} className='text-white' /> |
|
|
</div> |
|
|
<Typography.Title heading={5} className='mb-0'> |
|
|
{t('调试信息')} |
|
|
</Typography.Title> |
|
|
</div> |
|
|
|
|
|
{styleState.isMobile && onCloseDebugPanel && ( |
|
|
<Button |
|
|
icon={<X size={16} />} |
|
|
onClick={onCloseDebugPanel} |
|
|
theme='borderless' |
|
|
type='tertiary' |
|
|
size='small' |
|
|
className='!rounded-lg' |
|
|
/> |
|
|
)} |
|
|
</div> |
|
|
|
|
|
<div className='flex-1 overflow-hidden debug-panel'> |
|
|
<Tabs |
|
|
renderArrow={renderArrow} |
|
|
type='card' |
|
|
collapsible |
|
|
className='h-full' |
|
|
style={{ height: '100%', display: 'flex', flexDirection: 'column' }} |
|
|
activeKey={activeKey} |
|
|
onChange={handleTabChange} |
|
|
> |
|
|
<TabPane |
|
|
tab={ |
|
|
<div className='flex items-center gap-2'> |
|
|
<Eye size={16} /> |
|
|
{t('预览请求体')} |
|
|
{customRequestMode && ( |
|
|
<span className='px-1.5 py-0.5 text-xs bg-orange-100 text-orange-600 rounded-full'> |
|
|
自定义 |
|
|
</span> |
|
|
)} |
|
|
</div> |
|
|
} |
|
|
itemKey='preview' |
|
|
> |
|
|
<CodeViewer |
|
|
content={debugData.previewRequest} |
|
|
title='preview' |
|
|
language='json' |
|
|
/> |
|
|
</TabPane> |
|
|
|
|
|
<TabPane |
|
|
tab={ |
|
|
<div className='flex items-center gap-2'> |
|
|
<Send size={16} /> |
|
|
{t('实际请求体')} |
|
|
</div> |
|
|
} |
|
|
itemKey='request' |
|
|
> |
|
|
<CodeViewer |
|
|
content={debugData.request} |
|
|
title='request' |
|
|
language='json' |
|
|
/> |
|
|
</TabPane> |
|
|
|
|
|
<TabPane |
|
|
tab={ |
|
|
<div className='flex items-center gap-2'> |
|
|
<Zap size={16} /> |
|
|
{t('响应')} |
|
|
</div> |
|
|
} |
|
|
itemKey='response' |
|
|
> |
|
|
<CodeViewer |
|
|
content={debugData.response} |
|
|
title='response' |
|
|
language='json' |
|
|
/> |
|
|
</TabPane> |
|
|
</Tabs> |
|
|
</div> |
|
|
|
|
|
<div className='flex items-center justify-between mt-4 pt-4 flex-shrink-0'> |
|
|
{(debugData.timestamp || debugData.previewTimestamp) && ( |
|
|
<div className='flex items-center gap-2'> |
|
|
<Clock size={14} className='text-gray-500' /> |
|
|
<Typography.Text className='text-xs text-gray-500'> |
|
|
{activeKey === 'preview' && debugData.previewTimestamp |
|
|
? `${t('预览更新')}: ${new Date(debugData.previewTimestamp).toLocaleString()}` |
|
|
: debugData.timestamp |
|
|
? `${t('最后请求')}: ${new Date(debugData.timestamp).toLocaleString()}` |
|
|
: ''} |
|
|
</Typography.Text> |
|
|
</div> |
|
|
)} |
|
|
</div> |
|
|
</Card> |
|
|
); |
|
|
}; |
|
|
|
|
|
export default DebugPanel; |
|
|
|