From 1134943bb2b8a03078c9688af89bb503ac4f498e Mon Sep 17 00:00:00 2001 From: "hangyu.tao" Date: Mon, 2 Feb 2026 10:56:33 +0800 Subject: [PATCH] =?UTF-8?q?fix(upload):=20=E4=BF=AE=E5=A4=8D=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E5=99=A8=E7=8E=AF=E5=A2=83=E4=B8=8B=E5=9B=BE=E7=89=87?= =?UTF-8?q?=E8=AF=BB=E5=8F=96NotReadableError=EF=BC=8C=E6=94=B9=E7=94=B1?= =?UTF-8?q?=E5=90=8E=E7=AB=AF=E6=8E=A5=E6=94=B6=E6=96=87=E4=BB=B6=E6=B5=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/index.html | 2 +- frontend/js/main.js | 110 ++++++++++++++++++-------------------------- 2 files changed, 46 insertions(+), 66 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index c67be98..b233c2b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1174,7 +1174,7 @@ - + diff --git a/frontend/js/main.js b/frontend/js/main.js index 25c01fa..41341f2 100644 --- a/frontend/js/main.js +++ b/frontend/js/main.js @@ -61,7 +61,7 @@ function canDelete() { } document.addEventListener("DOMContentLoaded", function () { - console.log("CRM System Main JS v3.5 Loaded - " + new Date().toLocaleString()); + console.log("CRM System Main JS v4.0 Loaded - " + new Date().toLocaleString()); // 登录守卫 const token = localStorage.getItem("crmToken"); if (!token && !window.location.pathname.endsWith("login.html")) { @@ -3240,76 +3240,56 @@ document.addEventListener("DOMContentLoaded", function () { if (!this.files.length) return; const files = Array.from(this.files); - const newBase64Images = []; + if (files.length === 0) return; - for (const file of files) { - // 验证是否为图片 - if (!file.type.startsWith("image/")) { + const formData = new FormData(); + files.forEach(file => { + if (file.type.startsWith("image/")) { + formData.append("screenshots", file); + } else { console.warn(`File is not an image: ${file.name}`); - continue; } + }); - // Helper to convert file to base64 with retry - const fileToBase64 = async (f, attempt = 1) => { - try { - return await new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = () => resolve(reader.result); - reader.onerror = () => reject(reader.error || new Error("FileReader error")); - reader.onabort = () => reject(new Error("File read aborted")); - reader.readAsDataURL(f); - }); - } catch (err) { - if (attempt < 3 && err.name === "NotReadableError") { - console.warn(`Retrying ${f.name} read due to NotReadableError (attempt ${attempt})...`); - await new Promise(r => setTimeout(r, 200 * attempt)); - return fileToBase64(f, attempt + 1); - } - throw err; - } - }; - - try { - console.log(`[v3.5] Processing ${file.name} (${file.size} bytes)...`); - const base64 = await fileToBase64(file); - newBase64Images.push(base64); - console.log(`Successfully converted ${file.name}`); - } catch (error) { - console.error(`Primary method failed for ${file.name}:`, error); - - // Fallback: arrayBuffer - try { - console.log(`Trying arrayBuffer fallback for ${file.name}...`); - const buffer = await file.arrayBuffer(); - const bytes = new Uint8Array(buffer); - let binary = ""; - const chunkSize = 8192; - for (let i = 0; i < bytes.length; i += chunkSize) { - const chunk = bytes.slice(i, i + chunkSize); - binary += String.fromCharCode.apply(null, chunk); - } - const b64 = `data:${file.type};base64,${btoa(binary)}`; - newBase64Images.push(b64); - console.log(`Fallback successful for ${file.name}`); - } catch (fallbackError) { - console.error(`Both methods failed for ${file.name}:`, fallbackError); - alert(`无法读取文件 "${file.name}"。\n这种情况通常是浏览器或系统文件锁定导致的。\n\n建议尝试:\n1. 刷新页面后再试\n2. 使用浏览器的“无痕模式”\n3. 确认文件没有被其他软件打开`); - } - } + if (formData.getAll("screenshots").length === 0) { + alert("请选择有效的图片文件"); + return; } - if (type === "create") { - createUploadedScreenshots = [ - ...createUploadedScreenshots, - ...newBase64Images, - ]; - renderScreenshotPreviews("create", createUploadedScreenshots); - } else { - editUploadedScreenshots = [ - ...editUploadedScreenshots, - ...newBase64Images, - ]; - renderScreenshotPreviews("edit", editUploadedScreenshots); + try { + console.log(`[v4.0] Uploading ${files.length} files to server...`); + const response = await authenticatedFetch("/api/upload", { + method: "POST", + body: formData, + // Note: When sending FormData, the browser automatically sets the correct Content-Type with boundary + }); + + if (response.ok) { + const result = await response.json(); + const newBase64Images = result.filePaths || []; + console.log(`Successfully uploaded ${newBase64Images.length} images`); + + if (type === "create") { + createUploadedScreenshots = [ + ...createUploadedScreenshots, + ...newBase64Images, + ]; + renderScreenshotPreviews("create", createUploadedScreenshots); + } else { + editUploadedScreenshots = [ + ...editUploadedScreenshots, + ...newBase64Images, + ]; + renderScreenshotPreviews("edit", editUploadedScreenshots); + } + } else { + const errorText = await response.text(); + console.error("Upload failed:", errorText); + alert("图片上传失败,请稍后重试。"); + } + } catch (error) { + console.error("Error during upload:", error); + alert("上传过程出错,可能是浏览器权限问题或网络连接中断。"); } // 清除 input,以便再次选择同一张图片