whistle.mock-plugins 1.1.2-dev.2 → 1.1.3-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.
@@ -1,225 +0,0 @@
1
- import React, { useState } from 'react';
2
- import AppLayout from '../components/AppLayout';
3
- import '../styles/url-redirect.css';
4
-
5
- const UrlRedirect = () => {
6
- const [urlRedirects, setUrlRedirects] = useState([
7
- {
8
- id: 1,
9
- sourceUrl: 'https://api.example.com/users',
10
- targetUrl: 'http://localhost:3000/mock/users',
11
- description: '用户API重定向到本地',
12
- active: true
13
- },
14
- {
15
- id: 2,
16
- sourceUrl: 'https://api.example.com/products',
17
- targetUrl: 'http://localhost:3000/mock/products',
18
- description: '产品API重定向到本地',
19
- active: false
20
- }
21
- ]);
22
-
23
- const [isModalOpen, setIsModalOpen] = useState(false);
24
- const [currentRedirect, setCurrentRedirect] = useState(null);
25
- const [sourceUrl, setSourceUrl] = useState('');
26
- const [targetUrl, setTargetUrl] = useState('');
27
- const [description, setDescription] = useState('');
28
-
29
- const openModal = (redirect = null) => {
30
- if (redirect) {
31
- setCurrentRedirect(redirect);
32
- setSourceUrl(redirect.sourceUrl);
33
- setTargetUrl(redirect.targetUrl);
34
- setDescription(redirect.description);
35
- } else {
36
- setCurrentRedirect(null);
37
- setSourceUrl('');
38
- setTargetUrl('');
39
- setDescription('');
40
- }
41
- setIsModalOpen(true);
42
- };
43
-
44
- const closeModal = () => {
45
- setIsModalOpen(false);
46
- };
47
-
48
- const handleSubmit = (e) => {
49
- e.preventDefault();
50
-
51
- if (!sourceUrl || !targetUrl) {
52
- alert('请填写源URL和目标URL');
53
- return;
54
- }
55
-
56
- if (currentRedirect) {
57
- // 编辑现有配置
58
- setUrlRedirects(urlRedirects.map(redirect =>
59
- redirect.id === currentRedirect.id
60
- ? { ...redirect, sourceUrl, targetUrl, description }
61
- : redirect
62
- ));
63
- } else {
64
- // 添加新配置
65
- const newRedirect = {
66
- id: urlRedirects.length > 0 ? Math.max(...urlRedirects.map(r => r.id)) + 1 : 1,
67
- sourceUrl,
68
- targetUrl,
69
- description,
70
- active: true
71
- };
72
- setUrlRedirects([...urlRedirects, newRedirect]);
73
- }
74
-
75
- closeModal();
76
- };
77
-
78
- const toggleActive = (id) => {
79
- setUrlRedirects(urlRedirects.map(redirect =>
80
- redirect.id === id ? { ...redirect, active: !redirect.active } : redirect
81
- ));
82
- };
83
-
84
- const deleteRedirect = (id) => {
85
- if (window.confirm('确定要删除此条重定向配置吗?')) {
86
- setUrlRedirects(urlRedirects.filter(redirect => redirect.id !== id));
87
- }
88
- };
89
-
90
- return (
91
- <AppLayout>
92
- <div className="url-redirect-container">
93
- <div className="page-title-bar">
94
- <div>
95
- <h1 className="page-title">URL重定向配置</h1>
96
- <div className="page-description">
97
- 配置URL重定向规则,支持路径和查询参数匹配
98
- </div>
99
- </div>
100
- <div className="page-actions">
101
- <button className="add-button" onClick={() => openModal()}>
102
- 添加URL重定向
103
- </button>
104
- </div>
105
- </div>
106
-
107
- <div className="redirect-list-container">
108
- {urlRedirects.length === 0 ? (
109
- <div className="empty-data">
110
- <div className="empty-icon">🔄</div>
111
- <div className="empty-text">暂无URL重定向配置</div>
112
- <button className="add-button" onClick={() => openModal()}>
113
- 添加URL重定向
114
- </button>
115
- </div>
116
- ) : (
117
- <table className="redirect-table">
118
- <thead>
119
- <tr>
120
- <th>状态</th>
121
- <th>源URL</th>
122
- <th>目标URL</th>
123
- <th>描述</th>
124
- <th>操作</th>
125
- </tr>
126
- </thead>
127
- <tbody>
128
- {urlRedirects.map(redirect => (
129
- <tr key={redirect.id} className={redirect.active ? '' : 'inactive'}>
130
- <td>
131
- <label className="switch">
132
- <input
133
- type="checkbox"
134
- checked={redirect.active}
135
- onChange={() => toggleActive(redirect.id)}
136
- />
137
- <span className="slider"></span>
138
- </label>
139
- </td>
140
- <td className="url-text">{redirect.sourceUrl}</td>
141
- <td className="url-text">{redirect.targetUrl}</td>
142
- <td>{redirect.description || '-'}</td>
143
- <td>
144
- <div className="action-buttons">
145
- <button
146
- className="edit-button"
147
- onClick={() => openModal(redirect)}
148
- >
149
- 编辑
150
- </button>
151
- <button
152
- className="delete-button"
153
- onClick={() => deleteRedirect(redirect.id)}
154
- >
155
- 删除
156
- </button>
157
- </div>
158
- </td>
159
- </tr>
160
- ))}
161
- </tbody>
162
- </table>
163
- )}
164
- </div>
165
-
166
- {isModalOpen && (
167
- <div className="modal-overlay">
168
- <div className="modal">
169
- <div className="modal-header">
170
- <h2>{currentRedirect ? '编辑URL重定向' : '添加URL重定向'}</h2>
171
- <button className="close-button" onClick={closeModal}>×</button>
172
- </div>
173
- <form className="redirect-form" onSubmit={handleSubmit}>
174
- <div className="form-group">
175
- <label>源URL</label>
176
- <input
177
- type="text"
178
- value={sourceUrl}
179
- onChange={(e) => setSourceUrl(e.target.value)}
180
- placeholder="https://api.example.com/endpoint"
181
- required
182
- />
183
- <div className="help-text">需要重定向的原始URL</div>
184
- </div>
185
-
186
- <div className="form-group">
187
- <label>目标URL</label>
188
- <input
189
- type="text"
190
- value={targetUrl}
191
- onChange={(e) => setTargetUrl(e.target.value)}
192
- placeholder="http://localhost:3000/mock/endpoint"
193
- required
194
- />
195
- <div className="help-text">重定向的目标地址</div>
196
- </div>
197
-
198
- <div className="form-group">
199
- <label>描述(可选)</label>
200
- <input
201
- type="text"
202
- value={description}
203
- onChange={(e) => setDescription(e.target.value)}
204
- placeholder="重定向描述"
205
- />
206
- </div>
207
-
208
- <div className="form-actions">
209
- <button type="button" className="cancel-button" onClick={closeModal}>
210
- 取消
211
- </button>
212
- <button type="submit" className="submit-button">
213
- {currentRedirect ? '保存修改' : '添加重定向'}
214
- </button>
215
- </div>
216
- </form>
217
- </div>
218
- </div>
219
- )}
220
- </div>
221
- </AppLayout>
222
- );
223
- };
224
-
225
- export default UrlRedirect;
@@ -1,155 +0,0 @@
1
- import React from 'react';
2
- import { Link } from 'react-router-dom';
3
- import AppLayout from '../components/AppLayout';
4
- import '../styles/welcome.css';
5
-
6
- const Welcome = () => {
7
- // 模拟统计数据
8
- const stats = {
9
- featuresCount: 3,
10
- interfacesCount: 15,
11
- activeInterfacesCount: 12,
12
- requestsHandledCount: 256
13
- };
14
-
15
- // 模拟最近活动数据
16
- const recentActivities = [
17
- { id: 1, type: '创建', target: '功能', name: '商品列表', time: '2023-11-05 14:30' },
18
- { id: 2, type: '修改', target: '接口', name: '用户登录', time: '2023-11-05 14:15' },
19
- { id: 3, type: '启用', target: '接口', name: '获取用户信息', time: '2023-11-05 13:45' },
20
- { id: 4, type: '创建', target: '接口', name: '商品详情', time: '2023-11-05 13:30' },
21
- { id: 5, type: '禁用', target: '功能', name: '购物车', time: '2023-11-05 11:20' }
22
- ];
23
-
24
- return (
25
- <AppLayout>
26
- <div className="welcome-container">
27
- <div className="welcome-header">
28
- <h1>欢迎使用 Whistle Mock 数据工厂</h1>
29
- <p>快速创建和管理模拟数据,提高前端开发效率</p>
30
- </div>
31
-
32
- <div className="cards-container">
33
- {/* 功能概览卡片 */}
34
- <div className="card stats-card">
35
- <div className="card-header">
36
- <div className="card-icon">📊</div>
37
- <h2>功能概览</h2>
38
- </div>
39
- <div className="card-content">
40
- <div className="stat-item">
41
- <div className="stat-value">{stats.featuresCount}</div>
42
- <div className="stat-label">功能模块</div>
43
- </div>
44
- <div className="stat-item">
45
- <div className="stat-value">{stats.interfacesCount}</div>
46
- <div className="stat-label">接口配置</div>
47
- </div>
48
- <div className="stat-item">
49
- <div className="stat-value">{stats.activeInterfacesCount}</div>
50
- <div className="stat-label">启用接口</div>
51
- </div>
52
- <div className="stat-item">
53
- <div className="stat-value">{stats.requestsHandledCount}</div>
54
- <div className="stat-label">已处理请求</div>
55
- </div>
56
- </div>
57
- </div>
58
-
59
- {/* 快速开始卡片 */}
60
- <div className="card quickstart-card">
61
- <div className="card-header">
62
- <div className="card-icon">🚀</div>
63
- <h2>快速开始</h2>
64
- </div>
65
- <div className="card-content">
66
- <div className="steps">
67
- <div className="step">
68
- <div className="step-number">1</div>
69
- <div className="step-content">
70
- <div className="step-title">创建功能模块</div>
71
- <div className="step-desc">创建一个功能模块,用于组织相关接口</div>
72
- </div>
73
- </div>
74
- <div className="step">
75
- <div className="step-number">2</div>
76
- <div className="step-content">
77
- <div className="step-title">添加接口配置</div>
78
- <div className="step-desc">配置API接口的URL匹配规则和响应数据</div>
79
- </div>
80
- </div>
81
- <div className="step">
82
- <div className="step-number">3</div>
83
- <div className="step-content">
84
- <div className="step-title">启用接口使用</div>
85
- <div className="step-desc">启用功能模块和接口,即可在Whistle中使用</div>
86
- </div>
87
- </div>
88
- </div>
89
- <div className="quickstart-actions">
90
- <Link to="/mock-data">
91
- <button className="quickstart-button">开始创建功能</button>
92
- </Link>
93
- </div>
94
- </div>
95
- </div>
96
-
97
- {/* 使用说明卡片 */}
98
- <div className="card usage-card">
99
- <div className="card-header">
100
- <div className="card-icon">📘</div>
101
- <h2>使用说明</h2>
102
- </div>
103
- <div className="card-content">
104
- <div className="usage-item">
105
- <div className="usage-title">添加Whistle规则</div>
106
- <div className="usage-desc">
107
- 在Whistle中添加如下规则:
108
- <pre className="code-block">example.com mock://</pre>
109
- </div>
110
- </div>
111
- <div className="usage-item">
112
- <div className="usage-title">匹配规则说明</div>
113
- <div className="usage-desc">
114
- 支持多种匹配方式:
115
- <ul className="usage-list">
116
- <li>精确匹配: <code>/api/users</code></li>
117
- <li>通配符匹配: <code>/api/users/*</code></li>
118
- <li>正则匹配: <code>/\/api\/products\/\d+/</code></li>
119
- </ul>
120
- </div>
121
- </div>
122
- </div>
123
- </div>
124
- </div>
125
-
126
- {/* 最近活动 */}
127
- <div className="recent-activities">
128
- <div className="section-header">
129
- <h2>最近活动</h2>
130
- </div>
131
- <div className="activities-list">
132
- {recentActivities.map(activity => (
133
- <div key={activity.id} className="activity-item">
134
- <div className="activity-icon">
135
- {activity.type === '创建' ? '➕' :
136
- activity.type === '修改' ? '✏️' :
137
- activity.type === '启用' ? '✅' :
138
- activity.type === '禁用' ? '❌' : '🔄'}
139
- </div>
140
- <div className="activity-content">
141
- <div className="activity-title">
142
- {activity.type}了{activity.target}「{activity.name}」
143
- </div>
144
- <div className="activity-time">{activity.time}</div>
145
- </div>
146
- </div>
147
- ))}
148
- </div>
149
- </div>
150
- </div>
151
- </AppLayout>
152
- );
153
- };
154
-
155
- export default Welcome;
@@ -1,168 +0,0 @@
1
- .feature-card {
2
- background-color: #fff;
3
- border-radius: 10px;
4
- box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);
5
- overflow: hidden;
6
- transition: all 0.3s ease;
7
- height: 100%;
8
- display: flex;
9
- flex-direction: column;
10
- position: relative;
11
- }
12
-
13
-
14
- .feature-card-color-bar {
15
- height: 4px;
16
- width: 100%;
17
- }
18
-
19
- .feature-card-header {
20
- padding: 16px 20px;
21
- display: flex;
22
- justify-content: space-between;
23
- align-items: center;
24
- border-bottom: 1px solid #f0f0f0;
25
- }
26
-
27
- .feature-card-title {
28
- font-size: 1.1rem;
29
- font-weight: 600;
30
- color: #262626;
31
- margin: 0;
32
- white-space: nowrap;
33
- overflow: hidden;
34
- text-overflow: ellipsis;
35
- max-width: 70%;
36
- }
37
-
38
- .feature-card-badge {
39
- font-size: 12px;
40
- padding: 4px 10px;
41
- border-radius: 12px;
42
- font-weight: 500;
43
- display: inline-flex;
44
- align-items: center;
45
- }
46
-
47
- .feature-card-badge.active {
48
- background-color: #f6ffed;
49
- color: #52c41a;
50
- border: 1px solid #b7eb8f;
51
- }
52
-
53
- .feature-card-badge.inactive {
54
- background-color: #f5f5f5;
55
- color: #8c8c8c;
56
- border: 1px solid #d9d9d9;
57
- }
58
-
59
- .feature-card-content {
60
- padding: 16px 20px;
61
- flex: 1;
62
- display: flex;
63
- flex-direction: column;
64
- }
65
-
66
- .feature-card-description {
67
- color: #595959;
68
- font-size: 14px;
69
- line-height: 1.6;
70
- margin: 0 0 20px 0;
71
- flex-grow: 1;
72
- overflow: hidden;
73
- display: -webkit-box;
74
- -webkit-box-orient: vertical;
75
- -webkit-line-clamp: 2;
76
- }
77
-
78
- .feature-card-stats {
79
- display: flex;
80
- justify-content: space-between;
81
- margin-top: auto;
82
- padding-top: 12px;
83
- border-top: 1px dashed #f0f0f0;
84
- }
85
-
86
- .feature-stat {
87
- display: flex;
88
- flex-direction: column;
89
- align-items: flex-start;
90
- }
91
-
92
- .feature-stat-value {
93
- font-size: 20px;
94
- font-weight: 600;
95
- color: #262626;
96
- }
97
-
98
- .feature-stat-label {
99
- font-size: 12px;
100
- color: #8c8c8c;
101
- margin-top: 2px;
102
- }
103
-
104
- .feature-date {
105
- font-size: 14px;
106
- }
107
-
108
- .feature-card-footer {
109
- padding: 16px 20px;
110
- border-top: 1px solid #f0f0f0;
111
- display: flex;
112
- justify-content: space-between;
113
- gap: 10px;
114
- }
115
-
116
- .card-btn {
117
- padding: 8px 16px;
118
- border-radius: 6px;
119
- font-size: 14px;
120
- cursor: pointer;
121
- transition: all 0.2s;
122
- display: flex;
123
- align-items: center;
124
- justify-content: center;
125
- gap: 6px;
126
- font-weight: 500;
127
- }
128
-
129
- .edit-btn {
130
- background-color: #f7f7f7;
131
- color: #595959;
132
- border: 1px solid #d9d9d9;
133
- flex: 1;
134
- }
135
-
136
- .edit-btn:hover {
137
- background-color: #f0f0f0;
138
- border-color: #bfbfbf;
139
- }
140
-
141
- .primary-btn {
142
- background-color: transparent;
143
- border: 1px solid;
144
- flex: 1.5;
145
- }
146
-
147
- .primary-btn:hover {
148
- opacity: 0.85;
149
- background-color: rgba(24, 144, 255, 0.05);
150
- }
151
-
152
- /* 图标样式 */
153
- .icon-edit, .icon-manage {
154
- display: inline-block;
155
- width: 14px;
156
- height: 14px;
157
- background-size: contain;
158
- background-repeat: no-repeat;
159
- background-position: center;
160
- }
161
-
162
- .icon-edit {
163
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23595959' viewBox='0 0 24 24'%3E%3Cpath d='M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z'/%3E%3C/svg%3E");
164
- }
165
-
166
- .icon-manage {
167
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%231890ff' viewBox='0 0 24 24'%3E%3Cpath d='M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z'/%3E%3C/svg%3E");
168
- }