|
13 | 13 | 'subtitle': 'Send requests to your-host/your-path', |
14 | 14 | 'auto_refresh': 'Auto Refresh', |
15 | 15 | 'seconds': 'seconds', |
| 16 | + 'refresh': 'Refresh', |
16 | 17 | 'captured_requests': 'Captured Requests', |
17 | 18 | 'no_requests': 'No requests captured yet.', |
18 | 19 | 'welcome_title': 'Welcome!', |
|
32 | 33 | 'subtitle': '发送请求到 your-host/your-path', |
33 | 34 | 'auto_refresh': '自动刷新', |
34 | 35 | 'seconds': '秒', |
| 36 | + 'refresh': '刷新', |
35 | 37 | 'captured_requests': '已捕获的请求', |
36 | 38 | 'no_requests': '尚未捕获任何请求。', |
37 | 39 | 'welcome_title': '欢迎!', |
|
86 | 88 |
|
87 | 89 | // 页面加载完成后初始化语言 |
88 | 90 | document.addEventListener('DOMContentLoaded', () => { |
89 | | - initLanguage(); |
90 | | - |
91 | | - // 其他原有的DOMContentLoaded处理 |
92 | | - // ...existing code... |
| 91 | + // Note: initLanguage() will be called in the main DOMContentLoaded handler |
| 92 | + // to ensure proper coordination with other initialization code |
93 | 93 | }); |
94 | 94 | </script> |
95 | 95 | </head> |
|
104 | 104 | </div> |
105 | 105 | <div class="w-1/3 flex justify-end items-center pr-4"> |
106 | 106 | <div class="flex items-center space-x-2"> |
| 107 | + <button id="manualRefreshButton" class="hidden px-3 py-1 text-sm bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors" data-i18n-title="refresh_requests"> |
| 108 | + <svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 109 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> |
| 110 | + </svg> |
| 111 | + <span data-i18n="refresh">刷新</span> |
| 112 | + </button> |
107 | 113 | <input type="checkbox" id="autoRefreshCheckbox" class="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500"> |
108 | 114 | <label for="autoRefreshCheckbox" class="text-sm text-gray-600" data-i18n="auto_refresh">自动刷新</label> |
109 | 115 | <input type="number" id="refreshIntervalInput" class="w-16 text-sm border-gray-300 rounded-md shadow-sm" min="1" value="5"> |
|
223 | 229 | </div> |
224 | 230 | <script> |
225 | 231 | document.addEventListener('DOMContentLoaded', () => { |
| 232 | + // Initialize language first |
| 233 | + initLanguage(); |
| 234 | + |
226 | 235 | // --- Auto-refresh logic --- |
227 | 236 | const checkbox = document.getElementById('autoRefreshCheckbox'); |
228 | 237 | const intervalInput = document.getElementById('refreshIntervalInput'); |
| 238 | + const manualRefreshButton = document.getElementById('manualRefreshButton'); |
229 | 239 | let refreshInterval; |
230 | 240 |
|
231 | 241 | function startRefresh() { |
|
235 | 245 | interval = 5; // 如果输入无效,则重置为默认值 |
236 | 246 | intervalInput.value = '5'; |
237 | 247 | } |
238 | | - refreshInterval = setInterval(() => window.location.reload(), interval * 1000); |
| 248 | + refreshInterval = setInterval(() => updateRequestList(), interval * 1000); |
239 | 249 | } |
240 | 250 |
|
241 | 251 | function stopRefresh() { |
242 | 252 | clearInterval(refreshInterval); |
243 | 253 | refreshInterval = null; |
244 | 254 | } |
245 | 255 |
|
| 256 | + function toggleManualRefreshButton() { |
| 257 | + if (checkbox.checked) { |
| 258 | + manualRefreshButton.classList.add('hidden'); |
| 259 | + } else { |
| 260 | + manualRefreshButton.classList.remove('hidden'); |
| 261 | + } |
| 262 | + } |
| 263 | + |
| 264 | + // Function to update only the request list via AJAX |
| 265 | + function updateRequestList() { |
| 266 | + return fetch('/api/requests') |
| 267 | + .then(response => response.json()) |
| 268 | + .then(requests => { |
| 269 | + const requestList = document.querySelector('aside .overflow-y-auto'); |
| 270 | + const currentViewId = new URLSearchParams(window.location.search).get('view_id'); |
| 271 | + |
| 272 | + if (requests && requests.length > 0) { |
| 273 | + const ul = document.createElement('ul'); |
| 274 | + ul.className = 'divide-y'; |
| 275 | + |
| 276 | + requests.forEach(request => { |
| 277 | + const li = document.createElement('a'); |
| 278 | + li.href = `/?view_id=${request.ID}`; |
| 279 | + li.className = `block p-4 hover:bg-gray-50 ${currentViewId == request.ID ? 'bg-indigo-50 border-l-4 border-indigo-500' : ''}`; |
| 280 | + |
| 281 | + const methodColor = getMethodColor(request.Method); |
| 282 | + const timestamp = new Date(request.Timestamp).toLocaleString(); |
| 283 | + |
| 284 | + li.innerHTML = ` |
| 285 | + <div class="flex justify-between items-center mb-1"> |
| 286 | + <div class="font-bold truncate ${currentViewId == request.ID ? 'text-indigo-700' : 'text-gray-800'}"> |
| 287 | + <span class="px-2 mr-2 text-xs leading-5 font-semibold rounded-full ${methodColor}"> |
| 288 | + ${request.Method} |
| 289 | + </span> |
| 290 | + <span class="font-mono">${request.Path}</span> |
| 291 | + </div> |
| 292 | + </div> |
| 293 | + <div class="text-xs text-gray-500">${timestamp}</div> |
| 294 | + `; |
| 295 | + |
| 296 | + ul.appendChild(li); |
| 297 | + }); |
| 298 | + |
| 299 | + requestList.innerHTML = ''; |
| 300 | + requestList.appendChild(ul); |
| 301 | + } else { |
| 302 | + requestList.innerHTML = ` |
| 303 | + <div class="p-6 text-center text-gray-500"> |
| 304 | + <p data-i18n="no_requests">${translations[currentLang]['no_requests']}</p> |
| 305 | + </div> |
| 306 | + `; |
| 307 | + } |
| 308 | + }) |
| 309 | + .catch(error => { |
| 310 | + console.error('Error updating request list:', error); |
| 311 | + }); |
| 312 | + } |
| 313 | + |
| 314 | + // Helper function to get method color class |
| 315 | + function getMethodColor(method) { |
| 316 | + switch(method) { |
| 317 | + case 'GET': return 'bg-green-100 text-green-800'; |
| 318 | + case 'POST': return 'bg-blue-100 text-blue-800'; |
| 319 | + case 'PUT': return 'bg-yellow-100 text-yellow-800'; |
| 320 | + case 'DELETE': return 'bg-red-100 text-red-800'; |
| 321 | + default: return 'bg-gray-100 text-gray-800'; |
| 322 | + } |
| 323 | + } |
| 324 | + |
246 | 325 | checkbox.addEventListener('change', () => { |
247 | 326 | localStorage.setItem('autoRefreshInterval', intervalInput.value); |
248 | 327 | if (checkbox.checked) { |
|
252 | 331 | localStorage.setItem('autoRefreshEnabled', 'false'); |
253 | 332 | stopRefresh(); |
254 | 333 | } |
| 334 | + toggleManualRefreshButton(); |
| 335 | + }); |
| 336 | + |
| 337 | + // Manual refresh button click handler |
| 338 | + let isRefreshing = false; |
| 339 | + const originalButtonContent = ` |
| 340 | + <svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 341 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> |
| 342 | + </svg> |
| 343 | + <span data-i18n="refresh">${translations[currentLang]['refresh']}</span> |
| 344 | + `; |
| 345 | + |
| 346 | + function resetButtonState() { |
| 347 | + manualRefreshButton.innerHTML = originalButtonContent; |
| 348 | + isRefreshing = false; |
| 349 | + } |
| 350 | + |
| 351 | + manualRefreshButton.addEventListener('click', () => { |
| 352 | + if (isRefreshing) return; // Prevent multiple clicks |
| 353 | + |
| 354 | + isRefreshing = true; |
| 355 | + updateRequestList(); |
| 356 | + |
| 357 | + // Add visual feedback |
| 358 | + manualRefreshButton.innerHTML = ` |
| 359 | + <svg class="w-4 h-4 inline mr-1 animate-spin" fill="none" stroke="currentColor" viewBox="0 0 24 24"> |
| 360 | + <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"></path> |
| 361 | + </svg> |
| 362 | + <span data-i18n="refresh">${translations[currentLang]['refresh']}</span> |
| 363 | + `; |
| 364 | + |
| 365 | + setTimeout(resetButtonState, 500); |
255 | 366 | }); |
256 | 367 |
|
257 | 368 | intervalInput.addEventListener('change', () => { |
|
273 | 384 | if (isEnabled) { |
274 | 385 | startRefresh(); |
275 | 386 | } |
| 387 | + toggleManualRefreshButton(); // Initialize manual refresh button visibility |
276 | 388 |
|
277 | 389 | // --- Collapsible sections logic --- |
278 | 390 | const sections = ['headers', 'body']; |
|
0 commit comments