import React, { useState, useEffect, useRef } from 'react' // 通用 Input 组件 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 ? ( ) : ( )}
) } export default function Edit() { const params = new URLSearchParams(window.location.hash.split('?')[1] || '') const id = params.get('id') const [collection, setCollection] = useState(null) const [loading, setLoading] = useState(true) const [saving, setSaving] = useState(false) const [form, setForm] = useState({}) const [showDelete, setShowDelete] = useState(false) const [collectionImages, setCollectionImages] = useState([]) const fileInputRef = useRef(null) const [editingImageIndex, setEditingImageIndex] = useState(-1) useEffect(() => { if (id) { fetchDetail() } }, [id]) // 图片上传处理 const handleImageUpload = async (e) => { const files = Array.from(e.target.files) if (files.length === 0) return const token = localStorage.getItem('token') const maxImages = 3 // 检查是否超过限制 const currentCount = collection.images ? collection.images.length : 0 if (editingImageIndex === -1 && currentCount + files.length > maxImages) { alert(`最多只能上传${maxImages}张图片`) return } // 更换图片(点击已有图片) if (editingImageIndex >= 0) { const file = files[0] const oldImage = collection.images[editingImageIndex] const imgFormData = new FormData() imgFormData.append('file', file) try { // 先上传新图片 const uploadRes = await fetch(`/api/collections/upload-image?collection_id=${id}`, { method: 'POST', headers: { 'Authorization': 'Bearer ' + token }, body: imgFormData }) if (uploadRes.ok) { // 删除旧图片 await fetch(`/api/collections/images/${oldImage.id}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + token } }) // 重新加载详情 await fetchDetail() alert('图片已更换') } } catch (err) { console.error('图片更换失败:', err) alert('更换失败,请重试') } setEditingImageIndex(-1) return } // 添加新图片 for (const file of files) { const imgFormData = new FormData() imgFormData.append('file', file) try { const res = await fetch(`/api/collections/upload-image?collection_id=${id}`, { method: 'POST', headers: { 'Authorization': 'Bearer ' + token }, body: imgFormData }) if (res.ok) { // 重新加载藏品详情 await fetchDetail() } } catch (err) { console.error('图片上传失败:', err) } } } const handleDeleteImage = async (imageId) => { if (!confirm('确定删除这张图片吗?')) return const token = localStorage.getItem('token') try { const res = await fetch(`/api/collections/images/${imageId}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + token } }) if (res.ok) { await fetchDetail() } } catch (err) { console.error('图片删除失败:', err) } } const fetchDetail = async () => { setLoading(true) const token = localStorage.getItem('token') try { const res = await fetch(`/api/collections/${id}`, { headers: token ? { 'Authorization': 'Bearer ' + token } : {} }) const data = await res.json() const item = (data.code === 200 ? data.data : data) if (item && item.id) { setCollection(item) // 设置特殊信息默认值 const yearMatch = item.version ? item.version.match(/(20\d{2})/) : null const defaultForm = { ...item, issuer: item.issuer || '中国人民银行', issueYear: item.issueYear || (yearMatch ? yearMatch[1] : '2024'), material: item.material || '塑料钞', issueQuantity: item.issueQuantity || '1 亿' } setForm(defaultForm) // 加载图片 if (item.images && Array.isArray(item.images)) { setCollectionImages(item.images) } } } catch (e) { console.error('加载详情失败:', e) } setLoading(false) } 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 handleSave = async () => { setSaving(true) setError('') const token = localStorage.getItem('token') // 1. 先检查是否重复(同版别、同冠字号) if (form.prefixSerial) { const checkRes = await fetch(`/api/collections?search=${encodeURIComponent(form.prefixSerial)}`, { headers: { 'Authorization': 'Bearer ' + token } }) const checkData = await checkRes.json() const collections = checkData.data || checkData || [] const duplicate = collections.find(c => c.version === form.version && c.prefixSerial === form.prefixSerial && c.id !== id // 排除自己 ) if (duplicate) { setError('已经录入过同版同号藏品!') setSaving(false) return } } // 转换字段名:camelCase 转字段编码 const convertField = (obj) => { const map = { // f99 系统字段 id: 'f99_90_id', userId: 'f99_91_user_id', createdAt: 'f99_92_created_at', updatedAt: 'f99_93_updated_at', // f01 基本信息 name: 'f01_01_name', code: 'f01_02_code', category: 'f01_03_category', status: 'f01_04_status', remark: 'f01_05_remark', // f02 详细字段 prefixSerial: 'f02_10_prefix_serial', version: 'f02_11_version', packaging: 'f02_12_packaging', rarity: 'f02_13_rarity', // f03 评级信息 isGraded: 'f03_20_is_graded', gradingCompany: 'f03_21_grading_company', gradingScore: 'f03_22_grading_score', threeStar: 'f03_23_three_star', // f04 特殊信息 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', // f05 价格信息 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', // f06 其他信息 purpose: 'f06_50_purpose' } const result = {} // 数字字段列表 const numberFields = ['costPrice', 'targetPrice', 'goalPrice', 'repairFee', 'gradingFee'] for (const key in obj) { let value = obj[key] // 过滤空字符串为 null 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 } try { const res = await fetch(`/api/collections/${id}`, { method: 'PUT', headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token }, body: JSON.stringify(convertField(form)) }) if (res.ok) { alert('保存成功!') // 刷新首页统计数据 if (window.refreshHome) { window.refreshHome() } window.location.hash = '#/detail?id=' + id } else { const data = await res.json() alert('保存失败: ' + (data.error || '未知错误')) } } catch (e) { alert('保存失败: ' + e.message) } setSaving(false) } const handleDelete = () => { setShowDelete(true) } const doDelete = async () => { const token = localStorage.getItem('token') try { const res = await fetch(`/api/collections/${id}`, { method: 'DELETE', headers: { 'Authorization': 'Bearer ' + token } }) if (res.ok) { alert('删除成功!') if (window.refreshList) window.refreshList() window.location.hash = '#/list' } else { alert('删除失败') } } catch (e) { alert('删除失败') } setShowDelete(false) } const goBack = () => { window.location.hash = '#/detail?id=' + id } const statusOptions = [ { value: 'in_collection', label: '收藏中' }, { value: 'selling', label: '出售中' }, { value: 'sold', label: '已售' }, { value: 'grading', label: '送评中' }, { value: 'repairing', label: '修复中' }, { value: 'transit', label: '在途中' }, { value: 'other', label: '其他' } ] const categoryOptions = [ { value: '自持', label: '自持' }, { value: '寄存', label: '寄存' }, { value: '寄售', label: '寄售' }, { value: '共有', label: '共有' }, { value: '寻号', label: '寻号' }, { value: '其他', label: '其他' } ] const rarityOptions = [ { value: '通货', label: '通货' }, { value: '特色', label: '特色' }, { value: '少见', label: '少见' }, { value: '稀有', label: '稀有' }, { value: '珍品', label: '珍品' }, { value: '孤品', label: '孤品' } ] if (loading) { return
加载中...
} return (
{/* 顶部 */}
编辑藏品
{/* 图片预览区域 */}
藏品图片
{collectionImages.length > 0 && ( )}
{collectionImages.length > 0 ? (
{collectionImages.map((img, index) => (
{img.original_name
{index + 1}/{collectionImages.length}
))}
) : (
fileInputRef.current?.click()} style={{ aspectRatio: '1.5', background: 'rgba(255,255,255,0.05)', border: '2px dashed #fbbf24', borderRadius: '12px', display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', cursor: 'pointer', color: '#fbbf24', fontSize: '12px', transition: 'all 0.2s' }} >
📷
点击上传图片
最多可上传 1 张
)}
{/* 基本信息 */}
基本信息
编号
{form.code || '(保存后自动生成)'}
创建时间
{form.createdAt ? new Date(form.createdAt).toLocaleDateString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit' }).replace(/\//g, '-') : '(保存后自动生成)'}
{/* 评级信息 */}
评级信息
handleChange('isGraded', e.target.checked)} style={{ width: '22px', height: '22px', cursor: 'pointer', backgroundColor: form.isGraded ? '#22c55e' : 'rgba(255,255,255,0.1)', border: '2px solid #22c55e', borderRadius: '4px', appearance: 'none', WebkitAppearance: 'none' }} />
handleChange('threeStar', e.target.checked)} style={{ width: '22px', height: '22px', cursor: 'pointer', backgroundColor: form.threeStar ? '#22c55e' : 'rgba(255,255,255,0.1)', border: '2px solid #22c55e', borderRadius: '4px', appearance: 'none', WebkitAppearance: 'none' }} />
{/* 特殊信息 */}
特殊信息
{/* 价格信息 */}
价格信息
{/* 备注 */}
备注