whistle.mock-plugins 1.1.2 → 1.1.3-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.
- package/VERSION.md +0 -4
- package/app/cgi-bin/interfaces.js +5 -42
- package/app/public/js/index.js +1 -1
- package/app/public/js/index.js.LICENSE.txt +4 -6
- package/lib/ui-server.js +1 -15
- package/package.json +1 -1
- package/ui/src/index.js +24 -4
- package/ui/src/pages/InterfaceManagement.js +2 -2
- package/ui/src/pages/InterfaceManagementV2.js +334 -0
- package/ui/src/pages/MockDataV2.js +702 -0
- package/ui/src/pages/Settings.js +79 -157
- package/ui/src/del_App.js +0 -24
- package/ui/src/pages/del_FileProxy.js +0 -235
- package/ui/src/pages/del_FilesPage.js +0 -292
- package/ui/src/pages/del_Home.js +0 -128
- package/ui/src/pages/del_HomePage.js +0 -195
- package/ui/src/pages/del_InterfacePage.js +0 -342
- package/ui/src/pages/del_SettingsPage.js +0 -165
- package/ui/src/pages/del_UrlRedirect.js +0 -225
- package/ui/src/pages/del_Welcome.js +0 -155
- package/ui/src/styles/del_feature-card.css +0 -168
- package/ui/src/styles/del_file-proxy.css +0 -301
- package/ui/src/styles/del_home.css +0 -123
- package/ui/src/styles/del_index.css +0 -419
- package/ui/src/styles/del_layout.css +0 -159
- package/ui/src/styles/del_logs-page.css +0 -427
- package/ui/src/styles/del_main-nav.css +0 -52
- package/ui/src/styles/del_page-container.css +0 -11
- package/ui/src/styles/del_url-redirect.css +0 -279
- package/ui/src/styles/del_welcome.css +0 -282
|
@@ -1,292 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { Card, Table, Button, Space, Input, Tree, Modal, message, Breadcrumb, Typography } from 'antd';
|
|
3
|
-
import {
|
|
4
|
-
FileOutlined,
|
|
5
|
-
FolderOutlined,
|
|
6
|
-
PlusOutlined,
|
|
7
|
-
EditOutlined,
|
|
8
|
-
DeleteOutlined,
|
|
9
|
-
SaveOutlined,
|
|
10
|
-
UploadOutlined,
|
|
11
|
-
ExclamationCircleOutlined,
|
|
12
|
-
FolderAddOutlined
|
|
13
|
-
} from '@ant-design/icons';
|
|
14
|
-
import { ProxyAPI } from '../services/api';
|
|
15
|
-
|
|
16
|
-
const { Text } = Typography;
|
|
17
|
-
const { DirectoryTree } = Tree;
|
|
18
|
-
const { confirm } = Modal;
|
|
19
|
-
const { TextArea } = Input;
|
|
20
|
-
|
|
21
|
-
const FilesPage = () => {
|
|
22
|
-
const [files, setFiles] = useState([]);
|
|
23
|
-
const [loading, setLoading] = useState(false);
|
|
24
|
-
const [currentPath, setCurrentPath] = useState('');
|
|
25
|
-
const [breadcrumbs, setBreadcrumbs] = useState([{ title: '根目录', path: '' }]);
|
|
26
|
-
const [fileContent, setFileContent] = useState('');
|
|
27
|
-
const [currentFile, setCurrentFile] = useState(null);
|
|
28
|
-
const [fileModalVisible, setFileModalVisible] = useState(false);
|
|
29
|
-
const [folderModalVisible, setFolderModalVisible] = useState(false);
|
|
30
|
-
const [newFolderName, setNewFolderName] = useState('');
|
|
31
|
-
|
|
32
|
-
useEffect(() => {
|
|
33
|
-
fetchFiles();
|
|
34
|
-
}, [currentPath]);
|
|
35
|
-
|
|
36
|
-
const fetchFiles = async () => {
|
|
37
|
-
setLoading(true);
|
|
38
|
-
try {
|
|
39
|
-
const data = await ProxyAPI.listFiles(currentPath);
|
|
40
|
-
setFiles(data);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
console.error("获取文件列表失败:", error);
|
|
43
|
-
message.error('获取文件列表失败');
|
|
44
|
-
} finally {
|
|
45
|
-
setLoading(false);
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
const handleOpenFile = async (file) => {
|
|
50
|
-
if (file.isDirectory) {
|
|
51
|
-
navigateToFolder(file.path);
|
|
52
|
-
} else {
|
|
53
|
-
try {
|
|
54
|
-
setLoading(true);
|
|
55
|
-
const data = await ProxyAPI.getFile(file.path);
|
|
56
|
-
setCurrentFile(file);
|
|
57
|
-
setFileContent(data.content);
|
|
58
|
-
setFileModalVisible(true);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error("获取文件内容失败:", error);
|
|
61
|
-
message.error('获取文件内容失败');
|
|
62
|
-
} finally {
|
|
63
|
-
setLoading(false);
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
const navigateToFolder = (path) => {
|
|
69
|
-
setCurrentPath(path);
|
|
70
|
-
|
|
71
|
-
// 更新面包屑
|
|
72
|
-
const parts = path.split('/').filter(Boolean);
|
|
73
|
-
const crumbs = [{ title: '根目录', path: '' }];
|
|
74
|
-
|
|
75
|
-
let currentPathSegment = '';
|
|
76
|
-
parts.forEach(part => {
|
|
77
|
-
currentPathSegment += currentPathSegment ? `/${part}` : part;
|
|
78
|
-
crumbs.push({
|
|
79
|
-
title: part,
|
|
80
|
-
path: currentPathSegment
|
|
81
|
-
});
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
setBreadcrumbs(crumbs);
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
const handleBreadcrumbClick = (path) => {
|
|
88
|
-
navigateToFolder(path);
|
|
89
|
-
};
|
|
90
|
-
|
|
91
|
-
const handleSaveFile = async () => {
|
|
92
|
-
try {
|
|
93
|
-
setLoading(true);
|
|
94
|
-
await ProxyAPI.saveFile(currentFile.path, fileContent);
|
|
95
|
-
setFileModalVisible(false);
|
|
96
|
-
message.success('文件保存成功');
|
|
97
|
-
fetchFiles();
|
|
98
|
-
} catch (error) {
|
|
99
|
-
console.error("保存文件失败:", error);
|
|
100
|
-
message.error('保存文件失败');
|
|
101
|
-
} finally {
|
|
102
|
-
setLoading(false);
|
|
103
|
-
}
|
|
104
|
-
};
|
|
105
|
-
|
|
106
|
-
const handleDeleteFile = (file) => {
|
|
107
|
-
confirm({
|
|
108
|
-
title: '确认删除',
|
|
109
|
-
icon: <ExclamationCircleOutlined />,
|
|
110
|
-
content: `确定要删除${file.isDirectory ? '文件夹' : '文件'} "${file.name}" 吗?`,
|
|
111
|
-
okText: '确认',
|
|
112
|
-
cancelText: '取消',
|
|
113
|
-
onOk: async () => {
|
|
114
|
-
try {
|
|
115
|
-
await ProxyAPI.deleteFile(file.path);
|
|
116
|
-
message.success('删除成功');
|
|
117
|
-
fetchFiles();
|
|
118
|
-
} catch (error) {
|
|
119
|
-
console.error("删除失败:", error);
|
|
120
|
-
message.error('删除失败');
|
|
121
|
-
}
|
|
122
|
-
},
|
|
123
|
-
});
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
const handleCreateFile = async () => {
|
|
127
|
-
const newFileName = `new-file-${Date.now()}.json`;
|
|
128
|
-
const newFilePath = currentPath ? `${currentPath}/${newFileName}` : newFileName;
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
await ProxyAPI.saveFile(newFilePath, '{\n "data": "新文件内容"\n}');
|
|
132
|
-
message.success('文件创建成功');
|
|
133
|
-
fetchFiles();
|
|
134
|
-
|
|
135
|
-
// 创建后立即打开编辑
|
|
136
|
-
const newFile = { path: newFilePath, name: newFileName, isDirectory: false };
|
|
137
|
-
handleOpenFile(newFile);
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error("创建文件失败:", error);
|
|
140
|
-
message.error('创建文件失败');
|
|
141
|
-
}
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
const handleCreateFolder = () => {
|
|
145
|
-
setNewFolderName('');
|
|
146
|
-
setFolderModalVisible(true);
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
const handleFolderModalOk = async () => {
|
|
150
|
-
if (!newFolderName.trim()) {
|
|
151
|
-
message.error('请输入文件夹名称');
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const newFolderPath = currentPath ? `${currentPath}/${newFolderName}` : newFolderName;
|
|
156
|
-
|
|
157
|
-
try {
|
|
158
|
-
// 通过创建一个占位文件来创建文件夹
|
|
159
|
-
await ProxyAPI.saveFile(`${newFolderPath}/.placeholder`, '');
|
|
160
|
-
setFolderModalVisible(false);
|
|
161
|
-
message.success('文件夹创建成功');
|
|
162
|
-
fetchFiles();
|
|
163
|
-
} catch (error) {
|
|
164
|
-
console.error("创建文件夹失败:", error);
|
|
165
|
-
message.error('创建文件夹失败');
|
|
166
|
-
}
|
|
167
|
-
};
|
|
168
|
-
|
|
169
|
-
const columns = [
|
|
170
|
-
{
|
|
171
|
-
title: '名称',
|
|
172
|
-
dataIndex: 'name',
|
|
173
|
-
key: 'name',
|
|
174
|
-
render: (text, record) => (
|
|
175
|
-
<Space>
|
|
176
|
-
{record.isDirectory ? <FolderOutlined /> : <FileOutlined />}
|
|
177
|
-
<a onClick={() => handleOpenFile(record)}>{text}</a>
|
|
178
|
-
</Space>
|
|
179
|
-
),
|
|
180
|
-
},
|
|
181
|
-
{
|
|
182
|
-
title: '类型',
|
|
183
|
-
dataIndex: 'isDirectory',
|
|
184
|
-
key: 'type',
|
|
185
|
-
width: 100,
|
|
186
|
-
render: (isDirectory) => isDirectory ? '文件夹' : '文件',
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
title: '大小',
|
|
190
|
-
dataIndex: 'size',
|
|
191
|
-
key: 'size',
|
|
192
|
-
width: 120,
|
|
193
|
-
render: (size, record) => record.isDirectory ? '-' : `${size} 字节`,
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
title: '修改时间',
|
|
197
|
-
dataIndex: 'mtime',
|
|
198
|
-
key: 'mtime',
|
|
199
|
-
width: 200,
|
|
200
|
-
render: (text) => text ? new Date(text).toLocaleString() : '-',
|
|
201
|
-
},
|
|
202
|
-
{
|
|
203
|
-
title: '操作',
|
|
204
|
-
key: 'action',
|
|
205
|
-
width: 120,
|
|
206
|
-
render: (_, record) => (
|
|
207
|
-
<Button type="text" danger icon={<DeleteOutlined />} onClick={() => handleDeleteFile(record)}>
|
|
208
|
-
删除
|
|
209
|
-
</Button>
|
|
210
|
-
),
|
|
211
|
-
},
|
|
212
|
-
];
|
|
213
|
-
|
|
214
|
-
return (
|
|
215
|
-
<div className="files-page">
|
|
216
|
-
<Card
|
|
217
|
-
title="文件管理"
|
|
218
|
-
extra={
|
|
219
|
-
<Space>
|
|
220
|
-
<Button type="primary" icon={<PlusOutlined />} onClick={handleCreateFile}>
|
|
221
|
-
新建文件
|
|
222
|
-
</Button>
|
|
223
|
-
<Button icon={<FolderAddOutlined />} onClick={handleCreateFolder}>
|
|
224
|
-
新建文件夹
|
|
225
|
-
</Button>
|
|
226
|
-
</Space>
|
|
227
|
-
}
|
|
228
|
-
>
|
|
229
|
-
<Breadcrumb style={{ marginBottom: '16px' }}>
|
|
230
|
-
{breadcrumbs.map((crumb, index) => (
|
|
231
|
-
<Breadcrumb.Item key={index}>
|
|
232
|
-
<a onClick={() => handleBreadcrumbClick(crumb.path)}>{crumb.title}</a>
|
|
233
|
-
</Breadcrumb.Item>
|
|
234
|
-
))}
|
|
235
|
-
</Breadcrumb>
|
|
236
|
-
|
|
237
|
-
<Table
|
|
238
|
-
columns={columns}
|
|
239
|
-
dataSource={files}
|
|
240
|
-
rowKey="path"
|
|
241
|
-
loading={loading}
|
|
242
|
-
pagination={false}
|
|
243
|
-
/>
|
|
244
|
-
</Card>
|
|
245
|
-
|
|
246
|
-
<Modal
|
|
247
|
-
title={currentFile?.name || '文件内容'}
|
|
248
|
-
open={fileModalVisible}
|
|
249
|
-
onCancel={() => setFileModalVisible(false)}
|
|
250
|
-
footer={[
|
|
251
|
-
<Button key="cancel" onClick={() => setFileModalVisible(false)}>
|
|
252
|
-
取消
|
|
253
|
-
</Button>,
|
|
254
|
-
<Button
|
|
255
|
-
key="save"
|
|
256
|
-
type="primary"
|
|
257
|
-
icon={<SaveOutlined />}
|
|
258
|
-
onClick={handleSaveFile}
|
|
259
|
-
loading={loading}
|
|
260
|
-
>
|
|
261
|
-
保存
|
|
262
|
-
</Button>,
|
|
263
|
-
]}
|
|
264
|
-
width={800}
|
|
265
|
-
>
|
|
266
|
-
<TextArea
|
|
267
|
-
value={fileContent}
|
|
268
|
-
onChange={(e) => setFileContent(e.target.value)}
|
|
269
|
-
autoSize={{ minRows: 20, maxRows: 30 }}
|
|
270
|
-
/>
|
|
271
|
-
</Modal>
|
|
272
|
-
|
|
273
|
-
<Modal
|
|
274
|
-
title="新建文件夹"
|
|
275
|
-
open={folderModalVisible}
|
|
276
|
-
onOk={handleFolderModalOk}
|
|
277
|
-
onCancel={() => setFolderModalVisible(false)}
|
|
278
|
-
okText="创建"
|
|
279
|
-
cancelText="取消"
|
|
280
|
-
>
|
|
281
|
-
<Input
|
|
282
|
-
placeholder="请输入文件夹名称"
|
|
283
|
-
value={newFolderName}
|
|
284
|
-
onChange={(e) => setNewFolderName(e.target.value)}
|
|
285
|
-
onPressEnter={handleFolderModalOk}
|
|
286
|
-
/>
|
|
287
|
-
</Modal>
|
|
288
|
-
</div>
|
|
289
|
-
);
|
|
290
|
-
};
|
|
291
|
-
|
|
292
|
-
export default FilesPage;
|
package/ui/src/pages/del_Home.js
DELETED
|
@@ -1,128 +0,0 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import Layout from '../components/Layout';
|
|
3
|
-
import FeatureCard from '../components/FeatureCard';
|
|
4
|
-
import '../styles/home.css';
|
|
5
|
-
|
|
6
|
-
const Home = () => {
|
|
7
|
-
const [viewMode, setViewMode] = useState('cards'); // 'cards' or 'list'
|
|
8
|
-
const [sortOption, setSortOption] = useState('name'); // 'name', 'status', 'date'
|
|
9
|
-
|
|
10
|
-
const [features, setFeatures] = useState([
|
|
11
|
-
{
|
|
12
|
-
id: 1,
|
|
13
|
-
name: '用户数据',
|
|
14
|
-
description: '模拟用户信息相关接口',
|
|
15
|
-
active: true,
|
|
16
|
-
interfaceCount: 3,
|
|
17
|
-
createdAt: '2023-09-15'
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
id: 2,
|
|
21
|
-
name: '订单信息',
|
|
22
|
-
description: '模拟订单相关接口',
|
|
23
|
-
active: false,
|
|
24
|
-
interfaceCount: 2,
|
|
25
|
-
createdAt: '2023-08-20'
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
id: 3,
|
|
29
|
-
name: '商品列表',
|
|
30
|
-
description: '模拟商品数据接口',
|
|
31
|
-
active: true,
|
|
32
|
-
interfaceCount: 5,
|
|
33
|
-
createdAt: '2023-10-01'
|
|
34
|
-
}
|
|
35
|
-
]);
|
|
36
|
-
|
|
37
|
-
// 根据排序选项排序功能
|
|
38
|
-
const sortedFeatures = [...features].sort((a, b) => {
|
|
39
|
-
if (sortOption === 'name') {
|
|
40
|
-
return a.name.localeCompare(b.name);
|
|
41
|
-
} else if (sortOption === 'status') {
|
|
42
|
-
return (b.active ? 1 : 0) - (a.active ? 1 : 0);
|
|
43
|
-
} else if (sortOption === 'date') {
|
|
44
|
-
return new Date(b.createdAt) - new Date(a.createdAt);
|
|
45
|
-
}
|
|
46
|
-
return 0;
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
return (
|
|
50
|
-
<Layout>
|
|
51
|
-
<div className="content-header">
|
|
52
|
-
<div className="content-title">功能列表</div>
|
|
53
|
-
<div className="view-controls">
|
|
54
|
-
<span>视图:</span>
|
|
55
|
-
<div className="view-toggle">
|
|
56
|
-
<button
|
|
57
|
-
className={viewMode === 'cards' ? 'active' : ''}
|
|
58
|
-
onClick={() => setViewMode('cards')}
|
|
59
|
-
>
|
|
60
|
-
卡片
|
|
61
|
-
</button>
|
|
62
|
-
<button
|
|
63
|
-
className={viewMode === 'list' ? 'active' : ''}
|
|
64
|
-
onClick={() => setViewMode('list')}
|
|
65
|
-
>
|
|
66
|
-
列表
|
|
67
|
-
</button>
|
|
68
|
-
</div>
|
|
69
|
-
<div className="sort-control">
|
|
70
|
-
<span>排序:</span>
|
|
71
|
-
<select
|
|
72
|
-
value={sortOption}
|
|
73
|
-
onChange={(e) => setSortOption(e.target.value)}
|
|
74
|
-
>
|
|
75
|
-
<option value="name">名称</option>
|
|
76
|
-
<option value="status">状态</option>
|
|
77
|
-
<option value="date">创建日期</option>
|
|
78
|
-
</select>
|
|
79
|
-
</div>
|
|
80
|
-
</div>
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
{viewMode === 'cards' ? (
|
|
84
|
-
<div className="feature-cards">
|
|
85
|
-
{sortedFeatures.map(feature => (
|
|
86
|
-
<FeatureCard key={feature.id} feature={feature} />
|
|
87
|
-
))}
|
|
88
|
-
</div>
|
|
89
|
-
) : (
|
|
90
|
-
<div className="feature-list-view">
|
|
91
|
-
<table>
|
|
92
|
-
<thead>
|
|
93
|
-
<tr>
|
|
94
|
-
<th>名称</th>
|
|
95
|
-
<th>描述</th>
|
|
96
|
-
<th>状态</th>
|
|
97
|
-
<th>接口数量</th>
|
|
98
|
-
<th>创建日期</th>
|
|
99
|
-
<th>操作</th>
|
|
100
|
-
</tr>
|
|
101
|
-
</thead>
|
|
102
|
-
<tbody>
|
|
103
|
-
{sortedFeatures.map(feature => (
|
|
104
|
-
<tr key={feature.id}>
|
|
105
|
-
<td>{feature.name}</td>
|
|
106
|
-
<td>{feature.description}</td>
|
|
107
|
-
<td>
|
|
108
|
-
<span className={`status-badge ${feature.active ? 'active' : 'inactive'}`}>
|
|
109
|
-
{feature.active ? '已启用' : '未启用'}
|
|
110
|
-
</span>
|
|
111
|
-
</td>
|
|
112
|
-
<td>{feature.interfaceCount}</td>
|
|
113
|
-
<td>{feature.createdAt}</td>
|
|
114
|
-
<td>
|
|
115
|
-
<button className="action-btn edit">编辑</button>
|
|
116
|
-
<button className="action-btn">管理接口</button>
|
|
117
|
-
</td>
|
|
118
|
-
</tr>
|
|
119
|
-
))}
|
|
120
|
-
</tbody>
|
|
121
|
-
</table>
|
|
122
|
-
</div>
|
|
123
|
-
)}
|
|
124
|
-
</Layout>
|
|
125
|
-
);
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
export default Home;
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import React, { useState, useEffect } from 'react';
|
|
2
|
-
import { Card, Table, Button, Space, Typography, Statistic, Row, Col, Modal, Form, Input } from 'antd';
|
|
3
|
-
import { PlusOutlined, EditOutlined, DeleteOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
|
4
|
-
import { FeatureAPI } from '../services/api';
|
|
5
|
-
|
|
6
|
-
const { Title, Paragraph } = Typography;
|
|
7
|
-
const { confirm } = Modal;
|
|
8
|
-
|
|
9
|
-
const HomePage = () => {
|
|
10
|
-
const [features, setFeatures] = useState([]);
|
|
11
|
-
const [loading, setLoading] = useState(false);
|
|
12
|
-
const [modalVisible, setModalVisible] = useState(false);
|
|
13
|
-
const [form] = Form.useForm();
|
|
14
|
-
const [editFeatureId, setEditFeatureId] = useState(null);
|
|
15
|
-
|
|
16
|
-
useEffect(() => {
|
|
17
|
-
fetchFeatures();
|
|
18
|
-
}, []);
|
|
19
|
-
|
|
20
|
-
const fetchFeatures = async () => {
|
|
21
|
-
setLoading(true);
|
|
22
|
-
try {
|
|
23
|
-
const data = await FeatureAPI.getAllFeatures();
|
|
24
|
-
setFeatures(data);
|
|
25
|
-
} catch (error) {
|
|
26
|
-
console.error("获取功能列表失败:", error);
|
|
27
|
-
// 在实际项目中应添加错误提示
|
|
28
|
-
} finally {
|
|
29
|
-
setLoading(false);
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const handleAdd = () => {
|
|
34
|
-
form.resetFields();
|
|
35
|
-
setEditFeatureId(null);
|
|
36
|
-
setModalVisible(true);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const handleEdit = (record) => {
|
|
40
|
-
form.setFieldsValue({
|
|
41
|
-
name: record.name,
|
|
42
|
-
description: record.description,
|
|
43
|
-
});
|
|
44
|
-
setEditFeatureId(record.id);
|
|
45
|
-
setModalVisible(true);
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
const handleDelete = (record) => {
|
|
49
|
-
confirm({
|
|
50
|
-
title: '确认删除',
|
|
51
|
-
icon: <ExclamationCircleOutlined />,
|
|
52
|
-
content: `确定要删除功能 "${record.name}" 吗?`,
|
|
53
|
-
okText: '确认',
|
|
54
|
-
cancelText: '取消',
|
|
55
|
-
onOk: async () => {
|
|
56
|
-
try {
|
|
57
|
-
await FeatureAPI.deleteFeature(record.id);
|
|
58
|
-
fetchFeatures();
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error("删除功能失败:", error);
|
|
61
|
-
}
|
|
62
|
-
},
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
const handleCancel = () => {
|
|
67
|
-
setModalVisible(false);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
const handleOk = async () => {
|
|
71
|
-
try {
|
|
72
|
-
const values = await form.validateFields();
|
|
73
|
-
if (editFeatureId) {
|
|
74
|
-
await FeatureAPI.updateFeature(editFeatureId, values);
|
|
75
|
-
} else {
|
|
76
|
-
await FeatureAPI.createFeature(values);
|
|
77
|
-
}
|
|
78
|
-
setModalVisible(false);
|
|
79
|
-
fetchFeatures();
|
|
80
|
-
} catch (error) {
|
|
81
|
-
console.error("保存功能失败:", error);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
|
|
85
|
-
const columns = [
|
|
86
|
-
{
|
|
87
|
-
title: '名称',
|
|
88
|
-
dataIndex: 'name',
|
|
89
|
-
key: 'name',
|
|
90
|
-
},
|
|
91
|
-
{
|
|
92
|
-
title: '描述',
|
|
93
|
-
dataIndex: 'description',
|
|
94
|
-
key: 'description',
|
|
95
|
-
ellipsis: true,
|
|
96
|
-
},
|
|
97
|
-
{
|
|
98
|
-
title: '接口数量',
|
|
99
|
-
dataIndex: 'interfaceCount',
|
|
100
|
-
key: 'interfaceCount',
|
|
101
|
-
},
|
|
102
|
-
{
|
|
103
|
-
title: '创建时间',
|
|
104
|
-
dataIndex: 'createdAt',
|
|
105
|
-
key: 'createdAt',
|
|
106
|
-
render: (text) => text ? new Date(text).toLocaleString() : '-',
|
|
107
|
-
},
|
|
108
|
-
{
|
|
109
|
-
title: '操作',
|
|
110
|
-
key: 'action',
|
|
111
|
-
render: (_, record) => (
|
|
112
|
-
<Space size="middle">
|
|
113
|
-
<Button type="text" icon={<EditOutlined />} onClick={() => handleEdit(record)}>
|
|
114
|
-
编辑
|
|
115
|
-
</Button>
|
|
116
|
-
<Button type="text" danger icon={<DeleteOutlined />} onClick={() => handleDelete(record)}>
|
|
117
|
-
删除
|
|
118
|
-
</Button>
|
|
119
|
-
</Space>
|
|
120
|
-
),
|
|
121
|
-
},
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
return (
|
|
125
|
-
<div className="home-page">
|
|
126
|
-
<Row gutter={16} style={{ marginBottom: '24px' }}>
|
|
127
|
-
<Col span={8}>
|
|
128
|
-
<Card>
|
|
129
|
-
<Statistic title="功能总数" value={features.length} />
|
|
130
|
-
</Card>
|
|
131
|
-
</Col>
|
|
132
|
-
<Col span={8}>
|
|
133
|
-
<Card>
|
|
134
|
-
<Statistic
|
|
135
|
-
title="接口总数"
|
|
136
|
-
value={features.reduce((sum, feature) => sum + (feature.interfaceCount || 0), 0)}
|
|
137
|
-
/>
|
|
138
|
-
</Card>
|
|
139
|
-
</Col>
|
|
140
|
-
<Col span={8}>
|
|
141
|
-
<Card>
|
|
142
|
-
<Statistic title="今日请求次数" value={0} />
|
|
143
|
-
</Card>
|
|
144
|
-
</Col>
|
|
145
|
-
</Row>
|
|
146
|
-
|
|
147
|
-
<Card
|
|
148
|
-
title="功能列表"
|
|
149
|
-
extra={
|
|
150
|
-
<Button type="primary" icon={<PlusOutlined />} onClick={handleAdd}>
|
|
151
|
-
添加功能
|
|
152
|
-
</Button>
|
|
153
|
-
}
|
|
154
|
-
>
|
|
155
|
-
<Table
|
|
156
|
-
columns={columns}
|
|
157
|
-
dataSource={features}
|
|
158
|
-
rowKey="id"
|
|
159
|
-
loading={loading}
|
|
160
|
-
/>
|
|
161
|
-
</Card>
|
|
162
|
-
|
|
163
|
-
<Modal
|
|
164
|
-
title={editFeatureId ? "编辑功能" : "添加功能"}
|
|
165
|
-
open={modalVisible}
|
|
166
|
-
onOk={handleOk}
|
|
167
|
-
onCancel={handleCancel}
|
|
168
|
-
okText="保存"
|
|
169
|
-
cancelText="取消"
|
|
170
|
-
>
|
|
171
|
-
<Form
|
|
172
|
-
form={form}
|
|
173
|
-
layout="vertical"
|
|
174
|
-
initialValues={{ name: '', description: '' }}
|
|
175
|
-
>
|
|
176
|
-
<Form.Item
|
|
177
|
-
name="name"
|
|
178
|
-
label="功能名称"
|
|
179
|
-
rules={[{ required: true, message: '请输入功能名称!' }]}
|
|
180
|
-
>
|
|
181
|
-
<Input placeholder="请输入功能名称" />
|
|
182
|
-
</Form.Item>
|
|
183
|
-
<Form.Item
|
|
184
|
-
name="description"
|
|
185
|
-
label="功能描述"
|
|
186
|
-
>
|
|
187
|
-
<Input.TextArea placeholder="请输入功能描述" rows={4} />
|
|
188
|
-
</Form.Item>
|
|
189
|
-
</Form>
|
|
190
|
-
</Modal>
|
|
191
|
-
</div>
|
|
192
|
-
);
|
|
193
|
-
};
|
|
194
|
-
|
|
195
|
-
export default HomePage;
|