DebugPanel.js 5.1 KB

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