whistle.mock-plugins 1.0.0

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 (68) hide show
  1. package/API.md +247 -0
  2. package/README.md +91 -0
  3. package/app/cgi-bin/features.js +285 -0
  4. package/app/cgi-bin/index.js +31 -0
  5. package/app/cgi-bin/interfaces.js +409 -0
  6. package/app/cgi-bin/logs.js +151 -0
  7. package/app/cgi-bin/test-interface.js +152 -0
  8. package/app/public/index.html +1 -0
  9. package/app/public/js/index.js +2 -0
  10. package/app/public/js/index.js.LICENSE.txt +150 -0
  11. package/app/util/index.js +24 -0
  12. package/index.html +52 -0
  13. package/initialize.js +32 -0
  14. package/lib/dataManager.js +500 -0
  15. package/lib/index.js +3 -0
  16. package/lib/models.js +64 -0
  17. package/lib/routes/features.js +213 -0
  18. package/lib/routes/interfaces.js +304 -0
  19. package/lib/routes/proxy.js +239 -0
  20. package/lib/ruleManager.js +551 -0
  21. package/lib/rules-server.js +267 -0
  22. package/lib/server.js +805 -0
  23. package/lib/storage.js +16 -0
  24. package/lib/ui-server.js +68 -0
  25. package/package.json +51 -0
  26. package/public/bundle.js +2 -0
  27. package/public/bundle.js.LICENSE.txt +38 -0
  28. package/public/index.html +1 -0
  29. package/public/plugin.mock-plugin.html +14 -0
  30. package/rules.txt +22 -0
  31. package/test/rulesServer.test.js +120 -0
  32. package/ui/src/App.css +173 -0
  33. package/ui/src/App.js +24 -0
  34. package/ui/src/components/AppLayout.js +149 -0
  35. package/ui/src/components/FeatureCard.js +53 -0
  36. package/ui/src/components/Layout.js +94 -0
  37. package/ui/src/components/MainNav.js +42 -0
  38. package/ui/src/components/PageContainer.js +16 -0
  39. package/ui/src/index.html +11 -0
  40. package/ui/src/index.js +43 -0
  41. package/ui/src/pages/FileProxy.js +235 -0
  42. package/ui/src/pages/FilesPage.js +292 -0
  43. package/ui/src/pages/Home.js +128 -0
  44. package/ui/src/pages/HomePage.js +195 -0
  45. package/ui/src/pages/InterfaceManagement.js +829 -0
  46. package/ui/src/pages/InterfacePage.js +342 -0
  47. package/ui/src/pages/MockData.js +472 -0
  48. package/ui/src/pages/Settings.js +192 -0
  49. package/ui/src/pages/SettingsPage.js +165 -0
  50. package/ui/src/pages/UrlRedirect.js +225 -0
  51. package/ui/src/pages/Welcome.js +155 -0
  52. package/ui/src/services/api.js +186 -0
  53. package/ui/src/styles/app-layout.css +337 -0
  54. package/ui/src/styles/feature-card.css +172 -0
  55. package/ui/src/styles/file-proxy.css +301 -0
  56. package/ui/src/styles/global.css +224 -0
  57. package/ui/src/styles/home.css +123 -0
  58. package/ui/src/styles/index.css +419 -0
  59. package/ui/src/styles/interface-management.css +694 -0
  60. package/ui/src/styles/layout.css +159 -0
  61. package/ui/src/styles/logs-page.css +427 -0
  62. package/ui/src/styles/main-nav.css +52 -0
  63. package/ui/src/styles/mock-data.css +850 -0
  64. package/ui/src/styles/page-container.css +11 -0
  65. package/ui/src/styles/settings.css +208 -0
  66. package/ui/src/styles/url-redirect.css +279 -0
  67. package/ui/src/styles/welcome.css +282 -0
  68. package/webpack.config.js +49 -0
@@ -0,0 +1,186 @@
1
+ /**
2
+ * API 服务模块
3
+ * 提供功能和接口相关的 API 调用
4
+ */
5
+ const API_BASE_URL = '/api';
6
+
7
+ /**
8
+ * 通用 API 请求方法
9
+ * @param {string} url - 请求 URL
10
+ * @param {object} options - 请求选项
11
+ * @returns {Promise<any>} - 请求结果
12
+ */
13
+ const request = async (url, options = {}) => {
14
+ const response = await fetch(url, {
15
+ ...options,
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ ...options.headers
19
+ }
20
+ });
21
+
22
+ const data = await response.json();
23
+
24
+ if (!response.ok) {
25
+ throw new Error(data.message || '请求失败');
26
+ }
27
+
28
+ return data;
29
+ };
30
+
31
+ /**
32
+ * 功能相关 API
33
+ */
34
+ export const FeatureAPI = {
35
+ /**
36
+ * 获取所有功能
37
+ * @returns {Promise<Array>} 功能列表
38
+ */
39
+ getAllFeatures: () => request(`${API_BASE_URL}/features`),
40
+
41
+ /**
42
+ * 获取单个功能
43
+ * @param {string} id - 功能 ID
44
+ * @returns {Promise<object>} 功能对象
45
+ */
46
+ getFeature: (id) => request(`${API_BASE_URL}/features/${id}`),
47
+
48
+ /**
49
+ * 创建功能
50
+ * @param {object} feature - 功能对象
51
+ * @returns {Promise<object>} 创建的功能对象
52
+ */
53
+ createFeature: (feature) => request(`${API_BASE_URL}/features`, {
54
+ method: 'POST',
55
+ body: JSON.stringify(feature)
56
+ }),
57
+
58
+ /**
59
+ * 更新功能
60
+ * @param {string} id - 功能 ID
61
+ * @param {object} feature - 功能对象
62
+ * @returns {Promise<object>} 更新的功能对象
63
+ */
64
+ updateFeature: (id, feature) => request(`${API_BASE_URL}/features/${id}`, {
65
+ method: 'PUT',
66
+ body: JSON.stringify(feature)
67
+ }),
68
+
69
+ /**
70
+ * 删除功能
71
+ * @param {string} id - 功能 ID
72
+ * @returns {Promise<object>} 删除结果
73
+ */
74
+ deleteFeature: (id) => request(`${API_BASE_URL}/features/${id}`, {
75
+ method: 'DELETE'
76
+ }),
77
+
78
+ /**
79
+ * 获取功能下的所有接口
80
+ * @param {string} id - 功能 ID
81
+ * @returns {Promise<Array>} 接口列表
82
+ */
83
+ getFeatureInterfaces: (id) => request(`${API_BASE_URL}/features/${id}/interfaces`)
84
+ };
85
+
86
+ /**
87
+ * 接口相关 API
88
+ */
89
+ export const InterfaceAPI = {
90
+ /**
91
+ * 获取单个接口
92
+ * @param {string} id - 接口 ID
93
+ * @returns {Promise<object>} 接口对象
94
+ */
95
+ getInterface: (id) => request(`${API_BASE_URL}/interfaces/${id}`),
96
+
97
+ /**
98
+ * 创建接口
99
+ * @param {object} interfaceObj - 接口对象
100
+ * @returns {Promise<object>} 创建的接口对象
101
+ */
102
+ createInterface: (interfaceObj) => request(`${API_BASE_URL}/interfaces`, {
103
+ method: 'POST',
104
+ body: JSON.stringify(interfaceObj)
105
+ }),
106
+
107
+ /**
108
+ * 更新接口
109
+ * @param {string} id - 接口 ID
110
+ * @param {object} interfaceObj - 接口对象
111
+ * @returns {Promise<object>} 更新的接口对象
112
+ */
113
+ updateInterface: (id, interfaceObj) => request(`${API_BASE_URL}/interfaces/${id}`, {
114
+ method: 'PUT',
115
+ body: JSON.stringify(interfaceObj)
116
+ }),
117
+
118
+ /**
119
+ * 删除接口
120
+ * @param {string} id - 接口 ID
121
+ * @returns {Promise<object>} 删除结果
122
+ */
123
+ deleteInterface: (id) => request(`${API_BASE_URL}/interfaces/${id}`, {
124
+ method: 'DELETE'
125
+ }),
126
+
127
+ /**
128
+ * 测试接口
129
+ * @param {string} id - 接口 ID
130
+ * @param {object} testData - 测试数据
131
+ * @returns {Promise<object>} 测试结果
132
+ */
133
+ testInterface: (id, testData) => request(`${API_BASE_URL}/interfaces/${id}/test`, {
134
+ method: 'POST',
135
+ body: JSON.stringify(testData)
136
+ })
137
+ };
138
+
139
+ /**
140
+ * 代理相关 API
141
+ */
142
+ export const ProxyAPI = {
143
+ /**
144
+ * 获取文件内容
145
+ * @param {string} path - 文件路径
146
+ * @returns {Promise<object>} 文件内容
147
+ */
148
+ getFile: (path) => request(`${API_BASE_URL}/proxy/file?path=${encodeURIComponent(path)}`),
149
+
150
+ /**
151
+ * 保存文件内容
152
+ * @param {string} path - 文件路径
153
+ * @param {string} content - 文件内容
154
+ * @returns {Promise<object>} 保存结果
155
+ */
156
+ saveFile: (path, content) => request(`${API_BASE_URL}/proxy/file`, {
157
+ method: 'POST',
158
+ body: JSON.stringify({ path, content })
159
+ }),
160
+
161
+ /**
162
+ * 列出文件
163
+ * @param {string} path - 目录路径
164
+ * @returns {Promise<Array>} 文件列表
165
+ */
166
+ listFiles: (path = '') => request(`${API_BASE_URL}/proxy/files?path=${encodeURIComponent(path)}`),
167
+
168
+ /**
169
+ * 删除文件
170
+ * @param {string} path - 文件路径
171
+ * @returns {Promise<object>} 删除结果
172
+ */
173
+ deleteFile: (path) => request(`${API_BASE_URL}/proxy/file?path=${encodeURIComponent(path)}`, {
174
+ method: 'DELETE'
175
+ }),
176
+
177
+ /**
178
+ * 测试 mock 数据模板
179
+ * @param {string} template - 模板字符串
180
+ * @returns {Promise<object>} 生成的数据
181
+ */
182
+ testMockTemplate: (template) => request(`${API_BASE_URL}/proxy/test`, {
183
+ method: 'POST',
184
+ body: JSON.stringify({ template })
185
+ })
186
+ };
@@ -0,0 +1,337 @@
1
+ /* 基础样式 */
2
+ * {
3
+ margin: 0;
4
+ padding: 0;
5
+ box-sizing: border-box;
6
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
7
+ }
8
+
9
+ body {
10
+ background-color: #f0f2f5;
11
+ color: #000000d9;
12
+ font-size: 14px;
13
+ line-height: 1.5;
14
+ }
15
+
16
+ .app-container {
17
+ display: flex;
18
+ flex-direction: column;
19
+ min-height: 100vh;
20
+ }
21
+
22
+ /* 顶部导航栏 */
23
+ .header {
24
+ background-color: #fff;
25
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
26
+ height: 48px;
27
+ display: flex;
28
+ align-items: center;
29
+ padding: 0 16px;
30
+ z-index: 100;
31
+ }
32
+
33
+ .logo {
34
+ font-weight: bold;
35
+ font-size: 18px;
36
+ color: #1890ff;
37
+ margin-right: 24px;
38
+ display: flex;
39
+ align-items: center;
40
+ }
41
+
42
+ .logo-icon {
43
+ margin-right: 8px;
44
+ width: 24px;
45
+ height: 24px;
46
+ background-color: #1890ff;
47
+ border-radius: 50%;
48
+ display: flex;
49
+ align-items: center;
50
+ justify-content: center;
51
+ color: white;
52
+ }
53
+
54
+ .search {
55
+ flex: 1;
56
+ max-width: 320px;
57
+ margin-right: 16px;
58
+ }
59
+
60
+ .search input {
61
+ width: 100%;
62
+ height: 32px;
63
+ border-radius: 4px;
64
+ border: 1px solid #d9d9d9;
65
+ padding: 0 8px 0 32px;
66
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' width='16' height='16'%3E%3Cpath fill='%23999' d='M15.5 14h-.79l-.28-.27a6.5 6.5 0 0 0 1.48-5.34c-.47-2.78-2.79-5-5.59-5.34a6.505 6.505 0 0 0-7.27 7.27c.34 2.8 2.56 5.12 5.34 5.59a6.5 6.5 0 0 0 5.34-1.48l.27.28v.79l4.25 4.25c.41.41 1.08.41 1.49 0 .41-.41.41-1.08 0-1.49L15.5 14zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z'/%3E%3C/svg%3E");
67
+ background-repeat: no-repeat;
68
+ background-position: 8px center;
69
+ }
70
+
71
+ .header-actions {
72
+ display: flex;
73
+ align-items: center;
74
+ }
75
+
76
+ .header-actions button {
77
+ background: none;
78
+ border: none;
79
+ cursor: pointer;
80
+ color: #595959;
81
+ padding: 4px 8px;
82
+ margin-left: 8px;
83
+ border-radius: 4px;
84
+ display: flex;
85
+ align-items: center;
86
+ }
87
+
88
+ .header-actions button:hover {
89
+ background-color: #f5f5f5;
90
+ color: #1890ff;
91
+ }
92
+
93
+ .header-actions button i {
94
+ margin-right: 4px;
95
+ }
96
+
97
+ /* 主内容区域 */
98
+ .main-container {
99
+ display: flex;
100
+ flex: 1;
101
+ overflow: hidden;
102
+ }
103
+
104
+ /* 左侧导航 */
105
+ .sidebar {
106
+ width: 250px;
107
+ background-color: #fff;
108
+ border-right: 1px solid #f0f0f0;
109
+ display: flex;
110
+ flex-direction: column;
111
+ overflow-y: auto;
112
+ }
113
+
114
+ .create-feature {
115
+ padding: 16px;
116
+ }
117
+
118
+ .create-feature button {
119
+ width: 100%;
120
+ height: 36px;
121
+ background-color: #1890ff;
122
+ color: white;
123
+ border: none;
124
+ border-radius: 4px;
125
+ cursor: pointer;
126
+ font-size: 14px;
127
+ display: flex;
128
+ align-items: center;
129
+ justify-content: center;
130
+ }
131
+
132
+ .create-feature button:hover {
133
+ background-color: #40a9ff;
134
+ }
135
+
136
+ .feature-list {
137
+ flex: 1;
138
+ padding: 0 8px;
139
+ overflow-y: auto;
140
+ }
141
+
142
+ .feature-item {
143
+ padding: 8px 12px;
144
+ margin-bottom: 4px;
145
+ border-radius: 4px;
146
+ cursor: pointer;
147
+ }
148
+
149
+ .feature-item:hover {
150
+ background-color: #f5f5f5;
151
+ }
152
+
153
+ .feature-item.active {
154
+ background-color: #e6f7ff;
155
+ }
156
+
157
+ .feature-link {
158
+ display: flex;
159
+ justify-content: space-between;
160
+ align-items: center;
161
+ text-decoration: none;
162
+ color: #595959;
163
+ }
164
+
165
+ .feature-item.active .feature-link {
166
+ color: #1890ff;
167
+ }
168
+
169
+ .feature-name {
170
+ font-size: 14px;
171
+ }
172
+
173
+ .feature-count {
174
+ background-color: #f0f0f0;
175
+ color: #8c8c8c;
176
+ font-size: 12px;
177
+ padding: 2px 6px;
178
+ border-radius: 10px;
179
+ min-width: 20px;
180
+ text-align: center;
181
+ }
182
+
183
+ .feature-item.active .feature-count {
184
+ background-color: #1890ff;
185
+ color: white;
186
+ }
187
+
188
+ /* 右侧内容区域 */
189
+ .content {
190
+ flex: 1;
191
+ padding: 16px;
192
+ overflow-y: auto;
193
+ }
194
+
195
+ .app-layout {
196
+ min-height: 100vh;
197
+ display: flex;
198
+ flex-direction: column;
199
+ }
200
+
201
+ .app-header {
202
+ position: fixed;
203
+ z-index: 100;
204
+ width: 100%;
205
+ display: flex;
206
+ justify-content: space-between;
207
+ align-items: center;
208
+ padding: 0 24px;
209
+ height: 64px;
210
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
211
+ }
212
+
213
+ .header-left {
214
+ display: flex;
215
+ align-items: center;
216
+ }
217
+
218
+ .logo-container {
219
+ display: flex;
220
+ align-items: center;
221
+ margin-right: 24px;
222
+ }
223
+
224
+ .logo-icon {
225
+ font-size: 24px;
226
+ color: #1890ff;
227
+ margin-right: 12px;
228
+ }
229
+
230
+ .logo-text {
231
+ color: #1890ff;
232
+ font-size: 18px;
233
+ font-weight: 600;
234
+ white-space: nowrap;
235
+ }
236
+
237
+ .collapse-button {
238
+ display: flex;
239
+ align-items: center;
240
+ justify-content: center;
241
+ }
242
+
243
+ .header-right {
244
+ display: flex;
245
+ align-items: center;
246
+ }
247
+
248
+ .app-sider {
249
+ overflow: auto;
250
+ height: 100vh;
251
+ position: fixed;
252
+ left: 0;
253
+ top: 64px;
254
+ bottom: 0;
255
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
256
+ z-index: 99;
257
+ transition: all 0.2s;
258
+ }
259
+
260
+ .app-content-wrapper {
261
+ margin-top: 64px;
262
+ margin-left: 220px;
263
+ padding: 0 24px 24px;
264
+ transition: all 0.2s;
265
+ }
266
+
267
+ .app-content {
268
+ padding: 24px;
269
+ overflow: initial;
270
+ min-height: calc(100vh - 64px - 48px - 32px);
271
+ transition: all 0.3s;
272
+ }
273
+
274
+ .page-header {
275
+ margin-bottom: 16px;
276
+ }
277
+
278
+ .app-breadcrumb {
279
+ font-size: 14px;
280
+ }
281
+
282
+ /* 当侧边栏折叠时的样式调整 */
283
+ .app-sider.ant-layout-sider-collapsed + .app-content-wrapper {
284
+ margin-left: 80px;
285
+ }
286
+
287
+ /* 菜单项样式定制 */
288
+ .ant-menu-item {
289
+ border-radius: 4px;
290
+ margin: 4px 0 !important;
291
+ }
292
+
293
+ .ant-menu-item-selected {
294
+ font-weight: 500;
295
+ }
296
+
297
+ .ant-menu-inline .ant-menu-item::after {
298
+ display: none;
299
+ }
300
+
301
+ /* 移动端响应式调整 */
302
+ @media (max-width: 768px) {
303
+ .app-sider {
304
+ position: absolute;
305
+ z-index: 100;
306
+ box-shadow: 2px 0 8px rgba(0, 0, 0, 0.15);
307
+ }
308
+
309
+ .app-content-wrapper {
310
+ margin-left: 0 !important;
311
+ padding: 0 16px 16px;
312
+ }
313
+
314
+ .app-content {
315
+ padding: 16px;
316
+ }
317
+
318
+ .logo-text {
319
+ display: none;
320
+ }
321
+
322
+ .app-header {
323
+ padding: 0 16px;
324
+ }
325
+ }
326
+
327
+ /* 暗色主题适配 */
328
+ .dark-theme .app-header,
329
+ .dark-theme .app-sider {
330
+ background-color: #141414 !important;
331
+ border-color: #303030 !important;
332
+ }
333
+
334
+ .dark-theme .logo-text,
335
+ .dark-theme .logo-icon {
336
+ color: #177ddc;
337
+ }
@@ -0,0 +1,172 @@
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
+ .feature-card:hover {
14
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
15
+ transform: translateY(-4px);
16
+ }
17
+
18
+ .feature-card-color-bar {
19
+ height: 4px;
20
+ width: 100%;
21
+ }
22
+
23
+ .feature-card-header {
24
+ padding: 16px 20px;
25
+ display: flex;
26
+ justify-content: space-between;
27
+ align-items: center;
28
+ border-bottom: 1px solid #f0f0f0;
29
+ }
30
+
31
+ .feature-card-title {
32
+ font-size: 1.1rem;
33
+ font-weight: 600;
34
+ color: #262626;
35
+ margin: 0;
36
+ white-space: nowrap;
37
+ overflow: hidden;
38
+ text-overflow: ellipsis;
39
+ max-width: 70%;
40
+ }
41
+
42
+ .feature-card-badge {
43
+ font-size: 12px;
44
+ padding: 4px 10px;
45
+ border-radius: 12px;
46
+ font-weight: 500;
47
+ display: inline-flex;
48
+ align-items: center;
49
+ }
50
+
51
+ .feature-card-badge.active {
52
+ background-color: #f6ffed;
53
+ color: #52c41a;
54
+ border: 1px solid #b7eb8f;
55
+ }
56
+
57
+ .feature-card-badge.inactive {
58
+ background-color: #f5f5f5;
59
+ color: #8c8c8c;
60
+ border: 1px solid #d9d9d9;
61
+ }
62
+
63
+ .feature-card-content {
64
+ padding: 16px 20px;
65
+ flex: 1;
66
+ display: flex;
67
+ flex-direction: column;
68
+ }
69
+
70
+ .feature-card-description {
71
+ color: #595959;
72
+ font-size: 14px;
73
+ line-height: 1.6;
74
+ margin: 0 0 20px 0;
75
+ flex-grow: 1;
76
+ overflow: hidden;
77
+ display: -webkit-box;
78
+ -webkit-box-orient: vertical;
79
+ -webkit-line-clamp: 2;
80
+ }
81
+
82
+ .feature-card-stats {
83
+ display: flex;
84
+ justify-content: space-between;
85
+ margin-top: auto;
86
+ padding-top: 12px;
87
+ border-top: 1px dashed #f0f0f0;
88
+ }
89
+
90
+ .feature-stat {
91
+ display: flex;
92
+ flex-direction: column;
93
+ align-items: flex-start;
94
+ }
95
+
96
+ .feature-stat-value {
97
+ font-size: 20px;
98
+ font-weight: 600;
99
+ color: #262626;
100
+ }
101
+
102
+ .feature-stat-label {
103
+ font-size: 12px;
104
+ color: #8c8c8c;
105
+ margin-top: 2px;
106
+ }
107
+
108
+ .feature-date {
109
+ font-size: 14px;
110
+ }
111
+
112
+ .feature-card-footer {
113
+ padding: 16px 20px;
114
+ border-top: 1px solid #f0f0f0;
115
+ display: flex;
116
+ justify-content: space-between;
117
+ gap: 10px;
118
+ }
119
+
120
+ .card-btn {
121
+ padding: 8px 16px;
122
+ border-radius: 6px;
123
+ font-size: 14px;
124
+ cursor: pointer;
125
+ transition: all 0.2s;
126
+ display: flex;
127
+ align-items: center;
128
+ justify-content: center;
129
+ gap: 6px;
130
+ font-weight: 500;
131
+ }
132
+
133
+ .edit-btn {
134
+ background-color: #f7f7f7;
135
+ color: #595959;
136
+ border: 1px solid #d9d9d9;
137
+ flex: 1;
138
+ }
139
+
140
+ .edit-btn:hover {
141
+ background-color: #f0f0f0;
142
+ border-color: #bfbfbf;
143
+ }
144
+
145
+ .primary-btn {
146
+ background-color: transparent;
147
+ border: 1px solid;
148
+ flex: 1.5;
149
+ }
150
+
151
+ .primary-btn:hover {
152
+ opacity: 0.85;
153
+ background-color: rgba(24, 144, 255, 0.05);
154
+ }
155
+
156
+ /* 图标样式 */
157
+ .icon-edit, .icon-manage {
158
+ display: inline-block;
159
+ width: 14px;
160
+ height: 14px;
161
+ background-size: contain;
162
+ background-repeat: no-repeat;
163
+ background-position: center;
164
+ }
165
+
166
+ .icon-edit {
167
+ 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");
168
+ }
169
+
170
+ .icon-manage {
171
+ 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");
172
+ }