diff --git a/furniture-3d-system/src/app/api/asset-center/upload-image/route.ts b/furniture-3d-system/src/app/api/asset-center/upload-image/route.ts new file mode 100644 index 00000000..82fc66bf --- /dev/null +++ b/furniture-3d-system/src/app/api/asset-center/upload-image/route.ts @@ -0,0 +1,56 @@ +/** + * 包材图片上传 API + * POST /api/asset-center/upload-image + */ + +import { NextRequest } from 'next/server' +import { writeFile, mkdir } from 'fs/promises' +import { existsSync } from 'fs' +import path from 'path' +import { handleRequest, jsonSuccess, jsonError } from '@/server/core/response' +import { ErrorCode } from '@/server/core/errors' + +const ALLOWED_TYPES = ['image/jpeg', 'image/png', 'image/webp'] +const MAX_FILE_SIZE = 10 * 1024 * 1024 // 10MB + +export async function POST(request: NextRequest) { + return handleRequest(request, async ({ requestId }) => { + const formData = await request.formData() + const file = formData.get('file') as File | null + + if (!file) { + return jsonError(ErrorCode.VALIDATION_REQUIRED_FIELD, '请选择要上传的图片', 400, requestId) + } + + if (!ALLOWED_TYPES.includes(file.type)) { + return jsonError(ErrorCode.FILE_TYPE_NOT_ALLOWED, '仅支持 JPG、PNG、WebP 格式', 400, requestId) + } + + if (file.size > MAX_FILE_SIZE) { + return jsonError(ErrorCode.FILE_TOO_LARGE, '图片大小不能超过 10MB', 400, requestId) + } + + const uploadDir = path.join(process.cwd(), 'public', 'packaging-images') + + if (!existsSync(uploadDir)) { + await mkdir(uploadDir, { recursive: true }) + } + + const ext = path.extname(file.name) || '.jpg' + const timestamp = Date.now() + const random = Math.random().toString(36).slice(2, 8) + const fileName = `${timestamp}_${random}${ext}` + const filePath = path.join(uploadDir, fileName) + + const bytes = await file.arrayBuffer() + await writeFile(filePath, Buffer.from(bytes)) + + const publicUrl = `/packaging-images/${fileName}` + + return jsonSuccess({ + url: publicUrl, + fileName, + size: file.size, + }, 201, requestId) + }, { requireAuth: true }) +} diff --git a/furniture-3d-system/src/app/asset-center/packaging-materials/PackagingMaterialsClient.tsx b/furniture-3d-system/src/app/asset-center/packaging-materials/PackagingMaterialsClient.tsx index d78c2f84..8ff51525 100644 --- a/furniture-3d-system/src/app/asset-center/packaging-materials/PackagingMaterialsClient.tsx +++ b/furniture-3d-system/src/app/asset-center/packaging-materials/PackagingMaterialsClient.tsx @@ -19,8 +19,6 @@ import { Package2, Pencil, Plus, - Power, - PowerOff, ToggleLeft, ToggleRight, Trash2, @@ -777,6 +775,33 @@ export default function PackagingMaterialsClient({ } } + async function handleUploadImage(variant: AssetVariantDTO, file: File) { + try { + const formData = new FormData() + formData.append('file', file) + + const response = await fetch('/api/asset-center/upload-image', { + method: 'POST', + body: formData, + }) + + const result = await response.json() + if (!response.ok || !result.data?.url) { + throw new Error(result.message || '上传失败') + } + + const updatedVariant = await updateAssetVariant(variant.id, { + thumbnailUrl: result.data.url, + }) + + setVariants((previous) => + previous.map((item) => (item.id === updatedVariant.id ? updatedVariant : item)), + ) + } catch (error) { + setDetailError(error instanceof Error ? error.message : '图片上传失败') + } + } + const selectedFamilyCanDelete = selectedFamily != null && (familyVariantCountMap.get(selectedFamily.id) ?? 0) === 0 @@ -1006,8 +1031,8 @@ export default function PackagingMaterialsClient({
-
- +
+ 图片 名称 编码 模板类型 @@ -1072,32 +1097,47 @@ export default function PackagingMaterialsClient({ } }} > -
- {variant.thumbnailUrl ? ( - {variant.name} event.stopPropagation()} + > +
-
-

{variant.name}

+
+ {variant.name}
-
- {variant.code} +
+ {variant.code}
-
- {template?.label ?? '未设置'} +
+ {template?.label ?? '未设置'}
-
- {specLabel} +
+ {specLabel}
-
+
{variant.costPrice != null ? ( ¥ {variant.costPrice} ) : rowComputedCost != null ? ( @@ -1106,7 +1146,7 @@ export default function PackagingMaterialsClient({ 缺成本 )}
-
+
{getStatusLabel(variant.status)} @@ -1131,18 +1171,6 @@ export default function PackagingMaterialsClient({ > -