413 lines
14 KiB
TypeScript
413 lines
14 KiB
TypeScript
'use client'
|
||
|
||
import { useEffect, useState } from 'react'
|
||
import { useSession } from 'next-auth/react'
|
||
import { useRouter } from 'next/navigation'
|
||
import Header from '@/components/Header'
|
||
|
||
interface Material {
|
||
id: string
|
||
userId: string
|
||
productModel: string
|
||
brandName?: string | null
|
||
seriesName?: string | null
|
||
skuCode?: string | null
|
||
materialTexture?: string | null
|
||
categoryName: string
|
||
sceneName: string
|
||
materialType: string
|
||
renderer: string
|
||
renderTime?: string | null
|
||
imageResolution?: string | null
|
||
colorMode?: string | null
|
||
authorName: string
|
||
}
|
||
|
||
export default function EditMaterialPage({ params }: { params: { id: string } }) {
|
||
const { data: session, status } = useSession()
|
||
const router = useRouter()
|
||
const [loading, setLoading] = useState(true)
|
||
const [submitting, setSubmitting] = useState(false)
|
||
const [error, setError] = useState('')
|
||
const [success, setSuccess] = useState('')
|
||
|
||
const [material, setMaterial] = useState<Material | null>(null)
|
||
const [productModel, setProductModel] = useState('')
|
||
const [brandName, setBrandName] = useState('')
|
||
const [seriesName, setSeriesName] = useState('')
|
||
const [skuCode, setSkuCode] = useState('')
|
||
const [materialTexture, setMaterialTexture] = useState('')
|
||
const [categoryName, setCategoryName] = useState('')
|
||
const [sceneName, setSceneName] = useState('')
|
||
const [materialType, setMaterialType] = useState('')
|
||
const [renderer, setRenderer] = useState('')
|
||
const [renderTime, setRenderTime] = useState('')
|
||
const [imageResolution, setImageResolution] = useState('')
|
||
const [colorMode, setColorMode] = useState('')
|
||
const [authorName, setAuthorName] = useState('')
|
||
|
||
const [categories, setCategories] = useState<Array<{ id: string; name: string }>>([])
|
||
const [scenes, setScenes] = useState<Array<{ id: string; name: string }>>([])
|
||
|
||
const materialTypes = [
|
||
'3DMAX', 'PSD', 'AI', 'CAD', 'C4D', '材质', '模特', '标准饰品', '其他'
|
||
]
|
||
|
||
useEffect(() => {
|
||
if (status === 'unauthenticated') {
|
||
router.push('/login')
|
||
return
|
||
}
|
||
|
||
if (status === 'authenticated') {
|
||
fetchMaterial()
|
||
fetchCategories()
|
||
fetchScenes()
|
||
}
|
||
}, [status, params.id])
|
||
|
||
const fetchMaterial = async () => {
|
||
try {
|
||
const response = await fetch(`/api/materials/${params.id}`)
|
||
if (!response.ok) {
|
||
throw new Error('获取素材信息失败')
|
||
}
|
||
const data = await response.json()
|
||
|
||
// 权限检查
|
||
if (data.userId !== (session?.user as any)?.id && (session?.user as any)?.role !== 'ADMIN') {
|
||
setError('您没有权限编辑此素材')
|
||
setTimeout(() => router.push('/'), 2000)
|
||
return
|
||
}
|
||
|
||
setMaterial(data)
|
||
setProductModel(data.productModel || '')
|
||
setBrandName(data.brandName || '')
|
||
setSeriesName(data.seriesName || '')
|
||
setSkuCode(data.skuCode || '')
|
||
setMaterialTexture(data.materialTexture || '')
|
||
setCategoryName(data.categoryName || '')
|
||
setSceneName(data.sceneName || '')
|
||
setMaterialType(data.materialType || '')
|
||
setRenderer(data.renderer || '')
|
||
setRenderTime(data.renderTime || '')
|
||
setImageResolution(data.imageResolution || '')
|
||
setColorMode(data.colorMode || '')
|
||
setAuthorName(data.authorName || '')
|
||
setLoading(false)
|
||
} catch (error: any) {
|
||
console.error('获取素材失败:', error)
|
||
setError(error.message || '获取素材信息失败')
|
||
setLoading(false)
|
||
}
|
||
}
|
||
|
||
const fetchCategories = async () => {
|
||
try {
|
||
const response = await fetch('/api/categories')
|
||
if (response.ok) {
|
||
const data = await response.json()
|
||
setCategories(data)
|
||
}
|
||
} catch (error) {
|
||
console.error('获取分类失败:', error)
|
||
}
|
||
}
|
||
|
||
const fetchScenes = async () => {
|
||
try {
|
||
const response = await fetch('/api/scenes')
|
||
if (response.ok) {
|
||
const data = await response.json()
|
||
setScenes(data)
|
||
}
|
||
} catch (error) {
|
||
console.error('获取场景失败:', error)
|
||
}
|
||
}
|
||
|
||
const handleSubmit = async (e: React.FormEvent) => {
|
||
e.preventDefault()
|
||
setError('')
|
||
setSuccess('')
|
||
|
||
if (!productModel || !categoryName || !sceneName || !materialType || !renderer || !authorName) {
|
||
setError('请填写所有必填字段')
|
||
return
|
||
}
|
||
|
||
setSubmitting(true)
|
||
|
||
try {
|
||
const response = await fetch(`/api/materials/${params.id}`, {
|
||
method: 'PUT',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
body: JSON.stringify({
|
||
productModel,
|
||
brandName,
|
||
seriesName,
|
||
skuCode,
|
||
materialTexture,
|
||
categoryName,
|
||
sceneName,
|
||
materialType,
|
||
renderer,
|
||
renderTime,
|
||
imageResolution,
|
||
colorMode,
|
||
authorName
|
||
})
|
||
})
|
||
|
||
const data = await response.json()
|
||
|
||
if (!response.ok) {
|
||
throw new Error(data.error || '更新失败')
|
||
}
|
||
|
||
setSuccess('素材信息更新成功!')
|
||
setTimeout(() => {
|
||
router.push('/')
|
||
}, 1500)
|
||
} catch (error: any) {
|
||
console.error('更新失败:', error)
|
||
setError(error.message || '更新失败,请稍后重试')
|
||
} finally {
|
||
setSubmitting(false)
|
||
}
|
||
}
|
||
|
||
if (status === 'loading' || loading) {
|
||
return (
|
||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
||
<Header session={session} />
|
||
<div className="max-w-2xl mx-auto px-4 py-8">
|
||
<div className="text-center text-gray-600 dark:text-gray-400">加载中...</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
return (
|
||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900">
|
||
<Header session={session} />
|
||
<div className="max-w-2xl mx-auto px-4 py-8">
|
||
<div className="bg-white dark:bg-gray-800 rounded-2xl shadow-lg p-8">
|
||
<h1 className="text-2xl font-bold text-gray-900 dark:text-white mb-6">编辑素材信息</h1>
|
||
|
||
{error && (
|
||
<div className="mb-4 p-4 bg-red-50 border border-red-200 text-red-600 rounded-lg">
|
||
{error}
|
||
</div>
|
||
)}
|
||
|
||
{success && (
|
||
<div className="mb-4 p-4 bg-green-50 border border-green-200 text-green-600 rounded-lg">
|
||
{success}
|
||
</div>
|
||
)}
|
||
|
||
<form onSubmit={handleSubmit} className="space-y-6">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||
产品型号 *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={productModel}
|
||
onChange={(e) => setProductModel(e.target.value)}
|
||
className="input-apple"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<p className="text-xs uppercase tracking-wide text-gray-400 dark:text-gray-500 mb-2">商品 / 品牌信息</p>
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">品牌名称</label>
|
||
<input
|
||
type="text"
|
||
value={brandName}
|
||
onChange={(e) => setBrandName(e.target.value)}
|
||
className="input-apple"
|
||
placeholder="例如:Apple、小米"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">系列 / 产品线</label>
|
||
<input
|
||
type="text"
|
||
value={seriesName}
|
||
onChange={(e) => setSeriesName(e.target.value)}
|
||
className="input-apple"
|
||
placeholder="例如:iPhone 15 Pro"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">SKU 编码</label>
|
||
<input
|
||
type="text"
|
||
value={skuCode}
|
||
onChange={(e) => setSkuCode(e.target.value)}
|
||
className="input-apple"
|
||
placeholder="例如:SKU-0001"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">材质 / 工艺</label>
|
||
<input
|
||
type="text"
|
||
value={materialTexture}
|
||
onChange={(e) => setMaterialTexture(e.target.value)}
|
||
className="input-apple"
|
||
placeholder="例如:钛金属、皮革、磨砂玻璃等"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<p className="text-xs uppercase tracking-wide text-gray-400 dark:text-gray-500 mb-2">渲染 / 制作信息</p>
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">渲染耗时</label>
|
||
<input
|
||
type="text"
|
||
value={renderTime}
|
||
onChange={(e) => setRenderTime(e.target.value)}
|
||
className="input-apple"
|
||
placeholder="例如:2 小时 / 120 分钟"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">输出分辨率</label>
|
||
<input
|
||
type="text"
|
||
value={imageResolution}
|
||
onChange={(e) => setImageResolution(e.target.value)}
|
||
className="input-apple"
|
||
placeholder="例如:5000 x 5000"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">颜色模式</label>
|
||
<select
|
||
value={colorMode}
|
||
onChange={(e) => setColorMode(e.target.value)}
|
||
className="input-apple"
|
||
>
|
||
<option value="">请选择</option>
|
||
<option value="RGB">RGB</option>
|
||
<option value="CMYK">CMYK</option>
|
||
<option value="LAB">Lab</option>
|
||
<option value="其他">其他</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||
分类 *
|
||
</label>
|
||
<select
|
||
value={categoryName}
|
||
onChange={(e) => setCategoryName(e.target.value)}
|
||
className="input-apple"
|
||
required
|
||
>
|
||
<option value="">请选择分类</option>
|
||
{categories.map((cat) => (
|
||
<option key={cat.id} value={cat.name}>{cat.name}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||
场景 *
|
||
</label>
|
||
<select
|
||
value={sceneName}
|
||
onChange={(e) => setSceneName(e.target.value)}
|
||
className="input-apple"
|
||
required
|
||
>
|
||
<option value="">请选择场景</option>
|
||
{scenes.map((scene) => (
|
||
<option key={scene.id} value={scene.name}>{scene.name}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||
素材类型 *
|
||
</label>
|
||
<select
|
||
value={materialType}
|
||
onChange={(e) => setMaterialType(e.target.value)}
|
||
className="input-apple"
|
||
required
|
||
>
|
||
<option value="">请选择素材类型</option>
|
||
{materialTypes.map((type) => (
|
||
<option key={type} value={type}>{type}</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||
渲染器 *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={renderer}
|
||
onChange={(e) => setRenderer(e.target.value)}
|
||
className="input-apple"
|
||
placeholder="例如: V-Ray, Corona, Arnold"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
||
作者 *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
value={authorName}
|
||
onChange={(e) => setAuthorName(e.target.value)}
|
||
className="input-apple"
|
||
required
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex gap-4 pt-4">
|
||
<button
|
||
type="button"
|
||
onClick={() => router.back()}
|
||
className="flex-1 px-6 py-3 border border-gray-300 dark:border-gray-600 rounded-apple font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors"
|
||
disabled={submitting}
|
||
>
|
||
取消
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="flex-1 btn-primary"
|
||
disabled={submitting}
|
||
>
|
||
{submitting ? '保存中...' : '保存修改'}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|