whistle.mock-plugins 1.2.2-beta.23 → 1.2.2-beta.25
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 +42 -2
- package/app/public/index.html +1 -1
- package/app/public/js/index.js +1 -1
- package/package.json +1 -1
- package/ui/src/App.css +1 -1
- package/ui/src/components/AppLayout.js +173 -78
- package/ui/src/components/FeatureCard.js +1 -1
- package/ui/src/components/interface-management/InterfaceTestModal.js +1 -1
- package/ui/src/components/interface-management/PreviewModal.js +4 -6
- package/ui/src/components/interface-management/ResponseContentEditor.js +7 -10
- package/ui/src/index.html +5 -2
- package/ui/src/index.js +5 -2
- package/ui/src/pages/InterfaceManagementV2.js +7 -9
- package/ui/src/pages/MockDataV2.js +4 -4
- package/ui/src/pages/Settings.js +39 -1
- package/ui/src/styles/cyber-theme.css +1234 -0
package/package.json
CHANGED
package/ui/src/App.css
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
2
|
-
import { Layout, Menu, Button, Breadcrumb,
|
|
1
|
+
import React, { useState, useMemo, useEffect } from 'react';
|
|
2
|
+
import { Layout, Menu, Button, Breadcrumb, Space, ConfigProvider, theme } from 'antd';
|
|
3
3
|
import {
|
|
4
4
|
AppstoreOutlined,
|
|
5
5
|
ApiOutlined,
|
|
@@ -14,14 +14,67 @@ import VersionModal from './VersionModal';
|
|
|
14
14
|
import '../styles/app-layout.css';
|
|
15
15
|
|
|
16
16
|
const { Header, Content, Sider } = Layout;
|
|
17
|
-
const { Title, Text } = Typography;
|
|
18
17
|
|
|
19
18
|
const AppLayout = ({ children }) => {
|
|
20
19
|
const location = useLocation();
|
|
21
20
|
const currentPath = location.pathname;
|
|
22
21
|
const [collapsed, setCollapsed] = useState(false);
|
|
23
22
|
const [versionModalVisible, setVersionModalVisible] = useState(false);
|
|
24
|
-
|
|
23
|
+
|
|
24
|
+
// 赛博朋克主题开关
|
|
25
|
+
const [enableCyber, setEnableCyber] = useState(() => {
|
|
26
|
+
return localStorage.getItem('cyberTheme') !== 'false';
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
document.body.classList.toggle('cyber-theme', enableCyber);
|
|
31
|
+
}, [enableCyber]);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const handleStorage = () => {
|
|
35
|
+
setEnableCyber(localStorage.getItem('cyberTheme') !== 'false');
|
|
36
|
+
};
|
|
37
|
+
window.addEventListener('storage', handleStorage);
|
|
38
|
+
return () => window.removeEventListener('storage', handleStorage);
|
|
39
|
+
}, []);
|
|
40
|
+
|
|
41
|
+
const cyberTheme = useMemo(() => ({
|
|
42
|
+
token: {
|
|
43
|
+
colorPrimary: '#00f0ff',
|
|
44
|
+
colorPrimaryHover: '#33f3ff',
|
|
45
|
+
colorPrimaryActive: '#00c2cc',
|
|
46
|
+
colorSuccess: '#00ff41',
|
|
47
|
+
colorWarning: '#f0e800',
|
|
48
|
+
colorError: '#ff4d4f',
|
|
49
|
+
colorInfo: '#00f0ff',
|
|
50
|
+
colorText: '#e0e0e0',
|
|
51
|
+
colorTextSecondary: '#a0a0a0',
|
|
52
|
+
colorTextTertiary: '#6b7280',
|
|
53
|
+
colorBorder: '#1a1a2e',
|
|
54
|
+
colorBorderSecondary: '#1a1a2e',
|
|
55
|
+
colorBgBase: '#050508',
|
|
56
|
+
colorBgContainer: '#0f0f1a',
|
|
57
|
+
colorBgElevated: '#141420',
|
|
58
|
+
colorBgLayout: '#0a0a12',
|
|
59
|
+
colorBgSpotlight: '#141420',
|
|
60
|
+
borderRadius: 6,
|
|
61
|
+
borderRadiusSM: 4,
|
|
62
|
+
borderRadiusLG: 8,
|
|
63
|
+
wireframe: false,
|
|
64
|
+
fontFamily: "'Noto Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
65
|
+
fontFamilyCode: "'Share Tech Mono', 'SFMono-Regular', Consolas, monospace",
|
|
66
|
+
},
|
|
67
|
+
algorithm: theme.darkAlgorithm,
|
|
68
|
+
}), []);
|
|
69
|
+
|
|
70
|
+
const lightTheme = useMemo(() => ({
|
|
71
|
+
token: {
|
|
72
|
+
borderRadius: 6,
|
|
73
|
+
borderRadiusSM: 4,
|
|
74
|
+
borderRadiusLG: 8,
|
|
75
|
+
},
|
|
76
|
+
algorithm: theme.defaultAlgorithm,
|
|
77
|
+
}), []);
|
|
25
78
|
|
|
26
79
|
// 版本弹窗控制方法
|
|
27
80
|
const showVersionModal = () => setVersionModalVisible(true);
|
|
@@ -83,88 +136,130 @@ const AppLayout = ({ children }) => {
|
|
|
83
136
|
];
|
|
84
137
|
|
|
85
138
|
const breadcrumbs = getBreadcrumbs();
|
|
86
|
-
const colorBgContainer = token.colorBgContainer;
|
|
87
|
-
const colorBgLayout = token.colorBgLayout;
|
|
88
|
-
const borderRadius = token.borderRadius;
|
|
89
139
|
|
|
90
140
|
return (
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
<
|
|
95
|
-
<div className="
|
|
96
|
-
<div className="logo-
|
|
97
|
-
<
|
|
141
|
+
<ConfigProvider theme={enableCyber ? cyberTheme : lightTheme}>
|
|
142
|
+
<Layout className="app-layout">
|
|
143
|
+
{/* 顶部导航栏 */}
|
|
144
|
+
<Header className="app-header" style={{ background: enableCyber ? '#0f0f1a' : '#ffffff', borderBottom: enableCyber ? '1px solid #1a1a2e' : '1px solid #f0f0f0' }}>
|
|
145
|
+
<div className="header-left">
|
|
146
|
+
<div className="logo-container">
|
|
147
|
+
<div className="logo-icon-group">
|
|
148
|
+
<ExperimentOutlined className="logo-icon logo-icon-secondary" style={{ color: enableCyber ? '#00ff41' : '#52c41a' }} />
|
|
149
|
+
</div>
|
|
150
|
+
<div
|
|
151
|
+
className="logo-text"
|
|
152
|
+
style={enableCyber ? {
|
|
153
|
+
background: 'linear-gradient(135deg, #00f0ff, #00ff41)',
|
|
154
|
+
WebkitBackgroundClip: 'text',
|
|
155
|
+
WebkitTextFillColor: 'transparent',
|
|
156
|
+
} : {
|
|
157
|
+
background: 'linear-gradient(135deg, #1890ff, #52c41a)',
|
|
158
|
+
WebkitBackgroundClip: 'text',
|
|
159
|
+
WebkitTextFillColor: 'transparent',
|
|
160
|
+
}}
|
|
161
|
+
>
|
|
162
|
+
MockMaster
|
|
163
|
+
</div>
|
|
98
164
|
</div>
|
|
99
|
-
<
|
|
165
|
+
<Button
|
|
166
|
+
type="text"
|
|
167
|
+
icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
|
|
168
|
+
onClick={() => setCollapsed(!collapsed)}
|
|
169
|
+
className="collapse-button"
|
|
170
|
+
/>
|
|
100
171
|
</div>
|
|
101
|
-
<
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
<Text type="secondary">v{__APP_VERSION__}</Text>
|
|
117
|
-
</Button>
|
|
118
|
-
</Space>
|
|
119
|
-
</div>
|
|
120
|
-
</Header>
|
|
121
|
-
|
|
122
|
-
<Layout>
|
|
123
|
-
{/* 侧边导航 */}
|
|
124
|
-
<Sider
|
|
125
|
-
width={220}
|
|
126
|
-
className="app-sider"
|
|
127
|
-
collapsed={collapsed}
|
|
128
|
-
collapsible
|
|
129
|
-
trigger={null}
|
|
130
|
-
style={{ background: colorBgContainer }}
|
|
131
|
-
>
|
|
132
|
-
<Menu
|
|
133
|
-
mode="inline"
|
|
134
|
-
selectedKeys={getSelectedKeys()}
|
|
135
|
-
items={menuItems}
|
|
136
|
-
style={{
|
|
137
|
-
height: '100%',
|
|
138
|
-
borderRight: 0,
|
|
139
|
-
padding: '8px',
|
|
140
|
-
}}
|
|
141
|
-
/>
|
|
142
|
-
</Sider>
|
|
172
|
+
<div className="header-right">
|
|
173
|
+
<Space>
|
|
174
|
+
<Button
|
|
175
|
+
type="text"
|
|
176
|
+
size="small"
|
|
177
|
+
className="version-button"
|
|
178
|
+
onClick={showVersionModal}
|
|
179
|
+
>
|
|
180
|
+
<span style={{ color: enableCyber ? '#6b7280' : '#000000d9', fontFamily: "'Share Tech Mono', monospace" }}>
|
|
181
|
+
v{__APP_VERSION__}
|
|
182
|
+
</span>
|
|
183
|
+
</Button>
|
|
184
|
+
</Space>
|
|
185
|
+
</div>
|
|
186
|
+
</Header>
|
|
143
187
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
className="app-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
minHeight: 280,
|
|
154
|
-
}}
|
|
188
|
+
<Layout>
|
|
189
|
+
{/* 侧边导航 */}
|
|
190
|
+
<Sider
|
|
191
|
+
width={220}
|
|
192
|
+
className="app-sider"
|
|
193
|
+
collapsed={collapsed}
|
|
194
|
+
collapsible
|
|
195
|
+
trigger={null}
|
|
196
|
+
style={{ background: enableCyber ? '#0f0f1a' : '#ffffff' }}
|
|
155
197
|
>
|
|
156
|
-
|
|
157
|
-
|
|
198
|
+
<Menu
|
|
199
|
+
mode="inline"
|
|
200
|
+
selectedKeys={getSelectedKeys()}
|
|
201
|
+
items={menuItems}
|
|
202
|
+
style={{
|
|
203
|
+
height: '100%',
|
|
204
|
+
borderRight: 0,
|
|
205
|
+
padding: '8px',
|
|
206
|
+
background: enableCyber ? '#0f0f1a' : '#ffffff',
|
|
207
|
+
}}
|
|
208
|
+
/>
|
|
209
|
+
</Sider>
|
|
210
|
+
|
|
211
|
+
{/* 主内容区 */}
|
|
212
|
+
<Layout className="app-content-wrapper" style={{ padding: 0, background: enableCyber ? '#0a0a12' : '#f5f5f5' }}>
|
|
213
|
+
{/* 面包屑和标题 */}
|
|
214
|
+
<div style={{ padding: '16px 24px 0' }}>
|
|
215
|
+
<Breadcrumb className="app-breadcrumb" style={{ marginBottom: 16 }}>
|
|
216
|
+
{breadcrumbs.map((crumb, index) => (
|
|
217
|
+
<Breadcrumb.Item key={index}>
|
|
218
|
+
{index < breadcrumbs.length - 1 ? (
|
|
219
|
+
<Link to={crumb.path} style={{ color: enableCyber ? '#a0a0a0' : '#000000d9' }}>{crumb.title}</Link>
|
|
220
|
+
) : (
|
|
221
|
+
<span style={{ color: enableCyber ? '#e0e0e0' : '#000000d9', fontWeight: 500 }}>{crumb.title}</span>
|
|
222
|
+
)}
|
|
223
|
+
</Breadcrumb.Item>
|
|
224
|
+
))}
|
|
225
|
+
</Breadcrumb>
|
|
226
|
+
<h1 style={{
|
|
227
|
+
margin: 0,
|
|
228
|
+
fontSize: 22,
|
|
229
|
+
fontWeight: 700,
|
|
230
|
+
color: enableCyber ? '#e0e0e0' : '#000000d9',
|
|
231
|
+
fontFamily: enableCyber ? "'Orbitron', sans-serif" : undefined,
|
|
232
|
+
letterSpacing: enableCyber ? '0.5px' : undefined,
|
|
233
|
+
}}>
|
|
234
|
+
{getPageTitle()}
|
|
235
|
+
</h1>
|
|
236
|
+
</div>
|
|
237
|
+
|
|
238
|
+
{/* 内容区 */}
|
|
239
|
+
<Content
|
|
240
|
+
className="app-content"
|
|
241
|
+
style={{
|
|
242
|
+
background: enableCyber ? '#0f0f1a' : '#ffffff',
|
|
243
|
+
padding: 24,
|
|
244
|
+
borderRadius: 6,
|
|
245
|
+
minHeight: 280,
|
|
246
|
+
margin: '16px 24px 24px',
|
|
247
|
+
border: enableCyber ? '1px solid #1a1a2e' : '1px solid #f0f0f0',
|
|
248
|
+
}}
|
|
249
|
+
>
|
|
250
|
+
{children}
|
|
251
|
+
</Content>
|
|
252
|
+
</Layout>
|
|
158
253
|
</Layout>
|
|
254
|
+
|
|
255
|
+
{/* 版本信息弹窗 */}
|
|
256
|
+
<VersionModal
|
|
257
|
+
visible={versionModalVisible}
|
|
258
|
+
onCancel={hideVersionModal}
|
|
259
|
+
/>
|
|
159
260
|
</Layout>
|
|
160
|
-
|
|
161
|
-
{/* 版本信息弹窗 */}
|
|
162
|
-
<VersionModal
|
|
163
|
-
visible={versionModalVisible}
|
|
164
|
-
onCancel={hideVersionModal}
|
|
165
|
-
/>
|
|
166
|
-
</Layout>
|
|
261
|
+
</ConfigProvider>
|
|
167
262
|
);
|
|
168
263
|
};
|
|
169
264
|
|
|
170
|
-
export default AppLayout;
|
|
265
|
+
export default AppLayout;
|
|
@@ -6,7 +6,7 @@ const FeatureCard = ({ feature }) => {
|
|
|
6
6
|
|
|
7
7
|
// 生成随机的主题色(实际项目中可能会根据某种规则来确定)
|
|
8
8
|
const getRandomColor = () => {
|
|
9
|
-
const colors = ['#
|
|
9
|
+
const colors = ['#00f0ff', '#00ff41', '#f0e800', '#ff00a0', '#bd00ff'];
|
|
10
10
|
return colors[Math.floor(Math.random() * colors.length)];
|
|
11
11
|
};
|
|
12
12
|
|
|
@@ -252,7 +252,7 @@ const InterfaceTestModal = ({
|
|
|
252
252
|
<span>
|
|
253
253
|
<span style={{ color: '#d9d9d9' }}>{value}</span>
|
|
254
254
|
{' '} → {' '}
|
|
255
|
-
<span style={{ color: '#
|
|
255
|
+
<span style={{ color: '#00ff41', fontWeight: 'bold' }}>{generatedValue}</span>
|
|
256
256
|
<span style={{ color: '#8c8c8c', fontSize: '12px' }}> (随机生成)</span>
|
|
257
257
|
</span>
|
|
258
258
|
) : generatedValue}
|
|
@@ -22,22 +22,20 @@ const PreviewModal = ({
|
|
|
22
22
|
<div style={{
|
|
23
23
|
marginBottom: '16px',
|
|
24
24
|
padding: '8px',
|
|
25
|
-
background: '#f9f9f9',
|
|
26
25
|
borderRadius: '4px',
|
|
27
|
-
borderLeft: '4px solid #
|
|
26
|
+
borderLeft: '4px solid #00f0ff'
|
|
28
27
|
}}>
|
|
29
28
|
<div style={{ fontWeight: 'bold', marginBottom: '4px' }}>描述:</div>
|
|
30
|
-
<div>{previewContent.description}</div>
|
|
29
|
+
<div style={{ opacity: 0.7 }}>{previewContent.description}</div>
|
|
31
30
|
</div>
|
|
32
31
|
)}
|
|
33
32
|
|
|
34
33
|
<div style={{
|
|
35
34
|
maxHeight: '60vh',
|
|
36
35
|
overflow: 'auto',
|
|
37
|
-
backgroundColor: '#f5f5f5',
|
|
38
36
|
padding: '16px',
|
|
39
37
|
borderRadius: '4px',
|
|
40
|
-
fontFamily: 'monospace
|
|
38
|
+
fontFamily: "'Share Tech Mono', monospace",
|
|
41
39
|
}}>
|
|
42
40
|
<pre style={{ margin: 0, whiteSpace: 'pre-wrap', wordWrap: 'break-word' }}>
|
|
43
41
|
{previewContent?.content}
|
|
@@ -47,4 +45,4 @@ const PreviewModal = ({
|
|
|
47
45
|
);
|
|
48
46
|
};
|
|
49
47
|
|
|
50
|
-
export default PreviewModal;
|
|
48
|
+
export default PreviewModal;
|
|
@@ -274,7 +274,7 @@ const ResponseContentEditor = ({
|
|
|
274
274
|
onClick={toggleTextareaFullscreen}
|
|
275
275
|
size="small"
|
|
276
276
|
type="text"
|
|
277
|
-
style={{ color: '#
|
|
277
|
+
style={{ color: '#595959' }}
|
|
278
278
|
>
|
|
279
279
|
退出全屏 (ESC)
|
|
280
280
|
</Button>
|
|
@@ -301,7 +301,7 @@ const ResponseContentEditor = ({
|
|
|
301
301
|
<div style={{ marginBottom: '12px' }}>
|
|
302
302
|
<Divider orientation="left" orientationMargin={0} style={{ margin: '0 0 10px 0', fontSize: '12px' }}>
|
|
303
303
|
<Space size={6}>
|
|
304
|
-
<FilterOutlined style={{ color: '#
|
|
304
|
+
<FilterOutlined style={{ color: '#1677ff' }} />
|
|
305
305
|
<span style={{ fontWeight: 'bold', color: '#333' }}>入参匹配规则</span>
|
|
306
306
|
{matchers.length > 0 && (
|
|
307
307
|
<Tag color="blue" style={{ fontSize: '11px', padding: '0 4px' }}>{matchers.length}条</Tag>
|
|
@@ -382,11 +382,10 @@ const ResponseContentEditor = ({
|
|
|
382
382
|
<div style={{
|
|
383
383
|
textAlign: 'center',
|
|
384
384
|
padding: '40px 20px',
|
|
385
|
-
background: '#fafafa',
|
|
386
385
|
borderRadius: '6px',
|
|
387
386
|
border: '1px dashed #d9d9d9'
|
|
388
387
|
}}>
|
|
389
|
-
<FileTextOutlined style={{ fontSize: '32px', color: '#
|
|
388
|
+
<FileTextOutlined style={{ fontSize: '32px', color: '#bfbfbf', marginBottom: '12px' }} />
|
|
390
389
|
<div style={{ color: '#999', marginBottom: '16px' }}>暂无响应数据</div>
|
|
391
390
|
<Button
|
|
392
391
|
type="primary"
|
|
@@ -408,7 +407,7 @@ const ResponseContentEditor = ({
|
|
|
408
407
|
<span>响应内容编辑</span>
|
|
409
408
|
<Badge
|
|
410
409
|
count={responses.length}
|
|
411
|
-
style={{ backgroundColor: '#
|
|
410
|
+
style={{ backgroundColor: '#00ff41' }}
|
|
412
411
|
/>
|
|
413
412
|
</div>
|
|
414
413
|
}
|
|
@@ -433,9 +432,7 @@ const ResponseContentEditor = ({
|
|
|
433
432
|
style={{
|
|
434
433
|
display: 'flex',
|
|
435
434
|
alignItems: 'center',
|
|
436
|
-
gap: '4px'
|
|
437
|
-
borderColor: '#1890ff',
|
|
438
|
-
color: '#1890ff'
|
|
435
|
+
gap: '4px'
|
|
439
436
|
}}
|
|
440
437
|
>
|
|
441
438
|
添加响应
|
|
@@ -449,8 +446,8 @@ const ResponseContentEditor = ({
|
|
|
449
446
|
display: 'flex',
|
|
450
447
|
alignItems: 'center',
|
|
451
448
|
gap: '4px',
|
|
452
|
-
borderColor: '#
|
|
453
|
-
color: '#
|
|
449
|
+
borderColor: '#00ff41',
|
|
450
|
+
color: '#00ff41'
|
|
454
451
|
}}
|
|
455
452
|
>
|
|
456
453
|
预览
|
package/ui/src/index.html
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="UTF-8">
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Whistle Mock
|
|
6
|
+
<title>Whistle Mock Plugin</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@300;400;500;700;900&family=Orbitron:wght@400;500;600;700;800;900&family=Share+Tech+Mono&display=swap" rel="stylesheet">
|
|
7
10
|
</head>
|
|
8
11
|
<body>
|
|
9
12
|
<div id="root"></div>
|
|
10
13
|
</body>
|
|
11
|
-
</html>
|
|
14
|
+
</html>
|
package/ui/src/index.js
CHANGED
|
@@ -3,10 +3,13 @@ import ReactDOM from 'react-dom';
|
|
|
3
3
|
import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
|
|
4
4
|
|
|
5
5
|
// 导入样式
|
|
6
|
-
import 'antd/dist/reset.css';
|
|
6
|
+
import 'antd/dist/reset.css';
|
|
7
7
|
import './styles/global.css';
|
|
8
8
|
import './App.css';
|
|
9
9
|
|
|
10
|
+
// 赛博朋克主题 CSS(规则均带 body.cyber-theme 前缀,默认不生效)
|
|
11
|
+
require('./styles/cyber-theme.css');
|
|
12
|
+
|
|
10
13
|
// 导入页面组件
|
|
11
14
|
import MockData from './pages/MockDataV2';
|
|
12
15
|
import InterfaceManagement from './pages/InterfaceManagementV2';
|
|
@@ -37,4 +40,4 @@ if (ReactDOM.createRoot) {
|
|
|
37
40
|
} else {
|
|
38
41
|
// 旧版本React
|
|
39
42
|
ReactDOM.render(<App />, root);
|
|
40
|
-
}
|
|
43
|
+
}
|
|
@@ -1006,13 +1006,13 @@ const InterfaceManagement = () => {
|
|
|
1006
1006
|
<div key={key}>
|
|
1007
1007
|
{key}: {value}
|
|
1008
1008
|
{value && value.startsWith('@') && (
|
|
1009
|
-
<span style={{ color: '#
|
|
1009
|
+
<span style={{ color: '#00ff41' }}> (随机)</span>
|
|
1010
1010
|
)}
|
|
1011
1011
|
</div>
|
|
1012
1012
|
))}
|
|
1013
1013
|
</div>
|
|
1014
1014
|
}>
|
|
1015
|
-
<span style={{ color: '#
|
|
1015
|
+
<span style={{ color: '#1677ff' }}>
|
|
1016
1016
|
{count}个
|
|
1017
1017
|
{hasRandomValue && <span style={{ marginLeft: 4 }}>🎲</span>}
|
|
1018
1018
|
</span>
|
|
@@ -1058,7 +1058,7 @@ const InterfaceManagement = () => {
|
|
|
1058
1058
|
{interfaceMatchers.filter(m => m.paramPath).map((m, i) => (
|
|
1059
1059
|
<div key={i} style={{ fontSize: '12px' }}>
|
|
1060
1060
|
{m.paramPath}: {m.paramValue}
|
|
1061
|
-
<span style={{ color: '#
|
|
1061
|
+
<span style={{ color: '#00ff41', marginLeft: 4 }}>
|
|
1062
1062
|
({m.matchType === 'exact' ? '精确' : m.matchType === 'contains' ? '包含' : '正则'})
|
|
1063
1063
|
</span>
|
|
1064
1064
|
</div>
|
|
@@ -1067,14 +1067,14 @@ const InterfaceManagement = () => {
|
|
|
1067
1067
|
)}
|
|
1068
1068
|
{responsesWithMatchers.length > 0 && (
|
|
1069
1069
|
<div>
|
|
1070
|
-
<div style={{ fontWeight: 'bold', marginBottom: 4, color: '#
|
|
1070
|
+
<div style={{ fontWeight: 'bold', marginBottom: 4, color: '#1677ff' }}>响应级规则(共{responseMatcherTotal}条)</div>
|
|
1071
1071
|
{responsesWithMatchers.map((resp, ri) => (
|
|
1072
1072
|
<div key={ri} style={{ marginBottom: 4 }}>
|
|
1073
1073
|
<div style={{ fontSize: '12px', color: '#aaa', marginBottom: 2 }}>{resp.name}:</div>
|
|
1074
1074
|
{resp.paramMatchers.filter(m => m.paramPath).map((m, mi) => (
|
|
1075
1075
|
<div key={mi} style={{ fontSize: '12px', paddingLeft: 8 }}>
|
|
1076
1076
|
{m.paramPath}: {m.paramValue}
|
|
1077
|
-
<span style={{ color: '#
|
|
1077
|
+
<span style={{ color: '#00ff41', marginLeft: 4 }}>
|
|
1078
1078
|
({m.matchType === 'exact' ? '精确' : m.matchType === 'contains' ? '包含' : '正则'})
|
|
1079
1079
|
</span>
|
|
1080
1080
|
</div>
|
|
@@ -1312,7 +1312,7 @@ const InterfaceManagement = () => {
|
|
|
1312
1312
|
) : (
|
|
1313
1313
|
<Badge
|
|
1314
1314
|
count={`共 ${filteredInterfaces.length} 个接口`}
|
|
1315
|
-
style={{ backgroundColor: '#
|
|
1315
|
+
style={{ backgroundColor: '#00ff41' }}
|
|
1316
1316
|
/>
|
|
1317
1317
|
)}
|
|
1318
1318
|
</span>
|
|
@@ -1500,7 +1500,6 @@ const InterfaceManagement = () => {
|
|
|
1500
1500
|
destroyOnClose
|
|
1501
1501
|
bodyStyle={{
|
|
1502
1502
|
padding: '0',
|
|
1503
|
-
background: '#fafafa',
|
|
1504
1503
|
overflowY: 'auto'
|
|
1505
1504
|
}}
|
|
1506
1505
|
footer={
|
|
@@ -1744,8 +1743,7 @@ const InterfaceManagement = () => {
|
|
|
1744
1743
|
bodyStyle={{
|
|
1745
1744
|
maxHeight: '75vh',
|
|
1746
1745
|
overflow: 'auto',
|
|
1747
|
-
padding: '0'
|
|
1748
|
-
background: '#fafafa'
|
|
1746
|
+
padding: '0'
|
|
1749
1747
|
}}
|
|
1750
1748
|
className="interface-modal"
|
|
1751
1749
|
>
|
|
@@ -454,7 +454,7 @@ const MockData = () => {
|
|
|
454
454
|
<Col xs={24} sm={12} md={8} lg={6} key={feature.id} style={{ marginBottom: 16 }}>
|
|
455
455
|
<Badge.Ribbon
|
|
456
456
|
text={feature.active ? '已启用' : '已禁用'}
|
|
457
|
-
color={feature.active ? '#
|
|
457
|
+
color={feature.active ? '#00ff41' : '#f5222d'}
|
|
458
458
|
style={{ display: 'block' }}
|
|
459
459
|
>
|
|
460
460
|
<Card
|
|
@@ -566,7 +566,7 @@ const MockData = () => {
|
|
|
566
566
|
width: 100,
|
|
567
567
|
align: 'center',
|
|
568
568
|
render: (count) => (
|
|
569
|
-
<Badge count={count || 0} showZero style={{ backgroundColor: '#
|
|
569
|
+
<Badge count={count || 0} showZero style={{ backgroundColor: '#1677ff' }} />
|
|
570
570
|
),
|
|
571
571
|
},
|
|
572
572
|
{
|
|
@@ -664,7 +664,7 @@ const MockData = () => {
|
|
|
664
664
|
<Badge
|
|
665
665
|
count="V2"
|
|
666
666
|
style={{
|
|
667
|
-
backgroundColor: '#
|
|
667
|
+
backgroundColor: '#00ff41',
|
|
668
668
|
fontSize: '12px',
|
|
669
669
|
height: '20px',
|
|
670
670
|
lineHeight: '20px',
|
|
@@ -689,7 +689,7 @@ const MockData = () => {
|
|
|
689
689
|
type="primary"
|
|
690
690
|
icon={<ExportOutlined />}
|
|
691
691
|
onClick={importFeatureConfig}
|
|
692
|
-
style={{ backgroundColor: '#
|
|
692
|
+
style={{ backgroundColor: '#00ff41', borderColor: '#00ff41' }}
|
|
693
693
|
>
|
|
694
694
|
导入功能
|
|
695
695
|
</Button>
|
package/ui/src/pages/Settings.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import AppLayout from '../components/AppLayout';
|
|
3
|
-
import { Card, Typography, Switch, Space, Modal, Button } from 'antd';
|
|
3
|
+
import { Card, Typography, Switch, Space, Modal, Button, message } from 'antd';
|
|
4
4
|
import { QuestionCircleOutlined } from '@ant-design/icons';
|
|
5
5
|
import axios from 'axios';
|
|
6
6
|
import '../styles/settings.css';
|
|
@@ -16,6 +16,7 @@ const Settings = () => {
|
|
|
16
16
|
const [interfaceFormDisplayMode, setInterfaceFormDisplayMode] = useState('modal');
|
|
17
17
|
const [interfaceParamMatcherEnabled, setInterfaceParamMatcherEnabled] = useState(true);
|
|
18
18
|
const [responseParamMatcherEnabled, setResponseParamMatcherEnabled] = useState(true);
|
|
19
|
+
const [cyberThemeEnabled, setCyberThemeEnabled] = useState(true);
|
|
19
20
|
|
|
20
21
|
useEffect(() => {
|
|
21
22
|
const savedMode = localStorage.getItem('dataManagementDisplayMode');
|
|
@@ -39,6 +40,10 @@ const Settings = () => {
|
|
|
39
40
|
const savedResponseParamMatcher = localStorage.getItem('responseParamMatcherEnabled');
|
|
40
41
|
if (savedResponseParamMatcher !== null) setResponseParamMatcherEnabled(savedResponseParamMatcher !== 'false');
|
|
41
42
|
});
|
|
43
|
+
|
|
44
|
+
// 读取赛博朋克主题开关(默认开启)
|
|
45
|
+
const savedCyber = localStorage.getItem('cyberTheme');
|
|
46
|
+
setCyberThemeEnabled(savedCyber !== 'false');
|
|
42
47
|
}, []);
|
|
43
48
|
|
|
44
49
|
const handleDisplayModeChange = (checked) => {
|
|
@@ -69,6 +74,17 @@ const Settings = () => {
|
|
|
69
74
|
window.dispatchEvent(new Event('storage'));
|
|
70
75
|
};
|
|
71
76
|
|
|
77
|
+
const handleCyberThemeChange = (checked) => {
|
|
78
|
+
setCyberThemeEnabled(checked);
|
|
79
|
+
localStorage.setItem('cyberTheme', String(checked));
|
|
80
|
+
window.dispatchEvent(new Event('storage'));
|
|
81
|
+
message.success(checked ? '赛博朋克主题已开启,刷新页面后生效' : '经典主题已开启,刷新页面后生效');
|
|
82
|
+
// 1.5秒后自动刷新
|
|
83
|
+
setTimeout(() => {
|
|
84
|
+
window.location.reload();
|
|
85
|
+
}, 1500);
|
|
86
|
+
};
|
|
87
|
+
|
|
72
88
|
const showMatcherRuleModal = (type) => {
|
|
73
89
|
if (type === 'interface') {
|
|
74
90
|
Modal.info({
|
|
@@ -229,6 +245,28 @@ const Settings = () => {
|
|
|
229
245
|
</div>
|
|
230
246
|
</div>
|
|
231
247
|
</Card>
|
|
248
|
+
|
|
249
|
+
<Card title="主题设置" className="settings-card" style={{ marginTop: 24 }}>
|
|
250
|
+
<div className="settings-item">
|
|
251
|
+
<div className="settings-item-header">
|
|
252
|
+
<div>
|
|
253
|
+
<div className="settings-item-title">赛博朋克风格</div>
|
|
254
|
+
<div className="settings-item-description">
|
|
255
|
+
启用深色赛博朋克霓虹主题,关闭则恢复经典亮色主题
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
<Space>
|
|
259
|
+
<Text type="secondary">
|
|
260
|
+
{cyberThemeEnabled ? '已开启' : '已关闭'}
|
|
261
|
+
</Text>
|
|
262
|
+
<Switch
|
|
263
|
+
checked={cyberThemeEnabled}
|
|
264
|
+
onChange={handleCyberThemeChange}
|
|
265
|
+
/>
|
|
266
|
+
</Space>
|
|
267
|
+
</div>
|
|
268
|
+
</div>
|
|
269
|
+
</Card>
|
|
232
270
|
</div>
|
|
233
271
|
</AppLayout>
|
|
234
272
|
);
|