From ea491c8d022d4d8c8ed6ccb28bf7d143f52d34b1 Mon Sep 17 00:00:00 2001 From: Trae AI Date: Fri, 2 Jan 2026 16:26:30 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E8=A7=86=E9=A2=91=E4=B8=8A?= =?UTF-8?q?=E4=BC=A0=E5=92=8C=E6=98=BE=E7=A4=BA=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Reject.js | 170 +++++++++++++++++++++++---- node_modules/.package-lock.json | 4 + package-lock.json | 4 + supply.html | 199 ++++++++++++++++++++++++++++---- 4 files changed, 334 insertions(+), 43 deletions(-) diff --git a/Reject.js b/Reject.js index 948d720..8f17782 100644 --- a/Reject.js +++ b/Reject.js @@ -768,37 +768,48 @@ app.post('/api/supplies/create', async (req, res) => { // 生成唯一的productId const productId = `product_${Date.now()}_${Math.floor(Math.random() * 1000)}`; - // 处理图片上传 + // 处理媒体文件上传(图片和视频) let uploadedImageUrls = []; if (Array.isArray(imageUrls) && imageUrls.length > 0) { - console.log('开始处理图片上传,共', imageUrls.length, '张图片'); + console.log('开始处理媒体文件上传,共', imageUrls.length, '个文件'); - for (const imageUrl of imageUrls) { - if (imageUrl.startsWith('data:image/')) { + for (const mediaUrl of imageUrls) { + if (mediaUrl.startsWith('data:image/') || mediaUrl.startsWith('data:video/')) { // 处理DataURL - const base64Data = imageUrl.replace(/^data:image\/(png|jpeg|jpg|gif);base64,/, ''); + let base64Data, ext, fileType; + if (mediaUrl.startsWith('data:image/')) { + // 图片类型 + base64Data = mediaUrl.replace(/^data:image\/(png|jpeg|jpg|gif);base64,/, ''); + ext = mediaUrl.match(/^data:image\/(png|jpeg|jpg|gif);base64,/)?.[1] || 'png'; + fileType = 'image'; + } else { + // 视频类型 + base64Data = mediaUrl.replace(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/, ''); + ext = mediaUrl.match(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/)?.[1] || 'mp4'; + fileType = 'video'; + } + let buffer = Buffer.from(base64Data, 'base64'); - const ext = imageUrl.match(/^data:image\/(png|jpeg|jpg|gif);base64,/)?.[1] || 'png'; const filename = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}.${ext}`; try { // 不再添加水印,前端已处理 console.log('【水印处理】前端已添加水印,跳过后端水印处理'); - // 使用OSS上传带水印的图片 - const ossUrl = await OssUploader.uploadBuffer(buffer, filename, `products/${productName || 'general'}`, 'image'); + // 使用OSS上传媒体文件 + const ossUrl = await OssUploader.uploadBuffer(buffer, filename, `products/${productName || 'general'}`, fileType); uploadedImageUrls.push(ossUrl); - console.log('图片上传成功:', ossUrl); + console.log(`${fileType}上传成功:`, ossUrl); } catch (uploadError) { - console.error('图片上传失败:', uploadError.message); - // 继续上传其他图片,不中断流程 + console.error(`${fileType}上传失败:`, uploadError.message); + // 继续上传其他文件,不中断流程 } } else { // 已经是URL,直接使用 - uploadedImageUrls.push(imageUrl); + uploadedImageUrls.push(mediaUrl); } } - console.log('图片处理完成,成功上传', uploadedImageUrls.length, '张图片'); + console.log('媒体文件处理完成,成功上传', uploadedImageUrls.length, '个文件'); } // 创建商品数据 @@ -883,16 +894,86 @@ app.post('/api/supplies/create', async (req, res) => { } }); -// 图片上传API - /api/upload-image +// 媒体文件上传API - /api/upload-media +console.log('正在注册媒体文件上传API路由: /api/upload-media'); +app.post('/api/upload-media', async (req, res) => { + console.log('收到媒体文件上传请求:', req.body); + try { + const { fileData, fileName, folder = 'general' } = req.body; + + // 验证参数 + if (!fileData) { + return sendResponse(res, false, null, '文件数据不能为空'); + } + + let base64Data, ext, fileType; + if (fileData.startsWith('data:image/')) { + // 图片类型 + base64Data = fileData.replace(/^data:image\/(png|jpeg|jpg|gif);base64,/, ''); + ext = fileData.match(/^data:image\/(png|jpeg|jpg|gif);base64,/)?.[1] || 'png'; + fileType = 'image'; + } else if (fileData.startsWith('data:video/')) { + // 视频类型 + base64Data = fileData.replace(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/, ''); + ext = fileData.match(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/)?.[1] || 'mp4'; + fileType = 'video'; + } else { + return sendResponse(res, false, null, '不支持的文件类型'); + } + + let buffer = Buffer.from(base64Data, 'base64'); + const filename = fileName || `${Date.now()}-${Math.random().toString(36).substr(2, 9)}.${ext}`; + + try { + // 使用OSS上传媒体文件 + const ossUrl = await OssUploader.uploadBuffer(buffer, filename, `uploads/${folder}`, fileType); + console.log(`${fileType}上传成功:`, ossUrl); + + sendResponse(res, true, { + url: ossUrl, + fileType: fileType, + message: `${fileType}上传成功` + }, `${fileType}上传成功`); + } catch (uploadError) { + console.error(`${fileType}上传失败:`, uploadError.message); + sendResponse(res, false, null, `${fileType}上传失败: ${uploadError.message}`); + } + } catch (error) { + console.error('媒体文件上传API错误:', error.message); + sendResponse(res, false, null, `媒体文件上传失败: ${error.message}`); + } +}); + +// 图片上传API - /api/upload-image(兼容旧接口) console.log('正在注册图片上传API路由: /api/upload-image'); app.post('/api/upload-image', async (req, res) => { - console.log('收到图片上传请求'); - // 注意:这里需要实现实际的图片上传逻辑,包括OSS配置 - // 由于当前缺少OSS配置,返回模拟数据 - sendResponse(res, true, { - imageUrl: 'https://example.com/image.jpg', - message: '图片上传成功' - }, '图片上传成功'); + console.log('收到图片上传请求,转发到媒体文件上传API'); + // 将请求转发到媒体文件上传API + const uploadMediaHandler = app._router.stack.find(layer => + layer.route && layer.route.path === '/api/upload-media' && layer.route.methods['post'] + ); + + if (uploadMediaHandler && uploadMediaHandler.route && uploadMediaHandler.route.stack[0]) { + return uploadMediaHandler.route.stack[0].handle(req, res); + } else { + sendResponse(res, false, null, '图片上传API内部错误'); + } +}); + +// 视频上传API - /api/upload-video(专门的视频上传接口) +console.log('正在注册视频上传API路由: /api/upload-video'); +app.post('/api/upload-video', async (req, res) => { + console.log('收到视频上传请求,转发到媒体文件上传API'); + // 将请求转发到媒体文件上传API + const uploadMediaHandler = app._router.stack.find(layer => + layer.route && layer.route.path === '/api/upload-media' && layer.route.methods['post'] + ); + + if (uploadMediaHandler && uploadMediaHandler.route && uploadMediaHandler.route.stack[0]) { + return uploadMediaHandler.route.stack[0].handle(req, res); + } else { + sendResponse(res, false, null, '视频上传API内部错误'); + } }); // 获取审核失败原因API - /api/supplies/:id/reject-reason @@ -1089,6 +1170,53 @@ app.put('/api/supplies/:id/edit', async (req, res) => { } } +<<<<<<< Updated upstream +======= + // 处理媒体文件上传(图片和视频) + let uploadedImageUrls = []; + if (Array.isArray(imageUrls) && imageUrls.length > 0) { + console.log('开始处理编辑媒体文件上传,共', imageUrls.length, '个文件'); + + for (const mediaUrl of imageUrls) { + if (mediaUrl.startsWith('data:image/') || mediaUrl.startsWith('data:video/')) { + // 处理DataURL + let base64Data, ext, fileType; + if (mediaUrl.startsWith('data:image/')) { + // 图片类型 + base64Data = mediaUrl.replace(/^data:image\/(png|jpeg|jpg|gif);base64,/, ''); + ext = mediaUrl.match(/^data:image\/(png|jpeg|jpg|gif);base64,/)?.[1] || 'png'; + fileType = 'image'; + } else { + // 视频类型 + base64Data = mediaUrl.replace(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/, ''); + ext = mediaUrl.match(/^data:video\/(mp4|mov|avi|wmv|flv);base64,/)?.[1] || 'mp4'; + fileType = 'video'; + } + + let buffer = Buffer.from(base64Data, 'base64'); + const filename = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}.${ext}`; + + try { + // 不再添加水印,前端已处理 + console.log('【水印处理】前端已添加水印,跳过后端水印处理'); + + // 使用OSS上传媒体文件 + const ossUrl = await OssUploader.uploadBuffer(buffer, filename, `products/${productName || 'general'}`, fileType); + uploadedImageUrls.push(ossUrl); + console.log(`${fileType}上传成功:`, ossUrl); + } catch (uploadError) { + console.error(`${fileType}上传失败:`, uploadError.message); + // 继续上传其他文件,不中断流程 + } + } else { + // 已经是URL,直接使用 + uploadedImageUrls.push(mediaUrl); + } + } + console.log('媒体文件处理完成,成功上传', uploadedImageUrls.length, '个文件'); + } + +>>>>>>> Stashed changes // 更新货源信息 const updateQuery = ` UPDATE products diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json index ee13f04..f39158a 100644 --- a/node_modules/.package-lock.json +++ b/node_modules/.package-lock.json @@ -1,5 +1,9 @@ { +<<<<<<< Updated upstream "name": "Review2", +======= + "name": "boss", +>>>>>>> Stashed changes "lockfileVersion": 3, "requires": true, "packages": { diff --git a/package-lock.json b/package-lock.json index a42c8c5..0ba49cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,5 +1,9 @@ { +<<<<<<< Updated upstream "name": "Review2", +======= + "name": "boss", +>>>>>>> Stashed changes "lockfileVersion": 3, "requires": true, "packages": { diff --git a/supply.html b/supply.html index 395d679..0b149c2 100644 --- a/supply.html +++ b/supply.html @@ -1052,13 +1052,13 @@ - +
- +
+
- +
图片要求:
1. 箱子堆头图(不得出现地址、电话及货源信息,直营包场除外);
@@ -1066,7 +1066,11 @@
3. 敲开鸡蛋后清晰显示蛋清、蛋黄状态,以体现新鲜度;
4. 其他能佐证蛋重、品种的辅助图片。
+<<<<<<< Updated upstream
最多上传5张图片
+======= +
最多上传2个媒体文件(图片或视频)
+>>>>>>> Stashed changes
@@ -1398,13 +1402,21 @@ - +
- +
+<<<<<<< Updated upstream
提示:图片暂不支持编辑
+======= +
最多上传2个媒体文件(图片或视频)
+ + +>>>>>>> Stashed changes
@@ -1860,14 +1872,17 @@ const reader = new FileReader(); reader.onload = async function(e) { - let imageUrl = e.target.result; + let mediaUrl = e.target.result; + let isVideo = file.type.startsWith('video/'); - // 为图片添加水印 - imageUrl = await addWatermarkToImage(imageUrl); + // 只为图片添加水印,视频不添加 + if (!isVideo) { + mediaUrl = await addWatermarkToImage(mediaUrl); + } - supplyData.uploadedImages.push(imageUrl); + supplyData.uploadedImages.push(mediaUrl); renderUploadedImages(); - // 图片上传后自动保存表单数据 + // 媒体文件上传后自动保存表单数据 saveFormData(); }; @@ -3563,6 +3578,94 @@ document.getElementById('imageUpload').click(); } +<<<<<<< Updated upstream +======= + // 触发编辑页面图片上传 + function triggerEditImageUpload() { + document.getElementById('editImageUpload').click(); + } + + // 处理编辑页面图片上传 + function handleEditImageUpload(event) { + const files = event.target.files; + const editUploadImages = document.getElementById('editUploadImages'); + + for (let i = 0; i < files.length; i++) { + if (editCurrentImages.length >= 2) { + alert('最多只能上传2个媒体文件(图片或视频)'); + break; + } + + const file = files[i]; + const reader = new FileReader(); + + reader.onload = async function(e) { + let mediaUrl = e.target.result; + let isVideo = file.type.startsWith('video/'); + + try { + // 只为图片添加水印,视频不添加 + if (!isVideo) { + mediaUrl = await addWatermarkToImage(mediaUrl); + } + + // 添加到当前媒体列表 + editCurrentImages.push(mediaUrl); + + // 更新显示 + updateEditImageDisplay(); + } catch (error) { + console.error('媒体处理失败:', error); + alert('媒体处理失败,请重试'); + } + }; + + reader.readAsDataURL(file); + } + + // 清空文件输入,以便再次选择同一文件 + event.target.value = ''; + } + + // 删除编辑页面图片 + function deleteEditImage(imageUrl) { + // 从当前图片列表中删除 + const index = editCurrentImages.indexOf(imageUrl); + if (index > -1) { + editCurrentImages.splice(index, 1); + } + + // 更新显示 + updateEditImageDisplay(); + } + + // 更新编辑页面媒体显示(图片或视频) + function updateEditImageDisplay() { + const editUploadImages = document.getElementById('editUploadImages'); + editUploadImages.innerHTML = ''; + + editCurrentImages.forEach(mediaUrl => { + const imageElement = document.createElement('div'); + imageElement.className = 'upload-image'; + + let mediaHtml = ''; + if (mediaUrl.startsWith('data:video/') || mediaUrl.endsWith('.mp4') || mediaUrl.endsWith('.mov') || mediaUrl.endsWith('.avi') || mediaUrl.endsWith('.wmv') || mediaUrl.endsWith('.flv')) { + // 视频文件 + mediaHtml = ``; + } else { + // 图片文件 + mediaHtml = `商品图片`; + } + + imageElement.innerHTML = ` + ${mediaHtml} + + `; + editUploadImages.appendChild(imageElement); + }); + } + +>>>>>>> Stashed changes // 为图片添加水印(前端Canvas实现) function addWatermarkToImage(imageUrl) { return new Promise((resolve, reject) => { @@ -3616,8 +3719,13 @@ const uploadArea = document.getElementById('uploadImages'); for (let i = 0; i < files.length; i++) { +<<<<<<< Updated upstream if (supplyData.uploadedImages.length >= 5) { alert('最多只能上传5张图片'); +======= + if (supplyData.uploadedImages.length >= 2) { + alert('最多只能上传2个媒体文件(图片或视频)'); +>>>>>>> Stashed changes break; } @@ -3643,16 +3751,26 @@ event.target.value = ''; } - // 渲染已上传图片 + // 渲染已上传媒体文件(图片和视频) function renderUploadedImages() { const uploadArea = document.getElementById('uploadImages'); uploadArea.innerHTML = ''; - supplyData.uploadedImages.forEach((imageUrl, index) => { + supplyData.uploadedImages.forEach((mediaUrl, index) => { const imageElement = document.createElement('div'); imageElement.className = 'upload-image'; + + let mediaHtml = ''; + if (mediaUrl.startsWith('data:video/')) { + // 视频文件 + mediaHtml = ``; + } else { + // 图片文件 + mediaHtml = `商品图片`; + } + imageElement.innerHTML = ` - 商品图片 + ${mediaHtml}
`; uploadArea.appendChild(imageElement); @@ -3942,28 +4060,65 @@ } const previewModal = document.getElementById('imagePreview'); - const previewImage = document.getElementById('previewImage'); - // 更新图片显示 + // 更新媒体显示 updatePreviewImage(); - // 重置缩放和拖动状态 - resetImageTransform(); + // 重置缩放和拖动状态(仅图片需要) + const isVideo = imageUrl.startsWith('data:video/') || imageUrl.endsWith('.mp4') || imageUrl.endsWith('.mov') || imageUrl.endsWith('.avi') || imageUrl.endsWith('.wmv') || imageUrl.endsWith('.flv'); + if (!isVideo) { + resetImageTransform(); + } previewModal.classList.add('active'); - // 设置图片查看器事件 - setupImageViewerEvents(); + // 设置图片查看器事件(仅图片需要) + if (!isVideo) { + setupImageViewerEvents(); + } } - // 更新预览图片 + // 专门的视频预览函数 + function previewMedia(mediaUrl, mediaList = []) { + previewImage(mediaUrl, mediaList); + } + + // 更新预览媒体(图片或视频) function updatePreviewImage() { + // 获取预览元素 const previewImage = document.getElementById('previewImage'); + const previewVideo = document.createElement('video'); + previewVideo.id = 'previewVideo'; + previewVideo.className = 'preview-video'; + previewVideo.style.maxWidth = '100%'; + previewVideo.style.maxHeight = '100%'; + previewVideo.controls = true; + previewVideo.muted = false; + const previewCounter = document.getElementById('previewCounter'); + const mediaUrl = currentPreviewImages[currentPreviewIndex]; + + // 判断是图片还是视频 + const isVideo = mediaUrl.startsWith('data:video/') || mediaUrl.endsWith('.mp4') || mediaUrl.endsWith('.mov') || mediaUrl.endsWith('.avi') || mediaUrl.endsWith('.wmv') || mediaUrl.endsWith('.flv'); - previewImage.src = currentPreviewImages[currentPreviewIndex]; + // 移除旧的视频元素(如果存在) + const existingVideo = document.getElementById('previewVideo'); + if (existingVideo) { + existingVideo.remove(); + } + + if (isVideo) { + // 视频预览 + previewImage.style.display = 'none'; + previewVideo.src = mediaUrl; + previewImage.parentElement.appendChild(previewVideo); + } else { + // 图片预览 + previewImage.style.display = 'block'; + previewImage.src = mediaUrl; + } - // 更新图片计数器 + // 更新计数器 if (previewCounter) { previewCounter.textContent = `${currentPreviewIndex + 1}/${currentPreviewImages.length}`; }