flnxi-Wu/app/edit/[id]/page.tsx
WuYongJie f646b71813
Some checks failed
CI Check / check (push) Has been cancelled
chore: 初始化本地仓库并同步最近改动
2025-11-18 17:50:16 +08:00

413 lines
14 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'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>
)
}