whistle.mock-plugins 1.3.1 → 1.4.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. package/VERSION.md +29 -0
  2. package/app/cgi-bin/interfaces.js +2 -0
  3. package/app/cgi-bin/settings.js +1 -0
  4. package/app/cgi-bin/stats.js +22 -12
  5. package/app/cgi-bin/webdav.js +56 -0
  6. package/app/public/index.html +1 -1
  7. package/app/public/js/11.index.js +2 -0
  8. package/app/public/js/11.index.js.LICENSE.txt +5 -0
  9. package/app/public/js/704.index.js +2 -0
  10. package/app/public/js/704.index.js.LICENSE.txt +1 -0
  11. package/app/public/js/798.index.js +2 -0
  12. package/app/public/js/798.index.js.LICENSE.txt +53 -0
  13. package/app/public/js/807.index.js +2 -0
  14. package/app/public/js/807.index.js.LICENSE.txt +1 -0
  15. package/app/public/js/968.index.js +2 -0
  16. package/app/public/js/968.index.js.LICENSE.txt +12 -0
  17. package/app/public/js/index.js +1 -1
  18. package/app/public/js/index.js.LICENSE.txt +0 -173
  19. package/lib/routes/settings.js +1 -0
  20. package/lib/webdav-sync.js +806 -0
  21. package/package.json +9 -2
  22. package/API.md +0 -247
  23. package/COLUMN_RESIZE_IMPLEMENTATION.md +0 -162
  24. package/FEATURE_ENHANCEMENT.md +0 -157
  25. package/TEST_GUIDE.md +0 -156
  26. package/data/features.json +0 -1
  27. package/data/interfaces.json +0 -1
  28. package/index.html +0 -52
  29. package/initialize.js +0 -32
  30. package/public/bundle.js +0 -2
  31. package/public/bundle.js.LICENSE.txt +0 -38
  32. package/public/index.html +0 -1
  33. package/public/plugin.mock-plugin.html +0 -14
  34. package/rules.txt +0 -22
  35. package/ui/src/App.css +0 -173
  36. package/ui/src/components/AppLayout.js +0 -407
  37. package/ui/src/components/FeatureCard.js +0 -58
  38. package/ui/src/components/KeyboardShortcutsHelp.js +0 -58
  39. package/ui/src/components/Layout.js +0 -94
  40. package/ui/src/components/MainNav.js +0 -42
  41. package/ui/src/components/PageContainer.js +0 -16
  42. package/ui/src/components/VersionModal.js +0 -99
  43. package/ui/src/components/interface-management/DataManagementDrawer.js +0 -115
  44. package/ui/src/components/interface-management/DataManagementModal.js +0 -110
  45. package/ui/src/components/interface-management/InterfaceTestModal.js +0 -313
  46. package/ui/src/components/interface-management/PreviewModal.js +0 -48
  47. package/ui/src/components/interface-management/README.md +0 -184
  48. package/ui/src/components/interface-management/RealResponseRecorder.js +0 -165
  49. package/ui/src/components/interface-management/ResponseContentEditor.js +0 -645
  50. package/ui/src/components/interface-management/constants.js +0 -36
  51. package/ui/src/components/interface-management/index.js +0 -7
  52. package/ui/src/components/interface-management/utils.js +0 -103
  53. package/ui/src/hooks/useKeyboardShortcuts.js +0 -99
  54. package/ui/src/index.html +0 -14
  55. package/ui/src/index.js +0 -50
  56. package/ui/src/pages/InterfaceManagementV2.js +0 -2351
  57. package/ui/src/pages/MockDataV2.js +0 -1089
  58. package/ui/src/pages/Settings.js +0 -299
  59. package/ui/src/pages/Sniffer.js +0 -521
  60. package/ui/src/pages/Stats.js +0 -288
  61. package/ui/src/services/del_api.js +0 -192
  62. package/ui/src/styles/app-layout.css +0 -373
  63. package/ui/src/styles/cyber-theme.css +0 -1234
  64. package/ui/src/styles/earth-theme.css +0 -618
  65. package/ui/src/styles/global.css +0 -224
  66. package/ui/src/styles/interface-management.css +0 -1321
  67. package/ui/src/styles/mock-data.css +0 -1185
  68. package/ui/src/styles/settings.css +0 -252
  69. package/ui/src/styles/version-modal.css +0 -116
  70. package/webpack.config.js +0 -54
@@ -1,645 +0,0 @@
1
- import React, { useState, useEffect } from 'react';
2
- import { ReactSortable } from 'react-sortablejs';
3
- import ReactDOM from 'react-dom';
4
- import { Card, Button, Space, Badge, Row, Col, Input, Select, Tooltip, message, Tag, Divider } from 'antd';
5
- import {
6
- CodeOutlined,
7
- EyeOutlined,
8
- FormatPainterOutlined,
9
- FileTextOutlined,
10
- PlusCircleOutlined,
11
- DeleteOutlined,
12
- CheckCircleOutlined,
13
- EditOutlined,
14
- FullscreenOutlined,
15
- FullscreenExitOutlined,
16
- MenuOutlined,
17
- FilterOutlined,
18
- PlusOutlined,
19
- QuestionCircleOutlined
20
- } from '@ant-design/icons';
21
- import { generateResponseId } from './utils';
22
-
23
- const { TextArea } = Input;
24
- const { Option } = Select;
25
-
26
- const MATCH_TYPE_LABELS = {
27
- exact: '精确',
28
- contains: '包含',
29
- regex: '正则',
30
- };
31
-
32
- const ResponseContentEditor = ({
33
- form,
34
- responses,
35
- activeResponseId,
36
- onPreview,
37
- enableSorting = false
38
- }) => {
39
- const { getFieldValue, setFieldsValue } = form;
40
- const [isEditing, setIsEditing] = useState(false);
41
- const [editingName, setEditingName] = useState('');
42
- const [isTextareaFullscreen, setIsTextareaFullscreen] = useState(false);
43
- const [showSorting, setShowSorting] = useState(false);
44
-
45
- const [responseParamMatcherEnabled, setResponseParamMatcherEnabled] = useState(() => {
46
- const v = localStorage.getItem('responseParamMatcherEnabled');
47
- return v === null ? true : v !== 'false';
48
- });
49
-
50
- useEffect(() => {
51
- const handleStorage = () => {
52
- const v = localStorage.getItem('responseParamMatcherEnabled');
53
- setResponseParamMatcherEnabled(v === null ? true : v !== 'false');
54
- };
55
- window.addEventListener('storage', handleStorage);
56
- return () => window.removeEventListener('storage', handleStorage);
57
- }, []);
58
-
59
- // 查找激活的响应
60
- const activeResponseIndex = responses.findIndex(r => r.id === activeResponseId);
61
- const activeResponse = activeResponseIndex >= 0 ? responses[activeResponseIndex] : null;
62
- // 拖拽排序:基于 ReactSortable 管理一个本地列表镜像
63
- const [sortableList, setSortableList] = useState(() => {
64
- return Array.isArray(responses) ? responses.map(r => ({ ...r })) : [];
65
- });
66
-
67
- useEffect(() => {
68
- setSortableList(Array.isArray(responses) ? responses.map(r => ({ ...r })) : []);
69
- }, [responses]);
70
-
71
- const handleAfterSort = (newList) => {
72
- setFieldsValue({ responses: newList });
73
- };
74
-
75
-
76
- // 全屏切换函数
77
- const toggleTextareaFullscreen = () => {
78
- setIsTextareaFullscreen(!isTextareaFullscreen);
79
- };
80
-
81
- // ESC键退出全屏 - 移到全屏覆盖层内部处理
82
- const handleFullscreenEscKey = (event) => {
83
- if (event.key === 'Escape') {
84
- event.preventDefault();
85
- event.stopPropagation();
86
- setIsTextareaFullscreen(false);
87
- }
88
- };
89
-
90
- // 添加新响应
91
- const handleAddResponse = () => {
92
- const newResponseId = generateResponseId();
93
- let currentResponses = getFieldValue('responses') || [];
94
-
95
- if (!Array.isArray(currentResponses)) {
96
- try {
97
- if (typeof currentResponses === 'string') {
98
- currentResponses = JSON.parse(currentResponses);
99
- }
100
- } catch (e) {
101
- currentResponses = [];
102
- }
103
- }
104
-
105
- if (!Array.isArray(currentResponses)) {
106
- currentResponses = [];
107
- }
108
-
109
- const newResponse = {
110
- id: newResponseId,
111
- name: `响应 ${currentResponses.length + 1}`,
112
- description: '',
113
- content: '{\n "code": 0,\n "message": "success",\n "data": {}\n}',
114
- paramMatchers: []
115
- };
116
-
117
- const updatedResponses = [...currentResponses, newResponse];
118
-
119
- setFieldsValue({
120
- responses: updatedResponses,
121
- activeResponseId: newResponseId
122
- });
123
- };
124
-
125
- // 删除响应
126
- const handleDeleteResponse = (responseId) => {
127
- let currentResponses = getFieldValue('responses') || [];
128
-
129
- if (currentResponses.length <= 1) {
130
- message.warning('至少需要保留一个响应');
131
- return;
132
- }
133
-
134
- const updatedResponses = currentResponses.filter(resp => resp.id !== responseId);
135
-
136
- let newActiveId = activeResponseId;
137
- if (responseId === activeResponseId) {
138
- newActiveId = updatedResponses[0]?.id;
139
- }
140
-
141
- setFieldsValue({
142
- responses: updatedResponses,
143
- activeResponseId: newActiveId
144
- });
145
- };
146
-
147
- // 切换响应
148
- const handleResponseSelect = (responseId) => {
149
- setFieldsValue({ activeResponseId: responseId });
150
- setIsEditing(false);
151
- };
152
-
153
- // 开始编辑响应名称
154
- const handleStartEdit = () => {
155
- if (activeResponse) {
156
- setEditingName(activeResponse.name || '');
157
- setIsEditing(true);
158
- }
159
- };
160
-
161
- // 完成编辑响应名称
162
- const handleFinishEdit = () => {
163
- if (activeResponseId && editingName.trim()) {
164
- const currentResponses = getFieldValue('responses') || [];
165
- const updatedResponses = currentResponses.map(resp =>
166
- resp.id === activeResponseId ? { ...resp, name: editingName.trim() } : resp
167
- );
168
- setFieldsValue({ responses: updatedResponses });
169
- }
170
- setIsEditing(false);
171
- setEditingName('');
172
- };
173
-
174
- // 取消编辑
175
- const handleCancelEdit = () => {
176
- setIsEditing(false);
177
- setEditingName('');
178
- };
179
-
180
- // 更新响应内容
181
- const updateActiveResponseContent = (content) => {
182
- if (activeResponseIndex >= 0) {
183
- const currentResponses = getFieldValue('responses') || [];
184
- const newResponses = [...currentResponses];
185
- newResponses[activeResponseIndex] = {
186
- ...newResponses[activeResponseIndex],
187
- content
188
- };
189
- setFieldsValue({ responses: newResponses });
190
- }
191
- };
192
-
193
- // 更新当前响应的 paramMatchers
194
- const updateActiveResponseParamMatchers = (paramMatchers) => {
195
- if (activeResponseIndex >= 0) {
196
- const currentResponses = getFieldValue('responses') || [];
197
- const newResponses = [...currentResponses];
198
- newResponses[activeResponseIndex] = {
199
- ...newResponses[activeResponseIndex],
200
- paramMatchers
201
- };
202
- setFieldsValue({ responses: newResponses });
203
- }
204
- };
205
-
206
- // 添加入参匹配规则
207
- const handleAddParamMatcher = () => {
208
- const currentMatchers = (activeResponse && activeResponse.paramMatchers) || [];
209
- updateActiveResponseParamMatchers([
210
- ...currentMatchers,
211
- { paramPath: '', paramValue: '', matchType: 'exact' }
212
- ]);
213
- };
214
-
215
- // 删除入参匹配规则
216
- const handleRemoveParamMatcher = (index) => {
217
- const currentMatchers = (activeResponse && activeResponse.paramMatchers) || [];
218
- const newMatchers = currentMatchers.filter((_, i) => i !== index);
219
- updateActiveResponseParamMatchers(newMatchers);
220
- };
221
-
222
- // 更新入参匹配规则某个字段
223
- const handleParamMatcherChange = (index, field, value) => {
224
- const currentMatchers = (activeResponse && activeResponse.paramMatchers) || [];
225
- const newMatchers = currentMatchers.map((m, i) =>
226
- i === index ? { ...m, [field]: value } : m
227
- );
228
- updateActiveResponseParamMatchers(newMatchers);
229
- };
230
-
231
- // 格式化JSON内容
232
- const formatJsonContent = () => {
233
- if (!activeResponse || !activeResponse.content) {
234
- message.warning('没有可格式化的JSON内容');
235
- return;
236
- }
237
-
238
- try {
239
- const parsedJson = JSON.parse(activeResponse.content);
240
- const formattedJson = JSON.stringify(parsedJson, null, 2);
241
-
242
- updateActiveResponseContent(formattedJson);
243
- message.success('JSON已格式化');
244
- } catch (error) {
245
- message.error('无效的JSON格式,无法格式化');
246
- }
247
- };
248
-
249
- // 预览响应内容
250
- const handlePreview = () => {
251
- if (!activeResponse) {
252
- message.error('未找到有效的响应内容');
253
- return;
254
- }
255
-
256
- if (onPreview) {
257
- onPreview();
258
- }
259
- };
260
-
261
- // 渲染全屏TextArea
262
- const renderFullscreenTextarea = () => {
263
- if (!isTextareaFullscreen || !activeResponse) return null;
264
-
265
- return ReactDOM.createPortal(
266
- <div
267
- className="textarea-fullscreen-overlay"
268
- onKeyDown={handleFullscreenEscKey}
269
- tabIndex={-1}
270
- >
271
- <div className="textarea-fullscreen-header">
272
- <Button
273
- icon={<FullscreenExitOutlined />}
274
- onClick={toggleTextareaFullscreen}
275
- size="small"
276
- type="text"
277
- style={{ color: '#595959' }}
278
- >
279
- 退出全屏 (ESC)
280
- </Button>
281
- </div>
282
- <TextArea
283
- className="textarea-fullscreen-editor"
284
- autoFocus
285
- placeholder="请输入响应内容..."
286
- value={activeResponse.content || ''}
287
- onChange={(e) => updateActiveResponseContent(e.target.value)}
288
- />
289
- </div>,
290
- document.body
291
- );
292
- };
293
-
294
- // 渲染响应级入参匹配规则区域
295
- const renderParamMatchers = () => {
296
- if (!activeResponse) return null;
297
-
298
- const matchers = activeResponse.paramMatchers || [];
299
-
300
- return (
301
- <div style={{ marginBottom: '12px' }}>
302
- <Divider orientation="left" orientationMargin={0} style={{ margin: '0 0 10px 0', fontSize: '12px' }}>
303
- <Space size={6}>
304
- <FilterOutlined style={{ color: '#1677ff' }} />
305
- <span style={{ fontWeight: 'bold', color: '#333' }}>入参匹配规则</span>
306
- {matchers.length > 0 && (
307
- <Tag color="blue" style={{ fontSize: '11px', padding: '0 4px' }}>{matchers.length}条</Tag>
308
- )}
309
- <Tooltip title="满足所有规则时返回此响应;无规则的响应作为默认兜底。响应按列表顺序依次检查,第一个匹配的响应被返回。">
310
- <QuestionCircleOutlined style={{ color: '#bbb', cursor: 'help', fontSize: '12px' }} />
311
- </Tooltip>
312
- </Space>
313
- </Divider>
314
- <div>
315
- {matchers.map((matcher, index) => (
316
- <div key={index} style={{ display: 'flex', gap: '8px', alignItems: 'center', marginBottom: '8px' }}>
317
- <Input
318
- style={{ flex: 1 }}
319
- placeholder="参数路径(如:userId 或 data.user.id)"
320
- value={matcher.paramPath || ''}
321
- onChange={(e) => handleParamMatcherChange(index, 'paramPath', e.target.value)}
322
- size="small"
323
- />
324
- <Input
325
- style={{ flex: 1 }}
326
- placeholder="期望值(如:123 或 admin)"
327
- value={matcher.paramValue || ''}
328
- onChange={(e) => handleParamMatcherChange(index, 'paramValue', e.target.value)}
329
- size="small"
330
- />
331
- <Select
332
- style={{ width: 90 }}
333
- value={matcher.matchType || 'exact'}
334
- onChange={(val) => handleParamMatcherChange(index, 'matchType', val)}
335
- size="small"
336
- >
337
- <Option value="exact">精确匹配</Option>
338
- <Option value="contains">包含</Option>
339
- <Option value="regex">正则</Option>
340
- </Select>
341
- <Button
342
- type="text"
343
- danger
344
- icon={<DeleteOutlined />}
345
- size="small"
346
- onClick={() => handleRemoveParamMatcher(index)}
347
- />
348
- </div>
349
- ))}
350
- <Button
351
- type="dashed"
352
- size="small"
353
- icon={<PlusOutlined />}
354
- onClick={handleAddParamMatcher}
355
- style={{ width: '100%' }}
356
- >
357
- 添加匹配规则
358
- </Button>
359
- {matchers.length === 0 && (
360
- <div style={{ color: '#bbb', fontSize: '12px', marginTop: '6px', textAlign: 'center' }}>
361
- 无规则时此响应作为默认兜底响应
362
- </div>
363
- )}
364
- </div>
365
- </div>
366
- );
367
- };
368
-
369
- // 如果没有响应数据,显示创建按钮
370
- if (!Array.isArray(responses) || responses.length === 0) {
371
- return (
372
- <Card
373
- title={
374
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
375
- <CodeOutlined />
376
- <span>响应内容编辑</span>
377
- </div>
378
- }
379
- style={{ marginBottom: 16 }}
380
- bodyStyle={{ padding: '16px' }}
381
- >
382
- <div style={{
383
- textAlign: 'center',
384
- padding: '40px 20px',
385
- borderRadius: '6px',
386
- border: '1px dashed #d9d9d9'
387
- }}>
388
- <FileTextOutlined style={{ fontSize: '32px', color: '#bfbfbf', marginBottom: '12px' }} />
389
- <div style={{ color: '#999', marginBottom: '16px' }}>暂无响应数据</div>
390
- <Button
391
- type="primary"
392
- icon={<PlusCircleOutlined />}
393
- onClick={handleAddResponse}
394
- >
395
- 创建第一个响应
396
- </Button>
397
- </div>
398
- </Card>
399
- );
400
- }
401
-
402
- return (
403
- <Card
404
- title={
405
- <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
406
- <CodeOutlined />
407
- <span>响应内容编辑</span>
408
- <Badge
409
- count={responses.length}
410
- style={{ backgroundColor: '#52c41a' }}
411
- />
412
- </div>
413
- }
414
- extra={
415
- <Space size="small">
416
- {enableSorting && (
417
- <Button
418
- type="default"
419
- icon={<MenuOutlined />}
420
- onClick={() => setShowSorting(!showSorting)}
421
- size="small"
422
- style={{ display: 'flex', alignItems: 'center', gap: '4px' }}
423
- >
424
- 排序
425
- </Button>
426
- )}
427
- <Button
428
- type="default"
429
- icon={<PlusCircleOutlined />}
430
- onClick={handleAddResponse}
431
- size="small"
432
- style={{
433
- display: 'flex',
434
- alignItems: 'center',
435
- gap: '4px'
436
- }}
437
- >
438
- 添加响应
439
- </Button>
440
- <Button
441
- type="default"
442
- icon={<EyeOutlined />}
443
- onClick={handlePreview}
444
- size="small"
445
- style={{
446
- display: 'flex',
447
- alignItems: 'center',
448
- gap: '4px',
449
- borderColor: '#52c41a',
450
- color: '#52c41a'
451
- }}
452
- >
453
- 预览
454
- </Button>
455
- <Button
456
- type="default"
457
- icon={<FormatPainterOutlined />}
458
- onClick={formatJsonContent}
459
- size="small"
460
- style={{
461
- display: 'flex',
462
- alignItems: 'center',
463
- gap: '4px',
464
- borderColor: '#fa8c16',
465
- color: '#fa8c16'
466
- }}
467
- >
468
- 格式化JSON
469
- </Button>
470
- </Space>
471
- }
472
- style={{ marginBottom: 16 }}
473
- bodyStyle={{ padding: '16px' }}
474
- >
475
- {enableSorting && showSorting && Array.isArray(sortableList) && sortableList.length > 1 && (
476
- <div style={{ marginBottom: 12 }}>
477
- <ReactSortable
478
- list={sortableList}
479
- setList={(newState) => {
480
- setSortableList(newState);
481
- handleAfterSort(newState);
482
- }}
483
- animation={150}
484
- ghostClass="sortable-ghost"
485
- style={{ display: 'flex', gap: 8, overflowX: 'auto', paddingBottom: 4 }}
486
- >
487
- {sortableList.map((item) => (
488
- <div
489
- key={item.id}
490
- className={`sortable-chip${item.id === activeResponseId ? ' sortable-chip-active' : ''}`}
491
- onClick={() => handleResponseSelect(item.id)}
492
- title={item.name || '未命名响应'}
493
- >
494
- {item.name || '未命名响应'}
495
- </div>
496
- ))}
497
- </ReactSortable>
498
- </div>
499
- )}
500
- {/* 响应选择和名称编辑合并区域 */}
501
- <div style={{ marginBottom: '16px' }}>
502
- <Row gutter={12} align="middle">
503
- <Col span={20}>
504
- <div style={{ marginBottom: '8px' }}>
505
- <label style={{
506
- fontSize: '12px',
507
- color: '#666',
508
- fontWeight: 'bold'
509
- }}>
510
- 选择响应 / 编辑名称
511
- </label>
512
- </div>
513
-
514
- <div style={{ position: 'relative' }}>
515
- {isEditing ? (
516
- <Input.Group compact>
517
- <Input
518
- style={{ width: 'calc(100% - 80px)' }}
519
- value={editingName}
520
- onChange={(e) => setEditingName(e.target.value)}
521
- onPressEnter={handleFinishEdit}
522
- onBlur={handleFinishEdit}
523
- placeholder="输入响应名称"
524
- autoFocus
525
- />
526
- <Button
527
- style={{ width: '40px' }}
528
- type="primary"
529
- icon={<CheckCircleOutlined />}
530
- onClick={handleFinishEdit}
531
- />
532
- <Button
533
- style={{ width: '40px' }}
534
- icon={<DeleteOutlined />}
535
- onClick={handleCancelEdit}
536
- />
537
- </Input.Group>
538
- ) : (
539
- <Input.Group compact>
540
- <Select
541
- style={{ width: 'calc(100% - 40px)' }}
542
- placeholder="选择要编辑的响应"
543
- value={activeResponseId}
544
- onChange={handleResponseSelect}
545
- >
546
- {responses.map((resp, index) => (
547
- <Option key={resp.id} value={resp.id}>
548
- {resp.name || `响应 ${index + 1}`}
549
- </Option>
550
- ))}
551
- </Select>
552
- <Tooltip title="编辑响应名称">
553
- <Button
554
- style={{ width: '40px' }}
555
- icon={<EditOutlined />}
556
- onClick={handleStartEdit}
557
- disabled={!activeResponse}
558
- />
559
- </Tooltip>
560
- </Input.Group>
561
- )}
562
- </div>
563
- </Col>
564
-
565
- <Col span={4}>
566
- <div style={{ marginBottom: '8px' }}>
567
- <label style={{
568
- fontSize: '12px',
569
- color: '#666',
570
- fontWeight: 'bold'
571
- }}>
572
- 操作
573
- </label>
574
- </div>
575
- <Tooltip title="删除当前响应">
576
- <Button
577
- type="text"
578
- danger
579
- icon={<DeleteOutlined />}
580
- onClick={() => activeResponseId && handleDeleteResponse(activeResponseId)}
581
- disabled={responses.length <= 1 || !activeResponse}
582
- style={{ width: '100%' }}
583
- >
584
- 删除
585
- </Button>
586
- </Tooltip>
587
- </Col>
588
- </Row>
589
- </div>
590
-
591
- {/* 响应级入参匹配规则区域 */}
592
- {activeResponse && responseParamMatcherEnabled && renderParamMatchers()}
593
-
594
- {/* 响应内容编辑区域 */}
595
- {activeResponse && (
596
- <>
597
- <div style={{ marginBottom: '8px' }}>
598
- <label style={{
599
- fontSize: '12px',
600
- color: '#666',
601
- fontWeight: 'bold'
602
- }}>
603
- 响应内容
604
- </label>
605
- </div>
606
-
607
- <div style={{ position: 'relative' }}>
608
- <TextArea
609
- rows={26}
610
- placeholder="请输入响应内容..."
611
- style={{
612
- fontFamily: 'Monaco, Menlo, "Ubuntu Mono", monospace',
613
- fontSize: '13px',
614
- lineHeight: '1.5',
615
- minHeight: '620px'
616
- }}
617
- value={activeResponse.content || ''}
618
- onChange={(e) => updateActiveResponseContent(e.target.value)}
619
- />
620
- <Button
621
- className="textarea-fullscreen-btn"
622
- icon={<FullscreenOutlined />}
623
- onClick={toggleTextareaFullscreen}
624
- size="small"
625
- style={{
626
- position: 'absolute',
627
- top: '8px',
628
- right: '12px',
629
- background: 'rgba(255, 255, 255, 0.9)',
630
- border: '1px solid #d9d9d9',
631
- zIndex: 10
632
- }}
633
- title="全屏编辑"
634
- />
635
- </div>
636
- </>
637
- )}
638
-
639
- {/* 渲染全屏编辑器 */}
640
- {renderFullscreenTextarea()}
641
- </Card>
642
- );
643
- };
644
-
645
- export default ResponseContentEditor;
@@ -1,36 +0,0 @@
1
- // 常量定义文件
2
- export const contentTypes = [
3
- { value: 'application/json; charset=utf-8', label: 'JSON' },
4
- { value: 'text/plain; charset=utf-8', label: '纯文本' },
5
- { value: 'text/html; charset=utf-8', label: 'HTML' },
6
- { value: 'application/xml; charset=utf-8', label: 'XML' },
7
- { value: 'application/javascript; charset=utf-8', label: 'JavaScript' },
8
- ];
9
-
10
- export const proxyTypes = [
11
- { value: 'response', label: '模拟响应' },
12
- { value: 'redirect', label: '重定向' },
13
- { value: 'url_redirect', label: 'URL重定向' },
14
- ];
15
-
16
- export const statusCodes = [
17
- { value: '200', label: '200 OK' },
18
- { value: '201', label: '201 Created' },
19
- { value: '204', label: '204 No Content' },
20
- { value: '400', label: '400 Bad Request' },
21
- { value: '401', label: '401 Unauthorized' },
22
- { value: '403', label: '403 Forbidden' },
23
- { value: '404', label: '404 Not Found' },
24
- { value: '500', label: '500 Internal Server Error' },
25
- ];
26
-
27
- export const httpMethods = [
28
- { value: 'ALL', label: '所有方法' },
29
- { value: 'GET', label: 'GET' },
30
- { value: 'POST', label: 'POST' },
31
- { value: 'PUT', label: 'PUT' },
32
- { value: 'DELETE', label: 'DELETE' },
33
- { value: 'PATCH', label: 'PATCH' },
34
- { value: 'HEAD', label: 'HEAD' },
35
- { value: 'OPTIONS', label: 'OPTIONS' },
36
- ];
@@ -1,7 +0,0 @@
1
- // 统一导出接口管理相关的组件
2
- export { default as ResponseContentEditor } from './ResponseContentEditor';
3
- export { default as InterfaceTestModal } from './InterfaceTestModal';
4
- export { default as PreviewModal } from './PreviewModal';
5
- export { default as DataManagementDrawer } from './DataManagementDrawer';
6
- export * from './constants';
7
- export * from './utils';