diff --git a/.trae/documents/前端生产级健壮性提升计划.md b/.trae/documents/前端生产级健壮性提升计划.md new file mode 100644 index 0000000..5b9a90c --- /dev/null +++ b/.trae/documents/前端生产级健壮性提升计划.md @@ -0,0 +1,67 @@ +## 前端生产级健壮性提升计划 + +### 1. 核心改进点 + +#### 添加刷新按钮 +- 在下载区域标题旁添加刷新按钮 +- 点击按钮重新加载App列表 +- 保持与现有按钮风格一致 + +#### 增强API调用健壮性 +- 统一API请求处理,添加错误处理 +- 完善各种错误场景的用户反馈 +- 添加加载状态提示 +- 处理网络错误和超时情况 + +#### 改进用户体验 +- 添加操作成功/失败的视觉反馈 +- 优化表单验证 +- 改进文件上传反馈 + +#### 前后端适配协调 +- 确保前端请求与后端API完全匹配 +- 处理后端返回的各种响应状态 +- 优化数据处理逻辑 + +### 2. 实现细节 + +#### 文件修改 +1. **index.html** + - 在下载区域标题旁添加刷新按钮 + - 添加加载状态指示器 + +2. **script.js** + - 添加`refreshAppList()`函数 + - 统一API请求处理函数 + - 完善错误处理逻辑 + - 添加加载状态管理 + - 优化现有函数,确保与后端API适配 + +3. **style.css** + - 微调样式以适应刷新按钮 + - 添加加载状态样式 + +#### 关键功能实现 +- **刷新功能**:点击按钮调用`loadAppList()`重新加载App列表 +- **API健壮性**:添加try-catch、错误状态码处理、超时处理 +- **用户反馈**:添加加载中提示、操作结果反馈 +- **表单验证**:增强对输入的验证,防止无效请求 + +### 3. 保持极简风格 +- 不添加复杂UI元素 +- 保持现有配色和布局 +- 使用原生JavaScript实现 +- 不引入额外库 + +### 4. 预期效果 +- 前端与后端API适配更协调 +- 提供更好的用户反馈和错误处理 +- 添加刷新功能方便用户手动更新App列表 +- 提升系统整体健壮性和用户体验 + +### 5. 测试要点 +- 刷新功能正常工作 +- API请求错误处理 +- 网络异常情况处理 +- 各种边界情况测试 +- 保持极简风格不变 \ No newline at end of file diff --git a/background/apps.json b/background/apps.json index e9418b7..8b13789 100644 --- a/background/apps.json +++ b/background/apps.json @@ -1,8 +1 @@ -[ - { - "id": "1766503652493156300", - "name": "MobaXterm_Portable_v25.4", - "fileName": "MobaXterm_Portable_v25.4.zip", - "date": "2025-12-23 23:27:32" - } -] + diff --git a/background/files/b8bef48ec6f9c42a90f3843b076dcf80.zip b/background/files/b8bef48ec6f9c42a90f3843b076dcf80.zip deleted file mode 100644 index 4240621..0000000 Binary files a/background/files/b8bef48ec6f9c42a90f3843b076dcf80.zip and /dev/null differ diff --git a/background/main.go b/background/main.go index eaa3b76..422499f 100644 --- a/background/main.go +++ b/background/main.go @@ -31,7 +31,7 @@ var ( appsMutex sync.RWMutex // 读写锁,提高并发性能 filesDir = "./files" jsonFile = "./apps.json" - port = ":6902" + port = ":6903" // 修改端口号为6903 ) // 生成唯一文件名 @@ -122,6 +122,7 @@ func main() { api.GET("/apps", getApps) api.POST("/apps", uploadApp) api.GET("/apps/:id", downloadApp) + api.GET("/apps/:id/", downloadApp) // 处理以/结尾的URL } // 启动服务器 @@ -228,6 +229,6 @@ func downloadApp(c *gin.Context) { return } - // 提供文件下载,设置原始文件名 + // 使用FileAttachment直接提供文件下载,避免重定向 c.FileAttachment(filePath, targetApp.FileName) } diff --git a/index.html b/index.html index 14bbe5a..b84c12c 100644 --- a/index.html +++ b/index.html @@ -32,7 +32,13 @@
暂无App
'; - return; - } - - appList.innerHTML = ''; - apps.forEach(app => { - const appItem = document.createElement('div'); - appItem.className = 'app-item'; - - const appInfo = document.createElement('div'); - appInfo.className = 'app-info'; - - const appName = document.createElement('h3'); - appName.textContent = app.name; - - const appDate = document.createElement('p'); - appDate.textContent = `上传时间:${app.date}`; - - const downloadBtn = document.createElement('button'); - downloadBtn.className = 'download-btn'; - downloadBtn.textContent = '下载'; - downloadBtn.onclick = () => downloadApp(app.id, app.fileName); - - const copyLinkBtn = document.createElement('button'); - copyLinkBtn.className = 'download-btn'; - copyLinkBtn.textContent = '复制直链'; - copyLinkBtn.onclick = () => copyDirectLink(app.id, app.fileName); - - appInfo.appendChild(appName); - appInfo.appendChild(appDate); - appItem.appendChild(appInfo); - - const buttonsContainer = document.createElement('div'); - buttonsContainer.style.display = 'flex'; - buttonsContainer.style.gap = '10px'; - buttonsContainer.appendChild(downloadBtn); - buttonsContainer.appendChild(copyLinkBtn); - - appItem.appendChild(buttonsContainer); - appList.appendChild(appItem); - }); - }) - .catch(error => { - console.error('获取App列表失败:', error); - appList.innerHTML = '获取App列表失败
'; + try { + showLoading(); + + const response = await fetch(`${API_BASE_URL}/apps`, { + method: 'GET', + headers: { + 'Accept': 'application/json' + }, + timeout: 10000 // 10秒超时 }); + + if (!response.ok) { + throw new Error(`HTTP错误!状态:${response.status}`); + } + + const apps = await response.json(); + + if (apps.length === 0) { + appList.innerHTML = '暂无App
'; + return; + } + + appList.innerHTML = ''; + apps.forEach(app => { + const appItem = document.createElement('div'); + appItem.className = 'app-item'; + + const appInfo = document.createElement('div'); + appInfo.className = 'app-info'; + + const appName = document.createElement('h3'); + appName.textContent = app.name; + + const appDate = document.createElement('p'); + appDate.textContent = `上传时间:${app.date}`; + + const downloadBtn = document.createElement('button'); + downloadBtn.className = 'download-btn'; + downloadBtn.textContent = '下载'; + downloadBtn.onclick = () => downloadApp(app.id, app.fileName); + + const copyLinkBtn = document.createElement('button'); + copyLinkBtn.className = 'download-btn'; + copyLinkBtn.textContent = '复制直链'; + copyLinkBtn.onclick = () => copyDirectLink(app.id, app.fileName); + + appInfo.appendChild(appName); + appInfo.appendChild(appDate); + appItem.appendChild(appInfo); + + const buttonsContainer = document.createElement('div'); + buttonsContainer.style.display = 'flex'; + buttonsContainer.style.gap = '10px'; + buttonsContainer.appendChild(downloadBtn); + buttonsContainer.appendChild(copyLinkBtn); + + appItem.appendChild(buttonsContainer); + appList.appendChild(appItem); + }); + } catch (error) { + console.error('获取App列表失败:', error); + appList.innerHTML = `获取App列表失败:${error.message}
`; + } finally { + hideLoading(); + } } // 上传App -function uploadApp() { +async function uploadApp() { let appName = document.getElementById('appName').value; const appFile = document.getElementById('appFile').files[0]; const message = document.getElementById('uploadMessage'); @@ -135,15 +181,25 @@ function uploadApp() { formData.append('name', appName); formData.append('file', appFile); - fetch(`${API_BASE_URL}/apps`, { - method: 'POST', - body: formData - }) - .then(response => response.json()) - .then(result => { + try { + message.textContent = '上传中...'; + message.style.color = '#0000ff'; + + const response = await fetch(`${API_BASE_URL}/apps`, { + method: 'POST', + body: formData, + timeout: 30000 // 30秒超时 + }); + + if (!response.ok) { + throw new Error(`HTTP错误!状态:${response.status}`); + } + + const result = await response.json(); + if (result.success) { message.textContent = '上传成功!'; - message.style.color = '#0000ff'; + message.style.color = '#008000'; loadAppList(); // 清空表单 document.getElementById('appName').value = ''; @@ -152,22 +208,26 @@ function uploadApp() { message.textContent = '上传失败:' + (result.message || '未知错误'); message.style.color = '#ff0000'; } - }) - .catch(error => { + } catch (error) { console.error('上传失败:', error); - message.textContent = '上传失败,请重试!'; + message.textContent = '上传失败:' + error.message; message.style.color = '#ff0000'; - }); + } } // 下载App function downloadApp(appId, fileName) { - const link = document.createElement('a'); - link.href = `${API_BASE_URL}/apps/${appId}`; - link.download = fileName; - document.body.appendChild(link); - link.click(); - document.body.removeChild(link); + try { + const link = document.createElement('a'); + link.href = `${API_BASE_URL}/apps/${appId}`; + link.download = fileName; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } catch (error) { + console.error('下载失败:', error); + alert('下载失败:' + error.message); + } } // 复制直链 @@ -202,6 +262,7 @@ function copyDirectLink(appId, fileName) { }) .catch(err => { console.error('复制失败:', err); + alert('复制失败:' + err.message); }); } diff --git a/style.css b/style.css index 9c764c3..726c318 100644 --- a/style.css +++ b/style.css @@ -50,6 +50,16 @@ button { padding: 10px 20px; cursor: pointer; font-size: 14px; + background-color: #fff; + transition: background-color 0.2s; +} + +button:hover { + background-color: #f0f0f0; +} + +button:active { + background-color: #e0e0e0; } p { @@ -90,6 +100,12 @@ p { font-size: 12px; } +#loadingIndicator { + font-size: 12px; + color: #666; +} + +/* 响应式设计 */ @media (max-width: 600px) { .container { margin: 10px; @@ -108,4 +124,9 @@ p { .download-btn { margin-top: 10px; } + + /* 刷新按钮和加载状态在移动端的适配 */ + #downloadSection h2 { + margin-bottom: 10px; + } } \ No newline at end of file