diff --git a/app/assets/material-library/page.tsx b/app/assets/material-library/page.tsx index 1295ede..4ca4148 100644 --- a/app/assets/material-library/page.tsx +++ b/app/assets/material-library/page.tsx @@ -1572,6 +1572,7 @@ export default function MaterialLibraryPage() { {/* Upload Modal */} {showUploadModal && ( setShowUploadModal(false)} onSuccess={() => { setShowUploadModal(false) diff --git a/app/materials/page.tsx b/app/materials/page.tsx index 3b49f9a..0373461 100644 --- a/app/materials/page.tsx +++ b/app/materials/page.tsx @@ -1567,6 +1567,7 @@ export default function Home() { {/* Upload Modal */} {showUploadModal && ( setShowUploadModal(false)} onSuccess={() => { setShowUploadModal(false) diff --git a/components/UploadModal.task-other.test.tsx b/components/UploadModal.task-other.test.tsx index 167e46c..b9d7e34 100644 --- a/components/UploadModal.task-other.test.tsx +++ b/components/UploadModal.task-other.test.tsx @@ -56,7 +56,7 @@ describe('UploadModal - 任务-其他', () => { expect(await screen.findByLabelText('任务号')).toBeInTheDocument() expect(screen.getByLabelText('钉钉审批编号')).toBeInTheDocument() - expect(screen.getByLabelText('素材类型 *')).toHaveValue('3D场景') + expect(screen.getByRole('button', { name: '素材类型 *' })).toHaveTextContent('3D场景') }) test('任务-其他应使用公共上传面板形式,任务号与钉钉审批编号并列显示', async () => { @@ -64,8 +64,8 @@ describe('UploadModal - 任务-其他', () => { render() - const materialTypeSelect = (await screen.findAllByRole('combobox'))[0] - await user.selectOptions(materialTypeSelect, '任务-其他') + await user.click(screen.getByRole('button', { name: '素材类型 *' })) + await user.click(await screen.findByRole('option', { name: '任务-其他' })) await waitFor(() => { expect(screen.getByLabelText('任务号 *')).toBeInTheDocument() @@ -86,8 +86,8 @@ describe('UploadModal - 任务-其他', () => { render() - const materialTypeSelect = (await screen.findAllByRole('combobox'))[0] - await user.selectOptions(materialTypeSelect, '任务-其他') + await user.click(screen.getByRole('button', { name: '素材类型 *' })) + await user.click(await screen.findByRole('option', { name: '任务-其他' })) const selectButton = await screen.findByRole('button', { name: '选择素材文件' }) const dropZone = selectButton.closest('[aria-disabled]') as HTMLDivElement diff --git a/components/UploadModal.tsx b/components/UploadModal.tsx index 3bc5ee3..ce0f41d 100644 --- a/components/UploadModal.tsx +++ b/components/UploadModal.tsx @@ -3,6 +3,7 @@ import { useState, useRef, useEffect, useCallback } from 'react' import { useGSAP } from '@gsap/react' import gsap from 'gsap' +import { FileUp } from 'lucide-react' import Image from 'next/image' import { chunkUpload } from '@/lib/chunk-upload' import { @@ -51,6 +52,7 @@ import { useCategoryData } from '@/hooks/useCategoryData' import { parseApiError } from '@/lib/error-handler' import { parseSPU } from '@/lib/spu-parser' import UploadForm from '@/components/upload/UploadForm' +import MaterialTypeSelect from '@/components/upload/MaterialTypeSelect' import { BatchMusicList, BatchVideoList, @@ -79,6 +81,7 @@ import { fieldLabelClass, inkButtonClass, outlineButtonClass, + uploadAreaButtonClass, } from '@/components/upload/upload-field-styles' import { getSpuProductModelValidationError, @@ -91,6 +94,7 @@ gsap.registerPlugin(useGSAP) interface UploadModalProps { onClose: () => void onSuccess: () => void + materialTypeCounts?: Record } function getSupportedMainFileDescription(materialType: string): string { @@ -123,7 +127,7 @@ function isPreviewOptionalMaterialType(materialType: string): boolean { return isVideoMaterialType(materialType) || PREVIEW_OPTIONAL_TYPES.includes(materialType) } -export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { +export default function UploadModal({ onClose, onSuccess, materialTypeCounts = {} }: UploadModalProps) { const SPECIAL_UPLOAD_TYPES = ['任务-详情', '任务-视频截图', '任务-页面', '证书-检测报告'] as const const [previewImages, setPreviewImages] = useState([]) // 预览图片数组(支持多图) const [previewUrls, setPreviewUrls] = useState([]) // 预览图片预览URLs @@ -2009,7 +2013,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { aria-labelledby="upload-material-title" className={`max-h-[90vh] w-full overflow-y-auto rounded-[12px] border border-[#e7e8ea] bg-white ${materialType === '任务-详情' ? 'max-w-4xl' : 'max-w-2xl'}`} > -
+

- 把文件拖进虚线框即可。内网请先切到顶部提示里的内网地址,上传会更快。 + 按素材类型填写信息。内网请切到顶部提示的地址,上传更快。

-
+
-
{!usesDedicatedUploader && ( @@ -2074,7 +2069,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 任务-详情上传组件 */} {materialType === '任务-详情' && ( -
+
{ onSuccess(); @@ -2093,7 +2088,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 任务-视频截图上传组件 */} {materialType === '任务-视频截图' && ( -
+
{ onSuccess(); @@ -2112,7 +2107,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 任务-页面上传组件 */} {materialType === '任务-页面' && ( -
+
{ onSuccess(); @@ -2131,7 +2126,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 证书-检测报告上传组件 */} {materialType === '证书-检测报告' && ( -
+
{ onSuccess(); @@ -2340,11 +2335,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 品牌VI灵感提示:预览图会自动从PDF提取 */} {materialType === '品牌VI灵感' && ( -
-

- 系统会从 PDF 第一页自动抽出预览图,不用再单独上传预览图或展示图。 -

-
+

未上传预览图时,系统会从 PDF 第一页自动提取。

)} {/* 模特展示图片(仅模特类型显示) */} @@ -2353,6 +2344,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { modelImageUrls={modelImageUrls} onModelImagesChange={handleModelImagesChange} onRemoveModelImage={removeModelImage} + disabled={interactionLocked} /> )} @@ -2413,10 +2405,10 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { )} {/* Material File(有专用上传器的任务类型不显示) */} - {!usesDedicatedUploader && ( + {!usesDedicatedUploader && materialType !== '标准模型' && (