whistle.mock-plugins 1.2.2-beta.10 → 1.2.2-beta.11
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.
package/package.json
CHANGED
|
@@ -209,111 +209,6 @@ const ResponseContentEditor = ({
|
|
|
209
209
|
updateActiveResponseParamMatchers(currentMatchers);
|
|
210
210
|
};
|
|
211
211
|
|
|
212
|
-
// 渲染入参匹配规则编辑区域
|
|
213
|
-
const renderParamMatchersSection = () => {
|
|
214
|
-
// 整个接口中是否有任意响应含有匹配规则
|
|
215
|
-
const anyHasMatchers = responses.some(
|
|
216
|
-
r => r.paramMatchers && Array.isArray(r.paramMatchers) && r.paramMatchers.length > 0
|
|
217
|
-
);
|
|
218
|
-
const currentHasMatchers = activeResponse.paramMatchers && activeResponse.paramMatchers.length > 0;
|
|
219
|
-
// 只有当接口中存在规则时,无规则的响应才叫"兜底"
|
|
220
|
-
const isFallback = anyHasMatchers && !currentHasMatchers;
|
|
221
|
-
|
|
222
|
-
return (
|
|
223
|
-
<div style={{
|
|
224
|
-
marginBottom: '16px',
|
|
225
|
-
border: '1px solid #e8e8e8',
|
|
226
|
-
borderRadius: '6px',
|
|
227
|
-
padding: '12px',
|
|
228
|
-
background: '#fafafa'
|
|
229
|
-
}}>
|
|
230
|
-
<div style={{
|
|
231
|
-
display: 'flex',
|
|
232
|
-
alignItems: 'center',
|
|
233
|
-
justifyContent: 'space-between',
|
|
234
|
-
marginBottom: '10px'
|
|
235
|
-
}}>
|
|
236
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
|
237
|
-
<FilterOutlined style={{ color: '#1890ff' }} />
|
|
238
|
-
<label style={{ fontSize: '12px', color: '#333', fontWeight: 'bold' }}>
|
|
239
|
-
入参匹配规则
|
|
240
|
-
</label>
|
|
241
|
-
{currentHasMatchers ? (
|
|
242
|
-
<Tag color="blue" style={{ fontSize: '11px', lineHeight: '18px', padding: '0 6px' }}>
|
|
243
|
-
{activeResponse.paramMatchers.length}条
|
|
244
|
-
</Tag>
|
|
245
|
-
) : isFallback ? (
|
|
246
|
-
<Tag color="default" style={{ fontSize: '11px', lineHeight: '18px', padding: '0 6px' }}>
|
|
247
|
-
兜底响应
|
|
248
|
-
</Tag>
|
|
249
|
-
) : null}
|
|
250
|
-
</div>
|
|
251
|
-
<Button
|
|
252
|
-
type="dashed"
|
|
253
|
-
size="small"
|
|
254
|
-
icon={<PlusCircleOutlined />}
|
|
255
|
-
onClick={handleAddMatcher}
|
|
256
|
-
style={{ fontSize: '12px' }}
|
|
257
|
-
>
|
|
258
|
-
添加规则
|
|
259
|
-
</Button>
|
|
260
|
-
</div>
|
|
261
|
-
|
|
262
|
-
{isFallback && (
|
|
263
|
-
<div style={{
|
|
264
|
-
color: '#999',
|
|
265
|
-
fontSize: '12px',
|
|
266
|
-
textAlign: 'center',
|
|
267
|
-
padding: '8px 0'
|
|
268
|
-
}}>
|
|
269
|
-
无匹配规则,当其他响应均不匹配时作为兜底返回
|
|
270
|
-
</div>
|
|
271
|
-
)}
|
|
272
|
-
|
|
273
|
-
{activeResponse.paramMatchers && activeResponse.paramMatchers.map((matcher, index) => (
|
|
274
|
-
<Row key={index} gutter={8} style={{ marginBottom: '8px' }} align="middle">
|
|
275
|
-
<Col span={8}>
|
|
276
|
-
<Input
|
|
277
|
-
size="small"
|
|
278
|
-
placeholder="参数路径,如 data.type"
|
|
279
|
-
value={matcher.paramPath}
|
|
280
|
-
onChange={(e) => handleMatcherChange(index, 'paramPath', e.target.value)}
|
|
281
|
-
/>
|
|
282
|
-
</Col>
|
|
283
|
-
<Col span={5}>
|
|
284
|
-
<Select
|
|
285
|
-
size="small"
|
|
286
|
-
style={{ width: '100%' }}
|
|
287
|
-
value={matcher.matchType || 'exact'}
|
|
288
|
-
onChange={(val) => handleMatcherChange(index, 'matchType', val)}
|
|
289
|
-
>
|
|
290
|
-
<Option value="exact">精确匹配</Option>
|
|
291
|
-
<Option value="contains">包含</Option>
|
|
292
|
-
<Option value="regex">正则</Option>
|
|
293
|
-
</Select>
|
|
294
|
-
</Col>
|
|
295
|
-
<Col span={9}>
|
|
296
|
-
<Input
|
|
297
|
-
size="small"
|
|
298
|
-
placeholder="期望值"
|
|
299
|
-
value={matcher.paramValue}
|
|
300
|
-
onChange={(e) => handleMatcherChange(index, 'paramValue', e.target.value)}
|
|
301
|
-
/>
|
|
302
|
-
</Col>
|
|
303
|
-
<Col span={2} style={{ textAlign: 'center' }}>
|
|
304
|
-
<Tooltip title="删除此规则">
|
|
305
|
-
<MinusCircleOutlined
|
|
306
|
-
style={{ color: '#ff4d4f', cursor: 'pointer', fontSize: '16px' }}
|
|
307
|
-
onClick={() => handleDeleteMatcher(index)}
|
|
308
|
-
/>
|
|
309
|
-
</Tooltip>
|
|
310
|
-
</Col>
|
|
311
|
-
</Row>
|
|
312
|
-
))}
|
|
313
|
-
</div>
|
|
314
|
-
);
|
|
315
|
-
};
|
|
316
|
-
|
|
317
212
|
// 格式化JSON内容
|
|
318
213
|
const formatJsonContent = () => {
|
|
319
214
|
if (!activeResponse || !activeResponse.content) {
|
|
@@ -643,7 +538,99 @@ const ResponseContentEditor = ({
|
|
|
643
538
|
</div>
|
|
644
539
|
|
|
645
540
|
{/* 入参匹配规则编辑区域 */}
|
|
646
|
-
{activeResponse &&
|
|
541
|
+
{activeResponse && (
|
|
542
|
+
<div style={{
|
|
543
|
+
marginBottom: '16px',
|
|
544
|
+
border: '1px solid #e8e8e8',
|
|
545
|
+
borderRadius: '6px',
|
|
546
|
+
padding: '12px',
|
|
547
|
+
background: '#fafafa'
|
|
548
|
+
}}>
|
|
549
|
+
<div style={{
|
|
550
|
+
display: 'flex',
|
|
551
|
+
alignItems: 'center',
|
|
552
|
+
justifyContent: 'space-between',
|
|
553
|
+
marginBottom: '10px'
|
|
554
|
+
}}>
|
|
555
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
|
|
556
|
+
<FilterOutlined style={{ color: '#1890ff' }} />
|
|
557
|
+
<label style={{ fontSize: '12px', color: '#333', fontWeight: 'bold' }}>
|
|
558
|
+
入参匹配规则
|
|
559
|
+
</label>
|
|
560
|
+
{activeResponse.paramMatchers && activeResponse.paramMatchers.length > 0 ? (
|
|
561
|
+
<Tag color="blue" style={{ fontSize: '11px', lineHeight: '18px', padding: '0 6px' }}>
|
|
562
|
+
{activeResponse.paramMatchers.length}条
|
|
563
|
+
</Tag>
|
|
564
|
+
) : (
|
|
565
|
+
<Tag color="default" style={{ fontSize: '11px', lineHeight: '18px', padding: '0 6px' }}>
|
|
566
|
+
兜底响应
|
|
567
|
+
</Tag>
|
|
568
|
+
)}
|
|
569
|
+
</div>
|
|
570
|
+
<Button
|
|
571
|
+
type="dashed"
|
|
572
|
+
size="small"
|
|
573
|
+
icon={<PlusCircleOutlined />}
|
|
574
|
+
onClick={handleAddMatcher}
|
|
575
|
+
style={{ fontSize: '12px' }}
|
|
576
|
+
>
|
|
577
|
+
添加规则
|
|
578
|
+
</Button>
|
|
579
|
+
</div>
|
|
580
|
+
|
|
581
|
+
{(!activeResponse.paramMatchers || activeResponse.paramMatchers.length === 0) && (
|
|
582
|
+
<div style={{
|
|
583
|
+
color: '#999',
|
|
584
|
+
fontSize: '12px',
|
|
585
|
+
textAlign: 'center',
|
|
586
|
+
padding: '8px 0'
|
|
587
|
+
}}>
|
|
588
|
+
无匹配规则,当其他响应均不匹配时作为兜底返回
|
|
589
|
+
</div>
|
|
590
|
+
)}
|
|
591
|
+
|
|
592
|
+
{activeResponse.paramMatchers && activeResponse.paramMatchers.map((matcher, index) => (
|
|
593
|
+
<Row key={index} gutter={8} style={{ marginBottom: '8px' }} align="middle">
|
|
594
|
+
<Col span={8}>
|
|
595
|
+
<Input
|
|
596
|
+
size="small"
|
|
597
|
+
placeholder="参数路径,如 data.type"
|
|
598
|
+
value={matcher.paramPath}
|
|
599
|
+
onChange={(e) => handleMatcherChange(index, 'paramPath', e.target.value)}
|
|
600
|
+
/>
|
|
601
|
+
</Col>
|
|
602
|
+
<Col span={5}>
|
|
603
|
+
<Select
|
|
604
|
+
size="small"
|
|
605
|
+
style={{ width: '100%' }}
|
|
606
|
+
value={matcher.matchType || 'exact'}
|
|
607
|
+
onChange={(val) => handleMatcherChange(index, 'matchType', val)}
|
|
608
|
+
>
|
|
609
|
+
<Option value="exact">精确匹配</Option>
|
|
610
|
+
<Option value="contains">包含</Option>
|
|
611
|
+
<Option value="regex">正则</Option>
|
|
612
|
+
</Select>
|
|
613
|
+
</Col>
|
|
614
|
+
<Col span={9}>
|
|
615
|
+
<Input
|
|
616
|
+
size="small"
|
|
617
|
+
placeholder="期望值"
|
|
618
|
+
value={matcher.paramValue}
|
|
619
|
+
onChange={(e) => handleMatcherChange(index, 'paramValue', e.target.value)}
|
|
620
|
+
/>
|
|
621
|
+
</Col>
|
|
622
|
+
<Col span={2} style={{ textAlign: 'center' }}>
|
|
623
|
+
<Tooltip title="删除此规则">
|
|
624
|
+
<MinusCircleOutlined
|
|
625
|
+
style={{ color: '#ff4d4f', cursor: 'pointer', fontSize: '16px' }}
|
|
626
|
+
onClick={() => handleDeleteMatcher(index)}
|
|
627
|
+
/>
|
|
628
|
+
</Tooltip>
|
|
629
|
+
</Col>
|
|
630
|
+
</Row>
|
|
631
|
+
))}
|
|
632
|
+
</div>
|
|
633
|
+
)}
|
|
647
634
|
|
|
648
635
|
{/* 响应内容编辑区域 */}
|
|
649
636
|
{activeResponse && (
|
|
@@ -302,17 +302,7 @@ const InterfaceManagement = () => {
|
|
|
302
302
|
let activeResponseId = '';
|
|
303
303
|
|
|
304
304
|
if (record.responses && Array.isArray(record.responses) && record.responses.length > 0) {
|
|
305
|
-
responses = record.responses
|
|
306
|
-
...r,
|
|
307
|
-
paramMatchers: r.paramMatchers || []
|
|
308
|
-
}));
|
|
309
|
-
// 向后兼容:旧数据接口级 paramMatchers 迁移到第一个响应
|
|
310
|
-
if (
|
|
311
|
-
record.paramMatchers && Array.isArray(record.paramMatchers) && record.paramMatchers.length > 0 &&
|
|
312
|
-
responses.every(r => !r.paramMatchers || r.paramMatchers.length === 0)
|
|
313
|
-
) {
|
|
314
|
-
responses[0] = { ...responses[0], paramMatchers: record.paramMatchers };
|
|
315
|
-
}
|
|
305
|
+
responses = record.responses;
|
|
316
306
|
activeResponseId = record.activeResponseId || responses[0].id;
|
|
317
307
|
} else if (record.responseContent) {
|
|
318
308
|
// 兼容旧数据格式,创建默认响应
|
|
@@ -512,22 +502,13 @@ const InterfaceManagement = () => {
|
|
|
512
502
|
}
|
|
513
503
|
|
|
514
504
|
// 构建接口数据 - 在编辑模式下,确保关键字段不丢失
|
|
515
|
-
// responses 隐藏字段可能被 Input 序列化为字符串,需要解析还原
|
|
516
|
-
let responsesValue = values.responses;
|
|
517
|
-
if (typeof responsesValue === 'string') {
|
|
518
|
-
try { responsesValue = JSON.parse(responsesValue); } catch (e) { responsesValue = []; }
|
|
519
|
-
}
|
|
520
|
-
if (!Array.isArray(responsesValue)) {
|
|
521
|
-
responsesValue = editingInterface ? editingInterface.responses : [];
|
|
522
|
-
}
|
|
523
|
-
|
|
524
505
|
const interfaceData = {
|
|
525
506
|
name: values.name || (editingInterface ? editingInterface.name : ''),
|
|
526
507
|
group: groupValue || (editingInterface ? editingInterface.group : '') || '', // 确保group不为undefined
|
|
527
508
|
urlPattern: values.pattern || (editingInterface ? editingInterface.urlPattern : ''),
|
|
528
509
|
proxyType: values.proxyType || (editingInterface ? editingInterface.proxyType : 'response'),
|
|
529
510
|
featureId: selectedFeatureId,
|
|
530
|
-
responses:
|
|
511
|
+
responses: values.responses || (editingInterface ? editingInterface.responses : []),
|
|
531
512
|
activeResponseId: values.activeResponseId || (editingInterface ? editingInterface.activeResponseId : null),
|
|
532
513
|
httpStatus: parseInt(values.statusCode, 10) || (editingInterface ? editingInterface.httpStatus : 200), // 转换为数字
|
|
533
514
|
contentType: values.contentType || (editingInterface ? editingInterface.contentType : 'application/json; charset=utf-8'),
|
|
@@ -835,26 +816,8 @@ const InterfaceManagement = () => {
|
|
|
835
816
|
const hasSmartMatch = record.responses.some(
|
|
836
817
|
r => r.paramMatchers && Array.isArray(r.paramMatchers) && r.paramMatchers.length > 0
|
|
837
818
|
);
|
|
819
|
+
const fallbackResp = record.responses.find(r => r.id === activeResponseId) || record.responses[0];
|
|
838
820
|
|
|
839
|
-
// 场景一:所有响应都没有规则 → 显示 Select 快捷切换
|
|
840
|
-
if (!hasSmartMatch) {
|
|
841
|
-
return (
|
|
842
|
-
<Select
|
|
843
|
-
value={activeResponseId || record.responses[0]?.id}
|
|
844
|
-
style={{ width: '100%' }}
|
|
845
|
-
size="small"
|
|
846
|
-
onChange={(value) => handleResponseSwitch(record.id, value)}
|
|
847
|
-
>
|
|
848
|
-
{record.responses.map(resp => (
|
|
849
|
-
<Option key={resp.id} value={resp.id}>
|
|
850
|
-
{resp.name}
|
|
851
|
-
</Option>
|
|
852
|
-
))}
|
|
853
|
-
</Select>
|
|
854
|
-
);
|
|
855
|
-
}
|
|
856
|
-
|
|
857
|
-
// 场景二/三:有响应含规则 → 显示 Tag 摘要
|
|
858
821
|
const tooltipContent = (
|
|
859
822
|
<div style={{ maxWidth: 260 }}>
|
|
860
823
|
{record.responses.map((resp) => {
|
|
@@ -891,9 +854,19 @@ const InterfaceManagement = () => {
|
|
|
891
854
|
|
|
892
855
|
return (
|
|
893
856
|
<Tooltip title={tooltipContent} placement="left">
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
857
|
+
{hasSmartMatch ? (
|
|
858
|
+
<Tag color="blue" style={{ cursor: 'default' }}>
|
|
859
|
+
智能匹配 · {total}个响应
|
|
860
|
+
</Tag>
|
|
861
|
+
) : total > 1 ? (
|
|
862
|
+
<Tag color="orange" style={{ cursor: 'default' }}>
|
|
863
|
+
手动切换 · {total}个响应
|
|
864
|
+
</Tag>
|
|
865
|
+
) : (
|
|
866
|
+
<span style={{ color: '#999', fontSize: '12px' }}>
|
|
867
|
+
{fallbackResp?.name || '1个响应'}
|
|
868
|
+
</span>
|
|
869
|
+
)}
|
|
897
870
|
</Tooltip>
|
|
898
871
|
);
|
|
899
872
|
},
|