whistle.mock-plugins 1.2.1 → 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.
|
@@ -42,13 +42,13 @@ const COLUMN_CONFIG = [
|
|
|
42
42
|
{ key: 'group', title: '分组', required: false },
|
|
43
43
|
{ key: 'urlPattern', title: 'URL匹配规则', required: false },
|
|
44
44
|
{ key: 'proxyType', title: '处理方式', required: false },
|
|
45
|
-
{ key: 'currentResponse', title: '
|
|
45
|
+
{ key: 'currentResponse', title: '响应配置', required: false },
|
|
46
46
|
{ key: 'responseDelay', title: '延迟(毫秒)', required: false },
|
|
47
47
|
{ key: 'httpStatus', title: '状态码', required: false },
|
|
48
48
|
{ key: 'contentType', title: '内容类型', required: false },
|
|
49
49
|
{ key: 'targetUrl', title: '目标URL', required: false },
|
|
50
50
|
{ key: 'customHeaders', title: '自定义头', required: false },
|
|
51
|
-
{ key: 'paramMatchers', title: '
|
|
51
|
+
{ key: 'paramMatchers', title: '匹配规则', required: false },
|
|
52
52
|
{ key: 'httpMethod', title: '请求方法', required: false },
|
|
53
53
|
{ key: 'action', title: '操作', required: true }
|
|
54
54
|
];
|
|
@@ -800,35 +800,74 @@ const InterfaceManagement = () => {
|
|
|
800
800
|
},
|
|
801
801
|
},
|
|
802
802
|
{
|
|
803
|
-
title: '
|
|
803
|
+
title: '响应配置',
|
|
804
804
|
dataIndex: 'activeResponseId',
|
|
805
805
|
key: 'currentResponse',
|
|
806
|
-
width:
|
|
806
|
+
width: 160,
|
|
807
807
|
render: (activeResponseId, record) => {
|
|
808
|
-
|
|
808
|
+
if (record.proxyType !== 'response') {
|
|
809
|
+
return <span style={{ color: '#ccc' }}>-</span>;
|
|
810
|
+
}
|
|
809
811
|
if (!record.responses || !Array.isArray(record.responses) || record.responses.length === 0) {
|
|
810
|
-
return <span
|
|
812
|
+
return <span style={{ color: '#ff4d4f', fontSize: '12px' }}>无响应数据</span>;
|
|
811
813
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
const
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
const
|
|
818
|
-
|
|
814
|
+
|
|
815
|
+
const total = record.responses.length;
|
|
816
|
+
const hasSmartMatch = record.responses.some(
|
|
817
|
+
r => r.paramMatchers && Array.isArray(r.paramMatchers) && r.paramMatchers.length > 0
|
|
818
|
+
);
|
|
819
|
+
const fallbackResp = record.responses.find(r => r.id === activeResponseId) || record.responses[0];
|
|
820
|
+
|
|
821
|
+
const tooltipContent = (
|
|
822
|
+
<div style={{ maxWidth: 260 }}>
|
|
823
|
+
{record.responses.map((resp) => {
|
|
824
|
+
const matchers = resp.paramMatchers || [];
|
|
825
|
+
return (
|
|
826
|
+
<div key={resp.id} style={{ marginBottom: 6 }}>
|
|
827
|
+
<div style={{ display: 'flex', alignItems: 'center', gap: 4 }}>
|
|
828
|
+
{matchers.length > 0 ? (
|
|
829
|
+
<Tag color="blue" style={{ fontSize: '11px', margin: 0 }}>{matchers.length}条规则</Tag>
|
|
830
|
+
) : (
|
|
831
|
+
<Tag color="default" style={{ fontSize: '11px', margin: 0 }}>兜底</Tag>
|
|
832
|
+
)}
|
|
833
|
+
<span style={{ fontWeight: resp.id === activeResponseId ? 'bold' : 'normal' }}>
|
|
834
|
+
{resp.name}
|
|
835
|
+
</span>
|
|
836
|
+
</div>
|
|
837
|
+
{matchers.length > 0 && (
|
|
838
|
+
<div style={{ paddingLeft: 8, marginTop: 2 }}>
|
|
839
|
+
{matchers.map((m, i) => (
|
|
840
|
+
<div key={i} style={{ fontSize: '11px', color: '#666' }}>
|
|
841
|
+
{m.paramPath} = {m.paramValue}
|
|
842
|
+
<span style={{ color: '#52c41a', marginLeft: 4 }}>
|
|
843
|
+
({m.matchType === 'exact' ? '精确' : m.matchType === 'contains' ? '包含' : '正则'})
|
|
844
|
+
</span>
|
|
845
|
+
</div>
|
|
846
|
+
))}
|
|
847
|
+
</div>
|
|
848
|
+
)}
|
|
849
|
+
</div>
|
|
850
|
+
);
|
|
851
|
+
})}
|
|
852
|
+
</div>
|
|
853
|
+
);
|
|
854
|
+
|
|
819
855
|
return (
|
|
820
|
-
<
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
856
|
+
<Tooltip title={tooltipContent} placement="left">
|
|
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
|
+
)}
|
|
870
|
+
</Tooltip>
|
|
832
871
|
);
|
|
833
872
|
},
|
|
834
873
|
},
|
|
@@ -945,38 +984,75 @@ const InterfaceManagement = () => {
|
|
|
945
984
|
}
|
|
946
985
|
},
|
|
947
986
|
{
|
|
948
|
-
title: '
|
|
949
|
-
dataIndex: '
|
|
987
|
+
title: '匹配规则',
|
|
988
|
+
dataIndex: 'responses',
|
|
950
989
|
key: 'paramMatchers',
|
|
951
990
|
width: 100,
|
|
952
|
-
render: (
|
|
991
|
+
render: (responses, record) => {
|
|
953
992
|
if (record.proxyType !== 'response') {
|
|
954
|
-
return '
|
|
993
|
+
return <span style={{ color: '#ccc' }}>-</span>;
|
|
955
994
|
}
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
const
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
995
|
+
|
|
996
|
+
// 汇总所有响应的 paramMatchers(响应级),兼容旧数据接口级 paramMatchers
|
|
997
|
+
const responsesWithMatchers = Array.isArray(responses)
|
|
998
|
+
? responses.filter(r => r.paramMatchers && Array.isArray(r.paramMatchers) && r.paramMatchers.length > 0)
|
|
999
|
+
: [];
|
|
1000
|
+
const totalRules = responsesWithMatchers.reduce((sum, r) => sum + r.paramMatchers.length, 0);
|
|
1001
|
+
|
|
1002
|
+
// 兼容旧数据:接口级 paramMatchers
|
|
1003
|
+
const legacyMatchers = record.paramMatchers || [];
|
|
1004
|
+
|
|
1005
|
+
if (totalRules === 0 && legacyMatchers.length === 0) {
|
|
1006
|
+
return <span style={{ color: '#ccc' }}>-</span>;
|
|
962
1007
|
}
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
<
|
|
971
|
-
|
|
972
|
-
|
|
1008
|
+
|
|
1009
|
+
// 新数据:按响应分组展示
|
|
1010
|
+
if (totalRules > 0) {
|
|
1011
|
+
const tooltipContent = (
|
|
1012
|
+
<div style={{ maxWidth: 280 }}>
|
|
1013
|
+
{responsesWithMatchers.map(resp => (
|
|
1014
|
+
<div key={resp.id} style={{ marginBottom: 6 }}>
|
|
1015
|
+
<div style={{ fontWeight: 'bold', marginBottom: 2 }}>{resp.name}:</div>
|
|
1016
|
+
{resp.paramMatchers.map((m, i) => (
|
|
1017
|
+
<div key={i} style={{ paddingLeft: 8, fontSize: '12px' }}>
|
|
1018
|
+
{m.paramPath} = {m.paramValue}
|
|
1019
|
+
<span style={{ color: '#52c41a', marginLeft: 4 }}>
|
|
1020
|
+
({m.matchType === 'exact' ? '精确' : m.matchType === 'contains' ? '包含' : '正则'})
|
|
1021
|
+
</span>
|
|
1022
|
+
</div>
|
|
1023
|
+
))}
|
|
973
1024
|
</div>
|
|
974
1025
|
))}
|
|
1026
|
+
{Array.isArray(responses) && responses.some(r => !r.paramMatchers || r.paramMatchers.length === 0) && (
|
|
1027
|
+
<div style={{ color: '#999', fontSize: '12px', marginTop: 4 }}>
|
|
1028
|
+
+ 兜底响应(无规则)
|
|
1029
|
+
</div>
|
|
1030
|
+
)}
|
|
975
1031
|
</div>
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
1032
|
+
);
|
|
1033
|
+
return (
|
|
1034
|
+
<Tooltip title={tooltipContent} placement="left">
|
|
1035
|
+
<span style={{ color: '#1890ff', cursor: 'default' }}>{totalRules}条规则</span>
|
|
1036
|
+
</Tooltip>
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// 旧数据兼容展示
|
|
1041
|
+
const tooltipContent = (
|
|
1042
|
+
<div>
|
|
1043
|
+
{legacyMatchers.map((matcher, index) => (
|
|
1044
|
+
<div key={index}>
|
|
1045
|
+
{matcher.paramPath}: {matcher.paramValue}
|
|
1046
|
+
<span style={{ color: '#52c41a', marginLeft: 4 }}>
|
|
1047
|
+
({matcher.matchType === 'exact' ? '精确' : matcher.matchType === 'contains' ? '包含' : '正则'})
|
|
1048
|
+
</span>
|
|
1049
|
+
</div>
|
|
1050
|
+
))}
|
|
1051
|
+
</div>
|
|
1052
|
+
);
|
|
1053
|
+
return (
|
|
1054
|
+
<Tooltip title={tooltipContent} placement="left">
|
|
1055
|
+
<span style={{ color: '#fa8c16', cursor: 'default' }}>{legacyMatchers.length}条(旧)</span>
|
|
980
1056
|
</Tooltip>
|
|
981
1057
|
);
|
|
982
1058
|
}
|
|
@@ -1698,97 +1774,9 @@ const InterfaceManagement = () => {
|
|
|
1698
1774
|
</div>
|
|
1699
1775
|
</div>
|
|
1700
1776
|
|
|
1701
|
-
{/*
|
|
1702
|
-
<Form.Item
|
|
1703
|
-
|
|
1704
|
-
tooltip="设置请求参数的匹配条件,只有当请求参数满足条件时才返回对应的响应。支持嵌套属性路径,如:a.b.c"
|
|
1705
|
-
>
|
|
1706
|
-
<Form.List name="paramMatchers">
|
|
1707
|
-
{(fields, { add, remove }) => (
|
|
1708
|
-
<>
|
|
1709
|
-
{fields.map(({ key, name, ...restField }) => (
|
|
1710
|
-
<div key={key} style={{ display: 'flex', gap: '8px', alignItems: 'center', marginBottom: '8px' }}>
|
|
1711
|
-
<Form.Item
|
|
1712
|
-
{...restField}
|
|
1713
|
-
name={[name, 'paramPath']}
|
|
1714
|
-
rules={[
|
|
1715
|
-
{ required: true, message: '请输入参数路径' },
|
|
1716
|
-
{
|
|
1717
|
-
pattern: /^[a-zA-Z_$][a-zA-Z0-9_$.]*$/,
|
|
1718
|
-
message: '请输入有效的参数路径,如:userId 或 data.user.id'
|
|
1719
|
-
}
|
|
1720
|
-
]}
|
|
1721
|
-
style={{ flex: 1, margin: 0 }}
|
|
1722
|
-
>
|
|
1723
|
-
<Input placeholder="参数路径(如:userId 或 data.user.id)" />
|
|
1724
|
-
</Form.Item>
|
|
1725
|
-
<Form.Item
|
|
1726
|
-
{...restField}
|
|
1727
|
-
name={[name, 'paramValue']}
|
|
1728
|
-
rules={[{ required: true, message: '请输入期望值' }]}
|
|
1729
|
-
style={{ flex: 1, margin: 0 }}
|
|
1730
|
-
>
|
|
1731
|
-
<Input placeholder="期望值(如:123 或 admin)" />
|
|
1732
|
-
</Form.Item>
|
|
1733
|
-
<Form.Item
|
|
1734
|
-
{...restField}
|
|
1735
|
-
name={[name, 'matchType']}
|
|
1736
|
-
style={{ width: 100, margin: 0 }}
|
|
1737
|
-
initialValue="exact"
|
|
1738
|
-
>
|
|
1739
|
-
<Select placeholder="匹配类型">
|
|
1740
|
-
<Option value="exact">精确匹配</Option>
|
|
1741
|
-
<Option value="contains">包含</Option>
|
|
1742
|
-
<Option value="regex">正则</Option>
|
|
1743
|
-
</Select>
|
|
1744
|
-
</Form.Item>
|
|
1745
|
-
<Button
|
|
1746
|
-
type="text"
|
|
1747
|
-
danger
|
|
1748
|
-
icon={<DeleteOutlined />}
|
|
1749
|
-
onClick={() => remove(name)}
|
|
1750
|
-
/>
|
|
1751
|
-
</div>
|
|
1752
|
-
))}
|
|
1753
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginTop: '8px' }}>
|
|
1754
|
-
<Button
|
|
1755
|
-
type="dashed"
|
|
1756
|
-
onClick={() => add()}
|
|
1757
|
-
icon={<PlusOutlined />}
|
|
1758
|
-
style={{ flex: 1 }}
|
|
1759
|
-
>
|
|
1760
|
-
添加参数匹配规则
|
|
1761
|
-
</Button>
|
|
1762
|
-
<Popover
|
|
1763
|
-
content={
|
|
1764
|
-
<div style={{ maxWidth: '300px' }}>
|
|
1765
|
-
<div style={{ marginBottom: '8px', fontWeight: 'bold' }}>匹配规则说明:</div>
|
|
1766
|
-
<div style={{ lineHeight: '1.6' }}>
|
|
1767
|
-
<div>• <strong>精确匹配</strong>:参数值完全相等</div>
|
|
1768
|
-
<div>• <strong>包含</strong>:参数值包含指定内容</div>
|
|
1769
|
-
<div>• <strong>正则</strong>:参数值符合正则表达式</div>
|
|
1770
|
-
<div>• <strong>嵌套路径</strong>:使用点号分隔,如 data.user.id</div>
|
|
1771
|
-
</div>
|
|
1772
|
-
</div>
|
|
1773
|
-
}
|
|
1774
|
-
title="参数匹配规则"
|
|
1775
|
-
trigger="click"
|
|
1776
|
-
placement="top"
|
|
1777
|
-
>
|
|
1778
|
-
<Button
|
|
1779
|
-
type="text"
|
|
1780
|
-
icon={<QuestionCircleOutlined />}
|
|
1781
|
-
style={{
|
|
1782
|
-
color: '#999',
|
|
1783
|
-
padding: '4px'
|
|
1784
|
-
}}
|
|
1785
|
-
size="small"
|
|
1786
|
-
/>
|
|
1787
|
-
</Popover>
|
|
1788
|
-
</div>
|
|
1789
|
-
</>
|
|
1790
|
-
)}
|
|
1791
|
-
</Form.List>
|
|
1777
|
+
{/* 入参匹配规则已移至各响应内部配置,此处保留隐藏字段向后兼容旧数据 */}
|
|
1778
|
+
<Form.Item name="paramMatchers" hidden initialValue={[]}>
|
|
1779
|
+
<Input />
|
|
1792
1780
|
</Form.Item>
|
|
1793
1781
|
|
|
1794
1782
|
{/* 隐藏的表单字段用于存储响应数据 */}
|