import React, { useState, useRef, useEffect } from 'react'
// Input 组件(复用 Add.jsx 的定义)
const Input = ({ form, handleChange, label, field, type = 'text', options = null }) => {
const onChange = (e) => {
const value = e.target.value
if (type === 'number' && value !== '') {
const num = parseFloat(value)
handleChange(field, isNaN(num) ? '' : num)
} else handleChange(field, value)
}
return (
{label}
{options ? (
) : (
)}
)
}
// 选项定义
const statusOptions = [
{ value: 'in_collection', label: '收藏中' },
{ value: 'selling', label: '出售中' },
{ value: 'sold', label: '已售' },
{ value: 'grading', label: '送评中' },
{ value: 'repairing', label: '修复中' },
{ value: 'transit', label: '在途中' },
{ value: 'seeking', label: '寻号中' },
{ value: 'other', label: '其他' }
]
const categoryOptions = [
{ value: '自持', label: '自持' },
{ value: '寄存', label: '寄存' },
{ value: '寄售', label: '寄售' },
{ value: '共有', label: '共有' },
{ value: '其他', label: '其他' }
]
const rarityOptions = [
{ value: '通货', label: '通货' },
{ value: '特色', label: '特色' },
{ value: '少见', label: '少见' },
{ value: '稀有', label: '稀有' },
{ value: '珍品', label: '珍品' },
{ value: '孤品', label: '孤品' }
]
const packagingOptions = [
{ value: '标十', label: '标十' },
{ value: '标百', label: '标百' },
{ value: '单张', label: '单张' },
{ value: '裸钞', label: '裸钞' }
]
export default function Edit() {
// 从 URL 获取藏品 ID
const params = new URLSearchParams(window.location.hash.split('?')[1] || '')
const editId = params.get('id')
const [form, setForm] = useState({
name: '', code: '', category: '自持', rarity: '通货', prefixSerial: '',
version: '2024 龙', denomination: '', status: 'in_collection', packaging: '单张',
material: '塑料钞', issueQuantity: '1 亿', purpose: '收藏', isGraded: false,
gradingCompany: '', gradingScore: '', threeStar: false, specialMark: '',
serialFeature: '', issuer: '中国人民银行', issueYear: '2024',
costPrice: '', targetPrice: '', goalPrice: '', repairFee: '', gradingFee: '', remark: ''
})
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [error, setError] = useState('')
const [uploadImages, setUploadImages] = useState([])
const fileInputRef = useRef(null)
// 加载现有藏品数据
useEffect(() => {
if (editId) {
const token = localStorage.getItem('token')
fetch(`/api/collections/${editId}`, {
headers: { 'Authorization': 'Bearer ' + token }
})
.then(res => res.json())
.then(data => {
// 填充表单
setForm({
name: data.name || '',
code: data.code || '',
category: data.category || '自持',
rarity: data.rarity || '通货',
prefixSerial: data.prefixSerial || '',
version: data.version || '2024 龙',
denomination: data.denomination || '',
status: data.status || 'in_collection',
packaging: data.packaging || '单张',
isGraded: data.isGraded || false,
gradingCompany: data.gradingCompany || '',
gradingScore: data.gradingScore || '',
threeStar: data.threeStar || false,
specialMark: data.specialMark || '',
serialFeature: data.serialFeature || '',
issuer: data.issuer || '中国人民银行',
issueYear: data.issueYear || '2024',
material: data.material || '塑料钞',
issueQuantity: data.issueQuantity || '1 亿',
costPrice: data.costPrice || '',
targetPrice: data.targetPrice || '',
goalPrice: data.goalPrice || '',
repairFee: data.repairFee || '',
gradingFee: data.gradingFee || '',
purpose: data.purpose || '收藏',
remark: data.remark || ''
})
// 加载图片
if (data.images && Array.isArray(data.images)) {
const images = data.images.map(img => ({
file: null,
preview: `http://120.26.133.10:3000/${img.path || `uploads/collections/${img.filename}`}`,
name: img.original_name || img.filename,
size: 0,
id: img.id
}))
setUploadImages(images)
}
setLoading(false)
})
.catch(err => {
console.error('加载藏品失败:', err)
setError('加载失败:' + err.message)
setLoading(false)
})
} else {
setError('未指定藏品 ID')
setLoading(false)
}
}, [editId])
const handleChange = (key, value) => {
setForm({ ...form, [key]: value })
if (key === 'targetPrice' && value) setForm(prev => ({ ...prev, status: 'sold' }))
// 版别变化时同步发行年份
if (key === 'version') {
const yearMatch = value.match(/(20\d{2})/)
if (yearMatch) {
setForm(prev => ({ ...prev, issueYear: yearMatch[1] }))
}
}
}
// 图片上传处理
const handleImageUpload = async (e) => {
const files = Array.from(e.target.files)
if (files.length === 0 || !editId) return
const token = localStorage.getItem('token')
const file = files[0] // 只允许上传 1 张
const imgFormData = new FormData()
imgFormData.append('file', file)
try {
const res = await fetch(`/api/collections/upload-image?collection_id=${editId}`, {
method: 'POST',
headers: { 'Authorization': 'Bearer ' + token },
body: imgFormData
})
if (res.ok) {
// 重新加载藏品数据
const dataRes = await fetch(`/api/collections/${editId}`, {
headers: { 'Authorization': 'Bearer ' + token }
})
const data = await dataRes.json()
if (data.images && Array.isArray(data.images)) {
const images = data.images.map(img => ({
file: null,
preview: `http://120.26.133.10:3000/${img.path || `uploads/collections/${img.filename}`}`,
name: img.original_name || img.filename,
size: 0,
id: img.id
}))
setUploadImages(images)
}
alert('图片上传成功!')
} else {
alert('图片上传失败')
}
} catch (err) {
console.error('图片上传失败:', err)
alert('图片上传失败:' + err.message)
}
}
const handleSave = async () => {
if (!form.name || !form.version) { setError('名称和版别为必填项'); return }
setSaving(true)
setError('')
const token = localStorage.getItem('token')
const formData = convertField(form)
try {
// 1. 更新藏品信息
const res = await fetch(`/api/collections/${editId}`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify(formData)
})
if (!res.ok) {
const data = await res.json()
throw new Error(data.error?.message || data.detail || '更新失败')
}
alert('更新成功!')
window.location.hash = '#/list'
window.refreshList?.()
} catch (e) {
console.error('更新失败:', e)
alert('更新失败:' + e.message)
}
finally { setSaving(false) }
}
// 字段转换函数
const convertField = (obj) => {
const map = {
id: 'f99_90_id', userId: 'f99_91_user_id',
name: 'f01_01_name', code: 'f01_02_code', category: 'f01_03_category',
status: 'f01_04_status', remark: 'f01_05_remark',
prefixSerial: 'f02_10_prefix_serial', version: 'f02_11_version',
packaging: 'f02_12_packaging', rarity: 'f02_13_rarity',
isGraded: 'f03_20_is_graded', gradingCompany: 'f03_21_grading_company',
gradingScore: 'f03_22_grading_score', threeStar: 'f03_23_three_star',
specialMark: 'f04_30_special_mark', serialFeature: 'f04_31_serial_feature',
issuer: 'f04_32_issuer', issueYear: 'f04_33_issue_year',
material: 'f04_34_material', denomination: 'f04_35_denomination',
issueQuantity: 'f04_36_issue_quantity',
costPrice: 'f05_40_cost_price', targetPrice: 'f05_41_target_price',
goalPrice: 'f05_42_goal_price', repairFee: 'f05_43_repair_fee',
gradingFee: 'f05_44_grading_fee', purpose: 'f06_50_purpose'
}
const result = {}
const numberFields = ['costPrice', 'targetPrice', 'goalPrice', 'repairFee', 'gradingFee']
for (const key in obj) {
let value = obj[key]
if (value === '' || value === null) value = null
else if (numberFields.includes(key)) {
value = parseFloat(value)
if (isNaN(value)) value = null
}
result[map[key] || key] = value
}
return result
}
if (loading) {
return (
)
}
return (
{/* 顶部标题 */}
{error && (
⚠️ {error}
)}
{/* 藏品图片 */}
藏品图片 ({uploadImages.length}/1)
{uploadImages.length === 0 && (
)}
{/* 隐藏的文件输入框 */}
{uploadImages.length > 0 ? (
{uploadImages.map((img, index) => (
{index + 1}/{uploadImages.length}
))}
) : (
📷 暂无图片
)}
{/* 基本信息 */}
{/* 评级信息 */}
{/* 特殊信息 */}
{/* 价格信息 */}
{/* 备注 */}
{/* 按钮 */}
)
}