whistle.mock-plugins 1.1.1-dev.9 → 1.1.2-beta.1
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/VERSION.md +12 -19
- package/app/cgi-bin/interfaces.js +88 -9
- package/app/public/js/index.js +1 -1
- package/app/public/js/index.js.LICENSE.txt +6 -0
- package/lib/routes/interfaces.js +2 -0
- package/package.json +1 -1
- package/ui/src/pages/InterfaceManagement.js +405 -111
- package/ui/src/styles/interface-management.css +292 -16
|
@@ -4,11 +4,13 @@ import AppLayout from '../components/AppLayout';
|
|
|
4
4
|
import {
|
|
5
5
|
Table, Button, Modal, Form, Input, Select, message, Switch,
|
|
6
6
|
Popconfirm, Alert, Space, Card, Badge, Tooltip, Row, Col,
|
|
7
|
-
Popover, Checkbox, Tag
|
|
7
|
+
Popover, Checkbox, Tag, Input as AntInput, Radio, Drawer
|
|
8
8
|
} from 'antd';
|
|
9
9
|
import {
|
|
10
10
|
PlusOutlined, EditOutlined, DeleteOutlined,
|
|
11
|
-
FileTextOutlined, PlusCircleOutlined, SettingOutlined
|
|
11
|
+
FileTextOutlined, PlusCircleOutlined, SettingOutlined,
|
|
12
|
+
SearchOutlined, FilterOutlined, AppstoreOutlined,
|
|
13
|
+
UnorderedListOutlined, ReloadOutlined
|
|
12
14
|
} from '@ant-design/icons';
|
|
13
15
|
import '../styles/interface-management.css';
|
|
14
16
|
import axios from 'axios';
|
|
@@ -27,6 +29,7 @@ import {
|
|
|
27
29
|
} from '../components/interface-management';
|
|
28
30
|
|
|
29
31
|
const { Option } = Select;
|
|
32
|
+
const { Search } = AntInput;
|
|
30
33
|
|
|
31
34
|
// 列配置数据结构
|
|
32
35
|
const COLUMN_CONFIG = [
|
|
@@ -75,6 +78,12 @@ const InterfaceManagement = () => {
|
|
|
75
78
|
const [previewContent, setPreviewContent] = useState(null);
|
|
76
79
|
const [currentResponseId, setCurrentResponseId] = useState(null);
|
|
77
80
|
|
|
81
|
+
// 搜索和视图状态
|
|
82
|
+
const [searchValue, setSearchValue] = useState('');
|
|
83
|
+
const [viewMode, setViewMode] = useState('table'); // 'table' or 'card'
|
|
84
|
+
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
|
85
|
+
const [drawerVisible, setDrawerVisible] = useState(false);
|
|
86
|
+
|
|
78
87
|
// 表单实例
|
|
79
88
|
const [form] = Form.useForm();
|
|
80
89
|
|
|
@@ -172,6 +181,7 @@ const InterfaceManagement = () => {
|
|
|
172
181
|
|
|
173
182
|
setInterfaces(processedInterfaces);
|
|
174
183
|
console.log('处理后的接口列表:', processedInterfaces);
|
|
184
|
+
console.log('接口分组信息:', processedInterfaces.map(item => ({ id: item.id, name: item.name, group: item.group })));
|
|
175
185
|
|
|
176
186
|
// 提取并更新分组列表
|
|
177
187
|
updateGroups(processedInterfaces);
|
|
@@ -209,16 +219,25 @@ const InterfaceManagement = () => {
|
|
|
209
219
|
}
|
|
210
220
|
};
|
|
211
221
|
|
|
212
|
-
//
|
|
222
|
+
// 根据分组和搜索关键词筛选接口
|
|
213
223
|
const getFilteredInterfaces = () => {
|
|
214
224
|
// 首先按功能模块筛选
|
|
215
|
-
|
|
225
|
+
let featureFiltered = interfaces.filter(item =>
|
|
216
226
|
!selectedFeatureId || item.featureId === selectedFeatureId
|
|
217
227
|
);
|
|
218
228
|
|
|
219
229
|
// 然后按分组筛选
|
|
220
230
|
if (selectedGroup) {
|
|
221
|
-
|
|
231
|
+
featureFiltered = featureFiltered.filter(item => item.group === selectedGroup);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// 最后按搜索关键词筛选
|
|
235
|
+
if (searchValue) {
|
|
236
|
+
const searchLower = searchValue.toLowerCase();
|
|
237
|
+
return featureFiltered.filter(item =>
|
|
238
|
+
(item.name && item.name.toLowerCase().includes(searchLower)) ||
|
|
239
|
+
(item.urlPattern && item.urlPattern.toLowerCase().includes(searchLower))
|
|
240
|
+
);
|
|
222
241
|
}
|
|
223
242
|
|
|
224
243
|
return featureFiltered;
|
|
@@ -250,7 +269,8 @@ const InterfaceManagement = () => {
|
|
|
250
269
|
activeResponseId: defaultResponseId,
|
|
251
270
|
httpMethod: 'ALL',
|
|
252
271
|
headerItems: [], // 初始化为空数组
|
|
253
|
-
paramMatchers: [] // 初始化为空数组
|
|
272
|
+
paramMatchers: [], // 初始化为空数组
|
|
273
|
+
group: undefined // 确保分组字段为undefined,而不是空字符串
|
|
254
274
|
});
|
|
255
275
|
|
|
256
276
|
setCurrentResponseId(defaultResponseId);
|
|
@@ -297,10 +317,16 @@ const InterfaceManagement = () => {
|
|
|
297
317
|
}));
|
|
298
318
|
}
|
|
299
319
|
|
|
320
|
+
// 处理分组值,确保它是字符串而不是数组
|
|
321
|
+
let groupValue = record.group;
|
|
322
|
+
if (Array.isArray(groupValue)) {
|
|
323
|
+
groupValue = groupValue.length > 0 ? groupValue[0] : undefined;
|
|
324
|
+
}
|
|
325
|
+
|
|
300
326
|
// 设置表单值
|
|
301
327
|
form.setFieldsValue({
|
|
302
328
|
name: record.name,
|
|
303
|
-
group:
|
|
329
|
+
group: groupValue || undefined, // 使用undefined而不是空字符串
|
|
304
330
|
pattern: record.urlPattern,
|
|
305
331
|
proxyType: record.proxyType || 'response',
|
|
306
332
|
statusCode: (record.httpStatus || record.statusCode || 200).toString(),
|
|
@@ -314,6 +340,8 @@ const InterfaceManagement = () => {
|
|
|
314
340
|
responseDelay: record.responseDelay ? record.responseDelay.toString() : '0'
|
|
315
341
|
});
|
|
316
342
|
|
|
343
|
+
console.log('编辑接口时设置的分组值:', groupValue);
|
|
344
|
+
|
|
317
345
|
setCurrentResponseId(activeResponseId);
|
|
318
346
|
setEditingInterface(record);
|
|
319
347
|
setModalVisible(true);
|
|
@@ -373,10 +401,16 @@ const InterfaceManagement = () => {
|
|
|
373
401
|
});
|
|
374
402
|
}
|
|
375
403
|
|
|
404
|
+
// 处理分组值,确保它是字符串而不是数组
|
|
405
|
+
let groupValue = values.group;
|
|
406
|
+
if (Array.isArray(groupValue)) {
|
|
407
|
+
groupValue = groupValue.length > 0 ? groupValue[0] : '';
|
|
408
|
+
}
|
|
409
|
+
|
|
376
410
|
// 构建接口数据
|
|
377
411
|
const interfaceData = {
|
|
378
412
|
name: values.name,
|
|
379
|
-
group:
|
|
413
|
+
group: groupValue || '', // 确保group不为undefined
|
|
380
414
|
urlPattern: values.pattern,
|
|
381
415
|
proxyType: values.proxyType,
|
|
382
416
|
featureId: selectedFeatureId,
|
|
@@ -389,6 +423,8 @@ const InterfaceManagement = () => {
|
|
|
389
423
|
active: true
|
|
390
424
|
};
|
|
391
425
|
|
|
426
|
+
console.log('提交的分组值:', groupValue);
|
|
427
|
+
|
|
392
428
|
// 根据代理类型添加不同字段
|
|
393
429
|
if (values.proxyType === 'redirect' || values.proxyType === 'url_redirect') {
|
|
394
430
|
interfaceData.targetUrl = values.targetUrl;
|
|
@@ -423,6 +459,8 @@ const InterfaceManagement = () => {
|
|
|
423
459
|
const response = await axios.post('/cgi-bin/interfaces', interfaceData);
|
|
424
460
|
if (response.data && response.data.code === 0) {
|
|
425
461
|
message.success('接口创建成功');
|
|
462
|
+
console.log('接口创建成功,返回数据:', response.data);
|
|
463
|
+
console.log('返回的接口数据中的分组信息:', response.data.data?.group);
|
|
426
464
|
setModalVisible(false);
|
|
427
465
|
fetchInterfaces();
|
|
428
466
|
|
|
@@ -552,6 +590,60 @@ const InterfaceManagement = () => {
|
|
|
552
590
|
setColumnConfigVisible(visible);
|
|
553
591
|
};
|
|
554
592
|
|
|
593
|
+
// 搜索处理
|
|
594
|
+
const handleSearch = (value) => {
|
|
595
|
+
setSearchValue(value);
|
|
596
|
+
};
|
|
597
|
+
|
|
598
|
+
// 清除搜索
|
|
599
|
+
const handleClearSearch = () => {
|
|
600
|
+
setSearchValue('');
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
// 表格行选择处理
|
|
604
|
+
const onSelectChange = (selectedKeys) => {
|
|
605
|
+
setSelectedRowKeys(selectedKeys);
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
// 切换视图模式
|
|
609
|
+
const toggleViewMode = () => {
|
|
610
|
+
setViewMode(viewMode === 'table' ? 'card' : 'table');
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
// 刷新数据
|
|
614
|
+
const refreshData = () => {
|
|
615
|
+
fetchInterfaces();
|
|
616
|
+
message.success('数据已刷新');
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
// 批量操作接口状态
|
|
620
|
+
const handleBatchOperation = async (active) => {
|
|
621
|
+
if (!selectedRowKeys || selectedRowKeys.length === 0) {
|
|
622
|
+
message.warning('请先选择要操作的接口');
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
try {
|
|
627
|
+
setLoading(true);
|
|
628
|
+
const operations = selectedRowKeys.map(id =>
|
|
629
|
+
axios.patch(`/cgi-bin/interfaces/${id}`, { active })
|
|
630
|
+
);
|
|
631
|
+
|
|
632
|
+
await Promise.all(operations);
|
|
633
|
+
message.success(`已${active ? '启用' : '禁用'} ${selectedRowKeys.length} 个接口`);
|
|
634
|
+
setSelectedRowKeys([]);
|
|
635
|
+
fetchInterfaces();
|
|
636
|
+
|
|
637
|
+
// 刷新规则缓存
|
|
638
|
+
refreshCacheAfterUpdate();
|
|
639
|
+
} catch (error) {
|
|
640
|
+
console.error('批量操作失败:', error);
|
|
641
|
+
message.error('批量操作失败: ' + (error.response?.data?.message || error.message));
|
|
642
|
+
} finally {
|
|
643
|
+
setLoading(false);
|
|
644
|
+
}
|
|
645
|
+
};
|
|
646
|
+
|
|
555
647
|
// 定义所有可用的列
|
|
556
648
|
const allColumns = [
|
|
557
649
|
{
|
|
@@ -868,35 +960,69 @@ const InterfaceManagement = () => {
|
|
|
868
960
|
return (
|
|
869
961
|
<AppLayout>
|
|
870
962
|
<div className="interface-management-container">
|
|
871
|
-
<
|
|
872
|
-
<div className="
|
|
873
|
-
<
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
{
|
|
885
|
-
<
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
963
|
+
<Card className="interface-header-card">
|
|
964
|
+
<div className="interface-management-header">
|
|
965
|
+
<div className="feature-selector-container">
|
|
966
|
+
<div className="feature-selector">
|
|
967
|
+
<span>功能模块:</span>
|
|
968
|
+
<Select
|
|
969
|
+
value={selectedFeatureId}
|
|
970
|
+
onChange={handleSelectFeature}
|
|
971
|
+
style={{ width: 240 }}
|
|
972
|
+
placeholder="选择功能模块"
|
|
973
|
+
loading={featuresLoading}
|
|
974
|
+
dropdownMatchSelectWidth={false}
|
|
975
|
+
>
|
|
976
|
+
{(features || []).map(feature => (
|
|
977
|
+
<Option key={feature.id} value={feature.id}>
|
|
978
|
+
{feature.name}
|
|
979
|
+
{feature.active === false &&
|
|
980
|
+
<span style={{ color: '#ff4d4f', marginLeft: 8 }}>(已禁用)</span>
|
|
981
|
+
}
|
|
982
|
+
</Option>
|
|
983
|
+
))}
|
|
984
|
+
</Select>
|
|
985
|
+
</div>
|
|
986
|
+
|
|
987
|
+
<Search
|
|
988
|
+
placeholder="搜索接口名称或URL"
|
|
989
|
+
onSearch={handleSearch}
|
|
990
|
+
value={searchValue}
|
|
991
|
+
onChange={(e) => setSearchValue(e.target.value)}
|
|
992
|
+
style={{ width: 250, marginLeft: 16 }}
|
|
993
|
+
enterButton={<SearchOutlined />}
|
|
994
|
+
allowClear
|
|
995
|
+
/>
|
|
996
|
+
</div>
|
|
997
|
+
|
|
998
|
+
<div className="interface-actions">
|
|
999
|
+
<Space>
|
|
1000
|
+
<Tooltip title="刷新数据">
|
|
1001
|
+
<Button
|
|
1002
|
+
icon={<ReloadOutlined />}
|
|
1003
|
+
onClick={refreshData}
|
|
1004
|
+
loading={interfacesLoading}
|
|
1005
|
+
/>
|
|
1006
|
+
</Tooltip>
|
|
1007
|
+
<Tooltip title={viewMode === 'table' ? '切换到卡片视图' : '切换到表格视图'}>
|
|
1008
|
+
<Button
|
|
1009
|
+
icon={viewMode === 'table' ? <AppstoreOutlined /> : <UnorderedListOutlined />}
|
|
1010
|
+
onClick={toggleViewMode}
|
|
1011
|
+
/>
|
|
1012
|
+
</Tooltip>
|
|
1013
|
+
<Button
|
|
1014
|
+
type="primary"
|
|
1015
|
+
icon={<PlusOutlined />}
|
|
1016
|
+
onClick={handleAddInterface}
|
|
1017
|
+
disabled={!selectedFeatureId || selectedFeature?.active === false}
|
|
1018
|
+
className="add-interface-button"
|
|
1019
|
+
>
|
|
1020
|
+
添加接口
|
|
1021
|
+
</Button>
|
|
1022
|
+
</Space>
|
|
1023
|
+
</div>
|
|
890
1024
|
</div>
|
|
891
|
-
|
|
892
|
-
type="primary"
|
|
893
|
-
icon={<PlusOutlined />}
|
|
894
|
-
onClick={handleAddInterface}
|
|
895
|
-
disabled={!selectedFeatureId || selectedFeature?.active === false}
|
|
896
|
-
>
|
|
897
|
-
添加接口
|
|
898
|
-
</Button>
|
|
899
|
-
</div>
|
|
1025
|
+
</Card>
|
|
900
1026
|
|
|
901
1027
|
{!features.length && (
|
|
902
1028
|
<Alert
|
|
@@ -919,30 +1045,11 @@ const InterfaceManagement = () => {
|
|
|
919
1045
|
)}
|
|
920
1046
|
|
|
921
1047
|
{/* 分组筛选和操作区域 */}
|
|
922
|
-
<
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
padding: '12px 16px',
|
|
928
|
-
backgroundColor: '#f5f5f5',
|
|
929
|
-
borderRadius: '4px'
|
|
930
|
-
}}>
|
|
931
|
-
<div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
|
|
932
|
-
<span>分组筛选:</span>
|
|
933
|
-
<Select
|
|
934
|
-
value={selectedGroup}
|
|
935
|
-
onChange={value => setSelectedGroup(value)}
|
|
936
|
-
style={{ width: 200 }}
|
|
937
|
-
placeholder="选择分组"
|
|
938
|
-
allowClear
|
|
939
|
-
showSearch
|
|
940
|
-
loading={interfacesLoading}
|
|
941
|
-
>
|
|
942
|
-
{groups.map(group => (
|
|
943
|
-
<Option key={group} value={group}>{group}</Option>
|
|
944
|
-
))}
|
|
945
|
-
</Select>
|
|
1048
|
+
<Card className="filter-card" bordered={false}>
|
|
1049
|
+
<div className="filter-header">
|
|
1050
|
+
<div className="filter-title">
|
|
1051
|
+
<FilterOutlined /> 分组筛选
|
|
1052
|
+
</div>
|
|
946
1053
|
{selectedGroup && (
|
|
947
1054
|
<Button
|
|
948
1055
|
type="link"
|
|
@@ -954,8 +1061,27 @@ const InterfaceManagement = () => {
|
|
|
954
1061
|
)}
|
|
955
1062
|
</div>
|
|
956
1063
|
|
|
1064
|
+
<div className="group-tags-container">
|
|
1065
|
+
{groups.length === 0 ? (
|
|
1066
|
+
<div className="empty-groups">暂无分组</div>
|
|
1067
|
+
) : (
|
|
1068
|
+
<div className="group-tags">
|
|
1069
|
+
{groups.map(group => (
|
|
1070
|
+
<Tag
|
|
1071
|
+
key={group}
|
|
1072
|
+
color={selectedGroup === group ? "blue" : "default"}
|
|
1073
|
+
onClick={() => setSelectedGroup(group === selectedGroup ? null : group)}
|
|
1074
|
+
className={`group-tag ${selectedGroup === group ? 'active' : ''}`}
|
|
1075
|
+
>
|
|
1076
|
+
{group} ({interfaces.filter(item => item.group === group).length})
|
|
1077
|
+
</Tag>
|
|
1078
|
+
))}
|
|
1079
|
+
</div>
|
|
1080
|
+
)}
|
|
1081
|
+
</div>
|
|
1082
|
+
|
|
957
1083
|
{selectedGroup && (
|
|
958
|
-
<div
|
|
1084
|
+
<div className="group-actions">
|
|
959
1085
|
<Popconfirm
|
|
960
1086
|
title={`确定要启用分组 "${selectedGroup}" 中的所有接口吗?`}
|
|
961
1087
|
onConfirm={() => handleBatchToggleActive(true)}
|
|
@@ -987,40 +1113,60 @@ const InterfaceManagement = () => {
|
|
|
987
1113
|
</Popconfirm>
|
|
988
1114
|
</div>
|
|
989
1115
|
)}
|
|
990
|
-
</
|
|
1116
|
+
</Card>
|
|
991
1117
|
|
|
992
1118
|
{/* 接口列表状态栏 */}
|
|
993
|
-
{
|
|
994
|
-
<div
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1119
|
+
<Card className="list-header-card" bordered={false}>
|
|
1120
|
+
<div className="list-header">
|
|
1121
|
+
<div className="list-header-info">
|
|
1122
|
+
<div className="feature-info">
|
|
1123
|
+
<span className="label">功能模块:</span>
|
|
1124
|
+
<span className="value">{selectedFeature?.name}</span>
|
|
1125
|
+
{selectedGroup && (
|
|
1126
|
+
<Tag color="blue" className="group-badge">{selectedGroup}</Tag>
|
|
1127
|
+
)}
|
|
1128
|
+
</div>
|
|
1129
|
+
<div className="interface-stats">
|
|
1130
|
+
{searchValue ? (
|
|
1131
|
+
<Badge
|
|
1132
|
+
count={`搜索"${searchValue}" - ${filteredInterfaces.length}个结果`}
|
|
1133
|
+
style={{ backgroundColor: '#108ee9' }}
|
|
1134
|
+
/>
|
|
1135
|
+
) : (
|
|
1136
|
+
<Badge
|
|
1137
|
+
count={`共 ${filteredInterfaces.length} 个接口`}
|
|
1138
|
+
style={{ backgroundColor: '#52c41a' }}
|
|
1139
|
+
/>
|
|
1140
|
+
)}
|
|
1141
|
+
</div>
|
|
1142
|
+
</div>
|
|
1143
|
+
<div className="batch-actions">
|
|
1144
|
+
{selectedRowKeys.length > 0 && (
|
|
1145
|
+
<Space>
|
|
1146
|
+
<span className="selected-count">
|
|
1147
|
+
已选择 {selectedRowKeys.length} 项
|
|
1148
|
+
</span>
|
|
1149
|
+
<Popconfirm
|
|
1150
|
+
title="确定要批量启用选中的接口吗?"
|
|
1151
|
+
onConfirm={() => handleBatchOperation(true)}
|
|
1152
|
+
okText="确定"
|
|
1153
|
+
cancelText="取消"
|
|
1154
|
+
>
|
|
1155
|
+
<Button size="small" type="primary">批量启用</Button>
|
|
1156
|
+
</Popconfirm>
|
|
1157
|
+
<Popconfirm
|
|
1158
|
+
title="确定要批量禁用选中的接口吗?"
|
|
1159
|
+
onConfirm={() => handleBatchOperation(false)}
|
|
1160
|
+
okText="确定"
|
|
1161
|
+
cancelText="取消"
|
|
1162
|
+
>
|
|
1163
|
+
<Button size="small" danger>批量禁用</Button>
|
|
1164
|
+
</Popconfirm>
|
|
1165
|
+
</Space>
|
|
1020
1166
|
)}
|
|
1021
|
-
</
|
|
1167
|
+
</div>
|
|
1022
1168
|
</div>
|
|
1023
|
-
|
|
1169
|
+
</Card>
|
|
1024
1170
|
|
|
1025
1171
|
{/* 列配置区域 */}
|
|
1026
1172
|
<div style={{
|
|
@@ -1085,26 +1231,156 @@ const InterfaceManagement = () => {
|
|
|
1085
1231
|
</Popover>
|
|
1086
1232
|
</div>
|
|
1087
1233
|
|
|
1088
|
-
<
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1234
|
+
<Card className="list-container-card" bordered={false} bodyStyle={{ padding: 0 }}>
|
|
1235
|
+
{/* 表格视图 */}
|
|
1236
|
+
{viewMode === 'table' && (
|
|
1237
|
+
<div className="interface-list-container">
|
|
1238
|
+
<Table
|
|
1239
|
+
columns={columns}
|
|
1240
|
+
dataSource={filteredInterfaces}
|
|
1241
|
+
rowKey="id"
|
|
1242
|
+
loading={interfacesLoading}
|
|
1243
|
+
onChange={handleTableChange}
|
|
1244
|
+
rowSelection={{
|
|
1245
|
+
selectedRowKeys,
|
|
1246
|
+
onChange: onSelectChange,
|
|
1247
|
+
}}
|
|
1248
|
+
size="middle"
|
|
1249
|
+
pagination={{
|
|
1250
|
+
current: tableConfig.current,
|
|
1251
|
+
pageSize: tableConfig.pageSize,
|
|
1252
|
+
showSizeChanger: true,
|
|
1253
|
+
showQuickJumper: true,
|
|
1254
|
+
showTotal: (total, range) => `共 ${total} 个接口,显示第 ${range[0]}-${range[1]} 个`,
|
|
1255
|
+
pageSizeOptions: ['10', '20', '50', '100'],
|
|
1256
|
+
size: 'default'
|
|
1257
|
+
}}
|
|
1258
|
+
locale={{ emptyText: '暂无接口配置' }}
|
|
1259
|
+
sortDirections={['ascend', 'descend']}
|
|
1260
|
+
className="interface-table"
|
|
1261
|
+
/>
|
|
1262
|
+
</div>
|
|
1263
|
+
)}
|
|
1264
|
+
|
|
1265
|
+
{/* 卡片视图 */}
|
|
1266
|
+
{viewMode === 'card' && (
|
|
1267
|
+
<div className="interface-card-view">
|
|
1268
|
+
<Row gutter={[16, 16]} style={{ padding: 16 }}>
|
|
1269
|
+
{interfacesLoading ? (
|
|
1270
|
+
[1,2,3,4,5,6].map(i => (
|
|
1271
|
+
<Col xs={24} sm={12} md={8} lg={8} xl={6} key={`loading-${i}`}>
|
|
1272
|
+
<Card loading style={{ height: 180 }} />
|
|
1273
|
+
</Col>
|
|
1274
|
+
))
|
|
1275
|
+
) : filteredInterfaces.length === 0 ? (
|
|
1276
|
+
<Col span={24}>
|
|
1277
|
+
<div className="empty-data">暂无接口配置</div>
|
|
1278
|
+
</Col>
|
|
1279
|
+
) : (
|
|
1280
|
+
filteredInterfaces.map(item => (
|
|
1281
|
+
<Col xs={24} sm={12} md={8} lg={8} xl={6} key={item.id}>
|
|
1282
|
+
<Card
|
|
1283
|
+
className={`interface-card ${item.active === false ? 'inactive' : ''}`}
|
|
1284
|
+
hoverable
|
|
1285
|
+
>
|
|
1286
|
+
<div className="interface-card-header">
|
|
1287
|
+
<Switch
|
|
1288
|
+
checked={item.active !== false}
|
|
1289
|
+
onChange={() => handleToggleActive(item.id, item.active)}
|
|
1290
|
+
size="small"
|
|
1291
|
+
className="interface-card-switch"
|
|
1292
|
+
/>
|
|
1293
|
+
<div className="interface-card-title">{item.name}</div>
|
|
1294
|
+
{item.group && (
|
|
1295
|
+
<Tag
|
|
1296
|
+
color="blue"
|
|
1297
|
+
className="interface-card-group"
|
|
1298
|
+
onClick={() => setSelectedGroup(item.group)}
|
|
1299
|
+
>
|
|
1300
|
+
{item.group}
|
|
1301
|
+
</Tag>
|
|
1302
|
+
)}
|
|
1303
|
+
</div>
|
|
1304
|
+
|
|
1305
|
+
<div className="interface-card-content">
|
|
1306
|
+
<div className="url-pattern-container">
|
|
1307
|
+
<Tooltip title={item.urlPattern}>
|
|
1308
|
+
<div className="url-pattern">{item.urlPattern}</div>
|
|
1309
|
+
</Tooltip>
|
|
1310
|
+
</div>
|
|
1311
|
+
|
|
1312
|
+
<div className="interface-card-info">
|
|
1313
|
+
<div className="info-item">
|
|
1314
|
+
<span className="info-label">处理方式:</span>
|
|
1315
|
+
<Badge
|
|
1316
|
+
color={proxyTypes.find(t => t.value === item.proxyType)?.color || '#999'}
|
|
1317
|
+
text={proxyTypes.find(t => t.value === item.proxyType)?.label || item.proxyType}
|
|
1318
|
+
className="info-value"
|
|
1319
|
+
/>
|
|
1320
|
+
</div>
|
|
1321
|
+
<div className="info-item">
|
|
1322
|
+
<span className="info-label">状态码:</span>
|
|
1323
|
+
<span className={`status-badge status-${item.httpStatus >= 400 ? 'error' : item.httpStatus >= 300 ? 'warning' : 'success'}`}>
|
|
1324
|
+
{item.httpStatus || 200}
|
|
1325
|
+
</span>
|
|
1326
|
+
</div>
|
|
1327
|
+
</div>
|
|
1328
|
+
</div>
|
|
1329
|
+
|
|
1330
|
+
<div className="interface-card-actions">
|
|
1331
|
+
<Button
|
|
1332
|
+
type="primary"
|
|
1333
|
+
size="small"
|
|
1334
|
+
icon={<EditOutlined />}
|
|
1335
|
+
onClick={() => handleEditInterface(item)}
|
|
1336
|
+
>
|
|
1337
|
+
编辑
|
|
1338
|
+
</Button>
|
|
1339
|
+
<Popconfirm
|
|
1340
|
+
title="确定要删除此接口吗?"
|
|
1341
|
+
onConfirm={() => handleDeleteInterface(item.id)}
|
|
1342
|
+
okText="确定"
|
|
1343
|
+
cancelText="取消"
|
|
1344
|
+
>
|
|
1345
|
+
<Button
|
|
1346
|
+
danger
|
|
1347
|
+
size="small"
|
|
1348
|
+
icon={<DeleteOutlined />}
|
|
1349
|
+
>
|
|
1350
|
+
删除
|
|
1351
|
+
</Button>
|
|
1352
|
+
</Popconfirm>
|
|
1353
|
+
</div>
|
|
1354
|
+
</Card>
|
|
1355
|
+
</Col>
|
|
1356
|
+
))
|
|
1357
|
+
)}
|
|
1358
|
+
</Row>
|
|
1359
|
+
|
|
1360
|
+
{/* 卡片视图分页 */}
|
|
1361
|
+
{filteredInterfaces.length > 0 && (
|
|
1362
|
+
<div className="card-pagination">
|
|
1363
|
+
<Pagination
|
|
1364
|
+
current={tableConfig.current}
|
|
1365
|
+
pageSize={tableConfig.pageSize}
|
|
1366
|
+
total={filteredInterfaces.length}
|
|
1367
|
+
showSizeChanger
|
|
1368
|
+
showQuickJumper
|
|
1369
|
+
showTotal={(total, range) => `共 ${total} 个接口,显示第 ${range[0]}-${range[1]} 个`}
|
|
1370
|
+
pageSizeOptions={['10', '20', '50', '100']}
|
|
1371
|
+
size="default"
|
|
1372
|
+
onChange={(page, pageSize) => {
|
|
1373
|
+
saveTableConfig({
|
|
1374
|
+
current: page,
|
|
1375
|
+
pageSize: pageSize
|
|
1376
|
+
});
|
|
1377
|
+
}}
|
|
1378
|
+
/>
|
|
1379
|
+
</div>
|
|
1380
|
+
)}
|
|
1381
|
+
</div>
|
|
1382
|
+
)}
|
|
1383
|
+
</Card>
|
|
1108
1384
|
|
|
1109
1385
|
{/* 接口编辑/创建模态框 */}
|
|
1110
1386
|
<Modal
|
|
@@ -1157,6 +1433,24 @@ const InterfaceManagement = () => {
|
|
|
1157
1433
|
placeholder="选择或创建分组"
|
|
1158
1434
|
showSearch
|
|
1159
1435
|
allowClear
|
|
1436
|
+
style={{ width: '100%' }}
|
|
1437
|
+
mode="tags"
|
|
1438
|
+
maxTagCount="responsive"
|
|
1439
|
+
maxTagTextLength={10}
|
|
1440
|
+
onChange={(value) => {
|
|
1441
|
+
// 如果值是数组(tags模式),只取第一个元素
|
|
1442
|
+
if (Array.isArray(value) && value.length > 0) {
|
|
1443
|
+
// 如果第一个元素是空字符串,则设置为undefined
|
|
1444
|
+
const groupValue = value[0] === '' ? undefined : value[0];
|
|
1445
|
+
form.setFieldsValue({ group: groupValue });
|
|
1446
|
+
}
|
|
1447
|
+
}}
|
|
1448
|
+
onInputKeyDown={(e) => {
|
|
1449
|
+
// 防止回车键提交表单
|
|
1450
|
+
if (e.key === 'Enter') {
|
|
1451
|
+
e.stopPropagation();
|
|
1452
|
+
}
|
|
1453
|
+
}}
|
|
1160
1454
|
>
|
|
1161
1455
|
{Array.from(new Set(interfaces.map(item => item.group).filter(Boolean))).map(group => (
|
|
1162
1456
|
<Option key={group} value={group}>{group}</Option>
|