diff --git a/app/api/materials/upload/route.ts b/app/api/materials/upload/route.ts index 743a925f..a14fcbb7 100644 --- a/app/api/materials/upload/route.ts +++ b/app/api/materials/upload/route.ts @@ -483,9 +483,7 @@ export async function POST(request: NextRequest) { const rawTaskNo = (formData.get('taskNo') as string) || ''; const taskNo = isTaskOtherMaterial ? rawTaskNo.trim().toUpperCase() : rawTaskNo; const rawDingtalkApprovalNo = (formData.get('dingtalkApprovalNo') as string) || ''; - const dingtalkApprovalNo = isTaskMaterial - ? normalizeDingtalkApprovalNo(rawDingtalkApprovalNo) - : null; + const dingtalkApprovalNo = normalizeDingtalkApprovalNo(rawDingtalkApprovalNo); const rawProductModel = productModelInput || (isTaskOtherMaterial ? taskNo : ''); const productModel = VIDEO_TYPES.has(materialType) ? normalizeSpuProductModel(rawProductModel) @@ -653,13 +651,6 @@ export async function POST(request: NextRequest) { ); } - if (!isTaskMaterial && rawDingtalkApprovalNo.trim()) { - return NextResponse.json( - { error: '钉钉审批编号仅适用于“任务-”素材' }, - { status: 400 } - ); - } - const dingtalkApprovalNoError = getDingtalkApprovalNoError(dingtalkApprovalNo); if (dingtalkApprovalNoError) { return NextResponse.json( @@ -1200,6 +1191,7 @@ export async function POST(request: NextRequest) { videoSourceUrl, // 视频类型时保存视频URL videoCoverUrl, videoProcessingStatus: videoSourceUrl ? 'pending' : null, // 视频类型时初始化为pending状态 + dingtalkApprovalNo, // taskNo: 不写入,上传完成后再写入 // taskStatus: 不写入,上传完成后再写入 userId: (session.user as any).id, diff --git a/components/UploadModal.task-other.test.tsx b/components/UploadModal.task-other.test.tsx index 6fffce18..167e46c9 100644 --- a/components/UploadModal.task-other.test.tsx +++ b/components/UploadModal.task-other.test.tsx @@ -51,6 +51,14 @@ describe('UploadModal - 任务-其他', () => { }) as any }) + test('3D场景公共上传面板应显示任务号与钉钉审批编号', async () => { + render() + + expect(await screen.findByLabelText('任务号')).toBeInTheDocument() + expect(screen.getByLabelText('钉钉审批编号')).toBeInTheDocument() + expect(screen.getByLabelText('素材类型 *')).toHaveValue('3D场景') + }) + test('任务-其他应使用公共上传面板形式,任务号与钉钉审批编号并列显示', async () => { const user = userEvent.setup() diff --git a/components/UploadModal.tsx b/components/UploadModal.tsx index b67b2afa..785414ce 100644 --- a/components/UploadModal.tsx +++ b/components/UploadModal.tsx @@ -69,6 +69,15 @@ import { validateImageFiles, } from '@/components/upload/drag-drop-utils' import { getDingtalkApprovalNoError } from '@/lib/dingtalk-approval-no' +import { + dropZoneActiveClass, + dropZoneIdleClass, + fieldControlClass, + fieldHintClass, + fieldLabelClass, + inkButtonClass, + outlineButtonClass, +} from '@/components/upload/upload-field-styles' import { getSpuProductModelValidationError, isPlausibleSpuProductModel, @@ -197,7 +206,6 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { const [retryCount, setRetryCount] = useState(0) const isTaskOtherMaterial = materialType === '任务-其他' - const isTaskMaterial = materialType.startsWith('任务-') const usesDedicatedUploader = SPECIAL_UPLOAD_TYPES.includes(materialType as typeof SPECIAL_UPLOAD_TYPES[number]) const interactionLocked = uploading || previewProcessingCount > 0 @@ -1646,12 +1654,10 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { return } - if (isTaskMaterial) { - const dingtalkApprovalNoError = getDingtalkApprovalNoError(dingtalkApprovalNo) - if (dingtalkApprovalNoError) { - setError(dingtalkApprovalNoError) - return - } + const dingtalkApprovalNoError = getDingtalkApprovalNoError(dingtalkApprovalNo) + if (dingtalkApprovalNoError) { + setError(dingtalkApprovalNoError) + return } // 任务号为空时仅提醒,不阻止上传(已在UI中显示提醒) @@ -1762,7 +1768,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { productCategoryName, renderer: resolvedRenderer, taskNo: resolvedTaskNo, - dingtalkApprovalNo: isTaskMaterial ? dingtalkApprovalNo.trim() : '', + dingtalkApprovalNo: dingtalkApprovalNo.trim(), completedChunkUpload: chunkResult || undefined, }) @@ -1887,52 +1893,55 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { } return ( -
-
+
- -
-
-

上传素材

+
+
+
+

+ 上传素材 +

+

+ 把文件拖进虚线框即可。内网请先切到顶部提示里的内网地址,上传会更快。 +

+
-
-

- 💡 提示:可拖拽文件到对应的虚线框内上传 -

-

- 提示:如在内网环境,请通过顶部提示切换到内网地址以获得更快速度 -

-
-
- {/* 素材类型 - 第一栏,让用户先选择类型 */} +
-
- {/* 专用上传器会在自己的表单内渲染任务关联字段。 */} - {!usesDedicatedUploader && isTaskMaterial && ( + {!usesDedicatedUploader && ( )} - {!usesDedicatedUploader && !isTaskMaterial && ( -
- - setTaskNo(event.target.value)} - placeholder="例如:TASK202601030184" - className="w-full rounded-apple border border-gray-300 px-4 py-2 outline-none focus:border-gray-900 focus:ring-2 focus:ring-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:focus:border-gray-400 dark:focus:ring-gray-600" - disabled={interactionLocked} - aria-invalid={Boolean(taskNo && !validateTaskNo(taskNo.trim().toUpperCase()))} - /> - {taskNo && !validateTaskNo(taskNo.trim().toUpperCase()) && ( -

任务号格式错误

- )} -
- )} - {/* 任务-详情上传组件 */} {materialType === '任务-详情' && ( -
+
{ onSuccess(); @@ -1997,7 +1984,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 任务-视频截图上传组件 */} {materialType === '任务-视频截图' && ( -
+
{ onSuccess(); @@ -2016,7 +2003,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 任务-页面上传组件 */} {materialType === '任务-页面' && ( -
+
{ onSuccess(); @@ -2035,7 +2022,7 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 证书-检测报告上传组件 */} {materialType === '证书-检测报告' && ( -
+
{ onSuccess(); @@ -2244,9 +2231,9 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* 品牌VI灵感提示:预览图会自动从PDF提取 */} {materialType === '品牌VI灵感' && ( -
-

- 💡 提示:系统会自动从PDF文件的第一页提取预览图,无需手动上传预览图或展示图。 +

+

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

)} @@ -2311,37 +2298,36 @@ export default function UploadModal({ onClose, onSuccess }: UploadModalProps) { {/* Material File(有专用上传器的任务类型不显示) */} {!usesDedicatedUploader && (
-