DebugPanel.js 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import React, { useState, useEffect } from 'react';
  2. import {
  3. Card,
  4. Typography,
  5. Tabs,
  6. TabPane,
  7. Button,
  8. Dropdown,
  9. } from '@douyinfe/semi-ui';
  10. import {
  11. Code,
  12. Zap,
  13. Clock,
  14. X,
  15. Eye,
  16. Send,
  17. } from 'lucide-react';
  18. import { useTranslation } from 'react-i18next';
  19. import CodeViewer from './CodeViewer';
  20. const DebugPanel = ({
  21. debugData,
  22. activeDebugTab,
  23. onActiveDebugTabChange,
  24. styleState,
  25. onCloseDebugPanel,
  26. }) => {
  27. const { t } = useTranslation();
  28. const [activeKey, setActiveKey] = useState(activeDebugTab);
  29. useEffect(() => {
  30. setActiveKey(activeDebugTab);
  31. }, [activeDebugTab]);
  32. const handleTabChange = (key) => {
  33. setActiveKey(key);
  34. onActiveDebugTabChange(key);
  35. };
  36. const renderArrow = (items, pos, handleArrowClick, defaultNode) => {
  37. const style = {
  38. width: 32,
  39. height: 32,
  40. margin: '0 12px',
  41. display: 'flex',
  42. justifyContent: 'center',
  43. alignItems: 'center',
  44. borderRadius: '100%',
  45. background: 'rgba(var(--semi-grey-1), 1)',
  46. color: 'var(--semi-color-text)',
  47. cursor: 'pointer',
  48. };
  49. return (
  50. <Dropdown
  51. render={
  52. <Dropdown.Menu>
  53. {items.map(item => {
  54. return (
  55. <Dropdown.Item
  56. key={item.itemKey}
  57. onClick={() => handleTabChange(item.itemKey)}
  58. >
  59. {item.tab}
  60. </Dropdown.Item>
  61. );
  62. })}
  63. </Dropdown.Menu>
  64. }
  65. >
  66. {pos === 'start' ? (
  67. <div style={style} onClick={handleArrowClick}>
  68. </div>
  69. ) : (
  70. <div style={style} onClick={handleArrowClick}>
  71. </div>
  72. )}
  73. </Dropdown>
  74. );
  75. };
  76. return (
  77. <Card
  78. className="!rounded-2xl h-full flex flex-col"
  79. bodyStyle={{
  80. padding: styleState.isMobile ? '16px' : '24px',
  81. height: '100%',
  82. display: 'flex',
  83. flexDirection: 'column'
  84. }}
  85. >
  86. <div className="flex items-center justify-between mb-6 flex-shrink-0">
  87. <div className="flex items-center">
  88. <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">
  89. <Code size={20} className="text-white" />
  90. </div>
  91. <Typography.Title heading={5} className="mb-0">
  92. {t('调试信息')}
  93. </Typography.Title>
  94. </div>
  95. {styleState.isMobile && onCloseDebugPanel && (
  96. <Button
  97. icon={<X size={16} />}
  98. onClick={onCloseDebugPanel}
  99. theme="borderless"
  100. type="tertiary"
  101. size="small"
  102. className="!rounded-lg"
  103. />
  104. )}
  105. </div>
  106. <div className="flex-1 overflow-hidden debug-panel">
  107. <Tabs
  108. renderArrow={renderArrow}
  109. type="card"
  110. collapsible
  111. className="h-full"
  112. style={{ height: '100%', display: 'flex', flexDirection: 'column' }}
  113. activeKey={activeKey}
  114. onChange={handleTabChange}
  115. >
  116. <TabPane tab={
  117. <div className="flex items-center gap-2">
  118. <Eye size={16} />
  119. {t('预览请求体')}
  120. </div>
  121. } itemKey="preview">
  122. <CodeViewer
  123. content={debugData.previewRequest}
  124. title="preview"
  125. language="json"
  126. />
  127. </TabPane>
  128. <TabPane tab={
  129. <div className="flex items-center gap-2">
  130. <Send size={16} />
  131. {t('实际请求体')}
  132. </div>
  133. } itemKey="request">
  134. <CodeViewer
  135. content={debugData.request}
  136. title="request"
  137. language="json"
  138. />
  139. </TabPane>
  140. <TabPane tab={
  141. <div className="flex items-center gap-2">
  142. <Zap size={16} />
  143. {t('响应内容')}
  144. </div>
  145. } itemKey="response">
  146. <CodeViewer
  147. content={debugData.response}
  148. title="response"
  149. language="json"
  150. />
  151. </TabPane>
  152. </Tabs>
  153. </div>
  154. <div className="flex items-center justify-between mt-4 pt-4 flex-shrink-0">
  155. {(debugData.timestamp || debugData.previewTimestamp) && (
  156. <div className="flex items-center gap-2">
  157. <Clock size={14} className="text-gray-500" />
  158. <Typography.Text className="text-xs text-gray-500">
  159. {activeKey === 'preview' && debugData.previewTimestamp
  160. ? `${t('预览更新')}: ${new Date(debugData.previewTimestamp).toLocaleString()}`
  161. : debugData.timestamp
  162. ? `${t('最后请求')}: ${new Date(debugData.timestamp).toLocaleString()}`
  163. : ''}
  164. </Typography.Text>
  165. </div>
  166. )}
  167. </div>
  168. </Card>
  169. );
  170. };
  171. export default DebugPanel;