whistle.mock-plugins 1.3.1 → 1.4.0-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.
Files changed (70) hide show
  1. package/VERSION.md +29 -0
  2. package/app/cgi-bin/interfaces.js +2 -0
  3. package/app/cgi-bin/settings.js +1 -0
  4. package/app/cgi-bin/stats.js +22 -12
  5. package/app/cgi-bin/webdav.js +56 -0
  6. package/app/public/index.html +1 -1
  7. package/app/public/js/11.index.js +2 -0
  8. package/app/public/js/11.index.js.LICENSE.txt +5 -0
  9. package/app/public/js/704.index.js +2 -0
  10. package/app/public/js/704.index.js.LICENSE.txt +1 -0
  11. package/app/public/js/798.index.js +2 -0
  12. package/app/public/js/798.index.js.LICENSE.txt +53 -0
  13. package/app/public/js/807.index.js +2 -0
  14. package/app/public/js/807.index.js.LICENSE.txt +1 -0
  15. package/app/public/js/968.index.js +2 -0
  16. package/app/public/js/968.index.js.LICENSE.txt +12 -0
  17. package/app/public/js/index.js +1 -1
  18. package/app/public/js/index.js.LICENSE.txt +0 -173
  19. package/lib/routes/settings.js +1 -0
  20. package/lib/webdav-sync.js +806 -0
  21. package/package.json +9 -2
  22. package/API.md +0 -247
  23. package/COLUMN_RESIZE_IMPLEMENTATION.md +0 -162
  24. package/FEATURE_ENHANCEMENT.md +0 -157
  25. package/TEST_GUIDE.md +0 -156
  26. package/data/features.json +0 -1
  27. package/data/interfaces.json +0 -1
  28. package/index.html +0 -52
  29. package/initialize.js +0 -32
  30. package/public/bundle.js +0 -2
  31. package/public/bundle.js.LICENSE.txt +0 -38
  32. package/public/index.html +0 -1
  33. package/public/plugin.mock-plugin.html +0 -14
  34. package/rules.txt +0 -22
  35. package/ui/src/App.css +0 -173
  36. package/ui/src/components/AppLayout.js +0 -407
  37. package/ui/src/components/FeatureCard.js +0 -58
  38. package/ui/src/components/KeyboardShortcutsHelp.js +0 -58
  39. package/ui/src/components/Layout.js +0 -94
  40. package/ui/src/components/MainNav.js +0 -42
  41. package/ui/src/components/PageContainer.js +0 -16
  42. package/ui/src/components/VersionModal.js +0 -99
  43. package/ui/src/components/interface-management/DataManagementDrawer.js +0 -115
  44. package/ui/src/components/interface-management/DataManagementModal.js +0 -110
  45. package/ui/src/components/interface-management/InterfaceTestModal.js +0 -313
  46. package/ui/src/components/interface-management/PreviewModal.js +0 -48
  47. package/ui/src/components/interface-management/README.md +0 -184
  48. package/ui/src/components/interface-management/RealResponseRecorder.js +0 -165
  49. package/ui/src/components/interface-management/ResponseContentEditor.js +0 -645
  50. package/ui/src/components/interface-management/constants.js +0 -36
  51. package/ui/src/components/interface-management/index.js +0 -7
  52. package/ui/src/components/interface-management/utils.js +0 -103
  53. package/ui/src/hooks/useKeyboardShortcuts.js +0 -99
  54. package/ui/src/index.html +0 -14
  55. package/ui/src/index.js +0 -50
  56. package/ui/src/pages/InterfaceManagementV2.js +0 -2351
  57. package/ui/src/pages/MockDataV2.js +0 -1089
  58. package/ui/src/pages/Settings.js +0 -299
  59. package/ui/src/pages/Sniffer.js +0 -521
  60. package/ui/src/pages/Stats.js +0 -288
  61. package/ui/src/services/del_api.js +0 -192
  62. package/ui/src/styles/app-layout.css +0 -373
  63. package/ui/src/styles/cyber-theme.css +0 -1234
  64. package/ui/src/styles/earth-theme.css +0 -618
  65. package/ui/src/styles/global.css +0 -224
  66. package/ui/src/styles/interface-management.css +0 -1321
  67. package/ui/src/styles/mock-data.css +0 -1185
  68. package/ui/src/styles/settings.css +0 -252
  69. package/ui/src/styles/version-modal.css +0 -116
  70. package/webpack.config.js +0 -54
@@ -1,38 +0,0 @@
1
- /**
2
- * @license React
3
- * react-dom.production.min.js
4
- *
5
- * Copyright (c) Facebook, Inc. and its affiliates.
6
- *
7
- * This source code is licensed under the MIT license found in the
8
- * LICENSE file in the root directory of this source tree.
9
- */
10
-
11
- /**
12
- * @license React
13
- * react.production.min.js
14
- *
15
- * Copyright (c) Facebook, Inc. and its affiliates.
16
- *
17
- * This source code is licensed under the MIT license found in the
18
- * LICENSE file in the root directory of this source tree.
19
- */
20
-
21
- /**
22
- * @license React
23
- * scheduler.production.min.js
24
- *
25
- * Copyright (c) Facebook, Inc. and its affiliates.
26
- *
27
- * This source code is licensed under the MIT license found in the
28
- * LICENSE file in the root directory of this source tree.
29
- */
30
-
31
- /** @license React v16.13.1
32
- * react-is.production.min.js
33
- *
34
- * Copyright (c) Facebook, Inc. and its affiliates.
35
- *
36
- * This source code is licensed under the MIT license found in the
37
- * LICENSE file in the root directory of this source tree.
38
- */
package/public/index.html DELETED
@@ -1 +0,0 @@
1
- <!doctype html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Whistle Mock 插件</title><script defer="defer" src="/bundle.js"></script></head><body><div id="root"></div></body></html>
@@ -1,14 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta http-equiv="refresh" content="0;url=/ui/">
5
- <title>Redirecting...</title>
6
- </head>
7
- <body>
8
- <script>
9
- // 重定向到正确的路径
10
- window.location.href = '/ui/';
11
- </script>
12
- <p>如果页面没有自动跳转,<a href="/ui/">请点击这里</a></p>
13
- </body>
14
- </html>
package/rules.txt DELETED
@@ -1,22 +0,0 @@
1
- # Whistle Mock Plugin 使用指南
2
- # 版本: 0.3.0
3
-
4
- # ===== 基本规则配置 =====
5
- # 在 Whistle 的规则配置中,使用以下格式添加规则:
6
- * whistle.mock-plugins://
7
-
8
- # 该插件现在会自动匹配已配置的接口规则,对于没有在插件中配置的接口,不会进行拦截
9
-
10
- # ===== 规则匹配逻辑 =====
11
- # 1. 请求会先检查是否匹配接口配置中定义的 URL 模式
12
- # 2. 如果找到匹配的接口配置,且该接口处于启用状态,才会由插件处理
13
- # 3. 如果没有匹配的接口配置,请求会直接透传到真实服务器
14
-
15
- # ===== 常见配置示例 =====
16
- # 不需要添加其他规则,只需要保留上面的全局规则即可
17
- # 插件会根据在插件界面中配置的接口规则自动决定哪些请求需要处理
18
-
19
- # ===== 调试功能 =====
20
- # 通过访问以下URL可以查看插件的配置和状态:
21
- # 查看全部配置: whistle.mock-plugin/_debug/config
22
- # 查看启用的接口: whistle.mock-plugin/_debug/enabled-interfaces
package/ui/src/App.css DELETED
@@ -1,173 +0,0 @@
1
- .App {
2
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
3
- height: 100%;
4
- width: 100%;
5
- }
6
-
7
- /* 页面容器样式 */
8
- .page-container {
9
- width: 100%;
10
- height: 100%;
11
- background-color: #fff;
12
- border-radius: 8px;
13
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
14
- padding: 24px;
15
- transition: all 0.3s;
16
- }
17
-
18
- /* 页面标题栏样式 */
19
- .page-title-bar {
20
- display: flex;
21
- justify-content: space-between;
22
- align-items: center;
23
- margin-bottom: 24px;
24
- padding-bottom: 16px;
25
- border-bottom: 1px solid #f0f0f0;
26
- }
27
-
28
- .page-title {
29
- margin: 0;
30
- color: rgba(0, 0, 0, 0.85);
31
- font-weight: 600;
32
- font-size: 20px;
33
- line-height: 32px;
34
- }
35
-
36
- .page-description {
37
- margin-top: 8px;
38
- color: rgba(0, 0, 0, 0.45);
39
- font-size: 14px;
40
- }
41
-
42
- .page-actions {
43
- display: flex;
44
- gap: 12px;
45
- }
46
-
47
- /* 卡片样式 */
48
- .custom-card {
49
- border-radius: 8px;
50
- overflow: hidden;
51
- transition: all 0.3s;
52
- height: 100%;
53
- }
54
-
55
- .custom-card:hover {
56
- box-shadow: 0 4px 16px rgba(0, 0, 0, 0.08);
57
- transform: translateY(-3px);
58
- }
59
-
60
- /* 表格样式 */
61
- .custom-table {
62
- border-radius: 8px;
63
- overflow: hidden;
64
- }
65
-
66
- .custom-table .ant-table-thead > tr > th {
67
- background: #fafafa;
68
- font-weight: 600;
69
- }
70
-
71
- /* 表单样式 */
72
- .custom-form-item-label {
73
- font-weight: 500;
74
- color: rgba(0, 0, 0, 0.85);
75
- }
76
-
77
- .custom-form-item-hint {
78
- color: rgba(0, 0, 0, 0.45);
79
- font-size: 13px;
80
- margin-top: 4px;
81
- }
82
-
83
- /* 按钮样式 */
84
- .custom-button {
85
- border-radius: 4px;
86
- display: inline-flex;
87
- align-items: center;
88
- justify-content: center;
89
- }
90
-
91
- .custom-button-icon {
92
- margin-right: 8px;
93
- }
94
-
95
- /* 状态标签样式 */
96
- .status-tag {
97
- border-radius: 10px;
98
- padding: 2px 8px;
99
- font-size: 12px;
100
- line-height: 1.5;
101
- display: inline-flex;
102
- align-items: center;
103
- }
104
-
105
- .status-tag-success {
106
- background-color: #f6ffed;
107
- border: 1px solid #b7eb8f;
108
- color: #52c41a;
109
- }
110
-
111
- .status-tag-warning {
112
- background-color: #fffbe6;
113
- border: 1px solid #ffe58f;
114
- color: #faad14;
115
- }
116
-
117
- .status-tag-error {
118
- background-color: #fff2f0;
119
- border: 1px solid #ffccc7;
120
- color: #ff4d4f;
121
- }
122
-
123
- /* 动画效果 */
124
- .fade-in {
125
- animation: fadeIn 0.3s ease-in-out;
126
- }
127
-
128
- @keyframes fadeIn {
129
- from {
130
- opacity: 0;
131
- }
132
- to {
133
- opacity: 1;
134
- }
135
- }
136
-
137
- /* 滚动条样式 */
138
- ::-webkit-scrollbar {
139
- width: 6px;
140
- height: 6px;
141
- }
142
-
143
- ::-webkit-scrollbar-track {
144
- background: #f1f1f1;
145
- border-radius: 3px;
146
- }
147
-
148
- ::-webkit-scrollbar-thumb {
149
- background: #c1c1c1;
150
- border-radius: 3px;
151
- }
152
-
153
- ::-webkit-scrollbar-thumb:hover {
154
- background: #a8a8a8;
155
- }
156
-
157
- /* 响应式调整 */
158
- @media (max-width: 576px) {
159
- .page-container {
160
- padding: 16px;
161
- }
162
-
163
- .page-title-bar {
164
- flex-direction: column;
165
- align-items: flex-start;
166
- }
167
-
168
- .page-actions {
169
- margin-top: 16px;
170
- width: 100%;
171
- justify-content: flex-start;
172
- }
173
- }
@@ -1,407 +0,0 @@
1
- import React, { useState, useMemo, useEffect } from 'react';
2
- import { Layout, Menu, Button, Breadcrumb, Space, ConfigProvider, theme } from 'antd';
3
- import {
4
- AppstoreOutlined,
5
- ApiOutlined,
6
- SettingOutlined,
7
- FileTextOutlined,
8
- MenuFoldOutlined,
9
- MenuUnfoldOutlined,
10
- ExperimentOutlined,
11
- BarChartOutlined,
12
- ThunderboltOutlined
13
- } from '@ant-design/icons';
14
- import { Link, useLocation } from 'react-router-dom';
15
- import VersionModal from './VersionModal';
16
- import KeyboardShortcutsHelp from './KeyboardShortcutsHelp';
17
- import { useKeyboardShortcuts } from '../hooks/useKeyboardShortcuts';
18
- import '../styles/app-layout.css';
19
-
20
- const { Header, Content, Sider } = Layout;
21
-
22
- const THEMES = ['light', 'cyber', 'earth'];
23
-
24
- const AppLayout = ({ children }) => {
25
- const location = useLocation();
26
- const currentPath = location.pathname;
27
- const [collapsed, setCollapsed] = useState(false);
28
- const [versionModalVisible, setVersionModalVisible] = useState(false);
29
- const [shortcutsHelpVisible, setShortcutsHelpVisible] = useState(false);
30
-
31
- // 注册全局键盘快捷键
32
- useKeyboardShortcuts();
33
-
34
- // 监听快捷键帮助事件
35
- useEffect(() => {
36
- const handleHelp = () => setShortcutsHelpVisible(true);
37
- window.addEventListener('keyboard-shortcuts-help', handleHelp);
38
- return () => window.removeEventListener('keyboard-shortcuts-help', handleHelp);
39
- }, []);
40
-
41
- // 主题切换(兼容旧版 cyberTheme 存储)
42
- const [currentTheme, setCurrentTheme] = useState(() => {
43
- const saved = localStorage.getItem('theme');
44
- if (saved && THEMES.includes(saved)) return saved;
45
- // 兼容旧版 boolean 存储
46
- const oldCyber = localStorage.getItem('cyberTheme');
47
- return oldCyber === 'false' ? 'light' : oldCyber === 'true' ? 'cyber' : 'light';
48
- });
49
-
50
- useEffect(() => {
51
- document.body.classList.remove('cyber-theme', 'earth-theme');
52
- if (currentTheme !== 'light') {
53
- document.body.classList.add(`${currentTheme}-theme`);
54
- }
55
- }, [currentTheme]);
56
-
57
- useEffect(() => {
58
- const handleStorage = () => {
59
- const saved = localStorage.getItem('theme');
60
- if (saved && THEMES.includes(saved)) {
61
- setCurrentTheme(saved);
62
- } else {
63
- const oldCyber = localStorage.getItem('cyberTheme');
64
- setCurrentTheme(oldCyber === 'false' ? 'light' : 'cyber');
65
- }
66
- };
67
- window.addEventListener('storage', handleStorage);
68
- return () => window.removeEventListener('storage', handleStorage);
69
- }, []);
70
-
71
- const cyberTheme = useMemo(() => ({
72
- token: {
73
- colorPrimary: '#00f0ff',
74
- colorPrimaryHover: '#33f3ff',
75
- colorPrimaryActive: '#00c2cc',
76
- colorSuccess: '#00ff41',
77
- colorWarning: '#f0e800',
78
- colorError: '#ff4d4f',
79
- colorInfo: '#00f0ff',
80
- colorText: '#e0e0e0',
81
- colorTextSecondary: '#a0a0a0',
82
- colorTextTertiary: '#6b7280',
83
- colorBorder: '#1a1a2e',
84
- colorBorderSecondary: '#1a1a2e',
85
- colorBgBase: '#050508',
86
- colorBgContainer: '#0f0f1a',
87
- colorBgElevated: '#141420',
88
- colorBgLayout: '#0a0a12',
89
- colorBgSpotlight: '#141420',
90
- borderRadius: 6,
91
- borderRadiusSM: 4,
92
- borderRadiusLG: 8,
93
- wireframe: false,
94
- fontFamily: "'Noto Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
95
- fontFamilyCode: "'Share Tech Mono', 'SFMono-Regular', Consolas, monospace",
96
- },
97
- algorithm: theme.darkAlgorithm,
98
- }), []);
99
-
100
- const earthTheme = useMemo(() => ({
101
- token: {
102
- colorPrimary: '#b87333',
103
- colorPrimaryHover: '#c9894d',
104
- colorPrimaryActive: '#9a5f2a',
105
- colorSuccess: '#6b8e23',
106
- colorWarning: '#d4a017',
107
- colorError: '#c0392b',
108
- colorInfo: '#8d6e63',
109
- colorText: '#3d3630',
110
- colorTextSecondary: '#6d6459',
111
- colorTextTertiary: '#9e9488',
112
- colorBorder: '#e0d8cf',
113
- colorBorderSecondary: '#d5cdc2',
114
- colorBgBase: '#faf6f0',
115
- colorBgContainer: '#fdfcfa',
116
- colorBgElevated: '#f5f0e8',
117
- colorBgLayout: '#f0ebe3',
118
- colorBgSpotlight: '#f5f0e8',
119
- borderRadius: 6,
120
- borderRadiusSM: 4,
121
- borderRadiusLG: 8,
122
- wireframe: false,
123
- fontFamily: "'Noto Sans SC', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
124
- fontFamilyCode: "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace",
125
- },
126
- algorithm: theme.defaultAlgorithm,
127
- }), []);
128
-
129
- const lightTheme = useMemo(() => ({
130
- token: {
131
- borderRadius: 6,
132
- borderRadiusSM: 4,
133
- borderRadiusLG: 8,
134
- },
135
- algorithm: theme.defaultAlgorithm,
136
- }), []);
137
-
138
- const activeTheme = useMemo(() => {
139
- if (currentTheme === 'cyber') return cyberTheme;
140
- if (currentTheme === 'earth') return earthTheme;
141
- return lightTheme;
142
- }, [currentTheme, cyberTheme, earthTheme, lightTheme]);
143
-
144
- // 主题颜色映射(用于 inline style)
145
- const t = useMemo(() => {
146
- switch (currentTheme) {
147
- case 'cyber':
148
- return {
149
- headerBg: '#0f0f1a',
150
- headerBorder: '#1a1a2e',
151
- logoIcon: '#00ff41',
152
- logoGradient: 'linear-gradient(135deg, #00f0ff, #00ff41)',
153
- versionColor: '#6b7280',
154
- siderBg: '#0f0f1a',
155
- wrapperBg: '#0a0a12',
156
- breadcrumbLink: '#a0a0a0',
157
- breadcrumbCurrent: '#e0e0e0',
158
- titleColor: '#e0e0e0',
159
- titleFont: "'Orbitron', sans-serif",
160
- titleLetterSpacing: '0.5px',
161
- contentBg: '#0f0f1a',
162
- contentBorder: '#1a1a2e',
163
- };
164
- case 'earth':
165
- return {
166
- headerBg: '#fdfcfa',
167
- headerBorder: '#e0d8cf',
168
- logoIcon: '#b87333',
169
- logoGradient: 'linear-gradient(135deg, #b87333, #d4a017)',
170
- versionColor: '#6d6459',
171
- siderBg: '#f5f0e8',
172
- wrapperBg: '#f0ebe3',
173
- breadcrumbLink: '#6d6459',
174
- breadcrumbCurrent: '#3d3630',
175
- titleColor: '#3d3630',
176
- titleFont: undefined,
177
- titleLetterSpacing: undefined,
178
- contentBg: '#fdfcfa',
179
- contentBorder: '#e0d8cf',
180
- };
181
- default: // light
182
- return {
183
- headerBg: '#ffffff',
184
- headerBorder: '#f0f0f0',
185
- logoIcon: '#52c41a',
186
- logoGradient: 'linear-gradient(135deg, #1890ff, #52c41a)',
187
- versionColor: '#000000d9',
188
- siderBg: '#ffffff',
189
- wrapperBg: '#f5f5f5',
190
- breadcrumbLink: '#000000d9',
191
- breadcrumbCurrent: '#000000d9',
192
- titleColor: '#000000d9',
193
- titleFont: undefined,
194
- titleLetterSpacing: undefined,
195
- contentBg: '#ffffff',
196
- contentBorder: '#f0f0f0',
197
- };
198
- }
199
- }, [currentTheme]);
200
-
201
- // 版本弹窗控制方法
202
- const showVersionModal = () => setVersionModalVisible(true);
203
- const hideVersionModal = () => setVersionModalVisible(false);
204
-
205
- // 根据当前路径获取选中的菜单项和面包屑
206
- const getSelectedKeys = () => {
207
- if (currentPath === '/' || currentPath === '#/') return ['1'];
208
- if (currentPath.startsWith('/interface') || currentPath.includes('/interface')) return ['2'];
209
- if (currentPath === '/stats' || currentPath.includes('/stats')) return ['4'];
210
- if (currentPath === '/sniffer' || currentPath.includes('/sniffer')) return ['5'];
211
- if (currentPath === '/settings' || currentPath.includes('/settings')) return ['3'];
212
- return [];
213
- };
214
-
215
- // 根据当前路径获取面包屑
216
- const getBreadcrumbs = () => {
217
- const breadcrumbs = [
218
- { path: '/', title: '首页' }
219
- ];
220
-
221
- if (currentPath.startsWith('/interface')) {
222
- breadcrumbs.push({ path: '/interface', title: '接口管理' });
223
-
224
- // 如果有featureId参数,添加特定功能模块名称
225
- if (currentPath.includes('/interface/') && currentPath !== '/interface/') {
226
- breadcrumbs.push({ path: currentPath, title: '功能接口详情' });
227
- }
228
- } else if (currentPath === '/stats') {
229
- breadcrumbs.push({ path: '/stats', title: '请求统计' });
230
- } else if (currentPath === '/sniffer') {
231
- breadcrumbs.push({ path: '/sniffer', title: '接口嗅探' });
232
- } else if (currentPath === '/settings') {
233
- breadcrumbs.push({ path: '/settings', title: '系统设置' });
234
- }
235
-
236
- return breadcrumbs;
237
- };
238
-
239
- // 获取当前页面标题
240
- const getPageTitle = () => {
241
- if (currentPath === '/' || currentPath === '#/') return '功能模块管理';
242
- if (currentPath.startsWith('/interface')) return '接口配置管理';
243
- if (currentPath === '/stats') return '请求统计';
244
- if (currentPath === '/sniffer') return '接口嗅探';
245
- if (currentPath === '/settings') return '系统设置';
246
- return 'Whistle Mock Plugin';
247
- };
248
-
249
- // 菜单配置
250
- const menuItems = [
251
- {
252
- key: '1',
253
- icon: <AppstoreOutlined />,
254
- label: <Link to="/">功能模块</Link>,
255
- },
256
- {
257
- key: '2',
258
- icon: <ApiOutlined />,
259
- label: <Link to="/interface">接口管理</Link>,
260
- },
261
- {
262
- key: '4',
263
- icon: <BarChartOutlined />,
264
- label: <Link to="/stats">请求统计</Link>,
265
- },
266
- {
267
- key: '5',
268
- icon: <ThunderboltOutlined />,
269
- label: <Link to="/sniffer">接口嗅探</Link>,
270
- },
271
- {
272
- key: '3',
273
- icon: <SettingOutlined />,
274
- label: <Link to="/settings">系统设置</Link>,
275
- }
276
- ];
277
-
278
- const breadcrumbs = getBreadcrumbs();
279
-
280
- return (
281
- <ConfigProvider theme={activeTheme}>
282
- <Layout className="app-layout">
283
- {/* 顶部导航栏 */}
284
- <Header className="app-header" style={{ background: t.headerBg, borderBottom: `1px solid ${t.headerBorder}` }}>
285
- <div className="header-left">
286
- <div className="logo-container">
287
- <div className="logo-icon-group">
288
- <ExperimentOutlined className="logo-icon logo-icon-secondary" style={{ color: t.logoIcon }} />
289
- </div>
290
- <div
291
- className="logo-text"
292
- style={{
293
- background: t.logoGradient,
294
- WebkitBackgroundClip: 'text',
295
- WebkitTextFillColor: 'transparent',
296
- }}
297
- >
298
- MockMaster
299
- </div>
300
- </div>
301
- <Button
302
- type="text"
303
- icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
304
- onClick={() => setCollapsed(!collapsed)}
305
- className="collapse-button"
306
- />
307
- </div>
308
- <div className="header-right">
309
- <Space>
310
- <Button
311
- type="text"
312
- size="small"
313
- className="version-button"
314
- onClick={showVersionModal}
315
- >
316
- <span style={{ color: t.versionColor, fontFamily: "'Share Tech Mono', monospace" }}>
317
- v{__APP_VERSION__}
318
- </span>
319
- </Button>
320
- </Space>
321
- </div>
322
- </Header>
323
-
324
- <Layout>
325
- {/* 侧边导航 */}
326
- <Sider
327
- width={220}
328
- className="app-sider"
329
- collapsed={collapsed}
330
- collapsible
331
- trigger={null}
332
- style={{ background: t.siderBg }}
333
- >
334
- <Menu
335
- mode="inline"
336
- selectedKeys={getSelectedKeys()}
337
- items={menuItems}
338
- style={{
339
- height: '100%',
340
- borderRight: 0,
341
- padding: '8px',
342
- background: t.siderBg,
343
- }}
344
- />
345
- </Sider>
346
-
347
- {/* 主内容区 */}
348
- <Layout className="app-content-wrapper" style={{ padding: 0, background: t.wrapperBg }}>
349
- {/* 面包屑和标题 */}
350
- <div style={{ padding: '16px 24px 0' }}>
351
- <Breadcrumb className="app-breadcrumb" style={{ marginBottom: 16 }}>
352
- {breadcrumbs.map((crumb, index) => (
353
- <Breadcrumb.Item key={index}>
354
- {index < breadcrumbs.length - 1 ? (
355
- <Link to={crumb.path} style={{ color: t.breadcrumbLink }}>{crumb.title}</Link>
356
- ) : (
357
- <span style={{ color: t.breadcrumbCurrent, fontWeight: 500 }}>{crumb.title}</span>
358
- )}
359
- </Breadcrumb.Item>
360
- ))}
361
- </Breadcrumb>
362
- <h1 style={{
363
- margin: 0,
364
- fontSize: 22,
365
- fontWeight: 700,
366
- color: t.titleColor,
367
- fontFamily: t.titleFont,
368
- letterSpacing: t.titleLetterSpacing,
369
- }}>
370
- {getPageTitle()}
371
- </h1>
372
- </div>
373
-
374
- {/* 内容区 */}
375
- <Content
376
- className="app-content"
377
- style={{
378
- background: t.contentBg,
379
- padding: 24,
380
- borderRadius: 6,
381
- minHeight: 280,
382
- margin: '16px 24px 24px',
383
- border: `1px solid ${t.contentBorder}`,
384
- }}
385
- >
386
- {children}
387
- </Content>
388
- </Layout>
389
- </Layout>
390
-
391
- {/* 版本信息弹窗 */}
392
- <VersionModal
393
- visible={versionModalVisible}
394
- onCancel={hideVersionModal}
395
- />
396
-
397
- {/* 快捷键帮助弹窗 */}
398
- <KeyboardShortcutsHelp
399
- visible={shortcutsHelpVisible}
400
- onClose={() => setShortcutsHelpVisible(false)}
401
- />
402
- </Layout>
403
- </ConfigProvider>
404
- );
405
- };
406
-
407
- export default AppLayout;