'use client' import { useState, useRef, useEffect, useCallback } from 'react' import Image from 'next/image' import Modal from '@/components/ui/Modal' import { useThemeStyles, useTheme, cn } from '@/lib/ui-styles' interface EditMaterialModalProps { material: { id: string previewUrl: string productModel: string categoryName: string sceneName: string materialType: string renderer: string authorName: string brandName?: string | null seriesName?: string | null skuCode?: string | null materialTexture?: string | null renderTime?: string | null imageResolution?: string | null colorMode?: string | null templateId?: string | null platformName?: string | null // 音频相关字段 audioDuration?: string | null audioCategory?: string | null audioScene?: string | null } onClose: () => void onSuccess: () => void isOpen?: boolean } type UploadTemplateOption = { id: string name: string description: string | null materialType?: string fields: any[] isBuiltin?: boolean } export default function EditMaterialModal({ material, onClose, onSuccess, isOpen = true }: EditMaterialModalProps) { const styles = useThemeStyles() const theme = useTheme() const [previewImage, setPreviewImage] = useState(null) const [previewUrl, setPreviewUrl] = useState(material.previewUrl) const [templateFieldValues, setTemplateFieldValues] = useState>({}) const [selectedTemplate, setSelectedTemplate] = useState(null) const [templateCategoryOptions, setTemplateCategoryOptions] = useState>({}) const [optionsLoading, setOptionsLoading] = useState(true) const [updating, setUpdating] = useState(false) const [error, setError] = useState('') const [success, setSuccess] = useState('') const previewInputRef = useRef(null) // 初始化字段值 useEffect(() => { const initialValues: Record = { productModel: material.productModel, brandName: material.brandName || '', seriesName: material.seriesName || '', skuCode: material.skuCode || '', materialTexture: material.materialTexture || '', categoryName: material.categoryName || '', sceneName: material.sceneName || '', platformName: material.platformName || '', materialType: material.materialType || '3DMAX', renderer: material.renderer || 'CR', renderTime: material.renderTime || '', imageResolution: material.imageResolution || '', colorMode: material.colorMode || '', authorName: material.authorName || '', // 音频相关字段 audioDuration: material.audioDuration || '', audioCategory: material.audioCategory || '', audioScene: material.audioScene || '', } setTemplateFieldValues(initialValues) }, [material]) // 加载模板和分类选项 useEffect(() => { const loadTemplateAndOptions = async () => { setOptionsLoading(true) try { // 加载模板分类选项 const categoriesResponse = await fetch('/api/template-categories', { cache: 'no-store' }) if (categoriesResponse.ok) { const categoriesData = await categoriesResponse.json() const map = Array.isArray(categoriesData) ? categoriesData.reduce>((acc, field: any) => { if (field?.fieldKey && Array.isArray(field.options)) { acc[field.fieldKey] = field.options } return acc }, {}) : {} setTemplateCategoryOptions(map) } // 如果有templateId,加载模板 if (material.templateId) { const templateResponse = await fetch(`/api/admin/upload-templates/${material.templateId}`) if (templateResponse.ok) { const templateData = await templateResponse.json() setSelectedTemplate({ id: templateData.id, name: templateData.name, description: templateData.description, materialType: templateData.materialType, fields: templateData.fields || [], isBuiltin: templateData.isBuiltin, }) } } else { // 如果没有templateId,尝试根据materialType查找匹配的模板 const templatesResponse = await fetch('/api/upload-templates') if (templatesResponse.ok) { const templatesData = await templatesResponse.json() const templates = Array.isArray(templatesData) ? templatesData : [] const matchingTemplate = templates.find( (t: any) => t.materialType === material.materialType && t.status === 'active' ) if (matchingTemplate) { setSelectedTemplate({ id: matchingTemplate.id, name: matchingTemplate.name, description: matchingTemplate.description, materialType: matchingTemplate.materialType, fields: matchingTemplate.fields || [], isBuiltin: matchingTemplate.isBuiltin, }) } } } } catch (error) { console.error('[EditMaterialModal] Failed to load template:', error) } finally { setOptionsLoading(false) } } loadTemplateAndOptions() }, [material.templateId, material.materialType]) const handlePreviewChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0] if (file) { if (!file.type.startsWith('image/')) { setError('请选择图片文件') return } setPreviewImage(file) setPreviewUrl(URL.createObjectURL(file)) setError('') } } const handleSubmit = async (e: React.FormEvent) => { e.preventDefault() setError('') setSuccess('') // 验证必填字段 const requiredFields = selectedTemplate?.fields.filter((f: any) => f.required) || [] for (const field of requiredFields) { const value = templateFieldValues[field.key] if (!value || (typeof value === 'string' && value.trim() === '')) { setError(`请填写必填字段:${field.label}`) return } } setUpdating(true) try { const formData = new FormData() formData.append('id', material.id) // 添加所有模板字段值 Object.entries(templateFieldValues).forEach(([key, value]) => { if (value !== null && value !== undefined) { formData.append(key, String(value)) } }) if (previewImage) { formData.append('previewImage', previewImage) } const response = await fetch('/api/materials/update', { method: 'PUT', body: formData, }) if (response.ok) { setSuccess('素材信息已更新') setTimeout(() => { onSuccess() onClose() }, 1000) } else { const data = await response.json() setError(data.error || '更新失败') } } catch (error) { console.error('Update error:', error) setError('更新失败,请稍后再试') } finally { setUpdating(false) } } // 渲染模板字段(和UploadModal相同的逻辑) const renderTemplateField = (field: any) => { const fieldValue = templateFieldValues[field.key] || '' const isHalfWidth = field.layoutWidth === 'half' return (
{field.required && ( 必填 )}
{field.type === 'text' || field.type === 'uppercase' ? ( { const value = field.type === 'uppercase' ? e.target.value.toUpperCase() : e.target.value setTemplateFieldValues({ ...templateFieldValues, [field.key]: value, }) }} required={field.required} placeholder={ field.key === 'imageResolution' ? '例如:5000 * 5000' : `请输入${field.label || field.key}` } className={cn( 'w-full px-4 py-2.5 text-sm rounded-2xl outline-none transition-all duration-300 ease-[cubic-bezier(0.4,0,0.2,1)] border bg-white/90 dark:bg-slate-800/80 dark:text-white border-slate-200/70 dark:border-slate-700/60 shadow-[inset_0_1px_0_rgba(255,255,255,0.75)] dark:shadow-none focus-visible:ring-2 focus-visible:ring-blue-400/60 focus-visible:border-blue-400/60', field.required ? 'bg-gradient-to-br from-white via-blue-50/90 to-blue-100/60 dark:from-slate-800/80 dark:via-blue-900/30 dark:to-blue-900/20 border-blue-200/80 dark:border-blue-500/50 shadow-[0_18px_35px_-25px_rgba(37,99,235,1)]' : 'bg-white/90 dark:bg-slate-800/80', field.type === 'uppercase' && 'uppercase' )} /> ) : field.type === 'textarea' ? (