diff --git a/app/api/materials/download/route.ts b/app/api/materials/download/route.ts index bbecbca..e36004a 100644 --- a/app/api/materials/download/route.ts +++ b/app/api/materials/download/route.ts @@ -22,6 +22,11 @@ import { } from '@/lib/download-errors'; import { isProductPackageType } from '@/lib/material-type-mapper'; import { ensureMaterialDownloadLogsTable } from '@/lib/material-download-log'; +import { resolveStoredUploadPath } from '@/lib/upload-paths'; + +function normalizeStoredLocalPath(filePath: string) { + return String(filePath || '').replace(/\\/g, '/'); +} // GET - 下载素材 export async function GET(request: NextRequest) { @@ -156,10 +161,8 @@ export async function GET(request: NextRequest) { ); } - let filePath = material.zipFilePath; - if (!path.isAbsolute(filePath)) { - filePath = path.join(process.cwd(), filePath); - } + let filePath = normalizeStoredLocalPath(material.zipFilePath); + filePath = path.isAbsolute(filePath) ? filePath : resolveStoredUploadPath(filePath); return serveFile(filePath, material, downloadType || '源文件', validateOnly); } catch (error) { @@ -201,7 +204,10 @@ async function handleProductPackageDownload( where: { materialId: material.id, assetType, - fileUrl: { startsWith: 'uploads/' }, // 只查询本地 zip 文件 + OR: [ + { fileUrl: { startsWith: 'uploads/' } }, + { fileUrl: { startsWith: 'uploads\\' } }, + ], }, }); } @@ -214,10 +220,10 @@ async function handleProductPackageDownload( ); } - const filePath = asset.fileUrl; - const absolutePath = path.isAbsolute(filePath) - ? filePath - : path.join(process.cwd(), filePath); + const filePath = normalizeStoredLocalPath(asset.fileUrl); + const absolutePath = path.isAbsolute(filePath) + ? filePath + : resolveStoredUploadPath(filePath); console.log(`[Download] 文件路径: ${absolutePath}`); console.log(`[Download] 文件名: ${asset.fileName}`); diff --git a/app/api/materials/upload/product-package/complete/route.ts b/app/api/materials/upload/product-package/complete/route.ts index 3f3ed5e..6166339 100644 --- a/app/api/materials/upload/product-package/complete/route.ts +++ b/app/api/materials/upload/product-package/complete/route.ts @@ -33,6 +33,7 @@ interface PsdAsset { interface CompleteRequestBody { materialId: string taskNo?: string + noBarcodes?: boolean assets: { main?: AssetUpload[] sku?: AssetUpload[] @@ -79,6 +80,9 @@ export async function POST(request: NextRequest) { taskNo: body.taskNo, assets: body.assets, operatorUserId: (session.user as any).id, + options: { + allowSkuWithoutBarcodes: body.noBarcodes === true, + }, }) return NextResponse.json({ diff --git a/components/upload/ProductPackageUploader.tsx b/components/upload/ProductPackageUploader.tsx index 12d9908..326cd7a 100644 --- a/components/upload/ProductPackageUploader.tsx +++ b/components/upload/ProductPackageUploader.tsx @@ -114,6 +114,7 @@ export default function ProductPackageUploader({ const [isPsdDragging, setIsPsdDragging] = useState(false); // 上传状态 + const [noBarcodes, setNoBarcodes] = useState(false); const [uploading, setUploading] = useState(false); const [uploadProgress, setUploadProgress] = useState(0); const [processingStep, setProcessingStep] = useState(''); @@ -406,7 +407,7 @@ export default function ProductPackageUploader({ } const barcodes = parseBarcodeInput(image.barcodeInput || ''); - if (barcodes.length === 0) { + if (!noBarcodes && barcodes.length === 0) { throw new Error(`请为 SKU 图 ${image.file.name} 至少填写 1 个产品条码`); } @@ -560,6 +561,7 @@ export default function ProductPackageUploader({ body: JSON.stringify({ materialId, taskNo, // 传递taskNo,上传完成后写入数据库 + noBarcodes, assets: { main: mainAssets, sku: skuAssets, @@ -698,9 +700,9 @@ export default function ProductPackageUploader({ -
- 系统会按素材实际内容自动判定是否整套:同时拥有主图、SKU 图、详情图时,自动标记为整套,无需手工选择。 -
+

+ 同时上传主图 + SKU 图 + 详情图时自动标记为整套 +

{/* 主图上传 */}
@@ -729,15 +731,15 @@ export default function ProductPackageUploader({ className="hidden" disabled={uploading} /> -
+
{mainImages.map((img) => (
-
+
-

- 支持 JPG、PNG、WebP 等格式,可一次选择多张或拖拽上传。每张 SKU 图都要填写 SKU 编号,并录入至少 1 个产品条码。 -

{/* SKU图上传 */}
- +
+ + +
{/* biome-ignore lint/a11y/noStaticElementInteractions: 拖拽上传区域需要使用 drag/drop 事件 */}
-
+
{skuImages.map((img) => ( -
-
+
+
-
-
- - updateSkuImageMeta(img.id, { skuCode: e.target.value })} - placeholder="例如 SKU001" - disabled={uploading} - className="w-full rounded-apple border border-gray-300 px-3 py-2 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-white" - /> -
-
- -